Compare commits

...

8 Commits

Author SHA1 Message Date
anonymix007 f3e431b35e
Merge 8afbcd491e into 1a4c2e8807 2025-04-16 16:53:00 +03:00
anonymix007 8afbcd491e analyze-chid: Support EDID CHIDs 2025-04-02 22:36:09 +03:00
anonymix007 2f6b0c244c sd-device: Introduce sd_device_get_sysattr_value_full() 2025-04-02 22:36:09 +03:00
anonymix007 e9d6996138 chid: Setup EDID CHIDs 2025-04-02 22:36:09 +03:00
anonymix007 f4c98204d1 chid-fundamental: Introduce new CHID types
These are extra types needed to distinguish devices by the installed
display panel
2025-04-02 22:36:09 +03:00
anonymix007 162e5a40c2 boot: Add EDID parsing
Will be used for identifying the currently used display panel
and choosing the appropriate DTB
2025-04-02 22:36:09 +03:00
anonymix007 f5a6eb610c fundamental: Introduce EDID header parsing 2025-04-02 22:36:09 +03:00
anonymix007 a2f7f4c70d boot: Add be16toh, le16toh and le32toh 2025-04-02 22:36:09 +03:00
19 changed files with 324 additions and 21 deletions

View File

@ -1091,7 +1091,7 @@ io.systemd.credential:vmm.notify_socket=vsock-stream:2:254570042
</refsect2> </refsect2>
<refsect2> <refsect2>
<title><command>systemd-analyze chid</command></title> <title><command>systemd-analyze chid <optional>/sys/class/drm/&lt;card&gt;/edid</optional></command></title>
<para>Shows a list of Computer Hardware IDs (CHIDs) of the local system. These IDs identify the <para>Shows a list of Computer Hardware IDs (CHIDs) of the local system. These IDs identify the
system's computer hardware, based on SMBIOS data. See <ulink system's computer hardware, based on SMBIOS data. See <ulink

View File

@ -3,27 +3,42 @@
#include "analyze.h" #include "analyze.h"
#include "analyze-chid.h" #include "analyze-chid.h"
#include "chid-fundamental.h" #include "chid-fundamental.h"
#include "device-util.h"
#include "dirent-util.h"
#include "edid-fundamental.h"
#include "efi-api.h" #include "efi-api.h"
#include "escape.h" #include "escape.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "format-table.h" #include "format-table.h"
#include "parse-util.h" #include "parse-util.h"
#include "sd-device.h"
#include "strv.h" #include "strv.h"
#include "utf8.h" #include "utf8.h"
#include "virt.h" #include "virt.h"
static int parse_chid_type(const char *s, size_t *ret) { static int parse_chid_type(const char *s, size_t *ret) {
char *e;
unsigned u; unsigned u;
int r; int r;
assert(s); assert(s);
r = safe_atou(s, &u); if ((e = startswith(s, "ext"))) {
if (r < 0) r = safe_atou(e, &u);
return r; if (r < 0)
if (u >= CHID_TYPES_MAX) return r;
return -ERANGE; 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) if (ret)
*ret = u; *ret = u;
@ -43,6 +58,7 @@ static const char *const chid_smbios_friendly[_CHID_SMBIOS_FIELDS_MAX] = {
[CHID_SMBIOS_BIOS_MAJOR] = "bios-major", [CHID_SMBIOS_BIOS_MAJOR] = "bios-major",
[CHID_SMBIOS_BIOS_MINOR] = "bios-minor", [CHID_SMBIOS_BIOS_MINOR] = "bios-minor",
[CHID_SMBIOS_ENCLOSURE_TYPE] = "enclosure-type", [CHID_SMBIOS_ENCLOSURE_TYPE] = "enclosure-type",
[CHID_EDID_PANEL] = "edid-panel",
}; };
static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = { static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = {
@ -57,6 +73,7 @@ static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = {
[CHID_SMBIOS_BIOS_MAJOR] = 'R', [CHID_SMBIOS_BIOS_MAJOR] = 'R',
[CHID_SMBIOS_BIOS_MINOR] = 'r', [CHID_SMBIOS_BIOS_MINOR] = 'r',
[CHID_SMBIOS_ENCLOSURE_TYPE] = 'e', [CHID_SMBIOS_ENCLOSURE_TYPE] = 'e',
[CHID_EDID_PANEL] = 'E',
}; };
static char *chid_smbios_fields_string(uint32_t combination) { static char *chid_smbios_fields_string(uint32_t combination) {
@ -90,10 +107,17 @@ static int add_chid(Table *table, const EFI_GUID guids[static CHID_TYPES_MAX], s
if (!flags) if (!flags)
return log_oom(); 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, r = table_add_many(table,
TABLE_UINT, (unsigned) t, TABLE_STRING, flags,
TABLE_STRING, flags, TABLE_UUID, id);
TABLE_UUID, id);
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
@ -223,6 +247,71 @@ static int smbios_fields_acquire(char16_t *fields[static _CHID_SMBIOS_FIELDS_MAX
return 0; return 0;
} }
static int edid_parse(sd_device *drm_card, char16_t **ret_panel) {
assert(drm_card);
assert(ret_panel);
int r;
const char *edid_content = NULL;
size_t edid_size = 0;
r = sd_device_get_sysattr_value_full(drm_card, "edid", &edid_content, &edid_size);
if (r < 0)
return r;
if (edid_size == 0)
return -ENODEV;
EdidHeader header;
if (!edid_parse_blob(edid_content, edid_size, &header))
return -EBADMSG;
_cleanup_free_ char16_t *panel_id = new0(char16_t, 8);
if (!panel_id)
return log_oom();
if (!edid_get_panel_id(&header, panel_id))
return -EBADMSG;
*ret_panel = TAKE_PTR(panel_id);
return 0;
}
static int edid_search(char16_t **ret_panel) {
assert(ret_panel);
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
int r, n = 0;
r = sd_device_enumerator_new(&e);
if (r < 0)
return r;
r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
return r;
r = sd_device_enumerator_add_match_subsystem(e, "drm", true);
if (r < 0)
return r;
FOREACH_DEVICE(e, d) {
const char *drm_path = NULL;
r = edid_parse(d, ret_panel);
if (r == -ENOENT)
continue;
if (r < 0) {
r = sd_device_get_syspath(d, &drm_path);
if (r < 0)
return log_debug_errno(r, "Failed to get DRM device path: %m");
log_debug("Skipping DRM device with invalid EDID: %s", drm_path);
continue;
}
n++;
if (n > 1)
return -ENOTUNIQ;
}
return n = 0 ? -ENODEV : 0;
}
int verb_chid(int argc, char *argv[], void *userdata) { int verb_chid(int argc, char *argv[], void *userdata) {
_cleanup_(table_unrefp) Table *table = NULL; _cleanup_(table_unrefp) Table *table = NULL;
@ -243,6 +332,30 @@ int verb_chid(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return r; return r;
if (arg_drm_card_path) {
_cleanup_(sd_device_unrefp) sd_device *drm_card = NULL;
r = sd_device_new_from_path(&drm_card, arg_drm_card_path);
if (r < 0)
return log_error_errno(r, "Failed to open device %s: %m", arg_drm_card_path);
const char *subsys = NULL;
r = sd_device_get_subsystem(drm_card, &subsys);
if (r < 0)
return log_error_errno(r, "Failed to get subsystem for device %s: %m", arg_drm_card_path);
if (!streq(subsys, "drm"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot read EDID from a \"%s\" device", subsys);
r = edid_parse(drm_card, &smbios_fields[CHID_EDID_PANEL]);
if (r < 0)
return log_error_errno(r, "Failed to parse EDID for device %s: %m", arg_drm_card_path);
} else {
r = edid_search(&smbios_fields[CHID_EDID_PANEL]);
if (r == -ENOTUNIQ)
return log_error_errno(r, "Detected multiple monitors, use --drm-card option");
else if (r != -ENODEV && r < 0)
return log_error_errno(r, "Failed to find a valid EDID: %m");
}
EFI_GUID chids[CHID_TYPES_MAX] = {}; EFI_GUID chids[CHID_TYPES_MAX] = {};
chid_calculate((const char16_t* const*) smbios_fields, chids); chid_calculate((const char16_t* const*) smbios_fields, chids);
@ -257,7 +370,7 @@ int verb_chid(int argc, char *argv[], void *userdata) {
size_t t; size_t t;
r = parse_chid_type(*as, &t); r = parse_chid_type(*as, &t);
if (r < 0) 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); r = add_chid(table, chids, t);
if (r < 0) if (r < 0)

View File

@ -121,12 +121,14 @@ char *arg_profile = NULL;
bool arg_legend = true; bool arg_legend = true;
bool arg_table = false; bool arg_table = false;
ImagePolicy *arg_image_policy = NULL; ImagePolicy *arg_image_policy = NULL;
char *arg_drm_card_path = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep); STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
STATIC_DESTRUCTOR_REGISTER(arg_drm_card_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_unit, freep); STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
STATIC_DESTRUCTOR_REGISTER(arg_profile, freep); STATIC_DESTRUCTOR_REGISTER(arg_profile, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_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=PATH Operate on disk image as filesystem root\n"
" --image-policy=POLICY Specify disk image dissection policy\n" " --image-policy=POLICY Specify disk image dissection policy\n"
" -m --mask Parse parameter as numeric capability mask\n" " -m --mask Parse parameter as numeric capability mask\n"
" --drm-card=PATH Use this DRM card to get EDID\n"
"\nSee the %2$s for details.\n", "\nSee the %2$s for details.\n",
program_invocation_short_name, program_invocation_short_name,
link, link,
@ -333,6 +337,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TLDR, ARG_TLDR,
ARG_SCALE_FACTOR_SVG, ARG_SCALE_FACTOR_SVG,
ARG_DETAILED_SVG, ARG_DETAILED_SVG,
ARG_DRM_CARD_PATH,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -371,6 +376,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "mask", no_argument, NULL, 'm' }, { "mask", no_argument, NULL, 'm' },
{ "scale-svg", required_argument, NULL, ARG_SCALE_FACTOR_SVG }, { "scale-svg", required_argument, NULL, ARG_SCALE_FACTOR_SVG },
{ "detailed", no_argument, NULL, ARG_DETAILED_SVG }, { "detailed", no_argument, NULL, ARG_DETAILED_SVG },
{ "drm-card", required_argument, NULL, ARG_DRM_CARD_PATH },
{} {}
}; };
@ -580,6 +586,12 @@ static int parse_argv(int argc, char *argv[]) {
arg_detailed_svg = true; arg_detailed_svg = true;
break; break;
case ARG_DRM_CARD_PATH:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_drm_card_path);
if (r < 0)
return r;
break;
case '?': case '?':
return -EINVAL; return -EINVAL;
@ -638,6 +650,9 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_capability != CAPABILITY_LITERAL && !streq_ptr(argv[optind], "capability")) if (arg_capability != CAPABILITY_LITERAL && !streq_ptr(argv[optind], "capability"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --mask is only supported for capability."); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --mask is only supported for capability.");
if (arg_drm_card_path && !streq_ptr(argv[optind], "chid"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --drm-card is only supported for chid right now.");
return 1; /* work to do */ return 1; /* work to do */
} }

View File

@ -51,6 +51,7 @@ extern char *arg_profile;
extern bool arg_legend; extern bool arg_legend;
extern bool arg_table; extern bool arg_table;
extern ImagePolicy *arg_image_policy; extern ImagePolicy *arg_image_policy;
extern char *arg_drm_card_path;
int acquire_bus(sd_bus **bus, bool *use_full_bus); int acquire_bus(sd_bus **bus, bool *use_full_bus);

View File

@ -16,6 +16,7 @@
#include "chid.h" #include "chid.h"
#include "chid-fundamental.h" #include "chid-fundamental.h"
#include "edid.h"
#include "efi.h" #include "efi.h"
#include "sha1-fundamental.h" #include "sha1-fundamental.h"
#include "smbios.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_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_PRODUCT] = smbios_to_hashable_string(raw.baseboard_product);
ret_info->smbios_fields[CHID_SMBIOS_BASEBOARD_MANUFACTURER] = smbios_to_hashable_string(raw.baseboard_manufacturer); 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) { 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); const Device *devices = ASSERT_PTR(hwid_buffer);
EFI_GUID chids[CHID_TYPES_MAX] = {}; 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); status = populate_board_chids(chids);
if (EFI_STATUS_IS_ERROR(status)) if (EFI_STATUS_IS_ERROR(status))

36
src/boot/edid.c Normal file
View File

@ -0,0 +1,36 @@
/* 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;
/* EDID size is at least 128 as per the specification */
if (edid_discovered->SizeOfEdid < 128)
return EFI_BUFFER_TOO_SMALL;
EdidHeader header;
if (!edid_parse_blob(edid_discovered->Edid, edid_discovered->SizeOfEdid, &header))
return EFI_INCOMPATIBLE_VERSION;
*ret_panel = xnew0(char16_t, 8);
if (!edid_get_panel_id(&header, *ret_panel))
return EFI_INVALID_PARAMETER;
return EFI_SUCCESS;
}

6
src/boot/edid.h Normal file
View File

@ -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);

View File

@ -289,6 +289,7 @@ libefi_sources = files(
'device-path-util.c', 'device-path-util.c',
'devicetree.c', 'devicetree.c',
'drivers.c', 'drivers.c',
'edid.c',
'efi-firmware.c', 'efi-firmware.c',
'efi-string.c', 'efi-string.c',
'efivars.c', 'efivars.c',

View File

@ -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;

View File

@ -4,6 +4,7 @@
#include <stdint.h> #include <stdint.h>
#include "chid.h" #include "chid.h"
#include "edid.h"
#include "smbios.h" #include "smbios.h"
#include "tests.h" #include "tests.h"
@ -79,6 +80,11 @@ void smbios_raw_info_get_cached(RawSmbiosInfo *ret_info) {
*ret_info = current_info; *ret_info = current_info;
} }
EFI_STATUS edid_get_discovered_panel_id(char16_t **ret_panel) {
assert(ret_panel);
return EFI_UNSUPPORTED;
}
TEST(chid_match) { TEST(chid_match) {
for (size_t i = 0; i < ELEMENTSOF(info); i++) { for (size_t i = 0; i < ELEMENTSOF(info); i++) {
current_info = info[i].smbios_info; current_info = info[i].smbios_info;

View File

@ -242,7 +242,10 @@ void *find_configuration_table(const EFI_GUID *guid);
char16_t *get_extra_dir(const EFI_DEVICE_PATH *file_path); char16_t *get_extra_dir(const EFI_DEVICE_PATH *file_path);
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define be16toh(x) __builtin_bswap16(x)
# define be32toh(x) __builtin_bswap32(x) # define be32toh(x) __builtin_bswap32(x)
# define le16toh(x) (x)
# define le32toh(x) (x)
#else #else
# error "Unexpected byte order in EFI mode?" # error "Unexpected byte order in EFI mode?"
#endif #endif

View File

@ -147,6 +147,21 @@ const uint32_t chid_smbios_table[CHID_TYPES_MAX] = {
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT), (UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT),
[14] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER), [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]) { void chid_calculate(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], EFI_GUID ret_chids[static CHID_TYPES_MAX]) {

View File

@ -11,7 +11,9 @@
#include "efi-fundamental.h" #include "efi-fundamental.h"
#include "string-util-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 { typedef enum ChidSmbiosFields {
CHID_SMBIOS_MANUFACTURER, CHID_SMBIOS_MANUFACTURER,
@ -25,6 +27,7 @@ typedef enum ChidSmbiosFields {
CHID_SMBIOS_BIOS_MAJOR, CHID_SMBIOS_BIOS_MAJOR,
CHID_SMBIOS_BIOS_MINOR, CHID_SMBIOS_BIOS_MINOR,
CHID_SMBIOS_ENCLOSURE_TYPE, CHID_SMBIOS_ENCLOSURE_TYPE,
CHID_EDID_PANEL,
_CHID_SMBIOS_FIELDS_MAX, _CHID_SMBIOS_FIELDS_MAX,
} ChidSmbiosFields; } ChidSmbiosFields;

View File

@ -0,0 +1,47 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "edid-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));
bool 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 false;
const EdidHeader *edid_header = ASSERT_PTR(blob);
if (memcmp(edid_header->pattern, EDID_FIXED_HEADER_PATTERN, sizeof(EDID_FIXED_HEADER_PATTERN)) != 0)
return false;
*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 true;
}
bool edid_get_panel_id(const EdidHeader *edid_header, char16_t ret_panel[static 8]) {
assert(edid_header);
assert(ret_panel);
static const char hex[] = "0123456789abcdef";
for (size_t i = 0; i < 3; i++) {
uint8_t letter = (edid_header->manufacturer_id >> (5 * i)) & 0b11111;
if (letter > 0b11010)
return false;
ret_panel[2 - i] = letter + 'A' - 1;
}
ret_panel[3] = hex[(edid_header->manufacturer_product_code >> 12) & 0x0F];
ret_panel[4] = hex[(edid_header->manufacturer_product_code >> 8) & 0x0F];
ret_panel[5] = hex[(edid_header->manufacturer_product_code >> 4) & 0x0F];
ret_panel[6] = hex[(edid_header->manufacturer_product_code >> 0) & 0x0F];
ret_panel[7] = L'\0';
return true;
}

View File

@ -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;
bool edid_parse_blob(const void *blob, size_t blob_size, EdidHeader *ret_header);
bool edid_get_panel_id(const EdidHeader *edid_header, char16_t ret_panel[static 8]);

View File

@ -5,6 +5,7 @@ fundamental_include = include_directories('.')
fundamental_sources = files( fundamental_sources = files(
'bootspec-fundamental.c', 'bootspec-fundamental.c',
'chid-fundamental.c', 'chid-fundamental.c',
'edid-fundamental.c',
'efivars-fundamental.c', 'efivars-fundamental.c',
'iovec-util-fundamental.h', 'iovec-util-fundamental.h',
'sha1-fundamental.c', 'sha1-fundamental.c',

View File

@ -618,6 +618,7 @@ global:
sd_device_has_tag; sd_device_has_tag;
sd_device_get_property_value; sd_device_get_property_value;
sd_device_get_sysattr_value_full;
sd_device_get_sysattr_value; sd_device_get_sysattr_value;
sd_device_set_sysattr_value; sd_device_set_sysattr_value;

View File

@ -2333,6 +2333,7 @@ void device_clear_sysattr_cache(sd_device *device) {
typedef struct SysAttrCacheEntry { typedef struct SysAttrCacheEntry {
char *key; char *key;
char *value; char *value;
size_t size;
int error; int error;
} SysAttrCacheEntry; } SysAttrCacheEntry;
@ -2350,7 +2351,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
char, path_hash_func, path_compare, char, path_hash_func, path_compare,
SysAttrCacheEntry, sysattr_cache_entry_free); 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; int r;
assert(device); assert(device);
@ -2378,6 +2379,7 @@ static int device_cache_sysattr_value_full(sd_device *device, char *key, char *v
*entry = (SysAttrCacheEntry) { *entry = (SysAttrCacheEntry) {
.key = key, .key = key,
.value = value, .value = value,
.size = size,
.error = error, .error = error,
}; };
@ -2390,10 +2392,10 @@ static int device_cache_sysattr_value_full(sd_device *device, char *key, char *v
} }
int device_cache_sysattr_value(sd_device *device, char *key, char *value, int error) { 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; SysAttrCacheEntry *entry;
assert(device); assert(device);
@ -2409,6 +2411,8 @@ static int device_get_cached_sysattr_value(sd_device *device, const char *key, c
} }
if (ret_value) if (ret_value)
*ret_value = entry->value; *ret_value = entry->value;
if (ret_size)
*ret_size = entry->size;
return 0; return 0;
} }
@ -2459,16 +2463,17 @@ int device_chase(sd_device *device, const char *path, ChaseFlags flags, char **r
return 0; 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_full(sd_device *device, const char *sysattr, const char **ret_value, size_t *ret_size) {
_cleanup_free_ char *resolved = NULL, *value = NULL; _cleanup_free_ char *resolved = NULL, *value = NULL;
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
size_t size = 0;
int r; int r;
assert_return(device, -EINVAL); assert_return(device, -EINVAL);
assert_return(sysattr, -EINVAL); assert_return(sysattr, -EINVAL);
/* Look for possibly already cached result. */ /* 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) if (r != -ENOANO)
return r; return r;
@ -2496,13 +2501,12 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
goto cache_result; goto cache_result;
/* Look for cached result again with the resolved path. */ /* 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) if (r != -ENOANO)
return r; return r;
/* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to also get the /* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to also get the
* size of the result. See issue #20025. */ * size of the result. See issue #20025. */
size_t size;
r = read_virtual_file_fd(fd, SIZE_MAX, &value, &size); r = read_virtual_file_fd(fd, SIZE_MAX, &value, &size);
if (r < 0) if (r < 0)
goto cache_result; goto cache_result;
@ -2521,7 +2525,7 @@ cache_result:
return RET_GATHER(r, -ENOMEM); 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 (k < 0) {
if (r < 0) if (r < 0)
log_device_debug_errno(device, k, log_device_debug_errno(device, k,
@ -2543,6 +2547,8 @@ cache_result:
if (ret_value && r >= 0) if (ret_value && r >= 0)
*ret_value = value; *ret_value = value;
if (ret_size && r >= 0)
*ret_size = size;
/* device_cache_sysattr_value_full() takes 'resolved' and 'value' on success. */ /* device_cache_sysattr_value_full() takes 'resolved' and 'value' on success. */
TAKE_PTR(resolved); TAKE_PTR(resolved);
@ -2550,6 +2556,10 @@ cache_result:
return r; return r;
} }
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
return sd_device_get_sysattr_value_full(device, sysattr, ret_value, NULL);
}
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) { int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) {
const char *value; const char *value;
int r; int r;

View File

@ -109,7 +109,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_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_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_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_full(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_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); int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) _sd_printf_(3, 4);