mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-02 22:55:37 +00:00
146 lines
3.5 KiB
C
146 lines
3.5 KiB
C
/*
|
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#if defined(__linux__)
|
|
#include <efivar.h>
|
|
#elif defined(__FreeBSD__)
|
|
/* although against style, the order seems to matter on FreeBSD */
|
|
/* clang-format off */
|
|
#include <efivar.h>
|
|
#include <efivar-dp.h>
|
|
/* clang-format on */
|
|
#endif
|
|
|
|
#include <fwupdplugin.h>
|
|
|
|
#include "fu-uefi-devpath.h"
|
|
|
|
#if defined(__FreeBSD__)
|
|
#define EFIDP_END_TYPE 0x7f
|
|
#define EFIDP_END_ENTIRE 0xff
|
|
#endif
|
|
|
|
typedef struct {
|
|
guint8 type;
|
|
guint8 subtype;
|
|
GBytes *data;
|
|
} FuUefiDevPath;
|
|
|
|
static void
|
|
fu_uefi_efi_dp_free(FuUefiDevPath *dp)
|
|
{
|
|
if (dp->data != NULL)
|
|
g_bytes_unref(dp->data);
|
|
g_free(dp);
|
|
}
|
|
|
|
GBytes *
|
|
fu_uefi_devpath_find_data(GPtrArray *dps, guint8 type, guint8 subtype, GError **error)
|
|
{
|
|
for (guint i = 0; i < dps->len; i++) {
|
|
FuUefiDevPath *dp = g_ptr_array_index(dps, i);
|
|
if (dp->type == type && dp->subtype == subtype)
|
|
return dp->data;
|
|
}
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"no DP with type 0x%02x and subtype 0x%02x",
|
|
type,
|
|
subtype);
|
|
return NULL;
|
|
}
|
|
|
|
GPtrArray *
|
|
fu_uefi_devpath_parse(const guint8 *buf, gsize sz, FuUefiDevpathParseFlags flags, GError **error)
|
|
{
|
|
guint16 offset = 0;
|
|
g_autoptr(GPtrArray) dps = NULL;
|
|
|
|
/* sanity check */
|
|
if (sz < sizeof(efidp_header)) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"const_efidp is corrupt");
|
|
return NULL;
|
|
}
|
|
|
|
dps = g_ptr_array_new_with_free_func((GDestroyNotify)fu_uefi_efi_dp_free);
|
|
while (1) {
|
|
FuUefiDevPath *dp;
|
|
const efidp_header *hdr = (efidp_header *)(buf + offset);
|
|
guint16 hdr_length = GUINT16_FROM_LE(hdr->length);
|
|
|
|
/* check if last entry */
|
|
g_debug("DP type:0x%02x subtype:0x%02x size:0x%04x",
|
|
hdr->type,
|
|
hdr->subtype,
|
|
hdr->length);
|
|
if (hdr->type == EFIDP_END_TYPE && hdr->subtype == EFIDP_END_ENTIRE)
|
|
break;
|
|
|
|
/* work around a bug in efi_va_generate_file_device_path_from_esp */
|
|
if (offset + sizeof(efidp_header) + hdr->length > sz) {
|
|
hdr_length = 0;
|
|
fu_common_dump_full(G_LOG_DOMAIN,
|
|
"efidp",
|
|
buf + offset,
|
|
sz - offset,
|
|
32,
|
|
FU_DUMP_FLAGS_SHOW_ADDRESSES);
|
|
for (guint16 i = offset + 4; i <= sz - 4; i++) {
|
|
if (memcmp(buf + i, "\x7f\xff\x04\x00", 4) == 0) {
|
|
hdr_length = i - offset;
|
|
g_debug("found END_ENTIRE at 0x%04x", (guint)(i - offset));
|
|
break;
|
|
}
|
|
}
|
|
if (hdr_length == 0) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"DP length invalid and no END_ENTIRE "
|
|
"found, possibly data truncation?");
|
|
return NULL;
|
|
}
|
|
if ((flags & FU_UEFI_DEVPATH_PARSE_FLAG_REPAIR) == 0) {
|
|
g_set_error(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"DP length invalid, reported 0x%04x, maybe 0x%04x",
|
|
hdr->length,
|
|
hdr_length);
|
|
return NULL;
|
|
}
|
|
g_debug("DP length invalid! Truncating from 0x%04x to 0x%04x",
|
|
hdr->length,
|
|
hdr_length);
|
|
}
|
|
|
|
/* add new DP */
|
|
dp = g_new0(FuUefiDevPath, 1);
|
|
dp->type = hdr->type;
|
|
dp->subtype = hdr->subtype;
|
|
if (hdr_length > 0)
|
|
dp->data = g_bytes_new(buf + offset + 4, hdr_length);
|
|
g_ptr_array_add(dps, dp);
|
|
|
|
/* advance to next DP */
|
|
offset += hdr_length;
|
|
if (offset + sizeof(efidp_header) > sz) {
|
|
g_set_error_literal(error,
|
|
FWUPD_ERROR,
|
|
FWUPD_ERROR_INTERNAL,
|
|
"DP length invalid after fixing");
|
|
return NULL;
|
|
}
|
|
}
|
|
return g_steal_pointer(&dps);
|
|
}
|