diff --git a/arch/efi/efi/efi.c b/arch/efi/efi/efi.c index 7de8ec8..c1efe1d 100644 --- a/arch/efi/efi/efi.c +++ b/arch/efi/efi/efi.c @@ -248,7 +248,7 @@ void reset_cpu(unsigned long addr) { - BS->exit(efi_parent_image, EFI_SUCCESS, 0, NULL); + RT->reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL); while(1); } @@ -341,3 +341,19 @@ return EFI_SUCCESS; } + +static int do_efiexit(int argc, char *argv[]) +{ + return BS->exit(efi_parent_image, EFI_SUCCESS, 0, NULL); +} + +BAREBOX_CMD_HELP_START(efiexit) +BAREBOX_CMD_HELP_TEXT("Leave barebox and return to the calling EFI process\n") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(efiexit) + .cmd = do_efiexit, + BAREBOX_CMD_DESC("Usage: efiexit") + BAREBOX_CMD_GROUP(CMD_GRP_MISC) + BAREBOX_CMD_HELP(cmd_efiexit_help) +BAREBOX_CMD_END diff --git a/common/efi-devicepath.c b/common/efi-devicepath.c index 2b1d916..a53c6d2 100644 --- a/common/efi-devicepath.c +++ b/common/efi-devicepath.c @@ -1368,3 +1368,18 @@ return str.str; } + +u8 device_path_to_type(struct efi_device_path *dev_path) +{ + struct efi_device_path *dev_path_next; + + dev_path = unpack_device_path(dev_path); + dev_path_next = next_device_path_node(dev_path); + + while (!is_device_path_end(dev_path_next)) { + dev_path = dev_path_next; + dev_path_next = next_device_path_node(dev_path); + } + + return device_path_type(dev_path); +} diff --git a/common/startup.c b/common/startup.c index 2b92efc..e59b06d 100644 --- a/common/startup.c +++ b/common/startup.c @@ -51,6 +51,12 @@ mount("none", "ramfs", "/", NULL); mkdir("/dev", 0); mount("none", "devfs", "/dev", NULL); + + if (IS_ENABLED(CONFIG_FS_EFIVARFS)) { + mkdir("/efivars", 0); + mount("none", "efivarfs", "/efivars", NULL); + } + return 0; } fs_initcall(mount_root); diff --git a/fs/efivarfs.c b/fs/efivarfs.c index 58c637e..9a1503b 100644 --- a/fs/efivarfs.c +++ b/fs/efivarfs.c @@ -1,7 +1,7 @@ /* - * ramfs.c - a malloc based filesystem + * efivars.c - EFI variable filesystem * - * Copyright (c) 2007 Sascha Hauer , Pengutronix + * Copyright (c) 2014 Sascha Hauer , Pengutronix * * See file CREDITS for list of people who contributed to this * project. @@ -34,9 +34,20 @@ #include #include +struct efivarfs_inode { + s16 *name; + efi_guid_t vendor; + char *full_name; /* name including vendor namespacing */ + struct list_head node; +}; + +struct efivarfs_dir { + struct list_head *current; + DIR dir; +}; + struct efivarfs_priv { - struct efi_file_handle *root_dir; - struct efi_file_io_interface *protocol; + struct list_head inodes; }; static int char_to_nibble(char c) @@ -75,8 +86,8 @@ if (*str != '-') return -EINVAL; str++; - break; - } + break; + } } return 0; @@ -115,11 +126,81 @@ return 0; } +static int efivars_create(struct device_d *dev, const char *pathname, mode_t mode) +{ + struct efivarfs_priv *priv = dev->priv; + struct efivarfs_inode *inode; + efi_guid_t vendor; + efi_status_t efiret; + u8 dummydata; + char *name8; + s16 *name; + int ret; + + if (pathname[0] == '/') + pathname++; + + /* deny creating files with other vendor GUID than our own */ + ret = efivarfs_parse_filename(pathname, &vendor, &name); + if (ret) + return -ENOENT; + + if (memcmp(&vendor, &EFI_BAREBOX_VENDOR_GUID, sizeof(efi_guid_t))) + return -EPERM; + + inode = xzalloc(sizeof(*inode)); + inode->name = name; + inode->vendor = vendor; + + + name8 = strdup_wchar_to_char(inode->name); + inode->full_name = asprintf("%s-%pUl", name8, &inode->vendor); + free(name8); + + efiret = RT->set_variable(inode->name, &inode->vendor, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 1, &dummydata); + if (EFI_ERROR(efiret)) { + free(inode); + return -efi_errno(efiret); + } + + list_add_tail(&inode->node, &priv->inodes); + + return 0; +} + +static int efivars_unlink(struct device_d *dev, const char *pathname) +{ + struct efivarfs_priv *priv = dev->priv; + struct efivarfs_inode *inode, *tmp; + efi_status_t efiret; + + if (pathname[0] == '/') + pathname++; + + list_for_each_entry_safe(inode, tmp, &priv->inodes, node) { + if (!strcmp(inode->full_name, pathname)) { + efiret = RT->set_variable(inode->name, &inode->vendor, + 0, 0, NULL); + if (EFI_ERROR(efiret)) + return -efi_errno(efiret); + list_del(&inode->node); + free(inode); + } + } + + return 0; +} + struct efivars_file { void *buf; unsigned long size; efi_guid_t vendor; s16 *name; + u32 attributes; }; static int efivarfs_open(struct device_d *dev, FILE *f, const char *filename) @@ -127,7 +208,6 @@ struct efivars_file *efile; efi_status_t efiret; int ret; - uint32_t attributes; efile = xzalloc(sizeof(*efile)); @@ -135,28 +215,27 @@ if (ret) return -ENOENT; - efiret = RT->get_variable(efile->name, &efile->vendor, &attributes, &efile->size, NULL); + efiret = RT->get_variable(efile->name, &efile->vendor, + &efile->attributes, &efile->size, NULL); if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL) { ret = -efi_errno(efiret); goto out; } - efile->buf = malloc(efile->size + sizeof(uint32_t)); + efile->buf = malloc(efile->size); if (!efile->buf) { ret = -ENOMEM; goto out; } efiret = RT->get_variable(efile->name, &efile->vendor, NULL, &efile->size, - efile->buf + sizeof(uint32_t)); + efile->buf); if (EFI_ERROR(efiret)) { ret = -efi_errno(efiret); goto out; } - *(uint32_t *)efile->buf = attributes; - - f->size = efile->size + sizeof(uint32_t); + f->size = efile->size; f->inode = efile; return 0; @@ -187,6 +266,38 @@ return insize; } +static int efivarfs_write(struct device_d *_dev, FILE *f, const void *buf, size_t insize) +{ + struct efivars_file *efile = f->inode; + + if (efile->size < f->pos + insize) { + efile->buf = realloc(efile->buf, f->pos + insize); + efile->size = f->pos + insize; + } + + memcpy(efile->buf + f->pos, buf, insize); + + RT->set_variable(efile->name, &efile->vendor, efile->attributes, + efile->size ? efile->size : 1, efile->buf); + + return insize; +} + +static int efivarfs_truncate(struct device_d *dev, FILE *f, ulong size) +{ + struct efivars_file *efile = f->inode; + + efile->size = size; + efile->buf = realloc(efile->buf, efile->size + sizeof(uint32_t)); + + RT->set_variable(efile->name, &efile->vendor, efile->attributes, + efile->size ? efile->size : 1, efile->buf); + + f->size = efile->size; + + return 0; +} + static loff_t efivarfs_lseek(struct device_d *dev, FILE *f, loff_t pos) { f->pos = pos; @@ -194,67 +305,29 @@ return f->pos; } -struct efivarfs_dir_entry { - char *name; - struct efivarfs_dir_entry *next; -}; - -struct efivarfs_dir { - struct efivarfs_dir_entry *first; - struct efivarfs_dir_entry *current; - DIR dir; -}; - static DIR *efivarfs_opendir(struct device_d *dev, const char *pathname) { - efi_status_t efiret; - efi_guid_t vendor; - s16 name[1024]; + struct efivarfs_priv *priv = dev->priv; struct efivarfs_dir *edir; - unsigned long size; - unsigned char *name8; - - name[0] = 0; edir = xzalloc(sizeof(*edir)); - - while (1) { - struct efivarfs_dir_entry *entry; - - size = sizeof(name); - efiret = RT->get_next_variable(&size, name, &vendor); - if (EFI_ERROR(efiret)) - break; - - entry = xzalloc(sizeof(*entry)); - name8 = strdup_wchar_to_char(name); - - entry->name = asprintf("%s-%pUl", name8, &vendor); - - free(name8); - - if (!edir->first) - edir->first = entry; - - if (edir->current) - edir->current->next = entry; - - edir->current = entry; - } - - edir->current = edir->first; + edir->current = priv->inodes.next; return &edir->dir; } static struct dirent *efivarfs_readdir(struct device_d *dev, DIR *dir) { + struct efivarfs_priv *priv = dev->priv; struct efivarfs_dir *edir = container_of(dir, struct efivarfs_dir, dir); + struct efivarfs_inode *inode; - if (!edir->current) + if (edir->current == &priv->inodes) return NULL; - strcpy(dir->d.d_name, edir->current->name); + inode = list_entry(edir->current, struct efivarfs_inode, node); + + strcpy(dir->d.d_name, inode->full_name); edir->current = edir->current->next; @@ -264,17 +337,6 @@ static int efivarfs_closedir(struct device_d *dev, DIR *dir) { struct efivarfs_dir *edir = container_of(dir, struct efivarfs_dir, dir); - struct efivarfs_dir_entry *entry; - - entry = edir->first; - - while (entry) { - struct efivarfs_dir_entry *tmp; - free(entry->name); - tmp = entry->next; - free(entry); - entry = tmp; - } free(edir); @@ -308,18 +370,64 @@ static int efivarfs_probe(struct device_d *dev) { + efi_status_t efiret; + efi_guid_t vendor; + s16 name[1024]; + char *name8; + unsigned long size; + struct efivarfs_priv *priv; + + name[0] = 0; + + priv = xzalloc(sizeof(*priv)); + INIT_LIST_HEAD(&priv->inodes); + + while (1) { + struct efivarfs_inode *inode; + + size = sizeof(name); + efiret = RT->get_next_variable(&size, name, &vendor); + if (EFI_ERROR(efiret)) + break; + + inode = xzalloc(sizeof(*inode)); + inode->name = strdup_wchar(name); + + inode->vendor = vendor; + + name8 = strdup_wchar_to_char(inode->name); + inode->full_name = asprintf("%s-%pUl", name8, &vendor); + free(name8); + + list_add_tail(&inode->node, &priv->inodes); + } + + dev->priv = priv; + return 0; } static void efivarfs_remove(struct device_d *dev) { - free(dev->priv); + struct efivarfs_priv *priv = dev->priv; + struct efivarfs_inode *inode, *tmp; + + list_for_each_entry_safe(inode, tmp, &priv->inodes, node) { + free(inode->name); + free(inode); + } + + free(priv); } static struct fs_driver_d efivarfs_driver = { + .create = efivars_create, + .unlink = efivars_unlink, .open = efivarfs_open, .close = efivarfs_close, .read = efivarfs_read, + .write = efivarfs_write, + .truncate = efivarfs_truncate, .lseek = efivarfs_lseek, .opendir = efivarfs_opendir, .readdir = efivarfs_readdir, diff --git a/include/efi.h b/include/efi.h index 4ad9f69..9b4f16b 100644 --- a/include/efi.h +++ b/include/efi.h @@ -255,9 +255,11 @@ /* * Types and defines for EFI ResetSystem */ -#define EFI_RESET_COLD 0 -#define EFI_RESET_WARM 1 -#define EFI_RESET_SHUTDOWN 2 +typedef enum { + EFI_RESET_COLD = 0, + EFI_RESET_WARM = 1, + EFI_RESET_SHUTDOWN = 2 +} efi_reset_type_t; /* * EFI Runtime Services table @@ -277,9 +279,11 @@ u32 *Attributes, unsigned long *data_size, void *data); efi_status_t (EFIAPI *get_next_variable)(unsigned long *variable_name_size, s16 *variable_name, efi_guid_t *vendor); - void *set_variable; + efi_status_t (EFIAPI *set_variable)(s16 *variable_name, efi_guid_t *vendor, + u32 Attributes, unsigned long data_size, void *data); void *get_next_high_mono_count; - void *reset_system; + void (EFIAPI *reset_system)(efi_reset_type_t reset_type, efi_status_t reset_status, + unsigned long data_size, void *reset_data); void *update_capsule; void *query_capsule_caps; void *query_variable_info; @@ -465,6 +469,10 @@ #define EFI_VLANCONFIGDXE_INF_GUID \ EFI_GUID(0xe4f61863, 0xfe2c, 0x4b56, 0xa8, 0xf4, 0x08, 0x51, 0x9b, 0xc4, 0x39, 0xdf) +/* barebox specific GUIDs */ +#define EFI_BAREBOX_VENDOR_GUID \ + EFI_GUID(0x5b91f69c, 0x8b88, 0x4a2b, 0x92, 0x69, 0x5f, 0x1d, 0x80, 0x2b, 0x51, 0x75) + extern efi_guid_t efi_file_info_id; extern efi_guid_t efi_simple_file_system_protocol_guid; extern efi_guid_t efi_device_path_protocol_guid; @@ -615,6 +623,7 @@ } char *device_path_to_str(struct efi_device_path *dev_path); +u8 device_path_to_type(struct efi_device_path *dev_path); const char *efi_guid_string(efi_guid_t *g); diff --git a/include/wchar.h b/include/wchar.h index 80dcd81..702d8e2 100644 --- a/include/wchar.h +++ b/include/wchar.h @@ -5,6 +5,8 @@ typedef u16 wchar_t; +wchar_t *strdup_wchar(const wchar_t *src); + char *strcpy_wchar_to_char(char *dst, const wchar_t *src); wchar_t *strcpy_char_to_wchar(wchar_t *dst, const char *src); diff --git a/lib/wchar.c b/lib/wchar.c index 6368a01..b2e9e75 100644 --- a/lib/wchar.c +++ b/lib/wchar.c @@ -31,6 +31,22 @@ return len; } +wchar_t *strdup_wchar(const wchar_t *src) +{ + int len = wcslen(src); + wchar_t *tmp, *dst; + + if (!(dst = malloc((len + 1) * sizeof(wchar_t)))) + return NULL; + + tmp = dst; + + while ((*dst++ = *src++)) + /* nothing */; + + return tmp; +} + char *strcpy_wchar_to_char(char *dst, const wchar_t *src) { char *ret = dst;