boot: Add EDID (#36843)
This adds a couple of custom CHIDs which include panel manufacturer and product code.
This commit is contained in:
@@ -1713,6 +1713,15 @@ LEGEND: M → sys_vendor (LENOVO) ┄ F → product_family (ThinkPad X1 Carbon G
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--drm-device=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
<listitem><para>When provided with the <command>chid</command> command, use this sysfs path to a DRM
|
||||
device to fetch EDID from. Example: <filename>/sys/class/drm/card1-HDMI-A-1/</filename></para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
|
||||
+136
-7
@@ -1,9 +1,14 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "analyze.h"
|
||||
#include "analyze-chid.h"
|
||||
#include "chid-fundamental.h"
|
||||
#include "device-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "edid-fundamental.h"
|
||||
#include "efi-api.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
@@ -15,16 +20,27 @@
|
||||
#include "virt.h"
|
||||
|
||||
static int parse_chid_type(const char *s, size_t *ret) {
|
||||
char *e;
|
||||
unsigned u;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
r = safe_atou(s, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u >= CHID_TYPES_MAX)
|
||||
return -ERANGE;
|
||||
if ((e = startswith(s, "ext"))) {
|
||||
r = safe_atou(e, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u >= CHID_TYPES_MAX - EXTRA_CHID_BASE)
|
||||
return -ERANGE;
|
||||
u += EXTRA_CHID_BASE;
|
||||
} else {
|
||||
r = safe_atou(s, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u >= EXTRA_CHID_BASE)
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
|
||||
if (ret)
|
||||
*ret = u;
|
||||
@@ -44,6 +60,7 @@ static const char *const chid_smbios_friendly[_CHID_SMBIOS_FIELDS_MAX] = {
|
||||
[CHID_SMBIOS_BIOS_MAJOR] = "bios-major",
|
||||
[CHID_SMBIOS_BIOS_MINOR] = "bios-minor",
|
||||
[CHID_SMBIOS_ENCLOSURE_TYPE] = "enclosure-type",
|
||||
[CHID_EDID_PANEL] = "edid-panel",
|
||||
};
|
||||
|
||||
static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = {
|
||||
@@ -58,6 +75,7 @@ static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = {
|
||||
[CHID_SMBIOS_BIOS_MAJOR] = 'R',
|
||||
[CHID_SMBIOS_BIOS_MINOR] = 'r',
|
||||
[CHID_SMBIOS_ENCLOSURE_TYPE] = 'e',
|
||||
[CHID_EDID_PANEL] = 'E',
|
||||
};
|
||||
|
||||
static char *chid_smbios_fields_string(uint32_t combination) {
|
||||
@@ -91,8 +109,14 @@ static int add_chid(Table *table, const EFI_GUID guids[static CHID_TYPES_MAX], s
|
||||
if (!flags)
|
||||
return log_oom();
|
||||
|
||||
if (t < EXTRA_CHID_BASE)
|
||||
r = table_add_many(table, TABLE_UINT, (unsigned) t);
|
||||
else
|
||||
r = table_add_cell_stringf(table, NULL, "ext%zu", t - EXTRA_CHID_BASE);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
r = table_add_many(table,
|
||||
TABLE_UINT, (unsigned) t,
|
||||
TABLE_STRING, flags,
|
||||
TABLE_UUID, id);
|
||||
if (r < 0)
|
||||
@@ -224,6 +248,93 @@ static int smbios_fields_acquire(char16_t *fields[static _CHID_SMBIOS_FIELDS_MAX
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int edid_parse(sd_device *drm_dev, char16_t **ret_panel) {
|
||||
const char *edid_content;
|
||||
size_t edid_size;
|
||||
int r;
|
||||
|
||||
assert(drm_dev);
|
||||
assert(ret_panel);
|
||||
|
||||
r = sd_device_get_sysattr_value_with_size(drm_dev, "edid", &edid_content, &edid_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (edid_size == 0)
|
||||
return -ENXIO;
|
||||
|
||||
EdidHeader header;
|
||||
if (edid_parse_blob(edid_content, edid_size, &header) < 0)
|
||||
return -EBADMSG;
|
||||
|
||||
_cleanup_free_ char16_t *panel_id = new0(char16_t, 8);
|
||||
if (!panel_id)
|
||||
return -ENOMEM;
|
||||
|
||||
if (edid_get_panel_id(&header, panel_id) < 0)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret_panel = TAKE_PTR(panel_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int edid_search(char16_t **ret_panel) {
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
_cleanup_strv_free_ char **drm_paths = NULL;
|
||||
_cleanup_free_ char16_t *unique_panel = NULL;
|
||||
size_t n = 0;
|
||||
int r;
|
||||
|
||||
assert(ret_panel);
|
||||
|
||||
r = sd_device_enumerator_new(&e);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create device enumerator: %m");
|
||||
|
||||
r = sd_device_enumerator_allow_uninitialized(e);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allow uninitialized device enumerator: %m");
|
||||
|
||||
r = sd_device_enumerator_add_match_subsystem(e, "drm", true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add drm match subsystem to device enumerator: %m");
|
||||
|
||||
FOREACH_DEVICE(e, d) {
|
||||
_cleanup_free_ char16_t *panel = NULL;
|
||||
const char *drm_path;
|
||||
|
||||
r = sd_device_get_syspath(d, &drm_path);
|
||||
if (r < 0)
|
||||
return log_device_error_errno(d, r, "Failed to get syspath from device: %m");
|
||||
|
||||
r = edid_parse(d, &panel);
|
||||
if (ERRNO_IS_DEVICE_ABSENT(r))
|
||||
continue;
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(d, r, "Failed to parse EDID from DRM device, skipping: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!unique_panel)
|
||||
unique_panel = TAKE_PTR(panel);
|
||||
|
||||
if (strv_extend_with_size(&drm_paths, &n, drm_path) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (n == 1) {
|
||||
*ret_panel = TAKE_PTR(unique_panel);
|
||||
return 0;
|
||||
}
|
||||
if (n == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No monitors detected, skipping EDID CHID extensions.");
|
||||
|
||||
log_notice("Multiple monitors detected, skipping EDID CHID extensions.");
|
||||
STRV_FOREACH(s, drm_paths)
|
||||
log_info("Hint: use --drm-device=%s", *s);
|
||||
|
||||
return -ENOTUNIQ;
|
||||
}
|
||||
|
||||
int verb_chid(int argc, char *argv[], void *userdata) {
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
@@ -244,6 +355,24 @@ int verb_chid(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_drm_device_path) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *drm_dev = NULL;
|
||||
r = sd_device_new_from_path(&drm_dev, arg_drm_device_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open device %s: %m", arg_drm_device_path);
|
||||
|
||||
if (!device_in_subsystem(drm_dev, "drm"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot read EDID from a non-DRM device '%s'", arg_drm_device_path);
|
||||
|
||||
r = edid_parse(drm_dev, &smbios_fields[CHID_EDID_PANEL]);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse EDID for device %s: %m", arg_drm_device_path);
|
||||
} else {
|
||||
r = edid_search(&smbios_fields[CHID_EDID_PANEL]);
|
||||
if (r < 0 && !IN_SET(r, -ENOTUNIQ, -ENODEV))
|
||||
return r;
|
||||
}
|
||||
|
||||
EFI_GUID chids[CHID_TYPES_MAX] = {};
|
||||
chid_calculate((const char16_t* const*) smbios_fields, chids);
|
||||
|
||||
@@ -258,7 +387,7 @@ int verb_chid(int argc, char *argv[], void *userdata) {
|
||||
size_t t;
|
||||
r = parse_chid_type(*as, &t);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pare CHID type: %s", *as);
|
||||
return log_error_errno(r, "Failed to parse CHID type: %s", *as);
|
||||
|
||||
r = add_chid(table, chids, t);
|
||||
if (r < 0)
|
||||
|
||||
@@ -121,12 +121,14 @@ char *arg_profile = NULL;
|
||||
bool arg_legend = true;
|
||||
bool arg_table = false;
|
||||
ImagePolicy *arg_image_policy = NULL;
|
||||
char *arg_drm_device_path = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_drm_device_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_profile, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
@@ -287,6 +289,8 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --image=PATH Operate on disk image as filesystem root\n"
|
||||
" --image-policy=POLICY Specify disk image dissection policy\n"
|
||||
" -m --mask Parse parameter as numeric capability mask\n"
|
||||
" --drm-device=PATH Use this DRM device sysfs path to get EDID\n"
|
||||
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@@ -333,6 +337,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_TLDR,
|
||||
ARG_SCALE_FACTOR_SVG,
|
||||
ARG_DETAILED_SVG,
|
||||
ARG_DRM_DEVICE_PATH,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -371,6 +376,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "mask", no_argument, NULL, 'm' },
|
||||
{ "scale-svg", required_argument, NULL, ARG_SCALE_FACTOR_SVG },
|
||||
{ "detailed", no_argument, NULL, ARG_DETAILED_SVG },
|
||||
{ "drm-device", required_argument, NULL, ARG_DRM_DEVICE_PATH },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -580,6 +586,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_detailed_svg = true;
|
||||
break;
|
||||
|
||||
case ARG_DRM_DEVICE_PATH:
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_drm_device_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -638,6 +650,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_capability != CAPABILITY_LITERAL && !streq_ptr(argv[optind], "capability"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --mask is only supported for capability.");
|
||||
|
||||
if (arg_drm_device_path && !streq_ptr(argv[optind], "chid"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --drm-device is only supported for chid right now.");
|
||||
|
||||
return 1; /* work to do */
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ extern char *arg_profile;
|
||||
extern bool arg_legend;
|
||||
extern bool arg_table;
|
||||
extern ImagePolicy *arg_image_policy;
|
||||
extern char *arg_drm_device_path;
|
||||
|
||||
int acquire_bus(sd_bus **bus, bool *use_full_bus);
|
||||
|
||||
|
||||
@@ -9,23 +9,6 @@
|
||||
#include "macro.h"
|
||||
#include "string-util-fundamental.h"
|
||||
|
||||
/* What is interpreted as whitespace? */
|
||||
#define WHITESPACE " \t\n\r"
|
||||
#define NEWLINE "\n\r"
|
||||
#define QUOTES "\"\'"
|
||||
#define COMMENTS "#;"
|
||||
#define GLOB_CHARS "*?["
|
||||
#define DIGITS "0123456789"
|
||||
#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
|
||||
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
|
||||
#define ALPHANUMERICAL LETTERS DIGITS
|
||||
#define HEXDIGITS DIGITS "abcdefABCDEF"
|
||||
#define LOWERCASE_HEXDIGITS DIGITS "abcdef"
|
||||
#define URI_RESERVED ":/?#[]@!$&'()*+;=" /* [RFC3986] */
|
||||
#define URI_UNRESERVED ALPHANUMERICAL "-._~" /* [RFC3986] */
|
||||
#define URI_VALID URI_RESERVED URI_UNRESERVED /* [RFC3986] */
|
||||
|
||||
static inline char* strstr_ptr(const char *haystack, const char *needle) {
|
||||
if (!haystack || !needle)
|
||||
return NULL;
|
||||
|
||||
+4
-1
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "chid.h"
|
||||
#include "chid-fundamental.h"
|
||||
#include "edid.h"
|
||||
#include "efi.h"
|
||||
#include "sha1-fundamental.h"
|
||||
#include "smbios.h"
|
||||
@@ -72,6 +73,8 @@ static void smbios_info_populate(SmbiosInfo *ret_info) {
|
||||
ret_info->smbios_fields[CHID_SMBIOS_FAMILY] = smbios_to_hashable_string(raw.family);
|
||||
ret_info->smbios_fields[CHID_SMBIOS_BASEBOARD_PRODUCT] = smbios_to_hashable_string(raw.baseboard_product);
|
||||
ret_info->smbios_fields[CHID_SMBIOS_BASEBOARD_MANUFACTURER] = smbios_to_hashable_string(raw.baseboard_manufacturer);
|
||||
|
||||
edid_get_discovered_panel_id(&ret_info->smbios_fields[CHID_EDID_PANEL]);
|
||||
}
|
||||
|
||||
static void smbios_info_done(SmbiosInfo *info) {
|
||||
@@ -100,7 +103,7 @@ EFI_STATUS chid_match(const void *hwid_buffer, size_t hwid_length, uint32_t matc
|
||||
const Device *devices = ASSERT_PTR(hwid_buffer);
|
||||
|
||||
EFI_GUID chids[CHID_TYPES_MAX] = {};
|
||||
static const size_t priority[] = { 3, 6, 8, 10, 4, 5, 7, 9, 11 }; /* From most to least specific. */
|
||||
static const size_t priority[] = { 17, 16, 15, 3, 6, 8, 10, 4, 5, 7, 9, 11 }; /* From most to least specific. */
|
||||
|
||||
status = populate_board_chids(chids);
|
||||
if (EFI_STATUS_IS_ERROR(status))
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "edid.h"
|
||||
#include "edid-fundamental.h"
|
||||
#include "log.h"
|
||||
#include "proto/edid-discovered.h"
|
||||
#include "util.h"
|
||||
|
||||
EFI_STATUS edid_get_discovered_panel_id(char16_t **ret_panel) {
|
||||
assert(ret_panel);
|
||||
|
||||
EFI_EDID_DISCOVERED_PROTOCOL *edid_discovered = NULL;
|
||||
EFI_STATUS status = BS->LocateProtocol(MAKE_GUID_PTR(EFI_EDID_DISCOVERED_PROTOCOL), NULL, (void **) &edid_discovered);
|
||||
if (EFI_STATUS_IS_ERROR(status))
|
||||
return status;
|
||||
|
||||
if (!edid_discovered)
|
||||
return EFI_UNSUPPORTED;
|
||||
if (!edid_discovered->Edid)
|
||||
return EFI_UNSUPPORTED;
|
||||
if (edid_discovered->SizeOfEdid == 0)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
EdidHeader header;
|
||||
if (edid_parse_blob(edid_discovered->Edid, edid_discovered->SizeOfEdid, &header) < 0)
|
||||
return EFI_INCOMPATIBLE_VERSION;
|
||||
|
||||
_cleanup_free_ char16_t *panel = xnew0(char16_t, 8);
|
||||
if (edid_get_panel_id(&header, panel) < 0)
|
||||
return EFI_INVALID_PARAMETER;
|
||||
|
||||
*ret_panel = TAKE_PTR(panel);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "efi.h"
|
||||
|
||||
EFI_STATUS edid_get_discovered_panel_id(char16_t **ret_panel);
|
||||
@@ -3,7 +3,6 @@
|
||||
"name": "Device 1",
|
||||
"compatible": "test,device-1",
|
||||
"hwids": [
|
||||
"2bdcafa9-727c-5e8b-b13b-236f1391c25a",
|
||||
"c87886da-8026-5149-8c95-6d2504c1b7f9"
|
||||
"75caebc1-5b41-5d33-a262-154720304180"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -292,6 +292,7 @@ libefi_sources = files(
|
||||
'device-path-util.c',
|
||||
'devicetree.c',
|
||||
'drivers.c',
|
||||
'edid.c',
|
||||
'efi-firmware.c',
|
||||
'efi-string.c',
|
||||
'efivars.c',
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "efi.h"
|
||||
|
||||
#define EFI_EDID_DISCOVERED_PROTOCOL_GUID \
|
||||
GUID_DEF(0x1c0c34f6, 0xd380, 0x41fa, 0xa0, 0x49, 0x8a, 0xd0, 0x6c, 0x1a, 0x66, 0xaa)
|
||||
|
||||
typedef struct {
|
||||
uint32_t SizeOfEdid;
|
||||
uint8_t *Edid;
|
||||
} EFI_EDID_DISCOVERED_PROTOCOL;
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "chid.h"
|
||||
#include "edid.h"
|
||||
#include "efi-string.h"
|
||||
#include "smbios.h"
|
||||
#include "tests.h"
|
||||
|
||||
@@ -12,6 +14,7 @@ extern size_t hwids_section_len;
|
||||
|
||||
static struct {
|
||||
const RawSmbiosInfo smbios_info;
|
||||
const char16_t *panel_id;
|
||||
uint32_t device_type;
|
||||
} info[] = {
|
||||
{
|
||||
@@ -23,6 +26,7 @@ static struct {
|
||||
.baseboard_product = "FODM1",
|
||||
.baseboard_manufacturer = "First ODM",
|
||||
},
|
||||
.panel_id = u"TST42",
|
||||
.device_type = DEVICE_TYPE_DEVICETREE,
|
||||
},
|
||||
{
|
||||
@@ -79,9 +83,21 @@ void smbios_raw_info_get_cached(RawSmbiosInfo *ret_info) {
|
||||
*ret_info = current_info;
|
||||
}
|
||||
|
||||
static const char16_t *current_panel = NULL;
|
||||
EFI_STATUS edid_get_discovered_panel_id(char16_t **ret_panel) {
|
||||
assert(ret_panel);
|
||||
|
||||
if (!current_panel)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
*ret_panel = xstrdup16(current_panel);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
TEST(chid_match) {
|
||||
for (size_t i = 0; i < ELEMENTSOF(info); i++) {
|
||||
current_info = info[i].smbios_info;
|
||||
current_panel = info[i].panel_id;
|
||||
const Device *dev = NULL;
|
||||
/* Match and check */
|
||||
ASSERT_EQ(chid_match(hwids_section_data, hwids_section_len, info[i].device_type, &dev), EFI_SUCCESS);
|
||||
|
||||
@@ -233,7 +233,10 @@ void *find_configuration_table(const EFI_GUID *guid);
|
||||
char16_t *get_extra_dir(const EFI_DEVICE_PATH *file_path);
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define be16toh(x) __builtin_bswap16(x)
|
||||
# define be32toh(x) __builtin_bswap32(x)
|
||||
# define le16toh(x) (x)
|
||||
# define le32toh(x) (x)
|
||||
#else
|
||||
# error "Unexpected byte order in EFI mode?"
|
||||
#endif
|
||||
|
||||
@@ -148,6 +148,21 @@ const uint32_t chid_smbios_table[CHID_TYPES_MAX] = {
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT),
|
||||
|
||||
[14] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER),
|
||||
|
||||
/* Extra non-standard CHIDs */
|
||||
|
||||
[EXTRA_CHID_BASE + 0] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME)|
|
||||
(UINT32_C(1) << CHID_EDID_PANEL),
|
||||
|
||||
[EXTRA_CHID_BASE + 1] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_EDID_PANEL),
|
||||
|
||||
[EXTRA_CHID_BASE + 2] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_SKU) |
|
||||
(UINT32_C(1) << CHID_EDID_PANEL),
|
||||
};
|
||||
|
||||
void chid_calculate(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], EFI_GUID ret_chids[static CHID_TYPES_MAX]) {
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
#include "efi-fundamental.h"
|
||||
#include "string-util-fundamental.h"
|
||||
|
||||
#define CHID_TYPES_MAX 15
|
||||
#define CHID_TYPES_MAX 18
|
||||
/* Any chids starting from EXTRA_CHID_BASE are non-standard and are subject to change and renumeration at any time */
|
||||
#define EXTRA_CHID_BASE 15
|
||||
|
||||
typedef enum ChidSmbiosFields {
|
||||
CHID_SMBIOS_MANUFACTURER,
|
||||
@@ -25,6 +27,7 @@ typedef enum ChidSmbiosFields {
|
||||
CHID_SMBIOS_BIOS_MAJOR,
|
||||
CHID_SMBIOS_BIOS_MINOR,
|
||||
CHID_SMBIOS_ENCLOSURE_TYPE,
|
||||
CHID_EDID_PANEL,
|
||||
_CHID_SMBIOS_FIELDS_MAX,
|
||||
} ChidSmbiosFields;
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "edid-fundamental.h"
|
||||
#include "efivars-fundamental.h"
|
||||
|
||||
#define EDID_FIXED_HEADER_PATTERN "\x00\xFF\xFF\xFF\xFF\xFF\xFF"
|
||||
assert_cc(sizeof_field(EdidHeader, pattern) == sizeof(EDID_FIXED_HEADER_PATTERN));
|
||||
|
||||
int edid_parse_blob(const void *blob, size_t blob_size, EdidHeader *ret_header) {
|
||||
assert(ret_header);
|
||||
|
||||
/* EDID size is at least 128 as per the specification */
|
||||
if (blob_size < 128)
|
||||
return -EINVAL;
|
||||
|
||||
const EdidHeader *edid_header = ASSERT_PTR(blob);
|
||||
if (memcmp(edid_header->pattern, EDID_FIXED_HEADER_PATTERN, sizeof(EDID_FIXED_HEADER_PATTERN)) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*ret_header = (EdidHeader) {
|
||||
.pattern = EDID_FIXED_HEADER_PATTERN,
|
||||
.manufacturer_id = be16toh(edid_header->manufacturer_id),
|
||||
.manufacturer_product_code = le16toh(edid_header->manufacturer_product_code),
|
||||
.serial_number = le32toh(edid_header->serial_number),
|
||||
.week_of_manufacture = edid_header->week_of_manufacture,
|
||||
.year_of_manufacture = edid_header->year_of_manufacture,
|
||||
.edid_version = edid_header->edid_version,
|
||||
.edid_revision = edid_header->edid_revision,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int edid_get_panel_id(const EdidHeader *edid_header, char16_t ret_panel[static 8]) {
|
||||
assert(edid_header);
|
||||
assert(ret_panel);
|
||||
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
uint8_t letter = (edid_header->manufacturer_id >> (5 * i)) & 0b11111;
|
||||
if (letter > 0b11010)
|
||||
return -EINVAL;
|
||||
ret_panel[2 - i] = letter + 'A' - 1;
|
||||
}
|
||||
ret_panel[3] = LOWERCASE_HEXDIGITS[(edid_header->manufacturer_product_code >> 12) & 0x0F];
|
||||
ret_panel[4] = LOWERCASE_HEXDIGITS[(edid_header->manufacturer_product_code >> 8) & 0x0F];
|
||||
ret_panel[5] = LOWERCASE_HEXDIGITS[(edid_header->manufacturer_product_code >> 4) & 0x0F];
|
||||
ret_panel[6] = LOWERCASE_HEXDIGITS[(edid_header->manufacturer_product_code >> 0) & 0x0F];
|
||||
ret_panel[7] = L'\0';
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#if SD_BOOT
|
||||
# include "efi-string.h"
|
||||
# include "util.h"
|
||||
#else
|
||||
# include <endian.h>
|
||||
# include <stddef.h>
|
||||
# include <stdint.h>
|
||||
# include <uchar.h>
|
||||
#endif
|
||||
|
||||
#include "string-util-fundamental.h"
|
||||
|
||||
/* EDID structure, version 1.4 */
|
||||
typedef struct EdidHeader {
|
||||
uint8_t pattern[8]; /* fixed pattern */
|
||||
uint16_t manufacturer_id; /* big-endian 3-letter code */
|
||||
uint16_t manufacturer_product_code; /* little-endian */
|
||||
uint32_t serial_number; /* little-endian */
|
||||
uint8_t week_of_manufacture; /* week or model year flag (0xFF) */
|
||||
uint8_t year_of_manufacture; /* year or model if flag is set (0 is 1990) */
|
||||
uint8_t edid_version; /* 0x01 for 1.3 and 1.4 */
|
||||
uint8_t edid_revision; /* 0x03 for 1.3, 0x04 for 1.4 */
|
||||
} _packed_ EdidHeader;
|
||||
|
||||
int edid_parse_blob(const void *blob, size_t blob_size, EdidHeader *ret_header);
|
||||
int edid_get_panel_id(const EdidHeader *edid_header, char16_t ret_panel[static 8]);
|
||||
@@ -5,6 +5,7 @@ fundamental_include = include_directories('.')
|
||||
fundamental_sources = files(
|
||||
'bootspec-fundamental.c',
|
||||
'chid-fundamental.c',
|
||||
'edid-fundamental.c',
|
||||
'efivars-fundamental.c',
|
||||
'iovec-util-fundamental.h',
|
||||
'sha1-fundamental.c',
|
||||
|
||||
@@ -11,6 +11,23 @@
|
||||
#include "assert-fundamental.h"
|
||||
#include "macro-fundamental.h"
|
||||
|
||||
/* What is interpreted as whitespace? */
|
||||
#define WHITESPACE " \t\n\r"
|
||||
#define NEWLINE "\n\r"
|
||||
#define QUOTES "\"\'"
|
||||
#define COMMENTS "#;"
|
||||
#define GLOB_CHARS "*?["
|
||||
#define DIGITS "0123456789"
|
||||
#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
|
||||
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
|
||||
#define ALPHANUMERICAL LETTERS DIGITS
|
||||
#define HEXDIGITS DIGITS "abcdefABCDEF"
|
||||
#define LOWERCASE_HEXDIGITS DIGITS "abcdef"
|
||||
#define URI_RESERVED ":/?#[]@!$&'()*+;=" /* [RFC3986] */
|
||||
#define URI_UNRESERVED ALPHANUMERICAL "-._~" /* [RFC3986] */
|
||||
#define URI_VALID URI_RESERVED URI_UNRESERVED /* [RFC3986] */
|
||||
|
||||
#if SD_BOOT
|
||||
# define strlen strlen16
|
||||
# define strcmp strcmp16
|
||||
|
||||
@@ -1064,6 +1064,7 @@ LIBSYSTEMD_258 {
|
||||
global:
|
||||
sd_bus_message_dump_json;
|
||||
sd_device_enumerator_add_all_parents;
|
||||
sd_device_get_sysattr_value_with_size;
|
||||
sd_json_variant_type_from_string;
|
||||
sd_json_variant_type_to_string;
|
||||
sd_varlink_get_current_method;
|
||||
|
||||
@@ -2333,6 +2333,8 @@ void device_clear_sysattr_cache(sd_device *device) {
|
||||
typedef struct SysAttrCacheEntry {
|
||||
char *key;
|
||||
char *value;
|
||||
char *value_stripped;
|
||||
size_t size;
|
||||
int error;
|
||||
} SysAttrCacheEntry;
|
||||
|
||||
@@ -2342,6 +2344,7 @@ static SysAttrCacheEntry* sysattr_cache_entry_free(SysAttrCacheEntry *p) {
|
||||
|
||||
free(p->key);
|
||||
free(p->value);
|
||||
free(p->value_stripped);
|
||||
return mfree(p);
|
||||
}
|
||||
|
||||
@@ -2350,7 +2353,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
char, path_hash_func, path_compare,
|
||||
SysAttrCacheEntry, sysattr_cache_entry_free);
|
||||
|
||||
static int device_cache_sysattr_value_full(sd_device *device, char *key, char *value, int error, bool ignore_uevent) {
|
||||
static int device_cache_sysattr_value_full(sd_device *device, char *key, char *value, size_t size, int error, bool ignore_uevent) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
@@ -2375,9 +2378,20 @@ static int device_cache_sysattr_value_full(sd_device *device, char *key, char *v
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
_cleanup_free_ char *value_stripped = NULL;
|
||||
|
||||
if (value) {
|
||||
value_stripped = memdup_suffix0(value, size);
|
||||
if (!value_stripped)
|
||||
return -ENOMEM;
|
||||
delete_trailing_chars(value_stripped, NEWLINE);
|
||||
}
|
||||
|
||||
*entry = (SysAttrCacheEntry) {
|
||||
.key = key,
|
||||
.value = value,
|
||||
.value_stripped = value_stripped,
|
||||
.size = size,
|
||||
.error = error,
|
||||
};
|
||||
|
||||
@@ -2386,14 +2400,15 @@ static int device_cache_sysattr_value_full(sd_device *device, char *key, char *v
|
||||
return r;
|
||||
|
||||
TAKE_PTR(entry);
|
||||
TAKE_PTR(value_stripped);
|
||||
return 1; /* cached */
|
||||
}
|
||||
|
||||
int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error) {
|
||||
return device_cache_sysattr_value_full(device, key, value, error, /* ignore_uevent = */ true);
|
||||
return device_cache_sysattr_value_full(device, key, value, strlen(value), error, /* ignore_uevent = */ true);
|
||||
}
|
||||
|
||||
static int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
|
||||
static int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value, size_t *ret_size) {
|
||||
SysAttrCacheEntry *entry;
|
||||
|
||||
assert(device);
|
||||
@@ -2408,7 +2423,9 @@ static int device_get_cached_sysattr_value(sd_device *device, const char *key, c
|
||||
return -entry->error;
|
||||
}
|
||||
if (ret_value)
|
||||
*ret_value = entry->value;
|
||||
*ret_value = ret_size ? entry->value : entry->value_stripped;
|
||||
if (ret_size)
|
||||
*ret_size = entry->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2459,16 +2476,17 @@ int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **r
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
|
||||
_public_ int sd_device_get_sysattr_value_with_size(sd_device *device, const char *sysattr, const char **ret_value, size_t *ret_size) {
|
||||
_cleanup_free_ char *resolved = NULL, *value = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
size_t size = 0;
|
||||
int r;
|
||||
|
||||
assert_return(device, -EINVAL);
|
||||
assert_return(sysattr, -EINVAL);
|
||||
|
||||
/* Look for possibly already cached result. */
|
||||
r = device_get_cached_sysattr_value(device, sysattr, ret_value);
|
||||
r = device_get_cached_sysattr_value(device, sysattr, ret_value, ret_size);
|
||||
if (r != -ENOANO)
|
||||
return r;
|
||||
|
||||
@@ -2487,6 +2505,9 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
|
||||
return -ENOMEM;
|
||||
|
||||
r = readlink_value(prefixed, &value);
|
||||
if (r >= 0)
|
||||
size = strlen(value);
|
||||
|
||||
if (r != -EINVAL) /* -EINVAL means the path is not a symlink. */
|
||||
goto cache_result;
|
||||
}
|
||||
@@ -2496,18 +2517,16 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
|
||||
goto cache_result;
|
||||
|
||||
/* Look for cached result again with the resolved path. */
|
||||
r = device_get_cached_sysattr_value(device, resolved, ret_value);
|
||||
r = device_get_cached_sysattr_value(device, resolved, ret_value, ret_size);
|
||||
if (r != -ENOANO)
|
||||
return r;
|
||||
|
||||
/* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to also get the
|
||||
* size of the result. See issue #20025. */
|
||||
size_t size;
|
||||
r = read_virtual_file_fd(fd, SIZE_MAX, &value, &size);
|
||||
if (r < 0)
|
||||
goto cache_result;
|
||||
|
||||
delete_trailing_chars(value, NEWLINE);
|
||||
r = 0;
|
||||
|
||||
cache_result:
|
||||
@@ -2521,7 +2540,7 @@ cache_result:
|
||||
return RET_GATHER(r, -ENOMEM);
|
||||
}
|
||||
|
||||
int k = device_cache_sysattr_value_full(device, resolved, value, -r, /* ignore_uevent = */ false);
|
||||
int k = device_cache_sysattr_value_full(device, resolved, value, size, -r, /* ignore_uevent = */ false);
|
||||
if (k < 0) {
|
||||
if (r < 0)
|
||||
log_device_debug_errno(device, k,
|
||||
@@ -2541,13 +2560,18 @@ cache_result:
|
||||
}
|
||||
assert(k > 0);
|
||||
|
||||
if (ret_value && r >= 0)
|
||||
*ret_value = value;
|
||||
|
||||
/* device_cache_sysattr_value_full() takes 'resolved' and 'value' on success. */
|
||||
TAKE_PTR(resolved);
|
||||
sysattr = TAKE_PTR(resolved);
|
||||
TAKE_PTR(value);
|
||||
return r;
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return device_get_cached_sysattr_value(device, sysattr, ret_value, ret_size);
|
||||
}
|
||||
|
||||
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
|
||||
return sd_device_get_sysattr_value_with_size(device, sysattr, ret_value, NULL);
|
||||
}
|
||||
|
||||
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) {
|
||||
|
||||
@@ -133,6 +133,18 @@ static void test_sd_device_one(sd_device *d) {
|
||||
ASSERT_ERROR(r, ENOENT);
|
||||
}
|
||||
|
||||
if (streq(subsystem, "drm")) {
|
||||
const char *edid_content;
|
||||
size_t edid_size = 0;
|
||||
|
||||
r = sd_device_get_sysattr_value_with_size(d, "edid", &edid_content, &edid_size);
|
||||
if (r < 0)
|
||||
ASSERT_ERROR(r, ENOENT);
|
||||
|
||||
/* at least 128 if monitor is connected, otherwise 0 */
|
||||
ASSERT_TRUE(edid_size == 0 || edid_size >= 128);
|
||||
}
|
||||
|
||||
is_block = streq_ptr(subsystem, "block");
|
||||
|
||||
r = sd_device_get_devname(d, &devname);
|
||||
|
||||
@@ -108,7 +108,8 @@ int sd_device_has_tag(sd_device *device, const char *tag);
|
||||
int sd_device_has_current_tag(sd_device *device, const char *tag);
|
||||
int sd_device_get_property_value(sd_device *device, const char *key, const char **value);
|
||||
int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret);
|
||||
int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value);
|
||||
int sd_device_get_sysattr_value_with_size(sd_device *device, const char *sysattr, const char **ret_value, size_t *ret_size);
|
||||
int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value);
|
||||
|
||||
int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *value);
|
||||
int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4);
|
||||
|
||||
Reference in New Issue
Block a user