Skip to content

[libc][uefi] add crt1 #132150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libc/config/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "gpu/app.h"
#elif defined(__linux__)
#include "linux/app.h"
#elif defined(__UEFI__)
#include "uefi/app.h"
#endif

#endif // LLVM_LIBC_CONFIG_APP_H
34 changes: 34 additions & 0 deletions libc/config/uefi/app.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===-- Classes to capture properites of UEFI applications ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_CONFIG_UEFI_APP_H
#define LLVM_LIBC_CONFIG_UEFI_APP_H

#include "include/llvm-libc-types/EFI_HANDLE.h"
#include "include/llvm-libc-types/EFI_SYSTEM_TABLE.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/architectures.h"

#include <stdint.h>

namespace LIBC_NAMESPACE_DECL {

// Data structure which captures properties of a UEFI application.
struct AppProperties {
// UEFI system table
EFI_SYSTEM_TABLE *system_table;

// UEFI image handle
EFI_HANDLE image_handle;
};

[[gnu::weak]] extern AppProperties app;

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_CONFIG_UEFI_APP_H
1 change: 0 additions & 1 deletion libc/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,6 @@ add_header_macro(
add_header_macro(
uefi
../libc/include/Uefi.yaml
Uefi.h.def
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you're removing the usage of Uefi.h.def you should also remove the file.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember this change... Not sure why the line was removed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh now I remember, removed the file now.

Uefi.h
DEPENDS
.llvm_libc_common_h
Expand Down
6 changes: 1 addition & 5 deletions libc/include/Uefi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,4 @@ types:
- type_name: EFI_SYSTEM_TABLE
enums: []
functions: []
objects:
- object_name: efi_system_table
object_type: EFI_SYSTEM_TABLE *
- object_name: efi_image_handle
object_type: EFI_HANDLE
objects: []
2 changes: 1 addition & 1 deletion libc/include/llvm-libc-types/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ add_header(EFI_HANDLE HDR EFI_HANDLE.h)
add_header(EFI_TIME HDR EFI_TIME.h DEPENDS libc.include.llvm-libc-macros.stdint_macros)
add_header(EFI_TIMER_DELAY HDR EFI_TIMER_DELAY.h)
add_header(EFI_TPL HDR EFI_TPL.h DEPENDS .size_t)
add_header(EFI_STATUS HDR EFI_STATUS.h DEPENDS .size_t)
add_header(EFI_STATUS HDR EFI_STATUS.h DEPENDS libc.include.llvm-libc-macros.stdint_macros)

add_header(EFI_OPEN_PROTOCOL_INFORMATION_ENTRY
HDR
Expand Down
48 changes: 46 additions & 2 deletions libc/include/llvm-libc-types/EFI_STATUS.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,52 @@
#ifndef LLVM_LIBC_TYPES_EFI_STATUS_H
#define LLVM_LIBC_TYPES_EFI_STATUS_H

#include "size_t.h"
#include "../llvm-libc-macros/stdint-macros.h"

typedef size_t EFI_STATUS;
typedef uintptr_t EFI_STATUS;

#define EFI_SUCCESS 0

#define EFI_LOAD_ERROR 1
#define EFI_INVALID_PARAMETER 2
#define EFI_UNSUPPORTED 3
#define EFI_BAD_BUFFER_SIZE 4
#define EFI_BUFFER_TOO_SMALL 5
#define EFI_NOT_READY 6
#define EFI_DEVICE_ERROR 7
#define EFI_WRITE_PROTECTED 8
#define EFI_OUT_OF_RESOURCES 9
#define EFI_VOLUME_CORRUPTED 10
#define EFI_VOLUME_FULL 11
#define EFI_NO_MEDIA 12
#define EFI_MEDIA_CHANGED 13
#define EFI_NOT_FOUND 14
#define EFI_ACCESS_DENIED 15
#define EFI_NO_RESPONSE 16
#define EFI_NO_MAPPING 17
#define EFI_TIMEOUT 18
#define EFI_NOT_STARTED 19
#define EFI_ALREADY_STARTED 20
#define EFI_ABORTED 21
#define EFI_ICMP_ERROR 22
#define EFI_TFTP_ERROR 23
#define EFI_PROTOCOL_ERROR 24
#define EFI_INCOMPATIBLE_VERSION 25
#define EFI_SECURITY_VIOLATION 26
#define EFI_CRC_ERROR 27
#define EFI_END_OF_MEDIA 28
#define EFI_END_OF_FILE 31
#define EFI_INVALID_LANGUAGE 32
#define EFI_COMPROMISED_DATA 33
#define EFI_IP_ADDRESS_CONFLICT 34
#define EFI_HTTP_ERROR 35

#define EFI_WARN_UNKNOWN_GLYPH 1
#define EFI_WARN_DELETE_FAILURE 2
#define EFI_WARN_WRITE_FAILURE 3
#define EFI_WARN_BUFFER_TOO_SMALL 4
#define EFI_WARN_STALE_DATA 5
#define EFI_WARN_FILE_SYSTEM 6
#define EFI_WARN_RESET_REQUIRED 7

#endif // LLVM_LIBC_TYPES_EFI_STATUS_H
2 changes: 2 additions & 0 deletions libc/src/__support/OSUtil/uefi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ add_object_library(
HDRS
io.h
DEPENDS
libc.config.app_h
libc.include.llvm-libc-types.EFI_SYSTEM_TABLE
libc.src.__support.common
libc.src.__support.CPP.string_view
)
99 changes: 99 additions & 0 deletions libc/src/__support/OSUtil/uefi/error.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//===----------- UEFI implementation of error utils --------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_UEFI_ERROR_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_UEFI_ERROR_H

#include "hdr/errno_macros.h"
#include "include/llvm-libc-types/EFI_STATUS.h"
#include "src/__support/CPP/array.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"

namespace LIBC_NAMESPACE_DECL {

#define EFI_ERROR_MAX_BIT (cpp::numeric_limits<EFI_STATUS>::max())
#define EFI_ENCODE_ERROR(value) \
(EFI_ERROR_MAX_BIT | (EFI_ERROR_MAX_BIT >> 2) | (value))
#define EFI_ENCODE_WARNING(value) ((EFI_ERROR_MAX_BIT >> 2) | (value))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should be constexpr functions instead of macros.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


struct UefiStatusErrnoEntry {
EFI_STATUS status;
int errno_value;
};

static constexpr cpp::array<UefiStatusErrnoEntry, 43> UEFI_STATUS_ERRNO_MAP = {{
{EFI_SUCCESS, 0},
{EFI_ENCODE_ERROR(EFI_LOAD_ERROR), EINVAL},
{EFI_ENCODE_ERROR(EFI_INVALID_PARAMETER), EINVAL},
{EFI_ENCODE_ERROR(EFI_BAD_BUFFER_SIZE), EINVAL},
{EFI_ENCODE_ERROR(EFI_NOT_READY), EBUSY},
{EFI_ENCODE_ERROR(EFI_DEVICE_ERROR), EIO},
{EFI_ENCODE_ERROR(EFI_WRITE_PROTECTED), EPERM},
{EFI_ENCODE_ERROR(EFI_OUT_OF_RESOURCES), ENOMEM},
{EFI_ENCODE_ERROR(EFI_VOLUME_CORRUPTED), EROFS},
{EFI_ENCODE_ERROR(EFI_VOLUME_FULL), ENOSPC},
{EFI_ENCODE_ERROR(EFI_NO_MEDIA), ENODEV},
{EFI_ENCODE_ERROR(EFI_MEDIA_CHANGED), ENXIO},
{EFI_ENCODE_ERROR(EFI_NOT_FOUND), ENOENT},
{EFI_ENCODE_ERROR(EFI_ACCESS_DENIED), EACCES},
{EFI_ENCODE_ERROR(EFI_NO_RESPONSE), EBUSY},
{EFI_ENCODE_ERROR(EFI_NO_MAPPING), ENODEV},
{EFI_ENCODE_ERROR(EFI_TIMEOUT), EBUSY},
{EFI_ENCODE_ERROR(EFI_NOT_STARTED), EAGAIN},
{EFI_ENCODE_ERROR(EFI_ALREADY_STARTED), EINVAL},
{EFI_ENCODE_ERROR(EFI_ABORTED), EFAULT},
{EFI_ENCODE_ERROR(EFI_ICMP_ERROR), EIO},
{EFI_ENCODE_ERROR(EFI_TFTP_ERROR), EIO},
{EFI_ENCODE_ERROR(EFI_PROTOCOL_ERROR), EINVAL},
{EFI_ENCODE_ERROR(EFI_INCOMPATIBLE_VERSION), EINVAL},
{EFI_ENCODE_ERROR(EFI_SECURITY_VIOLATION), EPERM},
{EFI_ENCODE_ERROR(EFI_CRC_ERROR), EINVAL},
{EFI_ENCODE_ERROR(EFI_END_OF_MEDIA), EPIPE},
{EFI_ENCODE_ERROR(EFI_END_OF_FILE), EPIPE},
{EFI_ENCODE_ERROR(EFI_INVALID_LANGUAGE), EINVAL},
{EFI_ENCODE_ERROR(EFI_COMPROMISED_DATA), EINVAL},
{EFI_ENCODE_ERROR(EFI_IP_ADDRESS_CONFLICT), EINVAL},
{EFI_ENCODE_ERROR(EFI_HTTP_ERROR), EIO},
{EFI_ENCODE_WARNING(EFI_WARN_UNKNOWN_GLYPH), EINVAL},
{EFI_ENCODE_WARNING(EFI_WARN_DELETE_FAILURE), EROFS},
{EFI_ENCODE_WARNING(EFI_WARN_WRITE_FAILURE), EROFS},
{EFI_ENCODE_WARNING(EFI_WARN_BUFFER_TOO_SMALL), E2BIG},
{EFI_ENCODE_WARNING(EFI_WARN_STALE_DATA), EINVAL},
{EFI_ENCODE_WARNING(EFI_WARN_FILE_SYSTEM), EROFS},
{EFI_ENCODE_WARNING(EFI_WARN_RESET_REQUIRED), EINTR},
}};

LIBC_INLINE int uefi_status_to_errno(EFI_STATUS status) {
for (auto it = UEFI_STATUS_ERRNO_MAP.begin();
it != UEFI_STATUS_ERRNO_MAP.end(); it++) {
const struct UefiStatusErrnoEntry entry = *it;
if (entry.status == status)
return entry.errno_value;
}

// Unknown type
__builtin_unreachable();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should probably be given a defined value. These functions shouldn't be called with values out of range, but if it does happen it'll be easier to debug.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should trap here and the same for the function below for safety if possible.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

LIBC_INLINE EFI_STATUS errno_to_uefi_status(int errno_value) {
for (auto it = UEFI_STATUS_ERRNO_MAP.begin();
it != UEFI_STATUS_ERRNO_MAP.end(); it++) {
const struct UefiStatusErrnoEntry entry = *it;
if (entry.errno_value == errno_value)
return entry.status;
}

// Unknown type
__builtin_unreachable();
}

} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_UEFI_ERROR_H
6 changes: 4 additions & 2 deletions libc/src/__support/OSUtil/uefi/exit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
//===-----------------------------------------------------------------===//

#include "src/__support/OSUtil/exit.h"
#include "include/Uefi.h"
#include "config/uefi/app.h"
#include "include/llvm-libc-types/EFI_SYSTEM_TABLE.h"
#include "src/__support/macros/config.h"

namespace LIBC_NAMESPACE_DECL {
namespace internal {

[[noreturn]] void exit(int status) {
efi_system_table->BootServices->Exit(efi_image_handle, status, 0, nullptr);
app.system_table->BootServices->Exit(__llvm_libc_efi_image_handle, status, 0,
nullptr);
__builtin_unreachable();
}

Expand Down
15 changes: 10 additions & 5 deletions libc/src/__support/OSUtil/uefi/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,33 @@

#include "io.h"

#include "Uefi.h"
#include "config/uefi/app.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/macros/config.h"

namespace LIBC_NAMESPACE_DECL {

ssize_t read_from_stdin(char *buf, size_t size) { return 0; }
ssize_t read_from_stdin([[gnu::unused]] char *buf,
[[gnu::unused]] size_t size) {
return 0;
}

void write_to_stdout(cpp::string_view msg) {
// TODO: use mbstowcs once implemented
for (size_t i = 0; i < msg.size(); i++) {
char16_t e[2] = {msg[i], 0};
efi_system_table->ConOut->OutputString(
efi_system_table->ConOut, reinterpret_cast<const char16_t *>(&e));
app.system_table->ConOut->OutputString(
app.system_table->ConOut, reinterpret_cast<const char16_t *>(&e));
}
}

void write_to_stderr(cpp::string_view msg) {
// TODO: use mbstowcs once implemented
for (size_t i = 0; i < msg.size(); i++) {
char16_t e[2] = {msg[i], 0};
efi_system_table->StdErr->OutputString(
efi_system_table->StdErr, reinterpret_cast<const char16_t *>(&e));
app.system_table->StdErr->OutputString(
app.system_table->StdErr, reinterpret_cast<const char16_t *>(&e));
}
}

Expand Down
50 changes: 50 additions & 0 deletions libc/startup/uefi/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# TODO: Use generic "add_startup_object" https://github.com/llvm/llvm-project/issues/133156
function(add_startup_object name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apparently missed that each of the targets currently has their own definition of this. Please add a todo referencing #133156 (the bug I've filed for this)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

cmake_parse_arguments(
"ADD_STARTUP_OBJECT"
"ALIAS" # Option argument
"SRC" # Single value arguments
"DEPENDS;COMPILE_OPTIONS" # Multi value arguments
${ARGN}
)

get_fq_target_name(${name} fq_target_name)
if(ADD_STARTUP_OBJECT_ALIAS)
get_fq_deps_list(fq_dep_list ${ADD_STARTUP_OBJECT_DEPENDS})
add_library(${fq_target_name} ALIAS ${fq_dep_list})
return()
endif()

add_object_library(
${name}
SRCS ${ADD_STARTUP_OBJECT_SRC}
COMPILE_OPTIONS ${ADD_STARTUP_OBJECT_COMPILE_OPTIONS}
${ADD_STARTUP_OBJECT_UNPARSED_ARGUMENTS}
DEPENDS ${ADD_STARTUP_OBJECT_DEPENDS}
)
set_target_properties(
${fq_target_name}
PROPERTIES
OUTPUT_NAME ${name}.o
)
endfunction()

add_startup_object(
crt1
SRCS
crt1.cpp
DEPENDS
libc.config.app_h
libc.src.__support.OSUtil.uefi.uefi_util
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To have an object file depend on object files (or a library), you'd need to use relocatable linking, see https://maskray.me/blog/2022-11-21-relocatable-linking. We use this approach for Linux crt0.o, see:

merge_relocatable_object(
crt1
.${LIBC_TARGET_ARCHITECTURE}.start
.${LIBC_TARGET_ARCHITECTURE}.tls
.do_start
)

Unfortunately, as far as I'm aware COFF linkers don't support relocatable linking so that approach isn't going to work. The most straightforward alternative is probably to move errno_to_uefi_status definition to the header.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, yeah I think for the purposes of this platform and the capabilities currently in lld-link, making it inline and in the header would be best.

)

add_custom_target(libc-startup)
set(startup_components crt1)
foreach(target IN LISTS startup_components)
set(fq_target_name libc.startup.uefi.${target})
add_dependencies(libc-startup ${fq_target_name})
install(FILES $<TARGET_OBJECTS:${fq_target_name}>
DESTINATION ${LIBC_INSTALL_LIBRARY_DIR}
RENAME $<TARGET_PROPERTY:${fq_target_name},OUTPUT_NAME>
COMPONENT libc)
endforeach()
32 changes: 32 additions & 0 deletions libc/startup/uefi/crt1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===-- Implementation of crt for UEFI ------------------------------------===//
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "config/uefi/app.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be config/app.h. There's no reason to have the extra layer of indirection if it's not being used. Here and other files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

#include "include/llvm-libc-types/EFI_STATUS.h"
#include "src/__support/OSUtil/uefi/error.h"
#include "src/__support/macros/config.h"

namespace LIBC_NAMESPACE_DECL {
AppProperties app;
}

extern "C" {
EFI_HANDLE __llvm_libc_efi_image_handle;
EFI_SYSTEM_TABLE *__llvm_libc_efi_system_table;

int main(int argc, char **argv, char **envp);

EFI_STATUS EfiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
LIBC_NAMESPACE::app.image_handle = ImageHandle;
LIBC_NAMESPACE::app.system_table = SystemTable;

// TODO: we need the EFI_SHELL_PROTOCOL, malloc, free, and UTF16 -> UTF8
// conversion.
return LIBC_NAMESPACE::errno_to_uefi_status(main(0, nullptr, nullptr));
}
}
9 changes: 9 additions & 0 deletions libc/test/integration/startup/uefi/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
add_custom_target(libc-startup-tests)
add_dependencies(libc-integration-tests libc-startup-tests)

add_integration_test(
startup_no_args_test
SUITE libc-startup-tests
SRCS
main_without_args.cpp
)
11 changes: 11 additions & 0 deletions libc/test/integration/startup/uefi/main_without_args.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//===-- Loader test for main without args ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "test/IntegrationTest/test.h"

TEST_MAIN() { return 0; }
Loading