{"id":17016316,"url":"https://github.com/rdvdev2/posix-uefi-cmake","last_synced_at":"2025-03-22T15:16:35.645Z","repository":{"id":173616644,"uuid":"396387562","full_name":"rdvdev2/posix-uefi-cmake","owner":"rdvdev2","description":null,"archived":false,"fork":false,"pushed_at":"2021-08-15T14:59:34.000Z","size":423,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-27T14:56:44.494Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rdvdev2.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-15T14:59:21.000Z","updated_at":"2022-09-26T10:48:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"7e08cc9f-e292-479d-a645-4c31ff0222c7","html_url":"https://github.com/rdvdev2/posix-uefi-cmake","commit_stats":null,"previous_names":["rdvdev2/posix-uefi-cmake"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdvdev2%2Fposix-uefi-cmake","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdvdev2%2Fposix-uefi-cmake/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdvdev2%2Fposix-uefi-cmake/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdvdev2%2Fposix-uefi-cmake/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rdvdev2","download_url":"https://codeload.github.com/rdvdev2/posix-uefi-cmake/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244973811,"owners_count":20541025,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T06:32:50.947Z","updated_at":"2025-03-22T15:16:35.626Z","avatar_url":"https://github.com/rdvdev2.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"POSIX-UEFI\n==========\n\n\u003cblockquote\u003eWe hate that horrible and ugly UEFI API, we want the usual POSIX!\u003c/blockquote\u003e\n\nThis is a very small build environment that helps you to develop for UEFI under Linux (and other POSIX systems). It was\ngreatly inspired by [gnu-efi](https://sourceforge.net/projects/gnu-efi) (big big kudos to those guys), but it is a lot\nsmaller, easier to integrate (works with LLVM Clang and GNU gcc both) and easier to use because it provides a POSIX like\nAPI for your UEFI application.\n\nAn UEFI environment consist of two parts: a firmware with GUID protocol interfaces and a user library. We cannot change\nthe former, but we can make the second friendlier. That's what POSIX-UEFI does for your application. It is a small API\nwrapper library around the GUID protocols, not a fully blown POSIX compatible libc implementation.\n\nYou have two options on how to integrate it into your project:\n\nDistributing as Static Library\n------------------------------\n\nIn the `uefi` directory, run\n```sh\n$ USE_GCC=1 make\n```\nThis will create `build/uefi` with all the necessary files in it. These are:\n\n - **crt0.o**, the run-time that bootstraps POSIX-UEFI\n - **link.ld**, the linker script you must use with POSIX-UEFI (same as with gnu-efi)\n - **libuefi.a**, the library itself\n - **uefi.h**, the all-in-one C / C++ header\n\nYou can use this and link your application with it, but you won't be able to recompile it, plus you're on your own with\nthe linking and converting.\n\nStrictly speaking you'll only need **crt0.o** and **link.ld**, that will get you started and will call your application's\n\"main()\", but to get libc functions like memcmp, strcpy, malloc or fopen, you'll have to link with **libuefi.a**.\n\nFor now this only works with gcc, because Clang is configured in a way to directly create PE files, so it cannot create\nnor link with static ELF .a files.\n\nDistributing as Source\n----------------------\n\nThis is the preferred way, as it also provides a Makefile to set up your toolchain properly.\n\n 1. simply copy the `uefi` directory into your source tree (or set up a git submodule). A dozen files, about 132K in total.\n 2. create an extremely simple **Makefile** like the one below\n 3. compile your code for UEFI by running `make`\n\n```\nTARGET = helloworld.efi\ninclude uefi/Makefile\n```\nAn example **helloworld.c** goes like this:\n```c\n#include \u003cuefi.h\u003e\n\nint main(int argc, char **argv)\n{\n    printf(\"Hello World!\\n\");\n    return 0;\n}\n```\nBy default it uses Clang + lld, and PE is generated without conversion. If `USE_GCC` is set, then the host native's GNU gcc + ld\nused to create a shared object and get converted into an .efi file.\n\nIf you comment out `USE_UTF8` in uefi.h, then all character representation will use `wchar_t`, and there will be no string\nconversion between your application and the UEFI interfaces. This also means you must use `L\"\"` and `L''` literals everywhere,\nand your main would receive `wchar_t **argv`.\n\n### Available Makefile Options\n\n| Variable   | Description                                                                                          |\n|------------|------------------------------------------------------------------------------------------------------|\n| `TARGET`   | the target application (required)                                                                    |\n| `SRCS`     | list of source files you want to compile (defaults to \\*.c \\*.S)                                     |\n| `CFLAGS`   | compiler flags you want to use (empty by default, like \"-Wall -pedantic -std=c99\")                   |\n| `LDFLAGS`  | linker flags you want to use (I don't think you'll ever need this, just in case)                     |\n| `LIBS`     | additional libraries you want to link with (like \"-lm\", only static .a libraries allowed)            |\n| `USE_GCC`  | set this if you want native GNU gcc + ld + objccopy instead of LLVM Clang + Lld                      |\n| `ARCH`     | the target architecture                                                                              |\n\nHere's a more advanced **Makefile** example:\n```\nARCH = x86_64\nTARGET = helloworld.efi\nSRCS = $(wildcard *.c)\nCFLAGS = -pedantic -Wall -Wextra -Werror --std=c11 -O2\nLDFLAGS =\nLIBS = -lm\n\nUSE_GCC = 1\ninclude uefi/Makefile\n```\nThe build environment configurator was created in a way that it can handle any number of architectures, however\nonly `x86_64` crt0 has been throughfully tested for now. There's an `aarch64` crt0 too, but since I don't have\nan ARM UEFI board, it hasn't been tested on real machine. Should work though.\n\nNotable Differences to POSIX libc\n---------------------------------\n\nThis library is nowhere near as complete as glibc or musl for example. It only provides the very basic libc functions\nfor you, because simplicity was one of its main goals. It is the best to say this is just wrapper around the UEFI API,\nrather than a POSIX compatible libc.\n\nAll strings in the UEFI environment are stored with 16 bits wide characters. The library provides `wchar_t` type for that,\nand the `USE_UTF8` define to convert between `char` and `wchar_t` transparently. If you comment out `USE_UTF8`, then for\nexample your main() will NOT be like `main(int argc, char **argv)`, but `main(int argc, wchar_t **argv)` instead. All\nthe other string related libc functions (like strlen() for example) will use this wide character type too. For this reason,\nyou must specify your string literals with `L\"\"` and characters with `L''`. To handle both configurations, `char_t` type is\ndefined, which is either `char` or `wchar_t`, and the `CL()` macro which might add the `L` prefix to constant literals.\nFunctions that supposed to handle characters in int type (like `getchar`, `putchar`), do not truncate to unsigned char,\nrather to wchar_t.\n\nFile types in dirent are limited to directories and files only (DT_DIR, DT_REG), but for stat in addition to S_IFDIR and\nS_IFREG, S_IFIFO (for console streams: stdin, stdout, stderr), S_IFBLK (for Block IO) and S_IFCHR (for Serial IO) also\nreturned.\n\nNote that `getenv` and `setenv` aren't POSIX standard, because UEFI environment variables are binary blobs.\n\nThat's about it, everything else is the same.\n\nList of Provided POSIX Functions\n--------------------------------\n\n### dirent.h\n\n| Function      | Description                                                                |\n|---------------|----------------------------------------------------------------------------|\n| opendir       | as usual, but might accept wide char strings                               |\n| readdir       | as usual                                                                   |\n| rewinddir     | as usual                                                                   |\n| closedir      | as usual                                                                   |\n\nBecause UEFI has no concept of device files nor of symlinks, dirent fields are limited and only DT_DIR and DT_REG supported.\n\n### stdlib.h\n\n| Function      | Description                                                                |\n|---------------|----------------------------------------------------------------------------|\n| atoi          | as usual, but might accept wide char strings and understands \"0x\" prefix   |\n| atol          | as usual, but might accept wide char strings and understands \"0x\" prefix   |\n| strtol        | as usual, but might accept wide char strings                               |\n| malloc        | as usual                                                                   |\n| calloc        | as usual                                                                   |\n| realloc       | as usual                                                                   |\n| free          | as usual                                                                   |\n| abort         | as usual                                                                   |\n| exit          | as usual                                                                   |\n| exit_bs       | leave this entire UEFI bullshit behind (exit Boot Services)                |\n| mbtowc        | as usual (UTF-8 char to wchar_t)                                           |\n| wctomb        | as usual (wchar_t to UTF-8 char)                                           |\n| mbstowcs      | as usual (UTF-8 string to wchar_t string)                                  |\n| wcstombs      | as usual (wchar_t string to UTF-8 string)                                  |\n| srand         | as usual                                                                   |\n| rand          | as usual, but uses EFI_RNG_PROTOCOL if possible                            |\n| getenv        | pretty UEFI specific                                                       |\n| setenv        | pretty UEFI specific                                                       |\n\n```c\nint exit_bs();\n```\nExit Boot Services. Returns 0 on success.\n\n```c\nuint8_t *getenv(char_t *name, uintn_t *len);\n```\nQuery the value of environment variable `name`. On success, `len` is set, and a malloc'd buffer returned. It is\nthe caller's responsibility to free the buffer later. On error returns NULL.\n```c\nint setenv(char_t *name, uintn_t len, uint8_t *data);\n```\nSets an environment variable by `name` with `data` of length `len`. On success returns 1, otherwise 0 on error.\n\n### stdio.h\n\n| Function      | Description                                                                |\n|---------------|----------------------------------------------------------------------------|\n| fopen         | as usual, but might accept wide char strings, also for mode                |\n| fclose        | as usual                                                                   |\n| fflush        | as usual                                                                   |\n| fread         | as usual, only real files and blk io accepted (no stdin)                   |\n| fwrite        | as usual, only real files and blk io accepted (no stdout nor stderr)       |\n| fseek         | as usual, only real files and blk io accepted (no stdin, stdout, stderr)   |\n| ftell         | as usual, only real files and blk io accepted (no stdin, stdout, stderr)   |\n| feof          | as usual, only real files and blk io accepted (no stdin, stdout, stderr)   |\n| fprintf       | as usual, might be wide char strings, BUFSIZ, files, ser, stdout, stderr   |\n| printf        | as usual, might be wide char strings, max BUFSIZ, stdout only              |\n| sprintf       | as usual, might be wide char strings, max BUFSIZ                           |\n| vfprintf      | as usual, might be wide char strings, BUFSIZ, files, ser, stdout, stderr   |\n| vprintf       | as usual, might be wide char strings, max BUFSIZ, stdout only              |\n| vsprintf      | as usual, might be wide char strings, max BUFSIZ                           |\n| snprintf      | as usual, might be wide char strings                                       |\n| vsnprintf     | as usual, might be wide char strings                                       |\n| getchar       | as usual, blocking, stdin only (no stream redirects), returns UNICODE      |\n| getchar_ifany | non-blocking, returns 0 if there was no key press, UNICODE otherwise       |\n| putchar       | as usual, stdout only (no stream redirects)                                |\n\nFile open modes: `\"r\"` read, `\"w\"` write, `\"a\"` append. Because of UEFI peculiarities, `\"wd\"` creates directory.\n\nString formating is limited; only supports padding via number prefixes, `%d`, `%x`, `%X`, `%c`, `%s`, `%q` and\n`%p`. When `USE_UTF8` is not defined, then formating operates on wchar_t, so it also supports the non-standard `%S`\n(printing an UTF-8 string), `%Q` (printing an escaped UTF-8 string). These functions don't allocate memory, but in\nreturn the total length of the output string cannot be longer than `BUFSIZ` (8k if you haven't defined otherwise),\nexcept for the variants which have a maxlen argument. For convenience, `%D` requires `efi_physical_address_t` as\nargument, and it dumps memory, 16 bytes or one line at once. With the padding modifier you can dump more lines, for\nexample `%5D` gives you 5 lines (80 dumped bytes).\n\nSpecial \"device files\" you can open:\n\n| Name                | Description                                                          |\n|---------------------|----------------------------------------------------------------------|\n| `/dev/stdin`        | returns ST-\u003eConIn                                                    |\n| `/dev/stdout`       | returns ST-\u003eConOut, fprintf                                          |\n| `/dev/stderr`       | returns ST-\u003eStdErr, fprintf                                          |\n| `/dev/serial(baud)` | returns Serial IO protocol, fread, fwrite, fprintf                   |\n| `/dev/disk(n)`      | returns Block IO protocol, fseek, ftell, fread, fwrite, feof         |\n\nWith Block IO, fseek and buffer size for fread and fwrite is always truncated to the media's block size. So fseek(513)\nfor example will seek to 512 with standard block sizes, and 0 with large 4096 block sizes. To detect the media's block\nsize, use fstat.\n```c\nif(!fstat(f, \u0026st))\n    block_size = st.st_size / st.st_blocks;\n```\nTo interpret a GPT, there are typedefs like `efi_partition_table_header_t` and `efi_partition_entry_t` which you can point\nto the read data.\n\n### string.h\n\n| Function      | Description                                                                |\n|---------------|----------------------------------------------------------------------------|\n| memcpy        | as usual, works on bytes                                                   |\n| memmove       | as usual, works on bytes                                                   |\n| memset        | as usual, works on bytes                                                   |\n| memcmp        | as usual, works on bytes                                                   |\n| memchr        | as usual, works on bytes                                                   |\n| memrchr       | as usual, works on bytes                                                   |\n| memmem        | as usual, works on bytes                                                   |\n| memrmem       | as usual, works on bytes                                                   |\n| strcpy        | might work on wide char strings                                            |\n| strncpy       | might work on wide char strings                                            |\n| strcat        | might work on wide char strings                                            |\n| strncat       | might work on wide char strings                                            |\n| strcmp        | might work on wide char strings                                            |\n| strncmp       | might work on wide char strings                                            |\n| strdup        | might work on wide char strings                                            |\n| strchr        | might work on wide char strings                                            |\n| strrchr       | might work on wide char strings                                            |\n| strstr        | might work on wide char strings                                            |\n| strtok        | might work on wide char strings                                            |\n| strtok_r      | might work on wide char strings                                            |\n| strlen        | might work on wide char strings                                            |\n\n### sys/stat.h\n\n| Function      | Description                                                                |\n|---------------|----------------------------------------------------------------------------|\n| stat          | as usual, but might accept wide char strings                               |\n| fstat         | UEFI doesn't have fd, so it uses FILE\\*                                    |\n| mkdir         | as usual, but might accept wide char strings, and mode unused              |\n\nBecause UEFI has no concept of device major and minor number nor of inodes, struct stat's fields are limited.\nThe actual implementation of `fstat` is in stdio.c, because it needs to access static variables defined there.\n\n### time.h\n\n| Function      | Description                                                                |\n|---------------|----------------------------------------------------------------------------|\n| localtime     | argument unused, always returns current time in struct tm                  |\n| mktime        | as usual                                                                   |\n| time          | as usual                                                                   |\n\n### unistd.h\n\n| Function      | Description                                                                |\n|---------------|----------------------------------------------------------------------------|\n| usleep        | as usual (uses BS-\u003eStall)                                                  |\n| sleep         | as usual                                                                   |\n| unlink        | as usual, but might accept wide char strings                               |\n| rmdir         | as usual, but might accept wide char strings                               |\n\nAccessing UEFI Services\n-----------------------\n\nIt is very likely that you want to call UEFI specific functions directly. For that, POSIX-UEFI specifies some globals\nin `uefi.h`:\n\n| Global Variable | Description                                              |\n|-----------------|----------------------------------------------------------|\n| `*BS`, `gBS`    | *efi_boot_services_t*, pointer to the Boot Time Services |\n| `*RT`, `gRT`    | *efi_runtime_t*, pointer to the Runtime Services         |\n| `*ST`, `gST`    | *efi_system_table_t*, pointer to the UEFI System Table   |\n| `IM`            | *efi_handle_t* of your Loaded Image                      |\n\nThe EFI structures, enums, typedefs and defines are all converted to ANSI C standard POSIX style, for example\nBOOLEAN -\u003e  boolean_t, UINTN -\u003e uintn_t, EFI_MEMORY_DESCRIPTOR -\u003e efi_memory_descriptor_t, and of course\nEFI_BOOT_SERVICES -\u003e efi_boot_services_t.\n\nCalling UEFI functions is as simple as with EDK II, just do the call, no need for \"uefi_call_wrapper\":\n```c\n    ST-\u003eConOut-\u003eOutputString(ST-\u003eConOut, L\"Hello World!\\r\\n\");\n```\n(Note: unlike with printf, with OutputString you must use `L\"\"` and also print carrige return `L\"\\r\"` before `L\"\\n\"`. These\nare the small things that POSIX-UEFI does for you to make your life comfortable.)\n\nThere are two additional, non-POSIX calls in the library. One is `exit_bs()` to exit Boot Services, and the other is\na non-blocking version `getchar_ifany()`.\n\nUnlike gnu-efi, POSIX-UEFI does not pollute your application's namespace with unused GUID variables. It only provides\nheader definitions, so you must create each GUID instance if and when you need them.\n\nExample:\n```c\nefi_guid_t gopGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;\nefi_gop_t *gop = NULL;\n\nstatus = BS-\u003eLocateProtocol(\u0026gopGuid, NULL, (void**)\u0026gop);\n```\n\nAlso unlike gnu-efi, POSIX-UEFI does not provide standard EFI headers. It expects that you have installed those under\n/usr/include/efi from EDK II or gnu-efi, and POSIX-UEFI makes it possible to use those system wide headers without\nnaming conflicts. POSIX-UEFI itself ships the very minimum set of typedefs and structs (with POSIX-ized names).\n```c\n#include \u003cefi.h\u003e\n#include \u003cuefi.h\u003e /* this will work as expected! Both POSIX-UEFI and EDK II / gnu-efi typedefs available */\n```\nThe advantage of this is that you can use the simplicity of the POSIX-UEFI library and build environment, while getting\naccess to the most up-to-date protocol and interface definitions at the same time.\n\nLicense\n-------\n\nPOSIX_UEFI is licensed under the terms of the MIT license.\n\nCheers,\n\nbzt\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdvdev2%2Fposix-uefi-cmake","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frdvdev2%2Fposix-uefi-cmake","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdvdev2%2Fposix-uefi-cmake/lists"}