From f504ce8407bdb251170496f4b12be48ef7c21e38 Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Wed, 7 May 2025 18:01:07 +0300 Subject: [PATCH 1/8] fundamental: Move common string constants from basic/string-util.h to string-util-fundamental.h --- src/basic/string-util.h | 17 ----------------- src/fundamental/string-util-fundamental.h | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 03bff1d5fc..e00a5f5977 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -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; diff --git a/src/fundamental/string-util-fundamental.h b/src/fundamental/string-util-fundamental.h index a9638b4d0e..b523b6c8bf 100644 --- a/src/fundamental/string-util-fundamental.h +++ b/src/fundamental/string-util-fundamental.h @@ -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 From edda8d51f1cfb282d91ceebd61a8e4fc9e126dce Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:37:13 +0300 Subject: [PATCH 2/8] boot: Add be16toh, le16toh and le32toh --- src/boot/util.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/boot/util.h b/src/boot/util.h index 115258d5ee..2145b6e258 100644 --- a/src/boot/util.h +++ b/src/boot/util.h @@ -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 From c453aac65fda0043c714cf90d1d315dfb302999d Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:38:24 +0300 Subject: [PATCH 3/8] fundamental: Introduce EDID header parsing --- src/fundamental/edid-fundamental.c | 49 ++++++++++++++++++++++++++++++ src/fundamental/edid-fundamental.h | 29 ++++++++++++++++++ src/fundamental/meson.build | 1 + 3 files changed, 79 insertions(+) create mode 100644 src/fundamental/edid-fundamental.c create mode 100644 src/fundamental/edid-fundamental.h diff --git a/src/fundamental/edid-fundamental.c b/src/fundamental/edid-fundamental.c new file mode 100644 index 0000000000..9657d96704 --- /dev/null +++ b/src/fundamental/edid-fundamental.c @@ -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; +} diff --git a/src/fundamental/edid-fundamental.h b/src/fundamental/edid-fundamental.h new file mode 100644 index 0000000000..52473784d9 --- /dev/null +++ b/src/fundamental/edid-fundamental.h @@ -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 +# include +# include +# include +#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]); diff --git a/src/fundamental/meson.build b/src/fundamental/meson.build index 7b72372e83..5536be6e4c 100644 --- a/src/fundamental/meson.build +++ b/src/fundamental/meson.build @@ -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', From e86474151106df8619b40085e50af1c8945f9be3 Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:56:58 +0300 Subject: [PATCH 4/8] boot: Add EDID parsing Will be used for identifying the currently used display panel and choosing the appropriate DTB --- src/boot/edid.c | 34 ++++++++++++++++++++++++++++++++ src/boot/edid.h | 6 ++++++ src/boot/hwids/device1.json | 3 +-- src/boot/meson.build | 1 + src/boot/proto/edid-discovered.h | 12 +++++++++++ src/boot/test-chid-match.c | 16 +++++++++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/boot/edid.c create mode 100644 src/boot/edid.h create mode 100644 src/boot/proto/edid-discovered.h diff --git a/src/boot/edid.c b/src/boot/edid.c new file mode 100644 index 0000000000..62e338353e --- /dev/null +++ b/src/boot/edid.c @@ -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; +} diff --git a/src/boot/edid.h b/src/boot/edid.h new file mode 100644 index 0000000000..9832dadaab --- /dev/null +++ b/src/boot/edid.h @@ -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); diff --git a/src/boot/hwids/device1.json b/src/boot/hwids/device1.json index 6649a76491..5fa718c029 100644 --- a/src/boot/hwids/device1.json +++ b/src/boot/hwids/device1.json @@ -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" ] } diff --git a/src/boot/meson.build b/src/boot/meson.build index d4bd202f6f..c2a894d323 100644 --- a/src/boot/meson.build +++ b/src/boot/meson.build @@ -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', diff --git a/src/boot/proto/edid-discovered.h b/src/boot/proto/edid-discovered.h new file mode 100644 index 0000000000..7c83fcdcaf --- /dev/null +++ b/src/boot/proto/edid-discovered.h @@ -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; diff --git a/src/boot/test-chid-match.c b/src/boot/test-chid-match.c index 415681f201..5a22db70e3 100644 --- a/src/boot/test-chid-match.c +++ b/src/boot/test-chid-match.c @@ -4,6 +4,8 @@ #include #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); From 3481f8f86db20627a73b0087d344fc3b9fdb55ad Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:43:12 +0300 Subject: [PATCH 5/8] chid-fundamental: Introduce new CHID types These are extra types needed to distinguish devices by the installed display panel --- src/fundamental/chid-fundamental.c | 15 +++++++++++++++ src/fundamental/chid-fundamental.h | 5 ++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/fundamental/chid-fundamental.c b/src/fundamental/chid-fundamental.c index a99d0dae64..474aab7484 100644 --- a/src/fundamental/chid-fundamental.c +++ b/src/fundamental/chid-fundamental.c @@ -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]) { diff --git a/src/fundamental/chid-fundamental.h b/src/fundamental/chid-fundamental.h index 81c40e087f..5c210e006a 100644 --- a/src/fundamental/chid-fundamental.h +++ b/src/fundamental/chid-fundamental.h @@ -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; From eec1f041b159ea11233ac22f9b4832a82af4ece2 Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Mon, 24 Mar 2025 16:53:49 +0300 Subject: [PATCH 6/8] chid: Setup EDID CHIDs --- src/boot/chid.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/boot/chid.c b/src/boot/chid.c index 69e68e7d01..24739e956c 100644 --- a/src/boot/chid.c +++ b/src/boot/chid.c @@ -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)) From 907328749f7838762d901f302702b083c8b86b30 Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:40:29 +0300 Subject: [PATCH 7/8] sd-device: Introduce sd_device_get_sysattr_value_with_size() --- src/libsystemd/libsystemd.sym | 1 + src/libsystemd/sd-device/sd-device.c | 54 ++++++++++++++++------- src/libsystemd/sd-device/test-sd-device.c | 12 +++++ src/systemd/sd-device.h | 3 +- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 370e3b7c92..4543fb0e60 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -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; diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 98a10d5b1f..12763f53b5 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -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) { diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index 9758e07330..2db4f42c74 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -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); diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h index 8f6141b3df..71d8c89983 100644 --- a/src/systemd/sd-device.h +++ b/src/systemd/sd-device.h @@ -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); From 9fed4ec252ee6b4fce8a778222f93bdb3eb35395 Mon Sep 17 00:00:00 2001 From: anonymix007 <48598263+anonymix007@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:41:15 +0300 Subject: [PATCH 8/8] analyze-chid: Support EDID CHIDs --- man/systemd-analyze.xml | 9 +++ src/analyze/analyze-chid.c | 143 +++++++++++++++++++++++++++++++++++-- src/analyze/analyze.c | 15 ++++ src/analyze/analyze.h | 1 + 4 files changed, 161 insertions(+), 7 deletions(-) diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index b3975d5f03..e61c557fe9 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -1713,6 +1713,15 @@ LEGEND: M → sys_vendor (LENOVO) ┄ F → product_family (ThinkPad X1 Carbon G + + + + When provided with the chid command, use this sysfs path to a DRM + device to fetch EDID from. Example: /sys/class/drm/card1-HDMI-A-1/ + + + + diff --git a/src/analyze/analyze-chid.c b/src/analyze/analyze-chid.c index 01e49965f8..6d2c74345f 100644 --- a/src/analyze/analyze-chid.c +++ b/src/analyze/analyze-chid.c @@ -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) diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 0b029c5f01..f6a19f306f 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -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 */ } diff --git a/src/analyze/analyze.h b/src/analyze/analyze.h index 959b8a1b85..1ab018e071 100644 --- a/src/analyze/analyze.h +++ b/src/analyze/analyze.h @@ -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);