static ssize_t unparse_media_path(char *buffer, size_t buffer_size, EFI_DEVICE_PATH *path) { CDROM_DEVICE_PATH *cdrom = (CDROM_DEVICE_PATH *)path; MEDIA_PROTOCOL_DEVICE_PATH *media = (MEDIA_PROTOCOL_DEVICE_PATH *)path; FILE_PATH_DEVICE_PATH *file = (FILE_PATH_DEVICE_PATH *)path; char *text_guid; char file_name[80]; memset(file_name, 0, sizeof(file_name)); char a[16], b[16], c[16]; int rc; switch (path->subtype) { case 1: return unparse_media_hard_drive_path(buffer, buffer_size, path); case 2: return snprintf(buffer, buffer_size, "CD-ROM(%x,%" PRIx64 ",%" PRIx64 ")", get(a, cdrom->boot_entry), get(b, cdrom->start), get(c, cdrom->size)); case 3: return unparse_vendor_path(buffer, buffer_size, NULL, (VENDOR_DEVICE_PATH *)path); case 4: efichar_to_char(file_name, file->path_name, 80); return snprintf(buffer, buffer_size, "File(%s)", file_name); case 5: rc = efi_guid_to_str(&media->guid, &text_guid); if (rc < 0) return rc; rc = snprintf(buffer, buffer_size, "Media(%s)", text_guid); free(text_guid); return rc; case 6: rc = efi_guid_to_str(&media->guid, &text_guid); if (rc < 0) return rc; rc = snprintf(buffer, buffer_size, "FvFile(%s)", text_guid); free(text_guid); return rc > 0 ? rc + 1 : rc; case 7: rc = efi_guid_to_str(&media->guid, &text_guid); if (rc < 0) return rc; rc = snprintf(buffer, buffer_size, "FvVol(%s)", text_guid); free(text_guid); return rc > 0 ? rc + 1 : rc; } return 0; }
efi_guid_to_name(efi_guid_t *guid, char **name) { struct guidname *result; int rc = _get_common_guidname(guid, &result); if (rc >= 0) { *name = strndup(result->name, sizeof (result->name) -1); return *name ? (int)strlen(*name) : -1; } return efi_guid_to_str(guid, name); }
static int put_info(update_info *info) { efi_guid_t varguid = FWUPDATE_GUID; ssize_t dps, is; char *guidstr = NULL; char *varname; int error; int rc; rc = efi_guid_to_str(&info->guid, &guidstr); if (rc < 0) { err: return rc; } guidstr = onstack(guidstr, strlen(guidstr)+1); rc = asprintf(&varname, "fwupdate-%s-%"PRIx64, guidstr, info->hw_inst); if (rc < 0) goto err; varname = onstack(varname, strlen(varname)+1); dps = efidp_size((efidp)info->dp_ptr); /* make sure dps is at least big enough to have our structure */ if (dps < 0 || (size_t)dps < sizeof(*info)) { errno = EINVAL; return -1; } /* Make sure sizeof(*info) + dps won't integer overflow */ if ((size_t)dps > SSIZE_MAX - sizeof(*info)) { errno = EOVERFLOW; return -1; } is = sizeof(*info) + dps - sizeof(info->dp_ptr); update_info *info2; info2 = malloc(is); if (!info2) return -1; memcpy(info2, info, sizeof(*info)); memcpy(info2->dp, info->dp_ptr, dps); uint32_t attributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; rc = efi_set_variable(varguid, varname, (uint8_t *)info2, is, attributes, 0600); error = errno; free(info2); errno = error; return rc; }
static void pretty_guid(efi_guid_t *guid, char **gname) { char *pretty = NULL; if (gflag) efi_guid_to_name(guid, &pretty); if (pretty == NULL) efi_guid_to_str(guid, gname); else *gname = pretty; }
/** * efivar_create_sysfs_entry - create a new entry in sysfs * @new_var: efivar entry to create * * Returns 0 on success, negative error code on failure */ static int efivar_create_sysfs_entry(struct efivar_entry *new_var) { int i, short_name_size; char *short_name; unsigned long variable_name_size; efi_char16_t *variable_name; int ret; variable_name = new_var->var.VariableName; variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t); /* * Length of the variable bytes in ASCII, plus the '-' separator, * plus the GUID, plus trailing NUL */ short_name_size = variable_name_size / sizeof(efi_char16_t) + 1 + EFI_VARIABLE_GUID_LEN + 1; short_name = kzalloc(short_name_size, GFP_KERNEL); if (!short_name) return -ENOMEM; /* Convert Unicode to normal chars (assume top bits are 0), ala UTF-8 */ for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) { short_name[i] = variable_name[i] & 0xFF; } /* This is ugly, but necessary to separate one vendor's private variables from another's. */ *(short_name + strlen(short_name)) = '-'; efi_guid_to_str(&new_var->var.VendorGuid, short_name + strlen(short_name)); new_var->kobj.kset = efivars_kset; ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype, NULL, "%s", short_name); kfree(short_name); if (ret) return ret; kobject_uevent(&new_var->kobj, KOBJ_ADD); efivar_entry_add(new_var, &efivar_sysfs_list); return 0; }
static ssize_t efivar_guid_read(struct efivar_entry *entry, char *buf) { struct efi_variable *var = &entry->var; char *str = buf; if (!entry || !buf) return 0; efi_guid_to_str(&var->VendorGuid, str); str += strlen(str); str += sprintf(str, "\n"); return str - buf; }
/** * efivar_create_sysfs_entry - create a new entry in sysfs * @new_var: efivar entry to create * * Returns 0 on success, negative error code on failure */ static int efivar_create_sysfs_entry(struct efivar_entry *new_var) { int short_name_size; char *short_name; unsigned long utf8_name_size; efi_char16_t *variable_name = new_var->var.VariableName; int ret; /* * Length of the variable bytes in UTF8, plus the '-' separator, * plus the GUID, plus trailing NUL */ utf8_name_size = ucs2_utf8size(variable_name); short_name_size = utf8_name_size + 1 + EFI_VARIABLE_GUID_LEN + 1; short_name = kmalloc(short_name_size, GFP_KERNEL); if (!short_name) return -ENOMEM; ucs2_as_utf8(short_name, variable_name, short_name_size); /* This is ugly, but necessary to separate one vendor's private variables from another's. */ short_name[utf8_name_size] = '-'; efi_guid_to_str(&new_var->var.VendorGuid, short_name + utf8_name_size + 1); new_var->kobj.kset = efivars_kset; ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype, NULL, "%s", short_name); kfree(short_name); if (ret) return ret; kobject_uevent(&new_var->kobj, KOBJ_ADD); efivar_entry_add(new_var, &efivar_sysfs_list); return 0; }
static ssize_t unparse_media_hard_drive_path(char *buffer, size_t buffer_size, EFI_DEVICE_PATH *path) { HARDDRIVE_DEVICE_PATH *hd = (HARDDRIVE_DEVICE_PATH *)path; char text_uuid[40], *sig=text_uuid; char a[16], b[16], c[16]; int rc = 0; switch (hd->signature_type) { case 0x00: rc = sprintf(sig, "None"); if (rc < 0) return -1; break; case 0x01: rc = sprintf(sig, "%08x", *(uint32_t *)memcpy(a, &hd->signature, sizeof(hd->signature))); if (rc < 0) return -1; break; case 0x02: /* GPT */ rc = efi_guid_to_str((efi_guid_t *)hd->signature, &sig); if (rc < 0) return rc; break; default: return 0; } rc = snprintf(buffer, buffer_size, "HD(%x,%" PRIx64 ",%" PRIx64 ",%s)", get(a, hd->part_num), get(b, hd->start), get(c, hd->size), sig); if (hd->signature_type == 0x02) free(sig); return rc; }
static ssize_t unparse_vendor_path(char *buffer, size_t buffer_size, char *prefix, VENDOR_DEVICE_PATH *path) { char *text_guid; unsigned char *q = (uint8_t *)path + 20; int rc; size_t needed; off_t buf_offset = 0; rc = efi_guid_to_str(&path->vendor_guid, &text_guid); if (rc < 0) return -1; needed = snprintf(buffer, buffer_size, "%s(%s,", prefix ? prefix : "Vendor", text_guid); free(text_guid); if (needed < 0) return -1; buf_offset += needed; needed = unparse_raw(buffer + buf_offset, buffer_size == 0 ? 0 : buffer_size - buf_offset, q, path->length - 20); if (needed < 0) return -1; buf_offset += needed; needed = snprintf(buffer + buf_offset, buffer_size == 0 ? 0 : buffer_size - buf_offset, ")"); if (needed < 0) return -1; buf_offset += needed; return buf_offset; }
static fwup_resource * fu_provider_uefi_find (fwup_resource_iter *iter, const gchar *guid_str, GError **error) { efi_guid_t *guid_raw; fwup_resource *re_matched = NULL; fwup_resource *re = NULL; g_autofree gchar *guid_str_tmp = NULL; /* get the hardware we're referencing */ guid_str_tmp = g_strdup ("00000000-0000-0000-0000-000000000000"); while (fwup_resource_iter_next (iter, &re) > 0) { /* convert to strings */ fwup_get_guid (re, &guid_raw); if (efi_guid_to_str (guid_raw, &guid_str_tmp) < 0) { g_warning ("failed to convert guid to string"); continue; } /* FIXME: also match hardware_instance too */ if (g_strcmp0 (guid_str, guid_str_tmp) == 0) { re_matched = re; break; } } /* paradoxically, no hardware matched */ if (re_matched == NULL) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "No UEFI firmware matched %s", guid_str); } return re_matched; }
static int get_info(efi_guid_t *guid, uint64_t hw_inst, update_info **info) { efi_guid_t varguid = FWUPDATE_GUID; char *varname = NULL; char *guidstr = NULL; int rc; update_info *local; int error; rc = efi_guid_to_str(guid, &guidstr); if (rc < 0) return -1; guidstr = onstack(guidstr, strlen(guidstr)+1); rc = asprintf(&varname, "fwupdate-%s-%"PRIx64, guidstr, hw_inst); if (rc < 0) return -1; varname = onstack(varname, strlen(varname)+1); uint8_t *data = NULL; size_t data_size = 0; uint32_t attributes; rc = efi_get_variable(varguid, varname, &data, &data_size, &attributes); if (rc < 0) { if (errno != ENOENT) return -1; local = calloc(1, sizeof (*local)); if (!local) return -1; local->update_info_version = UPDATE_INFO_VERSION; local->guid = *guid; local->hw_inst = hw_inst; local->dp_ptr = calloc(1, 1024); if (!local->dp_ptr) { alloc_err: error = errno; free_info(local); errno = error; return -1; } ssize_t sz; sz = efidp_make_end_entire((uint8_t *)local->dp_ptr, 1024); if (sz < 0) { rc = sz; goto alloc_err; } *info = local; return 0; } /* If our size is wrong, or our data is otherwise bad, try to delete * the variable and create a new one. */ if (data_size < sizeof (*local) || !data) { if (data) free(data); get_err: rc = efi_del_variable(varguid, varname); if (rc < 0) return -1; return get_info(guid, hw_inst, info); } local = (update_info *)data; if (local->update_info_version != UPDATE_INFO_VERSION) goto get_err; ssize_t sz = efidp_size((efidp)local->dp); if (sz < 0) { free(data); errno = EINVAL; return -1; } efidp_header *dp = malloc((size_t)sz); if (!dp) { free(data); errno = ENOMEM; return -1; } memcpy(dp, local->dp, (size_t)sz); local->dp_ptr = dp; *info = local; return 0; }
static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, unsigned long name_size, void *data) { struct super_block *sb = (struct super_block *)data; struct efivar_entry *entry; struct inode *inode = NULL; struct dentry *dentry, *root = sb->s_root; unsigned long size = 0; char *name; int len, i; int err = -ENOMEM; entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return err; memcpy(entry->var.VariableName, name16, name_size); memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t)); len = ucs2_strlen(entry->var.VariableName); /* name, plus '-', plus GUID, plus NUL*/ name = kmalloc(len + 1 + EFI_VARIABLE_GUID_LEN + 1, GFP_KERNEL); if (!name) goto fail; for (i = 0; i < len; i++) name[i] = entry->var.VariableName[i] & 0xFF; name[len] = '-'; efi_guid_to_str(&entry->var.VendorGuid, name + len + 1); name[len + EFI_VARIABLE_GUID_LEN+1] = '\0'; inode = efivarfs_get_inode(sb, root->d_inode, S_IFREG | 0644, 0); if (!inode) goto fail_name; dentry = efivarfs_alloc_dentry(root, name); if (IS_ERR(dentry)) { err = PTR_ERR(dentry); goto fail_inode; } /* copied by the above to local storage in the dentry. */ kfree(name); efivar_entry_size(entry, &size); efivar_entry_add(entry, &efivarfs_list); mutex_lock(&inode->i_mutex); inode->i_private = entry; i_size_write(inode, size + sizeof(entry->var.Attributes)); mutex_unlock(&inode->i_mutex); d_add(dentry, inode); return 0; fail_inode: iput(inode); fail_name: kfree(name); fail: kfree(entry); return err; }
static gboolean fu_provider_uefi_coldplug (FuProvider *provider, GError **error) { AsVersionParseFlag parse_flags; g_autofree gchar *display_name = NULL; fwup_resource *re; gint supported; g_autofree gchar *guid = NULL; g_autoptr(FuDevice) dev = NULL; g_autoptr(fwup_resource_iter) iter = NULL; /* supported = 0 : ESRT unspported supported = 1 : unlocked, ESRT supported supported = 2 : it is locked but can be unlocked to support ESRT supported = 3 : it is locked, has been marked to be unlocked on next boot calling unlock again is OK. */ supported = fwup_supported (); if (supported == 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "UEFI firmware updating not supported"); return FALSE; } if (supported >= 2) { dev = fu_device_new (); fu_device_set_id (dev, "UEFI-dummy-dev0"); fu_device_add_guid (dev, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e"); fu_device_set_version (dev, "0"); fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_ONLINE); fu_device_add_flag (dev, FU_DEVICE_FLAG_LOCKED); fu_provider_device_add (provider, dev); return TRUE; } /* this can fail if we have no permissions */ if (fwup_resource_iter_create (&iter) < 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Cannot create fwup iter"); return FALSE; } /* set Display Name to the system for all capsules */ g_file_get_contents ("/sys/class/dmi/id/product_name", &display_name, NULL, NULL); if (display_name != NULL) g_strchomp (display_name); /* add each device */ guid = g_strdup ("00000000-0000-0000-0000-000000000000"); parse_flags = fu_provider_uefi_get_version_format (); while (fwup_resource_iter_next (iter, &re) > 0) { efi_guid_t *guid_raw; guint32 version_raw; guint64 hardware_instance = 0; /* FIXME */ g_autofree gchar *id = NULL; g_autofree gchar *version = NULL; g_autofree gchar *version_lowest = NULL; /* convert to strings */ fwup_get_guid (re, &guid_raw); if (efi_guid_to_str (guid_raw, &guid) < 0) { g_warning ("failed to convert guid to string"); continue; } fwup_get_fw_version(re, &version_raw); version = as_utils_version_from_uint32 (version_raw, parse_flags); id = g_strdup_printf ("UEFI-%s-dev%" G_GUINT64_FORMAT, guid, hardware_instance); dev = fu_device_new (); fu_device_set_id (dev, id); fu_device_add_guid (dev, guid); fu_device_set_version (dev, version); if (display_name != NULL) fu_device_set_name(dev, display_name); fwup_get_lowest_supported_fw_version (re, &version_raw); if (version_raw != 0) { version_lowest = as_utils_version_from_uint32 (version_raw, parse_flags); fu_device_set_version_lowest (dev, version_lowest); } fu_device_add_flag (dev, FU_DEVICE_FLAG_INTERNAL); fu_device_add_flag (dev, FU_DEVICE_FLAG_ALLOW_OFFLINE); fu_device_add_flag (dev, FU_DEVICE_FLAG_REQUIRE_AC); fu_provider_device_add (provider, dev); } return TRUE; }