mirror of
https://github.com/systemd/systemd.git
synced 2024-10-27 10:25:37 +03:00
boot: Improve device_path_to_str_internal()
The UEFI spec has a generic `Path` node representation that can be used for device path nodes that are unknown. So we can use that instead of giving up when we see a node other than FilePath. This also simplifies the FilePath case by just using xasprintf(). The code is really just a fallback for silly firmware that does not implement EFI_DEVICE_PATH_TO_TEXT_PROTOCOL (looking at you, Apple). The correctness of this was tested by round-tripping it through EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL, which yielded an identical device compared to our input path.
This commit is contained in:
parent
e53e5c0ac1
commit
78fffdea37
@ -39,34 +39,41 @@ EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DE
|
||||
}
|
||||
|
||||
static char16_t *device_path_to_str_internal(const EFI_DEVICE_PATH *dp) {
|
||||
_cleanup_free_ char16_t *str = NULL;
|
||||
size_t size = 0;
|
||||
|
||||
for (const EFI_DEVICE_PATH *node = dp; !device_path_is_end(node);
|
||||
node = device_path_next_node(node)) {
|
||||
|
||||
if (node->Type != MEDIA_DEVICE_PATH || node->SubType != MEDIA_FILEPATH_DP)
|
||||
return NULL;
|
||||
|
||||
size_t path_size = node->Length;
|
||||
if (path_size <= offsetof(FILEPATH_DEVICE_PATH, PathName) || path_size % sizeof(char16_t))
|
||||
return NULL;
|
||||
path_size -= offsetof(FILEPATH_DEVICE_PATH, PathName);
|
||||
char16_t *str = NULL;
|
||||
|
||||
for (const EFI_DEVICE_PATH *node = dp; !device_path_is_end(node); node = device_path_next_node(node)) {
|
||||
_cleanup_free_ char16_t *old = str;
|
||||
str = xmalloc(size + path_size);
|
||||
if (old) {
|
||||
memcpy(str, old, size);
|
||||
str[size / sizeof(char16_t) - 1] = '\\';
|
||||
|
||||
if (node->Type == END_DEVICE_PATH_TYPE && node->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {
|
||||
str = xasprintf("%ls%s,", strempty(old), old ? "\\" : "");
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(str + (size / sizeof(char16_t)),
|
||||
((uint8_t *) node) + offsetof(FILEPATH_DEVICE_PATH, PathName),
|
||||
path_size);
|
||||
size += path_size;
|
||||
/* Special-case this so that FilePath-only device path string look and behave nicely. */
|
||||
if (node->Type == MEDIA_DEVICE_PATH && node->SubType == MEDIA_FILEPATH_DP) {
|
||||
str = xasprintf("%ls%s%ls",
|
||||
strempty(old),
|
||||
old ? "\\" : "",
|
||||
((FILEPATH_DEVICE_PATH *) node)->PathName);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Instead of coding all the different types and sub-types here we just use the
|
||||
* generic node form. This function is a best-effort for firmware that does not
|
||||
* provide the EFI_DEVICE_PATH_TO_TEXT_PROTOCOL after all. */
|
||||
|
||||
size_t size = node->Length - sizeof(EFI_DEVICE_PATH);
|
||||
_cleanup_free_ char16_t *hex_data = hexdump((uint8_t *) node + sizeof(EFI_DEVICE_PATH), size);
|
||||
str = xasprintf("%ls%sPath(%u,%u%s%ls)",
|
||||
strempty(old),
|
||||
old ? "/" : "",
|
||||
node->Type,
|
||||
node->SubType,
|
||||
size == 0 ? "" : ",",
|
||||
hex_data);
|
||||
}
|
||||
|
||||
return TAKE_PTR(str);
|
||||
return str;
|
||||
}
|
||||
|
||||
EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) {
|
||||
@ -79,13 +86,7 @@ EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) {
|
||||
|
||||
err = BS->LocateProtocol(MAKE_GUID_PTR(EFI_DEVICE_PATH_TO_TEXT_PROTOCOL), NULL, (void **) &dp_to_text);
|
||||
if (err != EFI_SUCCESS) {
|
||||
/* If the device path to text protocol is not available we can still do a best-effort attempt
|
||||
* to convert it ourselves if we are given filepath-only device path. */
|
||||
str = device_path_to_str_internal(dp);
|
||||
if (!str)
|
||||
return err;
|
||||
|
||||
*ret = TAKE_PTR(str);
|
||||
*ret = device_path_to_str_internal(dp);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user