aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2012-01-19 14:34:33 +0000
committerMatt Fleming <matt.fleming@intel.com>2012-03-08 10:21:34 +0000
commit77148a3e497811ce873124ba848c934d2b12d592 (patch)
tree183cdc6ae912373399812dd455c56496c86964cb
parentca9b08518110583a49ae81019906b52b71bda1c9 (diff)
downloadefilinux-77148a3e497811ce873124ba848c934d2b12d592.tar.gz
efilinux: Minimal configuration file support
efilinux lacks a way of automatically booting a default kernel. Instead, the kernel image path and kernel command line arguments must be passed as arguments to efilinux on every invocation. This commit allows a simple config file to specify a default kernel and kernel arguments, which are passed to efilinux instead of requiring them to be entered at the EFI shell. Now, when efilinux is started it will first search for a file named 'efilinux.cfg' in the same directory as the efilinux executable. The syntax for a configuration file is exactly the same as the syntax for efilinux's command line arguments. For example, to boot a linux kernel the contents of 'efilinux.cfg' would be a single line, "-f 0:\EFI\BOOT\vmlinux console=ttys0 initrd=0:\EFI\BOOT\initrd" The contents of the file are passed to efilinux as though they were typed at the EFI shell prompt. Multiple lines are not supported. Cc: Darrent Hart <dvhart@linux.intel.com> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
-rw-r--r--efilinux.h2
-rw-r--r--entry.c174
-rw-r--r--fs/fs.c20
-rw-r--r--fs/fs.h1
4 files changed, 181 insertions, 16 deletions
diff --git a/efilinux.h b/efilinux.h
index c571e35..70fed1f 100644
--- a/efilinux.h
+++ b/efilinux.h
@@ -45,6 +45,8 @@
#define EFILINUX_VERSION_MAJOR 0
#define EFILINUX_VERSION_MINOR 9
+#define EFILINUX_CONFIG L"efilinux.cfg"
+
extern EFI_SYSTEM_TABLE *sys_table;
extern EFI_BOOT_SERVICES *boot;
extern EFI_RUNTIME_SERVICES *runtime;
diff --git a/entry.c b/entry.c
index 20d263b..a1e7967 100644
--- a/entry.c
+++ b/entry.c
@@ -154,29 +154,30 @@ static EFI_STATUS print_memory_map(void)
return err;
}
+static inline BOOLEAN isspace(CHAR16 ch)
+{
+ return ((unsigned char)ch <= ' ');
+}
+
static EFI_STATUS
parse_args(CHAR16 *options, UINT32 size, CHAR16 **name, char **cmdline)
{
CHAR16 *n, *o, *filename = NULL;
EFI_STATUS err;
- int i;
+ int i = 0;
*cmdline = NULL;
*name = NULL;
- if (!options || size == 0)
- goto fail;
-
- /* Skip the first word, that's our name. */
- for (i = 0; i < size && options[i] != ' '; i++)
- ;
+ /* Skip whitespace */
+ for (i = 0; i < size && isspace(options[i]); i++)
+ ;
/* No arguments */
if (i == size)
goto fail;
- n = &options[++i];
-
+ n = &options[i];
while (n <= &options[size]) {
if (*n == '-') {
switch (*++n) {
@@ -186,12 +187,12 @@ parse_args(CHAR16 *options, UINT32 size, CHAR16 **name, char **cmdline)
n++; /* Skip 'f' */
/* Skip whitespace */
- while (*n == ' ')
+ while (isspace(*n))
n++;
filename = n;
i = 0;
- while (*n && *n != ' ' && *n != '\n') {
+ while (*n && !isspace(*n)) {
i++;
n++;
}
@@ -267,6 +268,132 @@ out:
return err;
}
+static inline BOOLEAN
+get_path(EFI_LOADED_IMAGE *image, CHAR16 *path, UINTN len)
+{
+ CHAR16 *buf, *p, *q;
+ int i, dev;
+
+ dev = handle_to_dev(image->DeviceHandle);
+ if (dev == -1) {
+ Print(L"Couldn't find boot device handle\n");
+ return FALSE;
+ }
+
+ /* Find the path of the efilinux executable*/
+ p = DevicePathToStr(image->FilePath);
+ q = p + StrLen(p);
+
+ i = StrLen(p);
+ while (*q != '\\' && *q != '/') {
+ q--;
+ i--;
+ }
+
+ buf = malloc(i * sizeof(CHAR16));
+ if (!buf) {
+ Print(L"Failed to allocate buf\n");
+ FreePool(p);
+ return FALSE;
+ }
+
+ memcpy((char *)buf, (char *)p, i * sizeof(CHAR16));
+ FreePool(p);
+
+ buf[i] = '\0';
+ SPrint(path, len, L"%d:%s\\%s", dev, buf, EFILINUX_CONFIG);
+
+ return TRUE;
+}
+
+static BOOLEAN
+read_config_file(EFI_LOADED_IMAGE *image, CHAR16 **options,
+ UINT32 *options_size)
+{
+ struct file *file;
+ EFI_STATUS err;
+ CHAR16 path[4096];
+ CHAR16 *u_buf, *q;
+ char *a_buf, *p;
+ UINT64 size;
+ int i;
+
+ err = get_path(image, path, sizeof(path));
+ if (err != TRUE)
+ return FALSE;
+
+ err = file_open(path, &file);
+ if (err != EFI_SUCCESS)
+ return FALSE;
+
+ err = file_size(file, &size);
+ if (err != EFI_SUCCESS)
+ goto fail;
+
+ /*
+ * The config file contains ASCII characters, but the command
+ * line parser expects arguments to be UTF-16. Convert them
+ * once we've read them into 'a_buf'.
+ */
+
+ /* Make sure we don't overflow the UINT32 */
+ if (size > 0xffffffff || (size * 2) > 0xffffffff ) {
+ Print(L"Config file size too large. Ignoring.\n");
+ goto fail;
+ }
+
+ a_buf = malloc((UINTN)size);
+ if (!a_buf) {
+ Print(L"Failed to alloc buffer %d bytes\n", size);
+ goto fail;
+ }
+
+ u_buf = malloc((UINTN)size * 2);
+ if (!u_buf) {
+ Print(L"Failed to alloc buffer %d bytes\n", size);
+ free(a_buf);
+ goto fail;
+ }
+
+ err = file_read(file, (UINTN *)&size, a_buf);
+ if (err != EFI_SUCCESS)
+ goto fail;
+
+ Print(L"Using efilinux config file\n");
+
+ /*
+ * Read one line. Stamp a NUL-byte into the buffer once we've
+ * read the end of the first line.
+ */
+ for (p = a_buf, i = 0; *p && *p != '\n' && i < size; p++, i++)
+ ;
+ if (*p == '\n')
+ *p++ = '\0';
+
+ if (i == size && *p) {
+ Print(L"Error: missing newline at end of config file?\n");
+ goto fail;
+ }
+
+ if ((p - a_buf) < size)
+ Print(L"Warning: config file contains multiple lines?\n");
+
+ p = a_buf;
+ q = u_buf;
+ for (i = 0; i < size; i++)
+ *q++ = *p++;
+ free(a_buf);
+
+ *options = u_buf;
+ *options_size = (UINT32)size * 2;
+
+ file_close(file);
+ return TRUE;
+fail:
+ file_close(file);
+ return FALSE;
+}
+
/**
* efi_main - The entry point for the OS loader image.
* @image: firmware-allocated handle that identifies the image
@@ -278,7 +405,8 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table)
WCHAR *error_buf;
EFI_STATUS err;
EFI_LOADED_IMAGE *info;
- CHAR16 *name;
+ CHAR16 *name, *options;
+ UINT32 options_size;
char *cmdline;
InitializeLib(image, _table);
@@ -299,10 +427,24 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table)
if (err != EFI_SUCCESS)
goto fs_deinit;
- err = parse_args(info->LoadOptions, info->LoadOptionsSize,
- &name, &cmdline);
- if (err != EFI_SUCCESS)
- goto fs_deinit;
+ if (!read_config_file(info, &options, &options_size)) {
+ int i;
+
+ options = info->LoadOptions;
+ options_size = info->LoadOptionsSize;
+
+ /* Skip the first word, that's our name. */
+ for (i = 0; i < options_size && options[i] != ' '; i++)
+ ;
+ options = &options[i];
+ options_size -= i;
+ }
+
+ if (options && options_size != 0) {
+ err = parse_args(options, options_size, &name, &cmdline);
+ if (err != EFI_SUCCESS)
+ goto fs_deinit;
+ }
err = load_image(image, name, cmdline);
if (err != EFI_SUCCESS)
diff --git a/fs/fs.c b/fs/fs.c
index dccf418..1cbed4e 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -48,6 +48,26 @@ static struct fs_device *fs_devices;
static UINTN nr_fs_devices;
/**
+ * handle_to_dev - Return the device number for a handle
+ * @handle: the device handle to search for
+ */
+int
+handle_to_dev(EFI_HANDLE *handle)
+{
+ int i;
+
+ for (i = 0; i < nr_fs_devices; i++) {
+ if (fs_devices[i].handle == handle)
+ break;
+ }
+
+ if (i == nr_fs_devices)
+ return -1;
+
+ return i;
+}
+
+/**
* file_open - Open a file on a volume
* @name: pathname of the file to open
* @file: used to return a pointer to the allocated file on success
diff --git a/fs/fs.h b/fs/fs.h
index 6ffbb84..0f76d0c 100644
--- a/fs/fs.h
+++ b/fs/fs.h
@@ -102,6 +102,7 @@ extern EFI_STATUS file_open(CHAR16 *name, struct file **file);
extern EFI_STATUS file_close(struct file *f);
extern void list_boot_devices(void);
+extern int handle_to_dev(EFI_HANDLE *handle);
extern void fs_close(void);