Added POSIX-UEFI as dependency

This commit is contained in:
Eric-Paul Ickhorn 2023-05-26 21:25:16 +02:00
parent 61a8ca90e8
commit 8bb56b3246
23 changed files with 7380 additions and 0 deletions

21
libs/posix-uefi/LICENSE Normal file
View File

@ -0,0 +1,21 @@
Copyright (C) 2021 bzt (bztsrc@gitlab)
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

385
libs/posix-uefi/OLVASSEL.md Normal file
View File

@ -0,0 +1,385 @@
POSIX-UEFI
==========
<blockquote>Hányunk attól az ocsmány UEFI API-tól, a megszokott POSIX-ot akarjuk!</blockquote>
Ez egy nagyon minimális fordító környezet, ami segít UEFI-re fejleszteni Linux (vagy más POSIX kompatíbilis) rendszer alatt.
Nagy hatással volt rá a [gnu-efi](https://sourceforge.net/projects/gnu-efi) (minden elismerésem nekik), de annál kissebb,
egyszerűbb és könnyebben integrálható (működik LLVM Clang és GNU gcc környezettel is), valamint könnyebben is használható,
mivel POSIX-szerű API-t biztosít az UEFI alkalmazásod felé.
Az UEFI környezet két komponensből áll: a GUID protokoll interfészű firmverből és egy felhasználói könyvtárból. Az előbbit
nem tudjuk lecserélni, de az utóbbit barátságosabbá tehetjük. És pont ez az, amit a POSIX-UEFI csinál. Egy vékony
függvénykönyvtár ami becsomagolja a GUID protokollos API-t, nem több, nem kevesebb, nem egy teljes értékű libc implementáció.
Kétféleképp tudod integrálni a projektedbe:
Statikus Függvénykönyvtárként
-----------------------------
Azonos metódus, mint a gnu-efi-nél, nem igazán javasolt. Az `uefi` könyvtárban futtasd a következő parancsot
```sh
$ USE_GCC=1 make
```
Ez létrehozza az `build/uefi` mappát, minden szükséges fájllal együtt. Ezek:
- **crt0.o**, futásidejű kód, ami elind0tja a POSIX-UEFI-t
- **link.ld**, linkelő szkript, amit a POSIX-UEFI-vel használni kell (ugyanaz, mint gnu-efi esetén)
- **libuefi.a**, a függvénykönyvtár maga
- **uefi.h**, minden az egyben C / C++ fejlécfájl
Ezzel összeszerkesztheted a programodat, de nem fogod tudni újrafordítani, és a linkeléssel és konvertálással is magadra
maradsz.
Szigorúan véve csak a **crt0.o** és a **link.ld** fájlokra van szükség, ez elég ahhoz, hogy elindítsa és meghívja az alkalmazásod
"main()" eljárását. Viszont ahhoz, hogy a libc funkciókat (mint pl. memcmp, strcpy, malloc vagy fopen) használhasd, linkelned
kell a **libuefi.a** fájllal is.
Egyenlőre ez a metódus csak gcc-vel működik, mivel a Clang úgy van beállítva, hogy direktben PE fájlokat hoz létre, ezért nem tud
statikus ELF .a fájlokat generálni, sem linkelni velük.
Forrásként terjesztve
---------------------
Ez a javasolt mód, mivel ez biztosít egy Makefile-t is ami megfelelően beállítja a fordítókörnyezetedet.
1. másold be a `uefi` könyvtárat a forrásfádba (vagy állíts be egy git submodule-t és egy symlinket). Egy tucat fájl, kb. 132K összesen.
2. csinálj egy hihetetlenül egyszerű **Makefile**-t, mint például az alábbi
3. fordítsd le a programodat UEFI-re egy `make` hívással
```
TARGET = helloworld.efi
include uefi/Makefile
```
Egy minta **helloworld.c** így néz ki:
```c
#include <uefi.h>
int main(int argc, char **argv)
{
printf("Hello World!\n");
return 0;
}
```
Alapértelmezetten Clang + lld környezetet keres és állít be, ami direktben PE fájlt hoz létre konvertálás nélkül. Ha a `USE_GCC`
környezeti változó be van állítva, akkor a hoszt natív GNU gcc + ld használatával egy megosztott függvénykönyvtárat fordít, amit
aztán objcopy-val átkonvertál .efi fájllá, pont, mint ahogy a gnu-efi is csinálja.
**MEGJEGYZÉS**: ha nem akarod az egész repót klónozni, csakis az `uefi` könyvtárat, akkor
```
git clone --no-checkout https://gitlab.com/bztsrc/posix-uefi.git .
git sparse-checkout set --no-cone '/uefi/*'
git checkout
```
### Elérhető Makefile opciók
| Változó | Leírás |
|------------|------------------------------------------------------------------------------------------------------|
| `TARGET` | a cél program (szükséges megadni) |
| `SRCS` | források listája, amiket fordítani kell (alapértelmezetten minden \*.c \*.S fájl) |
| `CFLAGS` | további fordító opciók (alapértelmezetten üres, pl. "-Wall -pedantic -std=c99") |
| `LDFLAGS` | további linkelő opciók (nem hiszem, hogy valaha is szükség lesz erre, csak a teljesség kedvéért) |
| `LIBS` | további függvénykönyvtárak, amikkel linkelni szeretnél (pl "-lm", csak statikus .a jöhet szóba) |
| `EXTRA` | bármi további obj fájl, amit még hozzá szeretnél linkelni |
| `ALSO` | további make szabályok futtatása |
| `OUTDIR` | ha meg van adva, akkor ide generálja a projekted obj fájljait (alapértelmezetten nincs beállítva) |
| `USE_GCC` | ha beállítod, akkor natív GNU gcc + ld + objcopy környzetet használ LLVM Clang + Lld helyett |
| `ARCH` | a cél architektúra |
Itt van egy teljesebb **Makefile** példa:
```
ARCH = x86_64
TARGET = helloworld.efi
SRCS = $(wildcard *.c)
CFLAGS = -pedantic -Wall -Wextra -Werror --std=c11 -O2
LDFLAGS =
LIBS = -lm
OUTDIR = build/loader
USE_GCC = 1
include uefi/Makefile
```
A fordítási környezet konfiguráló úgy lett kialakítva, hogy akárhány architektúrával elboldogul, azonban eddig csak az
`x86_64` crt0 lett alaposan letesztelve. Van `aarch64` és `riscv64` crt0 is, de mivel nekem nincs sem ARM UEFI-s, sem
RISC-V UEFI-s gépem, **mindkettő teszteletlen**. Elvileg kéne működnie. Ha új architectúrára akarod portolni, akkor a
setjmp struct-ot kell megadni az uefi.h-ban, valamint csinálni neki egy crt0_X.c fájlt. Ennyi. Minden más
platformfüggetlenül lett megírva.
### Elérhető konfigurációs opciók
Ezeket az `uefi.h` elején lehet állítani.
| Define | Leírás |
|-----------------------|-------------------------------------------------------------------------------------------|
| `UEFI_NO_UTF8` | Ne használjon transzparens UTF-8 konverziót az alkalmazás és az UEFI interfész között |
| `UEFI_NO_TRACK_ALLOC` | Ne tartsa nyilván a foglalt méreteket (gyorsabb, de bufferen kívülről olvas realloc-nál) |
Lényeges eltérések a POSIX libc-től
-----------------------------------
Ez a függvénykönyvtár korántsem annyira teljes, mint a glibc vagy a musl például. Csakis a legszükségesebb libc funkciókat
biztosítja, mivel egyszerűségre törekszik. A legjobb talán UEFI API burkolóként tekinteni rá, és nem pedig egy teljes POSIX
kompatíbilis libc-ként.
UEFI alatt minden sztring 16 bit széles karakterekkel van tárolva. A függvénykönyvtár biztosít egy `wchar_t` típust ehhez,
és egy `UEFI_NO_UTF8` define opciót a transzparens `char` és `wchar_t` közötti konvertáláshoz. Ha megadod az `UEFI_NO_UTF8`-at,
akkor például a main() függvényed NEM `main(int argc, char **argv)` lesz, hanem `main(int argc, wchar_t **argv)`. Az összes
többi sztring függvény (mint például az strlen() is) ezt a széles karaktertípust fogja használni. Emiatt az összes sztring
konstansot `L""`-el, a karakterkonstansokat pedig `L''`-el kell definiálni. Hogy mindkét konfigurációt kezelni lehessen,
adott egy `char_t` típus, ami vagy `char` vagy `wchar_t`, és a `CL()` makró, ami `L` előtagot ad a konstansokhoz, amikor kell.
Azok a funkciók, amik a karaktereket int típusként kezelik (pl. `getchar`, `putchar`), nem unsigned char-ra csonkítanak, hanem
wchar_t-re.
Sajnos az UEFI-ben nincs olyan, hogy buffer átméretezés. Az AllocatePool nem fogad bemenetet, és nincs mód egymár allokált
buffer méretének lekérésére sem. Szóval két rossz közül válaszhatunk a `realloc`-nál:
1. magunk tartjuk nyilván a méreteket, ami bonyolultabb kódot és lassabb futást jelent.
2. megbékélünk vele, hogy az adatok másolása az új bufferbe elkerülhetetlenül a régi bufferen túli olvasást eredményez.
Ez utőbbi opció választható az `UEFI_NO_TRACK_ALLOC` define megadásával.
A fájl típusok a dirent-ben nagyon limitáltak, csak könyvtár és fájl megengedett (DT_DIR, DT_REG), de a stat pluszban az
S_IFDIR és S_IFREG típusokhoz, S_IFIFO (konzol folyamok: stdin, stdout, stderr), S_IFBLK (Block IO esetén) és S_IFCHR
(Serial IO esetén) típusokat is visszaadhat.
Továbbá a `getenv` és `setenv` sem POSIX sztandard, mivel az UEFI környezeti változók bináris bitkolbászok.
Nagyjából ennyi, minden más a megszokott.
Az elérhető POSIX funkciók listája
----------------------------------
### dirent.h
| Funkció | Leírás |
|---------------|----------------------------------------------------------------------------|
| opendir | megszokott, de széles karakterű sztringet is elfogadhat |
| readdir | megszokott |
| rewinddir | megszokott |
| closedir | megszokott |
Mivel az UEFI számára ismeretlen az eszközfájl és a szimbólikus link, a dirent mezők eléggé limitáltak, és csak DT_DIR
valamint DT_REF típusok támogatottak.
### stdlib.h
| Funkció | Leírás |
|---------------|----------------------------------------------------------------------------|
| atoi | megszokott, de széles karakterű sztringet és "0x" prefixet is elfogadhat |
| atol | megszokott, de széles karakterű sztringet és "0x" prefixet is elfogadhat |
| strtol | megszokott, de széles karakterű sztringet is elfogadhat |
| malloc | megszokott |
| calloc | megszokott |
| realloc | megszokott |
| free | megszokott |
| abort | megszokott |
| exit | megszokott |
| exit_bs | az egész UEFI szörnyűség elhagyása (exit Boot Services) |
| mbtowc | megszokott (UTF-8 karakter wchar_t-á) |
| wctomb | megszokott (wchar_t-ról UTF-8 karakterré) |
| mbstowcs | megszokott (UTF-8 sztringről wchar_t sztringé) |
| wcstombs | megszokott (wchar_t sztringről UTF-8 sztringé) |
| srand | megszokott |
| rand | megszokott, de EFI_RNG_PROTOCOL-t használ, ha lehetséges |
| getenv | eléggé UEFI specifikus |
| setenv | eléggé UEFI specifikus |
```c
int exit_bs();
```
Exit Boot Services, az UEFI sárkánylakta vidékének elhagyása. Siker esetén 0-át ad vissza. A sikeres hívást követően nem fogsz
tudni visszatérni a main()-ből, direktben kell átadnod a vezérlést.
```c
uint8_t *getenv(char_t *name, uintn_t *len);
```
A `name` környezeti változó értékének lekérdezése. Siker esetén a `len` be lesz állítva, és egy frissen allokált buffert
ad vissza. A hívó felelőssége a visszaadott buffer felszabadítása, ha már nem kell. Hiba esetén NULL-t ad vissza.
```c
int setenv(char_t *name, uintn_t len, uint8_t *data);
```
A `name` környezeti változó beállítása `len` hosszú `data` értékkel. Siker esetén 1-el tér vissza, hibánál 0-val.
### stdio.h
| Funkció | Leírás |
|---------------|----------------------------------------------------------------------------|
| remove | megszokott, de széles karakterű sztringet is elfogadhat |
| fopen | megszokott, de széles karakterű sztringet is elfogadhat, mode esetén is |
| fclose | megszokott |
| fflush | megszokott |
| fread | megszokott, csak igazi fájlok és blk io (nem stdin) |
| fwrite | megszokott, csak igazi fájlok és blk io (nem lehet stdout se stderr) |
| fseek | megszokott, csak igazi fájlok és blk io (nem stdin, stdout, stderr) |
| ftell | megszokott, csak igazi fájlok és blk io (nem stdin, stdout, stderr) |
| feof | megszokott, csak igazi fájlok és blk io (nem stdin, stdout, stderr) |
| fprintf | megszokott, de széles sztring is lehet, BUFSIZ, fájl, ser, stdout, stderr |
| printf | megszokott, de széles sztring is lehet, max BUFSIZ, csak stdout |
| sprintf | megszokott, de széles sztring is lehet, max BUFSIZ |
| vfprintf | megszokott, de széles sztring is lehet, BUFSIZ, fájl, ser, stdout, stderr |
| vprintf | megszokott, de széles sztring is lehet, max BUFSIZ, csak stdout |
| vsprintf | megszokott, de széles sztring is lehet, max BUFSIZ |
| snprintf | megszokott, de széles sztring is lehet |
| vsnprintf | megszokott, de széles sztring is lehet |
| getchar | megszokott, blokkol, csak stdin (nincs átirányítás), UNICODE-ot ad vissza |
| getchar_ifany | nem blokkoló, 0-át ad vissza ha nem volt billentyű, egyébként UNICODE-ot |
| putchar | megszokott, csak stdout (nincs átriányítás) |
A sztring formázás limitált: csak pozitív számokat fogad el prefixnek, `%d` és `%i`, `%x`, `%X`, `%c`, `%s`, `%q` és `%p` (nincs `%e`,
`%f`, `%g`, nincs csillag és dollárjel). Ha a `UEFI_NO_UTF8` definiálva van, akkor a formázás wchar_t-t használ, ezért ilyenkor
támogatott a nem szabványos `%S` (UTF-8 sztring kiírás) és `%Q` (eszképelt UTF-8 sztring kiírás) is. Ezek a funkciók nem
foglalnak le memóriát, cserébe a teljes hossz `BUFSIZ` lehet (8k ha nem definiálták másképp), kivéve azokat a variánsokat,
amik elfogadnak maxlen hossz paramétert. Kényelmi okokból támogatott a `%D` aminek `efi_physical_address_t` paramétert kell
adni, és a memóriát dumpolja, 16 bájtos sorokban. A szám módosítókkal lehet több sort is dumpoltatni, például `%5D` 5 sort
fog dumpolni (80 bájt).
Fájl megnyitási módok: `"r"` olvasás, `"w"` írás, `"a"` hozzáfűzés. UEFI sajátosságok miatt, `"wd"` könyvtárat hoz létre.
Speciális "eszköz fájlok", amiket meg lehet nyitni:
| Név | Leírás |
|---------------------|----------------------------------------------------------------------|
| `/dev/stdin` | ST->ConIn |
| `/dev/stdout` | ST->ConOut, fprintf |
| `/dev/stderr` | ST->StdErr, fprintf |
| `/dev/serial(baud)` | Serial IO protokoll, fread, fwrite, fprintf |
| `/dev/disk(n)` | Block IO protokoll, fseek, ftell, fread, fwrite, feof |
Block IO esetén az fseek és a buffer méret fread és fwritenál az eszköz blokméretére lesz igazítva. Például fseek(513)
az 512. bájtra pozicionál szabvány blokkméretnél, de 0-ra nagy 4096-os blokkoknál. A blokkméret detektálásához az fstat-ot
lehet használni.
```c
if(!fstat(f, &st))
block_size = st.st_size / st.st_blocks;
```
A partíciós GPT tábla értelmezéséhez típusdefiníciók állnak a rendelkezésre, mint `efi_partition_table_header_t` és
`efi_partition_entry_t`, amikkel a beolvasott adatokra lehet mutatni.
### string.h
| Funkció | Leírás |
|---------------|----------------------------------------------------------------------------|
| memcpy | megszokott, mindenképp bájt |
| memmove | megszokott, mindenképp bájt |
| memset | megszokott, mindenképp bájt |
| memcmp | megszokott, mindenképp bájt |
| memchr | megszokott, mindenképp bájt |
| memrchr | megszokott, mindenképp bájt |
| memmem | megszokott, mindenképp bájt |
| memrmem | megszokott, mindenképp bájt |
| strcpy | széles karakterű sztringet is elfogadhat |
| strncpy | széles karakterű sztringet is elfogadhat |
| strcat | széles karakterű sztringet is elfogadhat |
| strncat | széles karakterű sztringet is elfogadhat |
| strcmp | széles karakterű sztringet is elfogadhat |
| strncmp | széles karakterű sztringet is elfogadhat |
| strdup | széles karakterű sztringet is elfogadhat |
| strchr | széles karakterű sztringet is elfogadhat |
| strrchr | széles karakterű sztringet is elfogadhat |
| strstr | széles karakterű sztringet is elfogadhat |
| strtok | széles karakterű sztringet is elfogadhat |
| strtok_r | széles karakterű sztringet is elfogadhat |
| strlen | széles karakterű sztringet is elfogadhat |
### sys/stat.h
| Funkció | Leírás |
|---------------|----------------------------------------------------------------------------|
| stat | megszokott, de széles karakterű sztringet is elfogadhat |
| fstat | UEFI alatt nincs fd, ezért FILE\*-ot használ |
| mkdir | megszokott, de széles karakterű sztringet is elfogadhat, mode nem használt |
Mivel az UEFI számára ismeretlen az eszköz major és minor valamint az inode szám, a struct stat mezői limitáltak.
Az `fstat` implementációja az stdio.c-ben található, mivel el kell érnie bizonyos ott definiált statikus változókat.
### time.h
| Funkció | Leírás |
|---------------|----------------------------------------------------------------------------|
| localtime | paraméterek nem használtak, mindig a pontos időt adja vissza struct tm-ben |
| mktime | megszokott |
| time | megszokott |
### unistd.h
| Funkció | Leírás |
|---------------|----------------------------------------------------------------------------|
| usleep | megszokott (BS->Stall hívást használ) |
| sleep | megszokott |
| unlink | megszokott, de széles karakterű sztringet is elfogadhat |
| rmdir | megszokott, de széles karakterű sztringet is elfogadhat |
UEFI szolgáltatások elérése
---------------------------
Elég valószínű, hogy direktben UEFI szolgáltatást kell majd hívni. Ehhez a POSIX-UEFI néhány globális változót biztosít
az `uefi.h`-ban definiálva:
| Globális változó | Leírás |
|------------------|----------------------------------------------------------|
| `*BS`, `gBS` | *efi_boot_services_t*, mutató a Boot Time Services-ra |
| `*RT`, `gRT` | *efi_runtime_t*, mutató a Runtime Services-ra |
| `*ST`, `gST` | *efi_system_table_t*, mutató az UEFI System Table-re |
| `IM` | a betöltött fájlod *efi_handle_t*-je (loaded image) |
Az EFI struktúrák, enumok, típusdefiníciók, defineok mind ANSI C szabványos POSIX stílusúra lettek konvertálva, például
BOOLEAN -> boolean_t, UINTN -> uintn_t, EFI_MEMORY_DESCRIPTOR -> efi_memory_descriptor_t, és persze
EFI_BOOT_SERVICES -> efi_boot_services_t.
UEFI funkciók hívása pont olyan egyszerű, mint EDK II esetén, csak meg kell hívni, nincs szükség "uefi_call_wrapper"-re:
```c
ST->ConOut->OutputString(ST->ConOut, L"Hello World!\r\n");
```
(Megjegyzés: a printf-el ellentétben az OutputString-nél mindig kell `L""` és ki kell írni `L"\r"`-t a `L"\n"` előtt. Ezek
azok az apróságok, amikkel a POSIX-UEFI kényelmesebbé teszi az életedet.)
Van továbbá két, nem POSIX szabványos függvény a könyvtárban. Az egyik az `exit_bs()` az UEFI elhagyására, a másik a nem
blokkoló `getchar_ifany()`.
A gnu-efi-vel ellentétben a POSIX-UEFI nem szennyezi a névteret nemhasznált GUID változókkal. Csak define-okat biztosít,
ezért mindig neked kell létrehozni a GUID példányt, ha és amikor szükséged van rá.
Példa:
```c
efi_guid_t gopGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
efi_gop_t *gop = NULL;
status = BS->LocateProtocol(&gopGuid, NULL, (void**)&gop);
```
Szintén a gnu-efi-vel ellentétben a POSIX-UEFI nem biztosítja a szabványos UEFI fejlécfájlokat. Úgy veszi, hogy azokat
vagy az EDk II-ből vagy a gnu.efi-ből átmásoltad az /usr/include/efi alá, és biztosítja, hogy be legyenek húzva névtér
ütközés nélkül. Maga a POSIX-UEFI csak egy nagyon minimális mennyiségű típusdefiniciót biztosít (és azokat is POSIX-osított
névvel).
```c
#include <efi.h>
#include <uefi.h> /* ez működik! Mind a POSIX-UEFI és az EDK II / gnu-efi typedef-ek elérhetők */
```
Ennek az az előnye, hogy használhatod a POSIX-UEFI egyszerű könyvtárát és fordító környezetét, ugyanakkor hozzáférhetsz
a legfrissebb protokoll és interfész definíciókoz egyaránt.
__FONTOS FIGYELMEZTETÉS__
Bizonyos esetekben a GNU-EFI hedörök és a Clang kombinációja hibásan 32 bites `uint64_t` típust eredményezhet. Ha ez előjön,
akkor a
```c
#undef __STDC_VERSION__
#include <efi.h>
#include <uefi.h>
```
kerülőmegoldás lehet a problémára, mivel így nem húzza be az stdint.h-t, helyette az efibind.h fogja definiálni az uint64_t
típust mint `unsigned long long`.
Licensz
-------
POSIX_UEFI az MIT licensz alatt kerül terjesztésre.
Hozzájárulások
--------------
Szeretnék köszönetet mondani @vladimir132 -nek az alapos tesztelésért és a pontos és részletes visszajelzésekért.
Ez minden,
bzt

389
libs/posix-uefi/README.md Normal file
View File

@ -0,0 +1,389 @@
POSIX-UEFI
==========
<blockquote>We hate that horrible and ugly UEFI API, we want the usual POSIX!</blockquote>
------------------------------------------------------------------------------------------------------------------------
NOTE: a smartypants on reddit is worried that I'm supposedly "hiding something" because of the use of `-Wno-builtin-declaration-mismatch`
(gcc) and `-Wno-incompatible-library-redeclaration` (Clang) flags. **Here's my answer to you: I hide nothing**, that flag is only
needed because you can disable transparent UTF-8 conversion. You see, under the hood UEFI uses 16 bit characters, and for example
`strlen(wchar_t *str)` or `main(int argc, wchar_t *argv)` isn't exactly POSIX-standard, that's why there's a need for that flag.
You should know that had you have spent more time learning or just *reading this README* instead of falsely accusing others on reddit.
------------------------------------------------------------------------------------------------------------------------
This is a very small build environment that helps you to develop for UEFI under Linux (and other POSIX systems). It was
greatly inspired by [gnu-efi](https://sourceforge.net/projects/gnu-efi) (big big kudos to those guys), but it is a lot
smaller, easier to integrate (works with LLVM Clang and GNU gcc both) and easier to use because it provides a POSIX like
API for your UEFI application.
An UEFI environment consist of two parts: a firmware with GUID protocol interfaces and a user library. We cannot change
the former, but we can make the second friendlier. That's what POSIX-UEFI does for your application. It is a small API
wrapper library around the GUID protocols, not a fully blown POSIX compatible libc implementation.
You have two options on how to integrate it into your project:
Distributing as Static Library
------------------------------
Same method as with gnu-efi, not really recommended. In the `uefi` directory, run
```sh
$ USE_GCC=1 make
```
This will create `build/uefi` with all the necessary files in it. These are:
- **crt0.o**, the run-time that bootstraps POSIX-UEFI
- **link.ld**, the linker script you must use with POSIX-UEFI (same as with gnu-efi)
- **libuefi.a**, the library itself
- **uefi.h**, the all-in-one C / C++ header
You 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
the linking and converting.
Strictly speaking you'll only need **crt0.o** and **link.ld**, that will get you started and will call your application's
"main()", but to get libc functions like memcmp, strcpy, malloc or fopen, you'll have to link with **libuefi.a** too.
For now this only works with gcc, because Clang is configured in a way to directly create PE files, so it cannot create
nor link with static ELF .a files.
Distributing as Source
----------------------
This is the preferred way, as it also provides a Makefile to set up your toolchain properly.
1. simply copy the `uefi` directory into your source tree (or set up a git submodule and a symlink). A dozen files, about 132K in total.
2. create an extremely simple **Makefile** like the one below
3. compile your code for UEFI by running `make`
```
TARGET = helloworld.efi
include uefi/Makefile
```
An example **helloworld.c** goes like this:
```c
#include <uefi.h>
int main(int argc, char **argv)
{
printf("Hello World!\n");
return 0;
}
```
By default it uses Clang + lld, and PE is generated directly without conversion. If `USE_GCC` is set, then the host native's
GNU gcc + ld is used to create a shared object and get converted into an .efi file with objcopy, just like how gnu-efi does.
**NOTE**: if you don't want to clone this entire repo, just the `uefi` directory,
```
git clone --no-checkout https://gitlab.com/bztsrc/posix-uefi.git .
git sparse-checkout set --no-cone '/uefi/*'
git checkout
```
### Available Makefile Options
| Variable | Description |
|------------|-------------------------------------------------------------------------------------------------------------------|
| `TARGET` | the target application (required) |
| `SRCS` | list of source files you want to compile (defaults to \*.c \*.S) |
| `CFLAGS` | compiler flags you want to use (empty by default, like "-Wall -pedantic -std=c99") |
| `LDFLAGS` | linker flags you want to use (I don't think you'll ever need this, just in case) |
| `LIBS` | additional libraries you want to link with (like "-lm", only static .a libraries allowed) |
| `EXTRA` | any additional object files you might want to link with, these are also called as makefile rules before compiling |
| `ALSO` | additional makefile rules to be called after compiling |
| `OUTDIR` | if given, then your project's object files are generated into this directory (by default not set) |
| `USE_GCC` | set this if you want native GNU gcc + ld + objcopy instead of LLVM Clang + Lld |
| `ARCH` | the target architecture |
Here's a more advanced **Makefile** example:
```
ARCH = x86_64
TARGET = helloworld.efi
SRCS = $(wildcard *.c)
CFLAGS = -pedantic -Wall -Wextra -Werror --std=c11 -O2
LDFLAGS =
LIBS = -lm
OUTDIR = build/loader
USE_GCC = 1
include uefi/Makefile
```
The build environment configurator was created in a way that it can handle any number of architectures, however only
`x86_64` crt0 has been throughfully tested for now. There's an `aarch64` and a `riscv64` crt0 too, but since I don't
have neither an ARM UEFI, nor a RISC-V UEFI board, these **haven't been tested at all**. Should work though. If you want
to port it to another architecture, all you need is a setjmp struct in uefi.h, and an appropriate crt_X.c file. That's
it. Everything else was coded in an architecture independent way.
### Available Configuration Options
These can be set at the beginning of `uefi.h`.
| Define | Description |
|-----------------------|-------------------------------------------------------------------------------------------|
| `UEFI_NO_UTF8` | Do not use transparent UTF-8 conversion between the application and the UEFI interface |
| `UEFI_NO_TRACK_ALLOC` | Do not keep track of allocated buffers (faster, but causes out of bound reads on realloc) |
Notable Differences to POSIX libc
---------------------------------
This library is nowhere near as complete as glibc or musl for example. It only provides the very basic libc functions
for you, because simplicity was one of its main goals. It is the best to say this is just wrapper around the UEFI API,
rather than a POSIX compatible libc.
All strings in the UEFI environment are stored with 16 bits wide characters. The library provides `wchar_t` type for that,
and the `UEFI_NO_UTF8` define to convert between `char` and `wchar_t` transparently. If you have `UEFI_NO_UTF8`, then for
example your main() will NOT be like `main(int argc, char **argv)`, but `main(int argc, wchar_t **argv)` instead. All
the other string related libc functions (like strlen() for example) will use this wide character type too. For this reason,
you must specify your string literals with `L""` and characters with `L''`. To handle both configurations, `char_t` type is
defined, which is either `char` or `wchar_t`, and the `CL()` macro which might add the `L` prefix to constant literals.
Functions that are supposed to handle characters in int type (like `getchar`, `putchar`), do not truncate to unsigned char,
rather to wchar_t.
Sadly UEFI has no concept of reallocation. AllocatePool does not accept input, and there's no way to query the size of an
already allocated buffer. So we are left with two bad options with `realloc`:
1. we keep track of sizes ourselves, which means more complexcity and a considerable overhead, so performance loss.
2. make peace with the fact that copying data to the new buffer unavoidably reads out of bounds from the old buffer.
You can choose this latter with the `UEFI_NO_TRACK_ALLOC` define.
File types in dirent are limited to directories and files only (DT_DIR, DT_REG), but for stat in addition to S_IFDIR and
S_IFREG, S_IFIFO (for console streams: stdin, stdout, stderr), S_IFBLK (for Block IO) and S_IFCHR (for Serial IO) also
returned.
Note that `getenv` and `setenv` aren't POSIX standard, because UEFI environment variables are binary blobs.
That's about it, everything else is the same.
List of Provided POSIX Functions
--------------------------------
### dirent.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| opendir | as usual, but might accept wide char strings |
| readdir | as usual |
| rewinddir | as usual |
| closedir | as usual |
Because UEFI has no concept of device files nor of symlinks, dirent fields are limited and only DT_DIR and DT_REG supported.
### stdlib.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| atoi | as usual, but might accept wide char strings and understands "0x" prefix |
| atol | as usual, but might accept wide char strings and understands "0x" prefix |
| strtol | as usual, but might accept wide char strings |
| malloc | as usual |
| calloc | as usual |
| realloc | as usual |
| free | as usual |
| abort | as usual |
| exit | as usual |
| exit_bs | leave this entire UEFI bullshit behind (exit Boot Services) |
| mbtowc | as usual (UTF-8 char to wchar_t) |
| wctomb | as usual (wchar_t to UTF-8 char) |
| mbstowcs | as usual (UTF-8 string to wchar_t string) |
| wcstombs | as usual (wchar_t string to UTF-8 string) |
| srand | as usual |
| rand | as usual, but uses EFI_RNG_PROTOCOL if possible |
| getenv | pretty UEFI specific |
| setenv | pretty UEFI specific |
```c
int exit_bs();
```
Exit Boot Services. Returns 0 on success. You won't be able to return from main() after calling this successfully, you must
transfer control directly.
```c
uint8_t *getenv(char_t *name, uintn_t *len);
```
Query the value of environment variable `name`. On success, `len` is set, and a malloc'd buffer returned. It is
the caller's responsibility to free the buffer later. On error returns NULL.
```c
int setenv(char_t *name, uintn_t len, uint8_t *data);
```
Sets an environment variable by `name` with `data` of length `len`. On success returns 1, otherwise 0 on error.
### stdio.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| remove | as usual, but might accept wide char strings |
| fopen | as usual, but might accept wide char strings, also for mode |
| fclose | as usual |
| fflush | as usual |
| fread | as usual, only real files and blk io accepted (no stdin) |
| fwrite | as usual, only real files and blk io accepted (no stdout nor stderr) |
| fseek | as usual, only real files and blk io accepted (no stdin, stdout, stderr) |
| ftell | as usual, only real files and blk io accepted (no stdin, stdout, stderr) |
| feof | as usual, only real files and blk io accepted (no stdin, stdout, stderr) |
| fprintf | as usual, might be wide char strings, BUFSIZ, files, ser, stdout, stderr |
| printf | as usual, might be wide char strings, max BUFSIZ, stdout only |
| sprintf | as usual, might be wide char strings, max BUFSIZ |
| vfprintf | as usual, might be wide char strings, BUFSIZ, files, ser, stdout, stderr |
| vprintf | as usual, might be wide char strings, max BUFSIZ, stdout only |
| vsprintf | as usual, might be wide char strings, max BUFSIZ |
| snprintf | as usual, might be wide char strings |
| vsnprintf | as usual, might be wide char strings |
| getchar | as usual, blocking, stdin only (no stream redirects), returns UNICODE |
| getchar_ifany | non-blocking, returns 0 if there was no key press, UNICODE otherwise |
| putchar | as usual, stdout only (no stream redirects) |
String formating is limited; only supports padding via positive number prefixes, `%d`, `%i`, `%x`, `%X`, `%c`, `%s`, `%q` and
`%p` (no `%e`, `%f`, `%g`, no asterisk and dollar). When `UEFI_NO_UTF8` is defined, then formating operates on wchar_t, so
it also supports the non-standard `%S` (printing an UTF-8 string) and `%Q` (printing an escaped UTF-8 string). These
functions don't allocate memory, but in return the total length of the output string cannot be longer than `BUFSIZ`
(8k if you haven't defined otherwise), except for the variants which have a maxlen argument. For convenience, `%D` requires
`efi_physical_address_t` as argument, and it dumps memory, 16 bytes or one line at once. With the padding modifier you can
dump more lines, for example `%5D` gives you 5 lines (80 dumped bytes).
File open modes: `"r"` read, `"w"` write, `"a"` append. Because of UEFI peculiarities, `"wd"` creates directory.
Special "device files" you can open:
| Name | Description |
|---------------------|----------------------------------------------------------------------|
| `/dev/stdin` | returns ST->ConIn |
| `/dev/stdout` | returns ST->ConOut, fprintf |
| `/dev/stderr` | returns ST->StdErr, fprintf |
| `/dev/serial(baud)` | returns Serial IO protocol, fread, fwrite, fprintf |
| `/dev/disk(n)` | returns Block IO protocol, fseek, ftell, fread, fwrite, feof |
With Block IO, fseek and buffer size for fread and fwrite is always truncated to the media's block size. So fseek(513)
for example will seek to 512 with standard block sizes, and 0 with large 4096 block sizes. To detect the media's block
size, use fstat.
```c
if(!fstat(f, &st))
block_size = st.st_size / st.st_blocks;
```
To interpret a GPT, there are typedefs like `efi_partition_table_header_t` and `efi_partition_entry_t` which you can point
to the read data.
### string.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| memcpy | as usual, works on bytes |
| memmove | as usual, works on bytes |
| memset | as usual, works on bytes |
| memcmp | as usual, works on bytes |
| memchr | as usual, works on bytes |
| memrchr | as usual, works on bytes |
| memmem | as usual, works on bytes |
| memrmem | as usual, works on bytes |
| strcpy | might work on wide char strings |
| strncpy | might work on wide char strings |
| strcat | might work on wide char strings |
| strncat | might work on wide char strings |
| strcmp | might work on wide char strings |
| strncmp | might work on wide char strings |
| strdup | might work on wide char strings |
| strchr | might work on wide char strings |
| strrchr | might work on wide char strings |
| strstr | might work on wide char strings |
| strtok | might work on wide char strings |
| strtok_r | might work on wide char strings |
| strlen | might work on wide char strings |
### sys/stat.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| stat | as usual, but might accept wide char strings |
| fstat | UEFI doesn't have fd, so it uses FILE\* |
| mkdir | as usual, but might accept wide char strings, and mode unused |
Because UEFI has no concept of device major and minor number nor of inodes, struct stat's fields are limited.
The actual implementation of `fstat` is in stdio.c, because it needs to access static variables defined there.
### time.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| localtime | argument unused, always returns current time in struct tm |
| mktime | as usual |
| time | as usual |
### unistd.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| usleep | as usual (uses BS->Stall) |
| sleep | as usual |
| unlink | as usual, but might accept wide char strings |
| rmdir | as usual, but might accept wide char strings |
Accessing UEFI Services
-----------------------
It is very likely that you want to call UEFI specific functions directly. For that, POSIX-UEFI specifies some globals
in `uefi.h`:
| Global Variable | Description |
|-----------------|----------------------------------------------------------|
| `*BS`, `gBS` | *efi_boot_services_t*, pointer to the Boot Time Services |
| `*RT`, `gRT` | *efi_runtime_t*, pointer to the Runtime Services |
| `*ST`, `gST` | *efi_system_table_t*, pointer to the UEFI System Table |
| `IM` | *efi_handle_t* of your Loaded Image |
The EFI structures, enums, typedefs and defines are all converted to ANSI C standard POSIX style, for example
BOOLEAN -> boolean_t, UINTN -> uintn_t, EFI_MEMORY_DESCRIPTOR -> efi_memory_descriptor_t, and of course
EFI_BOOT_SERVICES -> efi_boot_services_t.
Calling UEFI functions is as simple as with EDK II, just do the call, no need for "uefi_call_wrapper":
```c
ST->ConOut->OutputString(ST->ConOut, L"Hello World!\r\n");
```
(Note: unlike with printf, with OutputString you must use `L""` and also print carrige return `L"\r"` before `L"\n"`. These
are the small things that POSIX-UEFI does for you to make your life comfortable.)
There are two additional, non-POSIX calls in the library. One is `exit_bs()` to exit Boot Services, and the other is
a non-blocking version `getchar_ifany()`.
Unlike gnu-efi, POSIX-UEFI does not pollute your application's namespace with unused GUID variables. It only provides
header definitions, so you must create each GUID instance if and when you need them.
Example:
```c
efi_guid_t gopGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
efi_gop_t *gop = NULL;
status = BS->LocateProtocol(&gopGuid, NULL, (void**)&gop);
```
Also unlike gnu-efi, POSIX-UEFI does not provide standard EFI headers. It expects that you have installed those under
/usr/include/efi from EDK II or gnu-efi, and POSIX-UEFI makes it possible to use those system wide headers without
naming conflicts. POSIX-UEFI itself ships the very minimum set of typedefs and structs (with POSIX-ized names).
```c
#include <efi.h>
#include <uefi.h> /* this will work as expected! Both POSIX-UEFI and EDK II / gnu-efi typedefs available */
```
The advantage of this is that you can use the simplicity of the POSIX-UEFI library and build environment, while getting
access to the most up-to-date protocol and interface definitions at the same time.
__IMPORTANT NOTE__
In some cases the combination of GNU-EFI headers and Clang might incorrectly define `uint64_t` as 32 bits. If this happens,
then
```c
#undef __STDC_VERSION__
#include <efi.h>
#include <uefi.h>
```
should workaround the problem by avoiding the inclusion of stdint.h and defining uint64_t by efibind.h as `unsigned long long`.
License
-------
POSIX_UEFI is licensed under the terms of the MIT license.
Contributors
------------
I'd like to say thanks to @vladimir132 for a through testing and very accurate and detailed feedbacks.
Cheers,
bzt

View File

@ -0,0 +1,239 @@
/*
* crt_aarch64.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief C runtime, bootstraps an EFI application to call standard main()
*
*/
#include <uefi.h>
/* this is implemented by the application */
extern int main(int argc, char_t **argv);
/* definitions for elf relocations */
#ifndef __clang__
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint64_t Elf64_Addr;
typedef struct
{
Elf64_Sxword d_tag; /* Dynamic entry type */
union
{
Elf64_Xword d_val; /* Integer value */
Elf64_Addr d_ptr; /* Address value */
} d_un;
} Elf64_Dyn;
#define DT_NULL 0 /* Marks end of dynamic section */
#define DT_RELA 7 /* Address of Rela relocs */
#define DT_RELASZ 8 /* Total size of Rela relocs */
#define DT_RELAENT 9 /* Size of one Rela reloc */
typedef struct
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
} Elf64_Rel;
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
#define R_AARCH64_RELATIVE 1027 /* Adjust by program base */
#endif
/* globals to store system table pointers */
efi_handle_t IM = NULL;
efi_system_table_t *ST = NULL;
efi_boot_services_t *BS = NULL;
efi_runtime_services_t *RT = NULL;
efi_loaded_image_protocol_t *LIP = NULL;
#ifndef UEFI_NO_UTF8
char *__argvutf8 = NULL;
#endif
/* we only need one .o file, so use inline Assembly here */
void bootstrap(void)
{
__asm__ __volatile__ (
/* call init in C */
" .align 4\n"
#ifndef __clang__
" .globl _start\n"
"_start:\n"
" ldr x2, =ImageBase\n"
" adrp x3, _DYNAMIC\n"
" add x3, x3, #:lo12:_DYNAMIC\n"
" bl uefi_init\n"
" ret\n"
/* fake a relocation record, so that EFI won't complain */
" .data\n"
"dummy: .long 0\n"
" .section .reloc, \"a\"\n"
"label1:\n"
" .long dummy-label1\n"
" .long 10\n"
" .word 0\n"
".text\n"
#else
" .globl __chkstk\n"
"__chkstk:\n"
" ret\n"
#endif
);
/* setjmp and longjmp */
__asm__ __volatile__ (
" .p2align 3\n"
" .globl setjmp\n"
"setjmp:\n"
" mov x16, sp\n"
" stp x19, x20, [x0, #0]\n"
" stp x21, x22, [x0, #16]\n"
" stp x23, x24, [x0, #32]\n"
" stp x25, x26, [x0, #48]\n"
" stp x27, x28, [x0, #64]\n"
" stp x29, x30, [x0, #80]\n"
" str x16, [x0, #96]\n"
" stp d8, d9, [x0, #112]\n"
" stp d10, d11, [x0, #128]\n"
" stp d12, d13, [x0, #144]\n"
" stp d14, d15, [x0, #160]\n"
" mov w0, #0\n"
" ret\n"
);
__asm__ __volatile__ (
" .globl longjmp\n"
"longjmp:\n"
" ldp x19, x20, [x0, #0]\n"
" ldp x21, x22, [x0, #16]\n"
" ldp x23, x24, [x0, #32]\n"
" ldp x25, x26, [x0, #48]\n"
" ldp x27, x28, [x0, #64]\n"
" ldp x29, x30, [x0, #80]\n"
" ldr x16, [x0, #96]\n"
" ldp d8, d9, [x0, #112]\n"
" ldp d10, d11, [x0, #128]\n"
" ldp d12, d13, [x0, #144]\n"
" ldp d14, d15, [x0, #160]\n"
" mov sp, x16\n"
" cmp w1, #0\n"
" mov w0, #1\n"
" csel w0, w1, w0, ne\n"
" br x30\n"
);
}
/**
* Initialize POSIX-UEFI and call the application's main() function
*/
efi_status_t uefi_init (
efi_handle_t image, efi_system_table_t *systab
#ifndef __clang__
, uintptr_t ldbase, Elf64_Dyn *dyn
#endif
) {
efi_guid_t shpGuid = EFI_SHELL_PARAMETERS_PROTOCOL_GUID;
efi_shell_parameters_protocol_t *shp = NULL;
efi_guid_t shiGuid = SHELL_INTERFACE_PROTOCOL_GUID;
efi_shell_interface_protocol_t *shi = NULL;
efi_guid_t lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
efi_status_t status;
int argc = 0, i, ret;
wchar_t **argv = NULL;
#ifndef UEFI_NO_UTF8
int j;
char *s;
#endif
#ifndef __clang__
long relsz = 0, relent = 0;
Elf64_Rel *rel = 0;
uintptr_t *addr;
/* handle relocations */
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
switch (dyn[i].d_tag) {
case DT_RELA: rel = (Elf64_Rel*)((unsigned long)dyn[i].d_un.d_ptr + ldbase); break;
case DT_RELASZ: relsz = dyn[i].d_un.d_val; break;
case DT_RELAENT: relent = dyn[i].d_un.d_val; break;
default: break;
}
}
if (rel && relent) {
while (relsz > 0) {
if(ELF64_R_TYPE (rel->r_info) == R_AARCH64_RELATIVE)
{ addr = (unsigned long *)(ldbase + rel->r_offset); *addr += ldbase; }
rel = (Elf64_Rel*) ((char *) rel + relent);
relsz -= relent;
}
}
#else
(void)i;
#endif
/* failsafes, should never happen */
if(!image || !systab || !systab->BootServices || !systab->BootServices->HandleProtocol ||
!systab->BootServices->OpenProtocol || !systab->BootServices->AllocatePool || !systab->BootServices->FreePool)
return EFI_UNSUPPORTED;
/* save EFI pointers and loaded image into globals */
IM = image;
ST = systab;
BS = systab->BootServices;
RT = systab->RuntimeServices;
BS->HandleProtocol(image, &lipGuid, (void **)&LIP);
/* get command line arguments */
status = BS->OpenProtocol(image, &shpGuid, (void **)&shp, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if(!EFI_ERROR(status) && shp) { argc = (int)shp->Argc; argv = shp->Argv; }
else {
/* if shell 2.0 failed, fallback to shell 1.0 interface */
status = BS->OpenProtocol(image, &shiGuid, (void **)&shi, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if(!EFI_ERROR(status) && shi) { argc = (int)shi->Argc; argv = shi->Argv; }
}
/* call main */
#ifndef UEFI_NO_UTF8
if(argc && argv) {
ret = (argc + 1) * ((int)sizeof(uintptr_t) + 1);
for(i = 0; i < argc; i++)
for(j = 0; argv[i] && argv[i][j]; j++)
ret += argv[i][j] < 0x80 ? 1 : (argv[i][j] < 0x800 ? 2 : 3);
status = BS->AllocatePool(LIP ? LIP->ImageDataType : EfiLoaderData, (uintn_t)ret, (void **)&__argvutf8);
if(EFI_ERROR(status) || !__argvutf8) { argc = 0; __argvutf8 = NULL; }
else {
s = __argvutf8 + argc * (int)sizeof(uintptr_t);
*((uintptr_t*)s) = (uintptr_t)0; s += sizeof(uintptr_t);
for(i = 0; i < argc; i++) {
*((uintptr_t*)(__argvutf8 + i * (int)sizeof(uintptr_t))) = (uintptr_t)s;
for(j = 0; argv[i] && argv[i][j]; j++) {
if(argv[i][j]<0x80) { *s++ = argv[i][j]; } else
if(argv[i][j]<0x800) { *s++ = ((argv[i][j]>>6)&0x1F)|0xC0; *s++ = (argv[i][j]&0x3F)|0x80; } else
{ *s++ = ((argv[i][j]>>12)&0x0F)|0xE0; *s++ = ((argv[i][j]>>6)&0x3F)|0x80; *s++ = (argv[i][j]&0x3F)|0x80; }
}
*s++ = 0;
}
}
}
ret = main(argc, (char**)__argvutf8);
if(__argvutf8) BS->FreePool(__argvutf8);
return ret;
#else
ret = main(argc, argv);
#endif
return ret ? EFIERR(ret) : EFI_SUCCESS;
}

View File

@ -0,0 +1,270 @@
/*
* crt_riscv64.c
*
* Copyright (C) 2023 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief C runtime, bootstraps an EFI application to call standard main()
*
*/
#include <uefi.h>
/* this is implemented by the application */
extern int main(int argc, char_t **argv);
/* definitions for elf relocations */
#ifndef __clang__
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint64_t Elf64_Addr;
typedef struct
{
Elf64_Sxword d_tag; /* Dynamic entry type */
union
{
Elf64_Xword d_val; /* Integer value */
Elf64_Addr d_ptr; /* Address value */
} d_un;
} Elf64_Dyn;
#define DT_NULL 0 /* Marks end of dynamic section */
#define DT_RELA 7 /* Address of Rela relocs */
#define DT_RELASZ 8 /* Total size of Rela relocs */
#define DT_RELAENT 9 /* Size of one Rela reloc */
typedef struct
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
} Elf64_Rel;
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
#define R_RISCV_RELATIVE 3 /* Adjust by program base */
#endif
/* globals to store system table pointers */
efi_handle_t IM = NULL;
efi_system_table_t *ST = NULL;
efi_boot_services_t *BS = NULL;
efi_runtime_services_t *RT = NULL;
efi_loaded_image_protocol_t *LIP = NULL;
#ifndef UEFI_NO_UTF8
char *__argvutf8 = NULL;
#endif
/* we only need one .o file, so use inline Assembly here */
void bootstrap(void)
{
__asm__ __volatile__ (
/* call init in C */
" .align 4\n"
#ifndef __clang__
" .globl _start\n"
"_start:\n"
" lla a2, ImageBase\n"
" lui a3, %hi(_DYNAMIC)\n"
" addi a3, a3, %lo(_DYNAMIC)\n"
" call uefi_init\n"
" ret\n"
/* fake a relocation record, so that EFI won't complain */
" .data\n"
"dummy: .long 0\n"
" .section .reloc, \"a\"\n"
"label1:\n"
" .long dummy-label1\n"
" .long 10\n"
" .word 0\n"
".text\n"
#else
" .globl __chkstk\n"
"__chkstk:\n"
" ret\n"
#endif
);
/* setjmp and longjmp */
__asm__ __volatile__ (
" .p2align 3\n"
" .globl setjmp\n"
"setjmp:\n"
" sd ra, 0(a0)\n"
" sd sp, 8(a0)\n"
" sd s0, 16(a0)\n"
" sd s1, 24(a0)\n"
" sd s2, 32(a0)\n"
" sd s3, 40(a0)\n"
" sd s4, 48(a0)\n"
" sd s5, 56(a0)\n"
" sd s6, 64(a0)\n"
" sd s7, 72(a0)\n"
" sd s8, 80(a0)\n"
" sd s9, 88(a0)\n"
" sd s10, 96(a0)\n"
" sd s11, 104(a0)\n"
#ifndef __riscv_float_abi_soft
" fsd fs0, 112(a0)\n"
" fsd fs1, 120(a0)\n"
" fsd fs2, 128(a0)\n"
" fsd fs3, 136(a0)\n"
" fsd fs4, 144(a0)\n"
" fsd fs5, 152(a0)\n"
" fsd fs6, 160(a0)\n"
" fsd fs7, 168(a0)\n"
" fsd fs8, 176(a0)\n"
" fsd fs9, 184(a0)\n"
" fsd fs10,192(a0)\n"
" fsd fs11,200(a0)\n"
#endif
" li a0, 0\n"
" ret\n"
);
__asm__ __volatile__ (
" .globl longjmp\n"
"longjmp:\n"
" ld ra, 0(a0)\n"
" ld sp, 8(a0)\n"
" ld s0, 16(a0)\n"
" ld s1, 24(a0)\n"
" ld s2, 32(a0)\n"
" ld s3, 40(a0)\n"
" ld s4, 48(a0)\n"
" ld s5, 56(a0)\n"
" ld s6, 64(a0)\n"
" ld s7, 72(a0)\n"
" ld s8, 80(a0)\n"
" ld s9, 88(a0)\n"
" ld s10, 96(a0)\n"
" ld s11, 104(a0)\n"
#ifndef __riscv_float_abi_soft
" fld fs0, 112(a0)\n"
" fld fs1, 120(a0)\n"
" fld fs2, 128(a0)\n"
" fld fs3, 136(a0)\n"
" fld fs4, 144(a0)\n"
" fld fs5, 152(a0)\n"
" fld fs6, 160(a0)\n"
" fld fs7, 168(a0)\n"
" fld fs8, 176(a0)\n"
" fld fs9, 184(a0)\n"
" fld fs10,192(a0)\n"
" fld fs11,200(a0)\n"
#endif
" seqz a0, a1\n"
" add a0, a0, a1\n"
" ret\n"
);
}
/**
* Initialize POSIX-UEFI and call the application's main() function
*/
efi_status_t uefi_init (
efi_handle_t image, efi_system_table_t *systab
#ifndef __clang__
, uintptr_t ldbase, Elf64_Dyn *dyn
#endif
) {
efi_guid_t shpGuid = EFI_SHELL_PARAMETERS_PROTOCOL_GUID;
efi_shell_parameters_protocol_t *shp = NULL;
efi_guid_t shiGuid = SHELL_INTERFACE_PROTOCOL_GUID;
efi_shell_interface_protocol_t *shi = NULL;
efi_guid_t lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
efi_status_t status;
int argc = 0, i, ret;
wchar_t **argv = NULL;
#ifndef UEFI_NO_UTF8
int j;
char *s;
#endif
#ifndef __clang__
long relsz = 0, relent = 0;
Elf64_Rel *rel = 0;
uintptr_t *addr;
/* handle relocations */
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
switch (dyn[i].d_tag) {
case DT_RELA: rel = (Elf64_Rel*)((unsigned long)dyn[i].d_un.d_ptr + ldbase); break;
case DT_RELASZ: relsz = dyn[i].d_un.d_val; break;
case DT_RELAENT: relent = dyn[i].d_un.d_val; break;
default: break;
}
}
if (rel && relent) {
while (relsz > 0) {
if(ELF64_R_TYPE (rel->r_info) == R_RISCV_RELATIVE)
{ addr = (unsigned long *)(ldbase + rel->r_offset); *addr += ldbase; }
rel = (Elf64_Rel*) ((char *) rel + relent);
relsz -= relent;
}
}
#else
(void)i;
#endif
/* failsafes, should never happen */
if(!image || !systab || !systab->BootServices || !systab->BootServices->HandleProtocol ||
!systab->BootServices->OpenProtocol || !systab->BootServices->AllocatePool || !systab->BootServices->FreePool)
return EFI_UNSUPPORTED;
/* save EFI pointers and loaded image into globals */
IM = image;
ST = systab;
BS = systab->BootServices;
RT = systab->RuntimeServices;
BS->HandleProtocol(image, &lipGuid, (void **)&LIP);
/* get command line arguments */
status = BS->OpenProtocol(image, &shpGuid, (void **)&shp, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if(!EFI_ERROR(status) && shp) { argc = (int)shp->Argc; argv = shp->Argv; }
else {
/* if shell 2.0 failed, fallback to shell 1.0 interface */
status = BS->OpenProtocol(image, &shiGuid, (void **)&shi, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if(!EFI_ERROR(status) && shi) { argc = (int)shi->Argc; argv = shi->Argv; }
}
/* call main */
#ifndef UEFI_NO_UTF8
if(argc && argv) {
ret = (argc + 1) * ((int)sizeof(uintptr_t) + 1);
for(i = 0; i < argc; i++)
for(j = 0; argv[i] && argv[i][j]; j++)
ret += argv[i][j] < 0x80 ? 1 : (argv[i][j] < 0x800 ? 2 : 3);
status = BS->AllocatePool(LIP ? LIP->ImageDataType : EfiLoaderData, (uintn_t)ret, (void **)&__argvutf8);
if(EFI_ERROR(status) || !__argvutf8) { argc = 0; __argvutf8 = NULL; }
else {
s = __argvutf8 + argc * (int)sizeof(uintptr_t);
*((uintptr_t*)s) = (uintptr_t)0; s += sizeof(uintptr_t);
for(i = 0; i < argc; i++) {
*((uintptr_t*)(__argvutf8 + i * (int)sizeof(uintptr_t))) = (uintptr_t)s;
for(j = 0; argv[i] && argv[i][j]; j++) {
if(argv[i][j]<0x80) { *s++ = argv[i][j]; } else
if(argv[i][j]<0x800) { *s++ = ((argv[i][j]>>6)&0x1F)|0xC0; *s++ = (argv[i][j]&0x3F)|0x80; } else
{ *s++ = ((argv[i][j]>>12)&0x0F)|0xE0; *s++ = ((argv[i][j]>>6)&0x3F)|0x80; *s++ = (argv[i][j]&0x3F)|0x80; }
}
*s++ = 0;
}
}
}
ret = main(argc, (char**)__argvutf8);
if(__argvutf8) BS->FreePool(__argvutf8);
return ret;
#else
ret = main(argc, argv);
#endif
return ret ? EFIERR(ret) : EFI_SUCCESS;
}

View File

@ -0,0 +1,241 @@
/*
* crt_x86_64.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief C runtime, bootstraps an EFI application to call standard main()
*
*/
#include <uefi.h>
/* this is implemented by the application */
extern int main(int argc, char_t **argv);
/* definitions for elf relocations */
#ifndef __clang__
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
typedef uint64_t Elf64_Addr;
typedef struct
{
Elf64_Sxword d_tag; /* Dynamic entry type */
union
{
Elf64_Xword d_val; /* Integer value */
Elf64_Addr d_ptr; /* Address value */
} d_un;
} Elf64_Dyn;
#define DT_NULL 0 /* Marks end of dynamic section */
#define DT_RELA 7 /* Address of Rela relocs */
#define DT_RELASZ 8 /* Total size of Rela relocs */
#define DT_RELAENT 9 /* Size of one Rela reloc */
typedef struct
{
Elf64_Addr r_offset; /* Address */
Elf64_Xword r_info; /* Relocation type and symbol index */
} Elf64_Rel;
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
#define R_X86_64_RELATIVE 8 /* Adjust by program base */
#endif
/* globals to store system table pointers */
efi_handle_t IM = NULL;
efi_system_table_t *ST = NULL;
efi_boot_services_t *BS = NULL;
efi_runtime_services_t *RT = NULL;
efi_loaded_image_protocol_t *LIP = NULL;
#ifndef UEFI_NO_UTF8
char *__argvutf8 = NULL;
#endif
/* we only need one .o file, so use inline Assembly here */
void bootstrap(void)
{
__asm__ __volatile__ (
/* call init in C */
" .align 4\n"
#ifndef __clang__
" .globl _start\n"
"_start:\n"
" lea ImageBase(%rip), %rdi\n"
" lea _DYNAMIC(%rip), %rsi\n"
" call uefi_init\n"
" ret\n"
/* fake a relocation record, so that EFI won't complain */
" .data\n"
"dummy: .long 0\n"
" .section .reloc, \"a\"\n"
"label1:\n"
" .long dummy-label1\n"
" .long 10\n"
" .word 0\n"
".text\n"
#else
" .globl __chkstk\n"
"__chkstk:\n"
" ret\n"
#endif
);
/* setjmp and longjmp */
__asm__ __volatile__ (
" .globl setjmp\n"
"setjmp:\n"
" pop %rsi\n"
" movq %rbx,0x00(%rdi)\n"
" movq %rsp,0x08(%rdi)\n"
" push %rsi\n"
" movq %rbp,0x10(%rdi)\n"
" movq %r12,0x18(%rdi)\n"
" movq %r13,0x20(%rdi)\n"
" movq %r14,0x28(%rdi)\n"
" movq %r15,0x30(%rdi)\n"
" movq %rsi,0x38(%rdi)\n"
" xor %rax,%rax\n"
" ret\n"
);
__asm__ __volatile__ (
" .globl longjmp\n"
"longjmp:\n"
" movl %esi, %eax\n"
" movq 0x00(%rdi), %rbx\n"
" movq 0x08(%rdi), %rsp\n"
" movq 0x10(%rdi), %rbp\n"
" movq 0x18(%rdi), %r12\n"
" movq 0x20(%rdi), %r13\n"
" movq 0x28(%rdi), %r14\n"
" movq 0x30(%rdi), %r15\n"
" xor %rdx,%rdx\n"
" mov $1,%rcx\n"
" cmp %rax,%rdx\n"
" cmove %rcx,%rax\n"
" jmp *0x38(%rdi)\n"
);
}
/**
* Initialize POSIX-UEFI and call the application's main() function
*/
efi_status_t uefi_init (
#ifndef __clang__
uintptr_t ldbase, Elf64_Dyn *dyn, efi_system_table_t *systab, efi_handle_t image
#else
efi_handle_t image, efi_system_table_t *systab
#endif
) {
efi_guid_t shpGuid = EFI_SHELL_PARAMETERS_PROTOCOL_GUID;
efi_shell_parameters_protocol_t *shp = NULL;
efi_guid_t shiGuid = SHELL_INTERFACE_PROTOCOL_GUID;
efi_shell_interface_protocol_t *shi = NULL;
efi_guid_t lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
efi_status_t status;
int argc = 0, i, ret;
wchar_t **argv = NULL;
#ifndef UEFI_NO_UTF8
int j;
char *s;
#endif
#ifndef __clang__
long relsz = 0, relent = 0;
Elf64_Rel *rel = 0;
uintptr_t *addr;
/* handle relocations */
for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
switch (dyn[i].d_tag) {
case DT_RELA: rel = (Elf64_Rel*)((unsigned long)dyn[i].d_un.d_ptr + ldbase); break;
case DT_RELASZ: relsz = dyn[i].d_un.d_val; break;
case DT_RELAENT: relent = dyn[i].d_un.d_val; break;
default: break;
}
}
if (rel && relent) {
while (relsz > 0) {
if(ELF64_R_TYPE (rel->r_info) == R_X86_64_RELATIVE)
{ addr = (unsigned long *)(ldbase + rel->r_offset); *addr += ldbase; }
rel = (Elf64_Rel*) ((char *) rel + relent);
relsz -= relent;
}
}
#else
(void)i;
#endif
/* make sure SSE is enabled, because some say there are buggy firmware in the wild not doing that */
__asm__ __volatile__ (
" movq %cr0, %rax\n"
" andb $0xF1, %al\n"
" movq %rax, %cr0\n"
" movq %cr4, %rax\n"
" orw $3 << 9, %ax\n"
" mov %rax, %cr4\n"
);
/* failsafes, should never happen */
if(!image || !systab || !systab->BootServices || !systab->BootServices->HandleProtocol ||
!systab->BootServices->OpenProtocol || !systab->BootServices->AllocatePool || !systab->BootServices->FreePool)
return EFI_UNSUPPORTED;
/* save EFI pointers and loaded image into globals */
IM = image;
ST = systab;
BS = systab->BootServices;
RT = systab->RuntimeServices;
BS->HandleProtocol(image, &lipGuid, (void **)&LIP);
/* get command line arguments */
status = BS->OpenProtocol(image, &shpGuid, (void **)&shp, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if(!EFI_ERROR(status) && shp) { argc = (int)shp->Argc; argv = shp->Argv; }
else {
/* if shell 2.0 failed, fallback to shell 1.0 interface */
status = BS->OpenProtocol(image, &shiGuid, (void **)&shi, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if(!EFI_ERROR(status) && shi) { argc = (int)shi->Argc; argv = shi->Argv; }
}
/* call main */
#ifndef UEFI_NO_UTF8
if(argc && argv) {
ret = (argc + 1) * ((int)sizeof(uintptr_t) + 1);
for(i = 0; i < argc; i++)
for(j = 0; argv[i] && argv[i][j]; j++)
ret += argv[i][j] < 0x80 ? 1 : (argv[i][j] < 0x800 ? 2 : 3);
status = BS->AllocatePool(LIP ? LIP->ImageDataType : EfiLoaderData, (uintn_t)ret, (void **)&__argvutf8);
if(EFI_ERROR(status) || !__argvutf8) { argc = 0; __argvutf8 = NULL; }
else {
s = __argvutf8 + argc * (int)sizeof(uintptr_t);
*((uintptr_t*)s) = (uintptr_t)0; s += sizeof(uintptr_t);
for(i = 0; i < argc; i++) {
*((uintptr_t*)(__argvutf8 + i * (int)sizeof(uintptr_t))) = (uintptr_t)s;
for(j = 0; argv[i] && argv[i][j]; j++) {
if(argv[i][j]<0x80) { *s++ = argv[i][j]; } else
if(argv[i][j]<0x800) { *s++ = ((argv[i][j]>>6)&0x1F)|0xC0; *s++ = (argv[i][j]&0x3F)|0x80; } else
{ *s++ = ((argv[i][j]>>12)&0x0F)|0xE0; *s++ = ((argv[i][j]>>6)&0x3F)|0x80; *s++ = (argv[i][j]&0x3F)|0x80; }
}
*s++ = 0;
}
}
}
ret = main(argc, (char**)__argvutf8);
if(__argvutf8) BS->FreePool(__argvutf8);
#else
ret = main(argc, argv);
#endif
return ret ? EFIERR(ret) : EFI_SUCCESS;
}

View File

@ -0,0 +1,76 @@
/*
* dirent.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief Implementing functions which are defined in dirent.h
*
*/
#include <uefi.h>
extern void __stdio_seterrno(efi_status_t status);
static struct dirent __dirent;
DIR *opendir (const char_t *__name)
{
DIR *dp = (DIR*)fopen(__name, CL("rd"));
if(dp) rewinddir(dp);
return dp;
}
struct dirent *readdir (DIR *__dirp)
{
efi_status_t status;
efi_file_info_t info;
uintn_t bs = sizeof(efi_file_info_t);
memset(&__dirent, 0, sizeof(struct dirent));
status = __dirp->Read(__dirp, &bs, &info);
if(EFI_ERROR(status) || !bs) {
if(EFI_ERROR(status)) __stdio_seterrno(status);
else errno = 0;
return NULL;
}
__dirent.d_type = info.Attribute & EFI_FILE_DIRECTORY ? DT_DIR : DT_REG;
#ifndef UEFI_NO_UTF8
__dirent.d_reclen = (unsigned short int)wcstombs(__dirent.d_name, info.FileName, FILENAME_MAX - 1);
#else
__dirent.d_reclen = strlen(info.FileName);
strncpy(__dirent.d_name, info.FileName, FILENAME_MAX - 1);
#endif
return &__dirent;
}
void rewinddir (DIR *__dirp)
{
if(__dirp)
__dirp->SetPosition(__dirp, 0);
}
int closedir (DIR *__dirp)
{
return fclose((FILE*)__dirp);
}

View File

@ -0,0 +1,63 @@
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start)
SECTIONS
{
.text 0x0 : {
_text = .;
*(.text.head)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.srodata)
*(.rodata*)
. = ALIGN(16);
}
_etext = .;
_text_size = . - _text;
.dynamic : { *(.dynamic) }
.data : ALIGN(4096)
{
_data = .;
*(.sdata)
*(.data)
*(.data1)
*(.data.*)
*(.got.plt)
*(.got)
/* the EFI loader doesn't seem to like a .bss section, so we stick
it all into .data: */
. = ALIGN(16);
_bss = .;
*(.sbss)
*(.scommon)
*(.dynbss)
*(.bss)
*(COMMON)
. = ALIGN(16);
_bss_end = .;
}
.rela.dyn : { *(.rela.dyn) }
.rela.plt : { *(.rela.plt) }
.rela.got : { *(.rela.got) }
.rela.data : { *(.rela.data) *(.rela.data*) }
. = ALIGN(512);
_edata = .;
_data_size = . - _data;
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
. = ALIGN(4096);
.dynstr : { *(.dynstr) }
. = ALIGN(4096);
.note.gnu.build-id : { *(.note.gnu.build-id) }
/DISCARD/ :
{
*(.rel.reloc)
*(.eh_frame)
*(.note.GNU-stack)
}
.comment 0 : { *(.comment) }
}

View File

@ -0,0 +1,64 @@
OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv")
OUTPUT_ARCH(riscv)
ENTRY(_start)
SECTIONS
{
.text 0x0 : {
_text = .;
*(.text.head)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.srodata)
*(.rodata*)
. = ALIGN(16);
}
_etext = .;
_text_size = . - _text;
.dynamic : { *(.dynamic) }
.data : ALIGN(4096)
{
_data = .;
*(.sdata)
*(.data)
*(.data1)
*(.data.*)
*(.got.plt)
*(.got)
/* the EFI loader doesn't seem to like a .bss section, so we stick
it all into .data: */
. = ALIGN(16);
_bss = .;
*(.sbss)
*(.scommon)
*(.dynbss)
*(.bss)
*(COMMON)
. = ALIGN(16);
_bss_end = .;
}
.rela.text : { *(.rela.text) *(.rela.text*) }
.rela.dyn : { *(.rela.dyn) }
.rela.plt : { *(.rela.plt) }
.rela.got : { *(.rela.got) }
.rela.data : { *(.rela.data) *(.rela.data*) }
. = ALIGN(512);
_edata = .;
_data_size = . - _data;
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
. = ALIGN(4096);
.dynstr : { *(.dynstr) }
. = ALIGN(4096);
.note.gnu.build-id : { *(.note.gnu.build-id) }
/DISCARD/ :
{
*(.rel.reloc)
*(.eh_frame)
*(.note.GNU-stack)
}
.comment 0 : { *(.comment) }
}

View File

@ -0,0 +1,76 @@
/* Same as elf_x86_64_fbsd_efi.lds, except for OUTPUT_FORMAT below - KEEP IN SYNC */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SECTIONS
{
. = 0;
ImageBase = .;
/* .hash and/or .gnu.hash MUST come first! */
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
. = ALIGN(4096);
.eh_frame :
{
*(.eh_frame)
}
. = ALIGN(4096);
.text :
{
_text = .;
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
. = ALIGN(16);
}
_etext = .;
_text_size = . - _text;
. = ALIGN(4096);
.reloc :
{
*(.reloc)
}
. = ALIGN(4096);
.data :
{
_data = .;
*(.rodata*)
*(.got.plt)
*(.got)
*(.data*)
*(.sdata)
/* the EFI loader doesn't seem to like a .bss section, so we stick
it all into .data: */
*(.sbss)
*(.scommon)
*(.dynbss)
*(.bss)
*(COMMON)
*(.rel.local)
}
.note.gnu.build-id : { *(.note.gnu.build-id) }
_edata = .;
_data_size = . - _etext;
. = ALIGN(4096);
.dynamic : { *(.dynamic) }
. = ALIGN(4096);
.rela :
{
*(.rela.data*)
*(.rela.got)
*(.rela.stab)
}
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
. = ALIGN(4096);
.dynstr : { *(.dynstr) }
. = ALIGN(4096);
.ignored.reloc :
{
*(.rela.reloc)
*(.eh_frame)
*(.note.GNU-stack)
}
.comment 0 : { *(.comment) }
}

View File

@ -0,0 +1,154 @@
/*
* qsort.c
*
* @brief from OpenBSD
*/
/* $OpenBSD: qsort.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <uefi.h>
static __inline char *med3(char *, char *, char *, __compar_fn_t cmp);
static __inline void swapfunc(char *, char *, int, int);
/*
* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
*/
#define swapcode(TYPE, parmi, parmj, n) { \
long i = (n) / sizeof (TYPE); \
TYPE *pi = (TYPE *) (parmi); \
TYPE *pj = (TYPE *) (parmj); \
do { \
TYPE t = *pi; \
*pi++ = *pj; \
*pj++ = t; \
} while (--i > 0); \
}
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
static __inline void
swapfunc(char *a, char *b, int n, int swaptype)
{
if (swaptype <= 1)
swapcode(long, a, b, n)
else
swapcode(char, a, b, n)
}
#define swap(a, b) \
if (swaptype == 0) { \
long t = *(long *)(a); \
*(long *)(a) = *(long *)(b); \
*(long *)(b) = t; \
} else \
swapfunc(a, b, es, swaptype)
#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
static __inline char *
med3(char *a, char *b, char *c, __compar_fn_t cmp)
{
return cmp(a, b) < 0 ?
(cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
:(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
}
void qsort(void *aa, size_t n, size_t es, __compar_fn_t cmp)
{
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
int d, r, swaptype, swap_cnt;
char *a = aa;
loop: SWAPINIT(a, es);
swap_cnt = 0;
if (n < 7) {
for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
pl -= es)
swap(pl, pl - es);
return;
}
pm = (char *)a + (n / 2) * es;
if (n > 7) {
pl = (char *)a;
pn = (char *)a + (n - 1) * es;
if (n > 40) {
d = (n / 8) * es;
pl = med3(pl, pl + d, pl + 2 * d, cmp);
pm = med3(pm - d, pm, pm + d, cmp);
pn = med3(pn - 2 * d, pn - d, pn, cmp);
}
pm = med3(pl, pm, pn, cmp);
}
swap(a, pm);
pa = pb = (char *)a + es;
pc = pd = (char *)a + (n - 1) * es;
for (;;) {
while (pb <= pc && (r = cmp(pb, a)) <= 0) {
if (r == 0) {
swap_cnt = 1;
swap(pa, pb);
pa += es;
}
pb += es;
}
while (pb <= pc && (r = cmp(pc, a)) >= 0) {
if (r == 0) {
swap_cnt = 1;
swap(pc, pd);
pd -= es;
}
pc -= es;
}
if (pb > pc)
break;
swap(pb, pc);
swap_cnt = 1;
pb += es;
pc -= es;
}
if (swap_cnt == 0) { /* Switch to insertion sort */
for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
pl -= es)
swap(pl, pl - es);
return;
}
pn = (char *)a + n * es;
r = min(pa - (char *)a, pb - pa);
vecswap(a, pb - r, r);
r = min(pd - pc, pn - pd - (int)es);
vecswap(pb, pn - r, r);
if ((r = pb - pa) > (int)es)
qsort(a, r / es, es, cmp);
if ((r = pd - pc) > (int)es) {
/* Iterate rather than recurse to save stack space */
a = pn - r;
n = r / es;
goto loop;
}
/* qsort(pn - r, r / es, es, cmp);*/
}

View File

@ -0,0 +1,68 @@
/*
* stat.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief Implementing functions which are defined in sys/stat.h
*
*/
#include <uefi.h>
/* fstat is in stdio.c because we can't access static variables otherwise... */
int stat (const char_t *__file, struct stat *__buf)
{
int ret;
FILE *f;
if(!__file || !*__file || !__buf) {
errno = EINVAL;
return -1;
}
f = fopen(__file, CL("*"));
if(!f) {
memset(__buf, 0, sizeof(struct stat));
return -1;
}
ret = fstat(f, __buf);
fclose(f);
return ret;
}
extern int mkdir (const char_t *__path, mode_t __mode)
{
FILE *f;
(void)__mode;
if(!__path || !*__path) {
errno = EINVAL;
return -1;
}
f = fopen(__path, CL("wd"));
if(!f) {
return -1;
}
fclose(f);
return 0;
}

View File

@ -0,0 +1,816 @@
/*
* stdio.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief Implementing functions which are defined in stdio.h
*
*/
#include <uefi.h>
static efi_file_handle_t *__root_dir = NULL;
static efi_serial_io_protocol_t *__ser = NULL;
static block_file_t *__blk_devs = NULL;
static uintn_t __blk_ndevs = 0;
extern time_t __mktime_efi(efi_time_t *t);
void __stdio_cleanup(void);
void __stdio_seterrno(efi_status_t status);
int __remove (const char_t *__filename, int isdir);
void __stdio_cleanup(void)
{
#ifndef UEFI_NO_UTF8
if(__argvutf8)
BS->FreePool(__argvutf8);
#endif
if(__blk_devs) {
free(__blk_devs);
__blk_devs = NULL;
__blk_ndevs = 0;
}
}
void __stdio_seterrno(efi_status_t status)
{
switch((int)(status & 0xffff)) {
case EFI_WRITE_PROTECTED & 0xffff: errno = EROFS; break;
case EFI_ACCESS_DENIED & 0xffff: errno = EACCES; break;
case EFI_VOLUME_FULL & 0xffff: errno = ENOSPC; break;
case EFI_NOT_FOUND & 0xffff: errno = ENOENT; break;
case EFI_INVALID_PARAMETER & 0xffff: errno = EINVAL; break;
default: errno = EIO; break;
}
}
int fstat (FILE *__f, struct stat *__buf)
{
efi_guid_t infGuid = EFI_FILE_INFO_GUID;
efi_file_info_t info;
uintn_t fsiz = (uintn_t)sizeof(efi_file_info_t);
efi_status_t status;
uintn_t i;
if(!__f || !__buf) {
errno = EINVAL;
return -1;
}
memset(__buf, 0, sizeof(struct stat));
if(__f == stdin) {
__buf->st_mode = S_IREAD | S_IFIFO;
return 0;
}
if(__f == stdout || __f == stderr) {
__buf->st_mode = S_IWRITE | S_IFIFO;
return 0;
}
if(__ser && __f == (FILE*)__ser) {
__buf->st_mode = S_IREAD | S_IWRITE | S_IFCHR;
return 0;
}
for(i = 0; i < __blk_ndevs; i++)
if(__f == (FILE*)__blk_devs[i].bio) {
__buf->st_mode = S_IREAD | S_IWRITE | S_IFBLK;
__buf->st_size = (off_t)__blk_devs[i].bio->Media->BlockSize * ((off_t)__blk_devs[i].bio->Media->LastBlock + 1);
__buf->st_blocks = __blk_devs[i].bio->Media->LastBlock + 1;
return 0;
}
status = __f->GetInfo(__f, &infGuid, &fsiz, &info);
if(EFI_ERROR(status)) {
__stdio_seterrno(status);
return -1;
}
__buf->st_mode = S_IREAD |
(info.Attribute & EFI_FILE_READ_ONLY ? 0 : S_IWRITE) |
(info.Attribute & EFI_FILE_DIRECTORY ? S_IFDIR : S_IFREG);
__buf->st_size = (off_t)info.FileSize;
__buf->st_blocks = (blkcnt_t)info.PhysicalSize;
__buf->st_atime = __mktime_efi(&info.LastAccessTime);
__buf->st_mtime = __mktime_efi(&info.ModificationTime);
__buf->st_ctime = __mktime_efi(&info.CreateTime);
return 0;
}
int fclose (FILE *__stream)
{
efi_status_t status = EFI_SUCCESS;
uintn_t i;
if(!__stream) {
errno = EINVAL;
return 0;
}
if(__stream == stdin || __stream == stdout || __stream == stderr || (__ser && __stream == (FILE*)__ser)) {
return 1;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i].bio)
return 1;
status = __stream->Close(__stream);
free(__stream);
return !EFI_ERROR(status);
}
int fflush (FILE *__stream)
{
efi_status_t status = EFI_SUCCESS;
uintn_t i;
if(!__stream) {
errno = EINVAL;
return 0;
}
if(__stream == stdin || __stream == stdout || __stream == stderr || (__ser && __stream == (FILE*)__ser)) {
return 1;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i].bio) {
return 1;
}
status = __stream->Flush(__stream);
return !EFI_ERROR(status);
}
int __remove (const char_t *__filename, int isdir)
{
efi_status_t status;
efi_guid_t infGuid = EFI_FILE_INFO_GUID;
efi_file_info_t info;
uintn_t fsiz = (uintn_t)sizeof(efi_file_info_t), i;
/* little hack to support read and write mode for Delete() and stat() without create mode or checks */
FILE *f = fopen(__filename, CL("*"));
if(errno)
return 1;
if(!f || f == stdin || f == stdout || f == stderr || (__ser && f == (FILE*)__ser)) {
errno = EBADF;
return 1;
}
for(i = 0; i < __blk_ndevs; i++)
if(f == (FILE*)__blk_devs[i].bio) {
errno = EBADF;
return 1;
}
if(isdir != -1) {
status = f->GetInfo(f, &infGuid, &fsiz, &info);
if(EFI_ERROR(status)) goto err;
if(isdir == 0 && (info.Attribute & EFI_FILE_DIRECTORY)) {
fclose(f); errno = EISDIR;
return -1;
}
if(isdir == 1 && !(info.Attribute & EFI_FILE_DIRECTORY)) {
fclose(f); errno = ENOTDIR;
return -1;
}
}
status = f->Delete(f);
if(EFI_ERROR(status)) {
err: __stdio_seterrno(status);
fclose(f);
return -1;
}
/* no need for fclose(f); */
free(f);
return 0;
}
int remove (const char_t *__filename)
{
return __remove(__filename, -1);
}
FILE *fopen (const char_t *__filename, const char_t *__modes)
{
FILE *ret;
efi_status_t status;
efi_guid_t sfsGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
efi_simple_file_system_protocol_t *sfs = NULL;
efi_guid_t infGuid = EFI_FILE_INFO_GUID;
efi_file_info_t info;
uintn_t fsiz = (uintn_t)sizeof(efi_file_info_t), par, i;
#ifndef UEFI_NO_UTF8
wchar_t wcname[BUFSIZ];
#endif
errno = 0;
if(!__filename || !*__filename || !__modes || (__modes[0] != CL('r') && __modes[0] != CL('w') && __modes[0] != CL('a') &&
__modes[0] != CL('*')) || (__modes[1] != 0 && __modes[1] != CL('d') && __modes[1] != CL('+'))) {
errno = EINVAL;
return NULL;
}
/* fake some device names. UEFI has no concept of device files */
if(!strcmp(__filename, CL("/dev/stdin"))) {
if(__modes[0] == CL('w') || __modes[0] == CL('a')) { errno = EPERM; return NULL; }
return stdin;
}
if(!strcmp(__filename, CL("/dev/stdout"))) {
if(__modes[0] == CL('r')) { errno = EPERM; return NULL; }
return stdout;
}
if(!strcmp(__filename, CL("/dev/stderr"))) {
if(__modes[0] == CL('r')) { errno = EPERM; return NULL; }
return stderr;
}
if(!memcmp(__filename, CL("/dev/serial"), 11 * sizeof(char_t))) {
par = (uintn_t)atol(__filename + 11);
if(!__ser) {
efi_guid_t serGuid = EFI_SERIAL_IO_PROTOCOL_GUID;
status = BS->LocateProtocol(&serGuid, NULL, (void**)&__ser);
if(EFI_ERROR(status) || !__ser) { errno = ENOENT; return NULL; }
}
__ser->SetAttributes(__ser, par > 9600 ? par : 115200, 0, 1000, NoParity, 8, OneStopBit);
return (FILE*)__ser;
}
if(!memcmp(__filename, CL("/dev/disk"), 9 * sizeof(char_t))) {
par = (uintn_t)atol(__filename + 9);
if(!__blk_ndevs) {
efi_guid_t bioGuid = EFI_BLOCK_IO_PROTOCOL_GUID;
efi_handle_t handles[128];
uintn_t handle_size = sizeof(handles);
status = BS->LocateHandle(ByProtocol, &bioGuid, NULL, &handle_size, (efi_handle_t*)&handles);
if(!EFI_ERROR(status)) {
handle_size /= (uintn_t)sizeof(efi_handle_t);
/* workaround a bug in TianoCore, it reports zero size even though the data is in the buffer */
if(handle_size < 1)
handle_size = (uintn_t)sizeof(handles) / sizeof(efi_handle_t);
__blk_devs = (block_file_t*)malloc(handle_size * sizeof(block_file_t));
if(__blk_devs) {
memset(__blk_devs, 0, handle_size * sizeof(block_file_t));
for(i = __blk_ndevs = 0; i < handle_size; i++)
if(handles[i] && !EFI_ERROR(BS->HandleProtocol(handles[i], &bioGuid, (void **) &__blk_devs[__blk_ndevs].bio)) &&
__blk_devs[__blk_ndevs].bio && __blk_devs[__blk_ndevs].bio->Media &&
__blk_devs[__blk_ndevs].bio->Media->BlockSize > 0)
__blk_ndevs++;
} else
__blk_ndevs = 0;
}
}
if(__blk_ndevs && par < __blk_ndevs)
return (FILE*)__blk_devs[par].bio;
errno = ENOENT;
return NULL;
}
if(!__root_dir && LIP) {
status = BS->HandleProtocol(LIP->DeviceHandle, &sfsGuid, (void **)&sfs);
if(!EFI_ERROR(status))
status = sfs->OpenVolume(sfs, &__root_dir);
}
if(!__root_dir) {
errno = ENODEV;
return NULL;
}
ret = (FILE*)malloc(sizeof(FILE));
if(!ret) return NULL;
/* normally write means read,write,create. But for remove (internal '*' mode), we need read,write without create
* also mode 'w' in POSIX means write-only (without read), but that's not working on certain firmware, we must
* pass read too. This poses a problem of truncating a write-only file, see issue #26, we have to do that manually */
#ifndef UEFI_NO_UTF8
mbstowcs((wchar_t*)&wcname, __filename, BUFSIZ - 1);
status = __root_dir->Open(__root_dir, &ret, (wchar_t*)&wcname,
#else
status = __root_dir->Open(__root_dir, &ret, (wchar_t*)__filename,
#endif
__modes[0] == CL('w') || __modes[0] == CL('a') ? (EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ | EFI_FILE_MODE_CREATE) :
EFI_FILE_MODE_READ | (__modes[0] == CL('*') || __modes[1] == CL('+') ? EFI_FILE_MODE_WRITE : 0),
__modes[1] == CL('d') ? EFI_FILE_DIRECTORY : 0);
if(EFI_ERROR(status)) {
err: __stdio_seterrno(status);
free(ret); return NULL;
}
if(__modes[0] == CL('*')) return ret;
status = ret->GetInfo(ret, &infGuid, &fsiz, &info);
if(EFI_ERROR(status)) goto err;
if(__modes[1] == CL('d') && !(info.Attribute & EFI_FILE_DIRECTORY)) {
ret->Close(ret); free(ret); errno = ENOTDIR; return NULL;
}
if(__modes[1] != CL('d') && (info.Attribute & EFI_FILE_DIRECTORY)) {
ret->Close(ret); free(ret); errno = EISDIR; return NULL;
}
if(__modes[0] == CL('a')) fseek(ret, 0, SEEK_END);
if(__modes[0] == CL('w')) {
/* manually truncate file size
* See https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.c
* function FileHandleSetSize */
info.FileSize = 0;
ret->SetInfo(ret, &infGuid, fsiz, &info);
}
return ret;
}
size_t fread (void *__ptr, size_t __size, size_t __n, FILE *__stream)
{
uintn_t bs = __size * __n, i, n;
efi_status_t status;
if(!__ptr || __size < 1 || __n < 1 || !__stream) {
errno = EINVAL;
return 0;
}
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return 0;
}
if(__ser && __stream == (FILE*)__ser) {
status = __ser->Read(__ser, &bs, __ptr);
} else {
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i].bio) {
n = __blk_devs[i].offset / __blk_devs[i].bio->Media->BlockSize;
bs = (bs / __blk_devs[i].bio->Media->BlockSize) * __blk_devs[i].bio->Media->BlockSize;
status = __blk_devs[i].bio->ReadBlocks(__blk_devs[i].bio, __blk_devs[i].bio->Media->MediaId, n, bs, __ptr);
if(EFI_ERROR(status)) {
__stdio_seterrno(status);
return 0;
}
__blk_devs[i].offset += bs;
return bs / __size;
}
status = __stream->Read(__stream, &bs, __ptr);
}
if(EFI_ERROR(status)) {
__stdio_seterrno(status);
return 0;
}
return bs / __size;
}
size_t fwrite (const void *__ptr, size_t __size, size_t __n, FILE *__stream)
{
uintn_t bs = __size * __n, n, i;
efi_status_t status;
if(!__ptr || __size < 1 || __n < 1 || !__stream) {
errno = EINVAL;
return 0;
}
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return 0;
}
if(__ser && __stream == (FILE*)__ser) {
status = __ser->Write(__ser, &bs, (void*)__ptr);
} else {
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i].bio) {
n = __blk_devs[i].offset / __blk_devs[i].bio->Media->BlockSize;
bs = (bs / __blk_devs[i].bio->Media->BlockSize) * __blk_devs[i].bio->Media->BlockSize;
status = __blk_devs[i].bio->WriteBlocks(__blk_devs[i].bio, __blk_devs[i].bio->Media->MediaId, n, bs, (void*)__ptr);
if(EFI_ERROR(status)) {
__stdio_seterrno(status);
return 0;
}
__blk_devs[i].offset += bs;
return bs / __size;
}
status = __stream->Write(__stream, &bs, (void *)__ptr);
}
if(EFI_ERROR(status)) {
__stdio_seterrno(status);
return 0;
}
return bs / __size;
}
int fseek (FILE *__stream, long int __off, int __whence)
{
off_t off = 0;
efi_status_t status;
efi_guid_t infoGuid = EFI_FILE_INFO_GUID;
efi_file_info_t info;
uintn_t fsiz = sizeof(efi_file_info_t), i;
if(!__stream || (__whence != SEEK_SET && __whence != SEEK_CUR && __whence != SEEK_END)) {
errno = EINVAL;
return -1;
}
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return -1;
}
if(__ser && __stream == (FILE*)__ser) {
errno = EBADF;
return -1;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i].bio) {
off = (uint64_t)__blk_devs[i].bio->Media->BlockSize * (uint64_t)__blk_devs[i].bio->Media->LastBlock;
switch(__whence) {
case SEEK_END:
__blk_devs[i].offset = off + __off;
break;
case SEEK_CUR:
__blk_devs[i].offset += __off;
break;
case SEEK_SET:
__blk_devs[i].offset = __off;
break;
}
if(__blk_devs[i].offset < 0) __blk_devs[i].offset = 0;
if(__blk_devs[i].offset > off) __blk_devs[i].offset = off;
__blk_devs[i].offset = (__blk_devs[i].offset / __blk_devs[i].bio->Media->BlockSize) *
__blk_devs[i].bio->Media->BlockSize;
return 0;
}
switch(__whence) {
case SEEK_END:
status = __stream->GetInfo(__stream, &infoGuid, &fsiz, &info);
if(!EFI_ERROR(status)) {
off = info.FileSize + __off;
status = __stream->SetPosition(__stream, off);
}
break;
case SEEK_CUR:
status = __stream->GetPosition(__stream, &off);
if(!EFI_ERROR(status)) {
off += __off;
status = __stream->SetPosition(__stream, off);
}
break;
default:
status = __stream->SetPosition(__stream, __off);
break;
}
return EFI_ERROR(status) ? -1 : 0;
}
long int ftell (FILE *__stream)
{
uint64_t off = 0;
uintn_t i;
efi_status_t status;
if(!__stream) {
errno = EINVAL;
return -1;
}
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return -1;
}
if(__ser && __stream == (FILE*)__ser) {
errno = EBADF;
return -1;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i].bio) {
return (long int)__blk_devs[i].offset;
}
status = __stream->GetPosition(__stream, &off);
return EFI_ERROR(status) ? -1 : (long int)off;
}
int feof (FILE *__stream)
{
uint64_t off = 0;
efi_guid_t infGuid = EFI_FILE_INFO_GUID;
efi_file_info_t info;
uintn_t fsiz = (uintn_t)sizeof(efi_file_info_t), i;
efi_status_t status;
if(!__stream) {
errno = EINVAL;
return 0;
}
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return 0;
}
if(__ser && __stream == (FILE*)__ser) {
errno = EBADF;
return 0;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i].bio) {
errno = EBADF;
return __blk_devs[i].offset == (off_t)__blk_devs[i].bio->Media->BlockSize * (off_t)__blk_devs[i].bio->Media->LastBlock;
}
status = __stream->GetPosition(__stream, &off);
if(EFI_ERROR(status)) {
err: __stdio_seterrno(status);
return 1;
}
status = __stream->GetInfo(__stream, &infGuid, &fsiz, &info);
if(EFI_ERROR(status)) goto err;
__stream->SetPosition(__stream, off);
return info.FileSize == off;
}
int vsnprintf(char_t *dst, size_t maxlen, const char_t *fmt, __builtin_va_list args)
{
#define needsescape(a) (a==CL('\"') || a==CL('\\') || a==CL('\a') || a==CL('\b') || a==CL('\033') || a==CL('\f') || \
a==CL('\r') || a==CL('\n') || a==CL('\t') || a==CL('\v'))
efi_physical_address_t m;
uint8_t *mem;
int64_t arg;
int len, sign, i, j;
char_t *p, *orig=dst, *end = dst + maxlen - 1, tmpstr[24], pad, n;
#ifdef UEFI_NO_UTF8
char *c;
#endif
if(dst==NULL || fmt==NULL)
return 0;
arg = 0;
while(*fmt && dst < end) {
if(*fmt==CL('%')) {
fmt++;
if(*fmt==CL('%')) goto put;
len=0; pad=CL(' ');
if(*fmt==CL('0')) pad=CL('0');
while(*fmt>=CL('0') && *fmt<=CL('9')) {
len *= 10;
len += *fmt-CL('0');
fmt++;
}
if(*fmt==CL('l')) fmt++;
if(*fmt==CL('c')) {
arg = __builtin_va_arg(args, uint32_t);
#ifndef UEFI_NO_UTF8
if(arg<0x80) { *dst++ = arg; } else
if(arg<0x800) { *dst++ = ((arg>>6)&0x1F)|0xC0; *dst++ = (arg&0x3F)|0x80; } else
{ *dst++ = ((arg>>12)&0x0F)|0xE0; *dst++ = ((arg>>6)&0x3F)|0x80; *dst++ = (arg&0x3F)|0x80; }
#else
*dst++ = (wchar_t)(arg & 0xffff);
#endif
fmt++;
continue;
} else
if(*fmt==CL('d') || *fmt==CL('i')) {
arg = __builtin_va_arg(args, int64_t);
sign=0;
if(arg<0) {
arg*=-1;
sign++;
}
i=23;
tmpstr[i]=0;
do {
tmpstr[--i]=CL('0')+(arg%10);
arg/=10;
} while(arg!=0 && i>0);
if(sign) {
tmpstr[--i]=CL('-');
}
if(len>0 && len<23) {
while(i && i>23-len) {
tmpstr[--i]=pad;
}
}
p=&tmpstr[i];
goto copystring;
} else
if(*fmt==CL('p')) {
arg = __builtin_va_arg(args, uint64_t);
len = 16; pad = CL('0'); goto hex;
} else
if(*fmt==CL('x') || *fmt==CL('X')) {
arg = __builtin_va_arg(args, int64_t);
hex: i=16;
tmpstr[i]=0;
do {
n=arg & 0xf;
/* 0-9 => '0'-'9', 10-15 => 'A'-'F' */
tmpstr[--i]=n+(n>9?(*fmt==CL('X')?0x37:0x57):0x30);
arg>>=4;
} while(arg!=0 && i>0);
/* padding, only leading zeros */
if(len>0 && len<=16) {
while(i>16-len) {
tmpstr[--i]=CL('0');
}
}
p=&tmpstr[i];
goto copystring;
} else
if(*fmt==CL('s') || *fmt==CL('q')) {
p = __builtin_va_arg(args, char_t*);
copystring: if(p==NULL) {
p=CL("(null)");
}
while(*p && dst + 2 < end) {
if(*fmt==CL('q') && needsescape(*p)) {
*dst++ = CL('\\');
switch(*p) {
case CL('\a'): *dst++ = CL('a'); break;
case CL('\b'): *dst++ = CL('b'); break;
case 27: *dst++ = CL('e'); break; /* gcc 10.2 doesn't like CL('\e') in ansi mode */
case CL('\f'): *dst++ = CL('f'); break;
case CL('\n'): *dst++ = CL('n'); break;
case CL('\r'): *dst++ = CL('r'); break;
case CL('\t'): *dst++ = CL('t'); break;
case CL('\v'): *dst++ = CL('v'); break;
default: *dst++ = *p++; break;
}
} else {
if(*p == CL('\n') && (orig == dst || *(dst - 1) != CL('\r'))) *dst++ = CL('\r');
*dst++ = *p++;
}
}
} else
#ifdef UEFI_NO_UTF8
if(*fmt==L'S' || *fmt==L'Q') {
c = __builtin_va_arg(args, char*);
if(c==NULL) goto copystring;
while(*p && dst + 2 < end) {
arg = *c;
if((*c & 128) != 0) {
if((*c & 32) == 0 ) {
arg = ((*c & 0x1F)<<6)|(*(c+1) & 0x3F);
c += 1;
} else
if((*c & 16) == 0 ) {
arg = ((*c & 0xF)<<12)|((*(c+1) & 0x3F)<<6)|(*(c+2) & 0x3F);
c += 2;
} else
if((*c & 8) == 0 ) {
arg = ((*c & 0x7)<<18)|((*(c+1) & 0x3F)<<12)|((*(c+2) & 0x3F)<<6)|(*(c+3) & 0x3F);
c += 3;
} else
arg = L'?';
}
if(!arg) break;
if(*fmt==L'Q' && needsescape(arg)) {
*dst++ = L'\\';
switch(arg) {
case L'\a': *dst++ = L'a'; break;
case L'\b': *dst++ = L'b'; break;
case 27: *dst++ = L'e'; break; /* gcc 10.2 doesn't like L'\e' in ansi mode */
case L'\f': *dst++ = L'f'; break;
case L'\n': *dst++ = L'n'; break;
case L'\r': *dst++ = L'r'; break;
case L'\t': *dst++ = L't'; break;
case L'\v': *dst++ = L'v'; break;
default: *dst++ = arg; break;
}
} else {
if(arg == L'\n') *dst++ = L'\r';
*dst++ = (wchar_t)(arg & 0xffff);
}
}
} else
#endif
if(*fmt==CL('D')) {
m = __builtin_va_arg(args, efi_physical_address_t);
for(j = 0; j < (len < 1 ? 1 : (len > 16 ? 16 : len)); j++) {
for(i = 44; i >= 0; i -= 4) {
n = (m >> i) & 15; *dst++ = n + (n>9?0x37:0x30);
if(dst >= end) goto zro;
}
*dst++ = CL(':'); if(dst >= end) goto zro;
*dst++ = CL(' '); if(dst >= end) goto zro;
mem = (uint8_t*)m;
for(i = 0; i < 16; i++) {
n = (mem[i] >> 4) & 15; *dst++ = n + (n>9?0x37:0x30); if(dst >= end) goto zro;
n = mem[i] & 15; *dst++ = n + (n>9?0x37:0x30); if(dst >= end) goto zro;
*dst++ = CL(' ');if(dst >= end) goto zro;
}
*dst++ = CL(' '); if(dst >= end) goto zro;
for(i = 0; i < 16; i++) {
*dst++ = (mem[i] < 32 || mem[i] >= 127 ? CL('.') : (char_t)mem[i]);
if(dst >= end) goto zro;
}
*dst++ = CL('\r'); if(dst >= end) goto zro;
*dst++ = CL('\n'); if(dst >= end) goto zro;
m += 16;
}
}
} else {
put: if(*fmt == CL('\n') && (orig == dst || *(dst - 1) != CL('\r'))) *dst++ = CL('\r');
*dst++ = *fmt;
}
fmt++;
}
zro:*dst=0;
return (int)(dst-orig);
#undef needsescape
}
int vsprintf(char_t *dst, const char_t *fmt, __builtin_va_list args)
{
return vsnprintf(dst, BUFSIZ, fmt, args);
}
int sprintf(char_t *dst, const char_t* fmt, ...)
{
int ret;
__builtin_va_list args;
__builtin_va_start(args, fmt);
ret = vsnprintf(dst, BUFSIZ, fmt, args);
__builtin_va_end(args);
return ret;
}
int snprintf(char_t *dst, size_t maxlen, const char_t* fmt, ...)
{
int ret;
__builtin_va_list args;
__builtin_va_start(args, fmt);
ret = vsnprintf(dst, maxlen, fmt, args);
__builtin_va_end(args);
return ret;
}
int vprintf(const char_t* fmt, __builtin_va_list args)
{
int ret;
wchar_t dst[BUFSIZ];
#ifndef UEFI_NO_UTF8
char_t tmp[BUFSIZ];
ret = vsnprintf(tmp, BUFSIZ, fmt, args);
mbstowcs(dst, tmp, BUFSIZ - 1);
#else
ret = vsnprintf(dst, BUFSIZ, fmt, args);
#endif
ST->ConOut->OutputString(ST->ConOut, (wchar_t *)&dst);
return ret;
}
int printf(const char_t* fmt, ...)
{
int ret;
__builtin_va_list args;
__builtin_va_start(args, fmt);
ret = vprintf(fmt, args);
__builtin_va_end(args);
return ret;
}
int vfprintf (FILE *__stream, const char_t *__format, __builtin_va_list args)
{
wchar_t dst[BUFSIZ];
char_t tmp[BUFSIZ];
uintn_t ret, i;
#ifndef UEFI_NO_UTF8
ret = (uintn_t)vsnprintf(tmp, BUFSIZ, __format, args);
ret = mbstowcs(dst, tmp, BUFSIZ - 1);
#else
ret = vsnprintf(dst, BUFSIZ, __format, args);
#endif
if(ret < 1 || !__stream || __stream == stdin) return 0;
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i].bio) {
errno = EBADF;
return -1;
}
if(__stream == stdout)
ST->ConOut->OutputString(ST->ConOut, (wchar_t*)&dst);
else if(__stream == stderr)
ST->StdErr->OutputString(ST->StdErr, (wchar_t*)&dst);
else if(__ser && __stream == (FILE*)__ser) {
#ifdef UEFI_NO_UTF8
wcstombs((char*)&tmp, dst, BUFSIZ - 1);
#endif
__ser->Write(__ser, &ret, (void*)&tmp);
} else
#ifndef UEFI_NO_UTF8
__stream->Write(__stream, &ret, (void*)&tmp);
#else
__stream->Write(__stream, &ret, (void*)&dst);
#endif
return (int)ret;
}
int fprintf (FILE *__stream, const char_t *__format, ...)
{
int ret;
__builtin_va_list args;
__builtin_va_start(args, __format);
ret = vfprintf(__stream, __format, args);
__builtin_va_end(args);
return ret;
}
int getchar_ifany (void)
{
efi_input_key_t key = { 0 };
efi_status_t status = ST->ConIn->ReadKeyStroke(ST->ConIn, &key);
return EFI_ERROR(status) ? 0 : key.UnicodeChar;
}
int getchar (void)
{
uintn_t idx;
BS->WaitForEvent(1, &ST->ConIn->WaitForKey, &idx);
return getchar_ifany();
}
int putchar (int __c)
{
wchar_t tmp[2];
tmp[0] = (wchar_t)__c;
tmp[1] = 0;
ST->ConOut->OutputString(ST->ConOut, (__c == L'\n' ? (wchar_t*)L"\r\n" : (wchar_t*)&tmp));
return (int)tmp[0];
}

View File

@ -0,0 +1,365 @@
/*
* stdlib.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief Implementing functions which are defined in stdlib.h
*
*/
#include <uefi.h>
int errno = 0;
static uint64_t __srand_seed = 6364136223846793005ULL;
extern void __stdio_cleanup(void);
#ifndef UEFI_NO_TRACK_ALLOC
static uintptr_t *__stdlib_allocs = NULL;
static uintn_t __stdlib_numallocs = 0;
#endif
int atoi(const char_t *s)
{
return (int)atol(s);
}
int64_t atol(const char_t *s)
{
int64_t sign = 1;
if(!s || !*s) return 0;
if(*s == CL('-')) { sign = -1; s++; }
if(s[0] == CL('0')) {
if(s[1] == CL('x'))
return strtol(s + 2, NULL, 16) * sign;
if(s[1] >= CL('0') && s[1] <= CL('7'))
return strtol(s, NULL, 8) * sign;
}
return strtol(s, NULL, 10) * sign;
}
int64_t strtol (const char_t *s, char_t **__endptr, int __base)
{
int64_t v=0, sign = 1;
if(!s || !*s) return 0;
if(*s == CL('-')) { sign = -1; s++; }
while(!(*s < CL('0') || (__base < 10 && *s >= __base + CL('0')) || (__base >= 10 && ((*s > CL('9') && *s < CL('A')) ||
(*s > CL('F') && *s < CL('a')) || *s > CL('f'))))) {
v *= __base;
if(*s >= CL('0') && *s <= (__base < 10 ? __base + CL('0') : CL('9')))
v += (*s)-CL('0');
else if(__base == 16 && *s >= CL('a') && *s <= CL('f'))
v += (*s)-CL('a')+10;
else if(__base == 16 && *s >= CL('A') && *s <= CL('F'))
v += (*s)-CL('A')+10;
s++;
}
if(__endptr) *__endptr = (char_t*)s;
return v * sign;
}
void *malloc (size_t __size)
{
void *ret = NULL;
efi_status_t status;
#ifndef UEFI_NO_TRACK_ALLOC
uintn_t i;
for(i = 0; i < __stdlib_numallocs && __stdlib_allocs[i] != 0; i += 2);
if(i == __stdlib_numallocs) {
/* no free slots found, (re)allocate the housekeeping array */
status = BS->AllocatePool(LIP ? LIP->ImageDataType : EfiLoaderData, (__stdlib_numallocs + 2) * sizeof(uintptr_t), &ret);
if(EFI_ERROR(status) || !ret) { errno = ENOMEM; return NULL; }
if(__stdlib_allocs) memcpy(ret, __stdlib_allocs, __stdlib_numallocs * sizeof(uintptr_t));
__stdlib_allocs = (uintptr_t*)ret;
__stdlib_allocs[i] = __stdlib_allocs[i + 1] = 0;
__stdlib_numallocs += 2;
ret = NULL;
}
#endif
status = BS->AllocatePool(LIP ? LIP->ImageDataType : EfiLoaderData, __size, &ret);
if(EFI_ERROR(status) || !ret) { errno = ENOMEM; ret = NULL; }
#ifndef UEFI_NO_TRACK_ALLOC
__stdlib_allocs[i] = (uintptr_t)ret;
__stdlib_allocs[i + 1] = (uintptr_t)__size;
#endif
return ret;
}
void *calloc (size_t __nmemb, size_t __size)
{
void *ret = malloc(__nmemb * __size);
if(ret) memset(ret, 0, __nmemb * __size);
return ret;
}
void *realloc (void *__ptr, size_t __size)
{
void *ret = NULL;
efi_status_t status;
#ifndef UEFI_NO_TRACK_ALLOC
uintn_t i;
#endif
if(!__ptr) return malloc(__size);
if(!__size) { free(__ptr); return NULL; }
#ifndef UEFI_NO_TRACK_ALLOC
/* get the slot which stores the old size for this buffer */
for(i = 0; i < __stdlib_numallocs && __stdlib_allocs[i] != (uintptr_t)__ptr; i += 2);
if(i == __stdlib_numallocs) { errno = ENOMEM; return NULL; }
/* allocate a new buffer and copy data from old buffer */
status = BS->AllocatePool(LIP ? LIP->ImageDataType : EfiLoaderData, __size, &ret);
if(EFI_ERROR(status) || !ret) { errno = ENOMEM; ret = NULL; }
else {
memcpy(ret, (void*)__stdlib_allocs[i], __stdlib_allocs[i + 1] < __size ? __stdlib_allocs[i + 1] : __size);
if(__size > __stdlib_allocs[i + 1]) memset((uint8_t*)ret + __stdlib_allocs[i + 1], 0, __size - __stdlib_allocs[i + 1]);
/* free old buffer and store new buffer in slot */
BS->FreePool((void*)__stdlib_allocs[i]);
__stdlib_allocs[i] = (uintptr_t)ret;
__stdlib_allocs[i + 1] = (uintptr_t)__size;
}
#else
status = BS->AllocatePool(LIP ? LIP->ImageDataType : EfiLoaderData, __size, &ret);
if(EFI_ERROR(status) || !ret) { errno = ENOMEM; return NULL; }
/* this means out of bounds read, but fine with POSIX as the end of new buffer supposed to be left uninitialized) */
memcpy(ret, (void*)__ptr, __size);
BS->FreePool((void*)__ptr);
#endif
return ret;
}
void free (void *__ptr)
{
efi_status_t status;
#ifndef UEFI_NO_TRACK_ALLOC
uintn_t i;
#endif
if(!__ptr) { errno = ENOMEM; return; }
#ifndef UEFI_NO_TRACK_ALLOC
/* find and clear the slot */
for(i = 0; i < __stdlib_numallocs && __stdlib_allocs[i] != (uintptr_t)__ptr; i += 2);
if(i == __stdlib_numallocs) { errno = ENOMEM; return; }
__stdlib_allocs[i] = 0;
__stdlib_allocs[i + 1] = 0;
/* if there are only empty slots, free the housekeeping array too */
for(i = 0; i < __stdlib_numallocs && __stdlib_allocs[i] == 0; i += 2);
if(i == __stdlib_numallocs) { BS->FreePool(__stdlib_allocs); __stdlib_allocs = NULL; __stdlib_numallocs = 0; }
#endif
status = BS->FreePool(__ptr);
if(EFI_ERROR(status)) errno = ENOMEM;
}
void abort ()
{
#ifndef UEFI_NO_TRACK_ALLOC
if(__stdlib_allocs)
BS->FreePool(__stdlib_allocs);
__stdlib_allocs = NULL;
__stdlib_numallocs = 0;
#endif
__stdio_cleanup();
BS->Exit(IM, EFI_ABORTED, 0, NULL);
}
void exit (int __status)
{
#ifndef UEFI_NO_TRACK_ALLOC
if(__stdlib_allocs)
BS->FreePool(__stdlib_allocs);
__stdlib_allocs = NULL;
__stdlib_numallocs = 0;
#endif
__stdio_cleanup();
BS->Exit(IM, !__status ? 0 : (__status < 0 ? EFIERR(-__status) : EFIERR(__status)), 0, NULL);
}
int exit_bs()
{
efi_status_t status = 0;
efi_memory_descriptor_t *memory_map = NULL;
uintn_t cnt = 3, memory_map_size=0, map_key=0, desc_size=0;
#ifndef UEFI_NO_TRACK_ALLOC
if(__stdlib_allocs)
BS->FreePool(__stdlib_allocs);
__stdlib_allocs = NULL;
__stdlib_numallocs = 0;
#endif
__stdio_cleanup();
while(cnt--) {
status = BS->GetMemoryMap(&memory_map_size, memory_map, &map_key, &desc_size, NULL);
if (status!=EFI_BUFFER_TOO_SMALL) break;
status = BS->ExitBootServices(IM, map_key);
if(!EFI_ERROR(status)) return 0;
}
return (int)(status & 0xffff);
}
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, __compar_fn_t cmp)
{
uint64_t s=0, e=nmemb, m;
int ret;
while (s < e) {
m = s + (e-s)/2;
ret = cmp(key, (uint8_t*)base + m*size);
if (ret < 0) e = m; else
if (ret > 0) s = m+1; else
return (void *)((uint8_t*)base + m*size);
}
return NULL;
}
int mblen(const char *s, size_t n)
{
const char *e = s+n;
int c = 0;
if(s) {
while(s < e && *s) {
if((*s & 128) != 0) {
if((*s & 32) == 0 ) s++; else
if((*s & 16) == 0 ) s+=2; else
if((*s & 8) == 0 ) s+=3;
}
c++;
s++;
}
}
return c;
}
int mbtowc (wchar_t * __pwc, const char *s, size_t n)
{
wchar_t arg;
int ret = 1;
if(!s || !*s) return 0;
arg = (wchar_t)*s;
if((*s & 128) != 0) {
if((*s & 32) == 0 && n > 0) { arg = ((*s & 0x1F)<<6)|(*(s+1) & 0x3F); ret = 2; } else
if((*s & 16) == 0 && n > 1) { arg = ((*s & 0xF)<<12)|((*(s+1) & 0x3F)<<6)|(*(s+2) & 0x3F); ret = 3; } else
if((*s & 8) == 0 && n > 2) { arg = ((*s & 0x7)<<18)|((*(s+1) & 0x3F)<<12)|((*(s+2) & 0x3F)<<6)|(*(s+3) & 0x3F); ret = 4; }
else return -1;
}
if(__pwc) *__pwc = arg;
return ret;
}
int wctomb (char *s, wchar_t u)
{
int ret = 0;
if(u<0x80) {
*s = u;
ret = 1;
} else if(u<0x800) {
*(s+0)=((u>>6)&0x1F)|0xC0;
*(s+1)=(u&0x3F)|0x80;
ret = 2;
} else {
*(s+0)=((u>>12)&0x0F)|0xE0;
*(s+1)=((u>>6)&0x3F)|0x80;
*(s+2)=(u&0x3F)|0x80;
ret = 3;
}
return ret;
}
size_t mbstowcs (wchar_t *__pwcs, const char *__s, size_t __n)
{
int r;
wchar_t *orig = __pwcs;
if(!__s || !*__s) return 0;
while(*__s) {
r = mbtowc(__pwcs, __s, __n - (size_t)(__pwcs - orig));
if(r < 0) return (size_t)-1;
__pwcs++;
__s += r;
}
*__pwcs = 0;
return (size_t)(__pwcs - orig);
}
size_t wcstombs (char *__s, const wchar_t *__pwcs, size_t __n)
{
int r;
char *orig = __s;
if(!__s || !__pwcs || !*__pwcs) return 0;
while(*__pwcs && ((size_t)(__s - orig + 4) < __n)) {
r = wctomb(__s, *__pwcs);
if(r < 0) return (size_t)-1;
__pwcs++;
__s += r;
}
*__s = 0;
return (size_t)(__s - orig);
}
void srand(unsigned int __seed)
{
__srand_seed = __seed - 1;
}
int rand()
{
efi_guid_t rngGuid = EFI_RNG_PROTOCOL_GUID;
efi_rng_protocol_t *rng = NULL;
efi_status_t status;
int ret = 0;
__srand_seed = 6364136223846793005ULL*__srand_seed + 1;
status = BS->LocateProtocol(&rngGuid, NULL, (void**)&rng);
if(!EFI_ERROR(status) && rng)
rng->GetRNG(rng, NULL, (uintn_t)sizeof(int), (uint8_t*)&ret);
ret ^= (int)(__srand_seed>>33);
return ret;
}
uint8_t *getenv(char_t *name, uintn_t *len)
{
efi_guid_t globGuid = EFI_GLOBAL_VARIABLE;
uint8_t tmp[EFI_MAXIMUM_VARIABLE_SIZE], *ret;
uint32_t attr;
efi_status_t status;
#ifndef UEFI_NO_UTF8
wchar_t wcname[256];
mbstowcs((wchar_t*)&wcname, name, 256);
status = RT->GetVariable((wchar_t*)&wcname, &globGuid, &attr, len, &tmp);
#else
status = RT->GetVariable(name, &globGuid, &attr, len, &tmp);
#endif
if(EFI_ERROR(status) || *len < 1 || !(ret = malloc((*len) + 1))) {
*len = 0;
return NULL;
}
memcpy(ret, tmp, *len);
ret[*len] = 0;
return ret;
}
int setenv(char_t *name, uintn_t len, uint8_t *data)
{
efi_guid_t globGuid = EFI_GLOBAL_VARIABLE;
efi_status_t status;
#ifndef UEFI_NO_UTF8
wchar_t wcname[256];
mbstowcs((wchar_t*)&wcname, name, 256);
status = RT->SetVariable(wcname, &globGuid, 0, len, data);
#else
status = RT->SetVariable(name, &globGuid, 0, len, data);
#endif
return !EFI_ERROR(status);
}

View File

@ -0,0 +1,262 @@
/*
* string.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief Implementing functions which are defined in string.h
*
*/
#include <uefi.h>
void *memcpy(void *dst, const void *src, size_t n)
{
uint8_t *a=(uint8_t*)dst,*b=(uint8_t*)src;
if(src && dst && src != dst && n>0) {
while(n--) *a++ = *b++;
}
return dst;
}
void *memmove(void *dst, const void *src, size_t n)
{
uint8_t *a=(uint8_t*)dst,*b=(uint8_t*)src;
if(src && dst && src != dst && n>0) {
if(a>b && a<b+n) {
a+=n-1; b+=n-1; while(n-->0) *a--=*b--;
} else {
while(n--) *a++ = *b++;
}
}
return dst;
}
void *memset(void *s, int c, size_t n)
{
uint8_t *p=(uint8_t*)s;
if(s && n>0) {
while(n--) *p++ = (uint8_t)c;
}
return s;
}
int memcmp(const void *s1, const void *s2, size_t n)
{
uint8_t *a=(uint8_t*)s1,*b=(uint8_t*)s2;
if(s1 && s2 && s1 != s2 && n>0) {
while(n--) {
if(*a != *b) return *a - *b;
a++; b++;
}
}
return 0;
}
void *memchr(const void *s, int c, size_t n)
{
uint8_t *e, *p=(uint8_t*)s;
if(s && n>0) {
for(e=p+n; p<e; p++) if(*p==(uint8_t)c) return p;
}
return NULL;
}
void *memrchr(const void *s, int c, size_t n)
{
uint8_t *e, *p=(uint8_t*)s;
if(s && n>0) {
for(e=p+n; p<e; --e) if(*e==(uint8_t)c) return e;
}
return NULL;
}
void *memmem(const void *haystack, size_t hl, const void *needle, size_t nl)
{
uint8_t *c = (uint8_t*)haystack;
if(!haystack || !needle || !hl || !nl || nl > hl) return NULL;
hl -= nl - 1;
while(hl) {
if(!memcmp(c, needle, nl)) return c;
c++; hl--;
}
return NULL;
}
void *memrmem(const void *haystack, size_t hl, const void *needle, size_t nl)
{
uint8_t *c = (uint8_t*)haystack;
if(!haystack || !needle || !hl || !nl || nl > hl) return NULL;
hl -= nl;
c += hl;
while(hl) {
if(!memcmp(c, needle, nl)) return c;
c--; hl--;
}
return NULL;
}
char_t *strcpy(char_t *dst, const char_t *src)
{
char_t *s = dst;
if(src && dst && src != dst) {
while(*src) {*dst++=*src++;} *dst=0;
}
return s;
}
char_t *strncpy(char_t *dst, const char_t *src, size_t n)
{
char_t *s = dst;
const char_t *e = src+n;
if(src && dst && src != dst && n>0) {
while(*src && src<e) {*dst++=*src++;} *dst=0;
}
return s;
}
char_t *strcat(char_t *dst, const char_t *src)
{
char_t *s = dst;
if(src && dst) {
dst += strlen(dst);
while(*src) {*dst++=*src++;} *dst=0;
}
return s;
}
int strcmp(const char_t *s1, const char_t *s2)
{
if(s1 && s2 && s1!=s2) {
while(*s1 && *s1==*s2){s1++;s2++;}
return *s1-*s2;
}
return 0;
}
char_t *strncat(char_t *dst, const char_t *src, size_t n)
{
char_t *s = dst;
const char_t *e = src+n;
if(src && dst && n>0) {
dst += strlen(dst);
while(*src && src<e) {*dst++=*src++;} *dst=0;
}
return s;
}
int strncmp(const char_t *s1, const char_t *s2, size_t n)
{
const char_t *e = s1+n;
if(s1 && s2 && s1!=s2 && n>0) {
while(s1<e && *s1 && *s1==*s2){s1++;s2++;}
return *s1-*s2;
}
return 0;
}
char_t *strdup(const char_t *s)
{
size_t i = (strlen(s)+1) * sizeof(char_t);
char_t *s2 = (char_t *)malloc(i);
if(s2 != NULL) memcpy(s2, (const void*)s, i);
return s2;
}
char_t *strchr(const char_t *s, int c)
{
if(s) {
while(*s) {
if(*s == (char_t)c) return (char_t*)s;
s++;
}
}
return NULL;
}
char_t *strrchr(const char_t *s, int c)
{
char_t *e;
if(s) {
e = (char_t*)s + strlen(s) - 1;
while(s <= e) {
if(*e == (char_t)c) return e;
e--;
}
}
return NULL;
}
char_t *strstr(const char_t *haystack, const char_t *needle)
{
return memmem(haystack, strlen(haystack) * sizeof(char_t), needle, strlen(needle) * sizeof(char_t));
}
static char_t *_strtok_r(char_t *s, const char_t *d, char_t **p)
{
int c, sc;
char_t *tok, *sp;
if(d == NULL || (s == NULL && (s=*p) == NULL)) return NULL;
again:
c = *s++;
for(sp = (char_t *)d; (sc=*sp++)!=0;) {
if(c == sc) goto again;
}
if (c == 0) { *p=NULL; return NULL; }
tok = s-1;
while(1) {
c = *s++;
sp = (char_t *)d;
do {
if((sc=*sp++) == c) {
if(c == 0) s = NULL;
else *(s-1) = 0;
*p = s;
return tok;
}
} while(sc != 0);
}
return NULL;
}
char_t *strtok(char_t *s, const char_t *delim)
{
static char_t *p;
return _strtok_r (s, delim, &p);
}
char_t *strtok_r(char_t *s, const char_t *delim, char_t **ptr)
{
return _strtok_r (s, delim, ptr);
}
size_t strlen (const char_t *__s)
{
size_t ret;
if(!__s) return 0;
for(ret = 0; __s[ret]; ret++);
return ret;
}

146
libs/posix-uefi/uefi/time.c Normal file
View File

@ -0,0 +1,146 @@
/*
* time.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief Implementing functions which are defined in time.h
*
*/
#include <uefi.h>
static struct tm __tm;
time_t __mktime_efi(efi_time_t *t);
/* from musl */
static uint64_t __year_to_secs(uint64_t year, int *is_leap)
{
int y, cycles, centuries, leaps, rem;
if (year-2ULL <= 136) {
y = (int)year;
leaps = (y-68)>>2;
if (!((y-68)&3)) {
leaps--;
if (is_leap) *is_leap = 1;
} else if (is_leap) *is_leap = 0;
return 31536000ULL*(uint64_t)(y-70) + 86400ULL*(uint64_t)leaps;
}
if (!is_leap) is_leap = &(int){0};
cycles = (int)((year-100) / 400);
rem = (year-100) % 400;
if (rem < 0) {
cycles--;
rem += 400;
}
if (!rem) {
*is_leap = 1;
centuries = 0;
leaps = 0;
} else {
if (rem >= 200) {
if (rem >= 300) { centuries = 3; rem -= 300; }
else { centuries = 2; rem -= 200; }
} else {
if (rem >= 100) { centuries = 1; rem -= 100; }
else centuries = 0;
}
if (!rem) {
*is_leap = 0;
leaps = 0;
} else {
leaps = rem / 4;
rem %= 4;
*is_leap = !rem;
}
}
leaps += 97*cycles + 24*centuries - *is_leap;
return (uint64_t)(year-100) * 31536000ULL + (uint64_t)leaps * 86400ULL + 946684800ULL + 86400ULL;
}
time_t __mktime_efi(efi_time_t *t)
{
__tm.tm_year = t->Year + (/* workaround some buggy firmware*/ t->Year > 2000 ? -1900 : 98);
__tm.tm_mon = t->Month - 1;
__tm.tm_mday = t->Day;
__tm.tm_hour = t->Hour;
__tm.tm_min = t->Minute;
__tm.tm_sec = t->Second;
__tm.tm_isdst = t->Daylight;
return mktime(&__tm);
}
/**
* This isn't POSIX, no arguments. Just returns the current time in struct tm
*/
struct tm *localtime (const time_t *__timer)
{
efi_time_t t = {0};
(void)__timer;
ST->RuntimeServices->GetTime(&t, NULL);
__mktime_efi(&t);
return &__tm;
}
time_t mktime(const struct tm *tm)
{
static const uint64_t secs_through_month[] = {
0, 31*86400, 59*86400, 90*86400,
120*86400, 151*86400, 181*86400, 212*86400,
243*86400, 273*86400, 304*86400, 334*86400 };
int is_leap;
uint64_t year = (uint64_t)tm->tm_year, t, adj;
int month = tm->tm_mon;
if (month >= 12 || month < 0) {
adj = (uint64_t)month / 12;
month %= 12;
if (month < 0) {
adj--;
month += 12;
}
year += adj;
}
t = __year_to_secs(year, &is_leap);
t += secs_through_month[month];
if (is_leap && month >= 2) t += 86400;
t += 86400ULL * (uint64_t)(tm->tm_mday-1);
t += 3600ULL * (uint64_t)tm->tm_hour;
t += 60ULL * (uint64_t)tm->tm_min;
t += (uint64_t)tm->tm_sec;
return (time_t)t;
}
time_t time(time_t *__timer)
{
time_t ret;
efi_time_t t = {0};
ST->RuntimeServices->GetTime(&t, NULL);
ret = __mktime_efi(&t);
if(__timer) *__timer = ret;
return ret;
}

1433
libs/posix-uefi/uefi/uefi.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,55 @@
/*
* unistd.c
*
* Copyright (C) 2021 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief Implementing functions which are defined in unistd.h
*
*/
#include <uefi.h>
int __remove(const wchar_t *__filename, int isdir);
int usleep (unsigned long int __useconds)
{
BS->Stall(__useconds);
return 0;
}
unsigned int sleep (unsigned int __seconds)
{
BS->Stall((unsigned long int)__seconds * 1000000UL);
return 0;
}
int unlink (const wchar_t *__filename)
{
return __remove(__filename, 0);
}
int rmdir (const wchar_t *__filename)
{
return __remove(__filename, 1);
}

View File

@ -0,0 +1,41 @@
POSIX-UEFI Segédeszközök
========================
Ezek kis parancsok, amik besegítenek az UEFI toolchain-edbe (csak libc-t használnak, nincs szükség EFI hedörökre). A POSIX-UEFI
által lefordított .efi kimeneti fájlokat konvertálják olyan különböző formátumú fájlokká, amiket az UEFI firmware használ.
* __efirom__ - ez PCI Option ROM képet készít
```
POSIX-UEFI utils - efirom by Michael Brown GPL
./efirom [--vendor=VVVV] [--device=DDDD] bementifájl kimentifájl
```
* __efiffs__ - ez DXE UEFI eszközmeghajtó képet készít (bővebb infó [ebben a wikiben](https://github.com/pbatard/efifs/wiki/Adding-a-driver-to-a-UEFI-firmware#adding-the-module-to-the-firmware) található arról, hogy hogyan kell a firmwarehez adni).
```
POSIX-UEFI utils - efiffs by bztsrc@gitlab MIT
./efiffs [-g <guid>] [-n <név>] [-v <ver>] [-t <típus>] [-p <típus>] bemenet [kimenet]
-g <guid> a GUID megadása (alapértelmezetten véletlenszám)
-n <név> az eszközmeghajtó neve (pl 'FAT')
-v <ver> az eszközmeghajtó verziója (pl '1.0')
-t <típus> az ffs típusa (alapértelmezetten 7, EFI_FV_FILETYPE_DRIVER)
-p <típus> a pe szekció típusa (alapértelmezetten 16, EFI_SECTION_PE32)
bemenet a bemeneti .efi fájl neve
kimenet a kimeneti fájl neve (alapértelmezetten a bemeneti névből generált)
```
* __efidsk__ - indítható lemezképet készít EFI Rendszer Partícióval egy könyvtár tartalmából. A POSIX-EFI-vel lefordított
programodat `EFI/BOOT/BOOTX64.EFI` néven kell bemásolni a könyvtárba, hogy magától elinduljon.
```
POSIX-UEFI utils - efidsk by bztsrc@gitlab MIT
./efidsk [-p|-c] [-s <méret>] bekönyvtár kimenet
-p csak a partíciót mentse, GPT nélkül
-c EFI CDROM mentése (ISO9660 El Torito no emulation boot catalog)
-s <méret> a partíció méretének megadása megabájtban (alapértelmezetten 33M)
bekönvtár ennek a könyvtárnak a tartalmából generálja
kimenet kimenti lemezképfájl neve
```

View File

@ -0,0 +1,41 @@
POSIX-UEFI Utilities
====================
These are small portable commands to help you with the UEFI toolchain (libc only, no EFI headers needed). They convert the .efi
output that you've compiled with POSIX-UEFI into different file formats required by the UEFI firmware.
* __efirom__ - creates a PCI Option ROM image
```
POSIX-UEFI utils - efirom by Michael Brown GPL
./efirom [--vendor=VVVV] [--device=DDDD] infile outfile
```
* __efiffs__ - creates a DXE UEFI driver image (see [this wiki](https://github.com/pbatard/efifs/wiki/Adding-a-driver-to-a-UEFI-firmware#adding-the-module-to-the-firmware) on how to add it to a firmware).
```
POSIX-UEFI utils - efiffs by bztsrc@gitlab MIT
./efiffs [-g <guid>] [-n <name>] [-v <ver>] [-t <type>] [-p <type>] infile [outfile]
-g <guid> specify the GUID (defaults to random)
-n <name> specify the driver's name (eg 'FAT')
-v <ver> specify the driver's version (eg '1.0')
-t <type> specify the ffs type (defaults to 7, EFI_FV_FILETYPE_DRIVER)
-p <type> specify the pe section type (defaults to 16, EFI_SECTION_PE32)
infile input .efi file
outfile output file name (default generated from infile)
```
* __efidsk__ - creates a bootable disk image with EFI System Partition from the contents of a directory. Copy the POSIX-UEFI
compiled file under the given directory as `EFI/BOOT/BOOTX64.EFI` to get it automatically booted.
```
POSIX-UEFI utils - efidsk by bztsrc@gitlab MIT
./efidsk [-p|-c] [-s <size>] indir outfile
-p save only the partition image without GPT
-c save EFI CDROM (ISO9660 El Torito no emulation boot catalog)
-s <size> set the size of partition in megabytes (defaults to 33M)
indir use the contents of this directory
outfile output image file name
```

View File

@ -0,0 +1,648 @@
/*
* utils/efidsk.c
*
* Copyright (C) 2022 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief small tool to create a disk image with EFI System Partition
*
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
typedef struct {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} __attribute__((packed)) guid_t;
guid_t dguid, pguid;
/**
* Print usage
*/
void usage(char *cmd)
{
printf("POSIX-UEFI utils - efidsk by bztsrc@gitlab MIT\r\n\r\n");
printf("%s [-p|-c] [-s <size>] indir outfile\r\n\r\n", cmd);
printf(" -p save only the partition image without GPT\r\n");
printf(" -c save EFI CDROM (ISO9660 El Torito no emulation boot catalog)\r\n");
printf(" -s <size> set the size of partition in megabytes (defaults to 33M)\r\n");
printf(" indir use the contents of this directory\r\n");
printf(" outfile output image file name\r\n");
exit(1);
}
#define SECTOR_PER_CLUSTER 1
#define FIRST_PARTITION 2048
struct tm *fat_ts;
int fs_len = 0, skipbytes = 0, fat_nextcluster, fat_bpc, fat_spf, fat_lfncnt, fat_numclu = 67584;
unsigned char *fs_base = NULL;
unsigned char *fat_rootdir, *fat_data, fat_lfn[769];
uint32_t *fat_fat32_1, *fat_fat32_2;
/**
* Add a new cluster
*/
unsigned char *fat_newclu(int parent)
{
int clu;
while(parent != fat_nextcluster && fat_fat32_1[parent] && fat_fat32_1[parent] != 0xFFFFFFF)
parent = fat_fat32_1[parent];
fat_fat32_1[parent] = fat_fat32_2[parent] = fat_nextcluster;
fat_fat32_1[fat_nextcluster] = fat_fat32_2[fat_nextcluster] = 0xFFFFFFF;
clu = fat_nextcluster++;
if(fat_nextcluster >= fat_numclu) { fprintf(stderr,"efidsk: not enough space on partition\r\n"); exit(1); }
return fat_data + clu * fat_bpc;
}
/**
* Read in file name
*/
unsigned char *fat_readlfn(unsigned char *dir, int *clu, int *size, int parent)
{
uint16_t uc2[256], *u;
unsigned char *s, *d;
int i = 0, n;
memset(fat_lfn, 0, sizeof(fat_lfn));
if(!dir[0]) return dir;
while(dir[0] == '.') dir += 32;
fat_lfncnt++;
if(parent != 2 && !((uint64_t)(dir - fs_base) & (fat_bpc - 1))) {
parent = fat_fat32_1[parent];
if(!parent || parent == 0xFFFFFFF) return NULL;
dir = fat_data + parent * fat_bpc;
}
if(dir[0xB] != 0xF) {
for(s = dir, d = fat_lfn, i = 0; *s && *s != ' ' && i < 8; i++)
*d++ = *s++;
if(dir[8] && dir[8] != ' ') {
*d++ = '.';
for(s = dir + 8; *s != ' ' && i < 3; i++)
*d++ = *s++;
}
} else {
memset(uc2, 0, sizeof(uc2));
n = dir[0] & 0x3F;
u = uc2 + (n - 1) * 13;
while(n--) {
for(i = 0; i < 5; i++)
u[i] = dir[i*2+2] << 8 | dir[i*2+1];
for(i = 0; i < 6; i++)
u[i+5] = dir[i*2+0xF] << 8 | dir[i*2+0xE];
u[11] = dir[0x1D] << 8 | dir[0x1C];
u[12] = dir[0x1F] << 8 | dir[0x1E];
u -= 13;
dir += 32;
if(!((uint64_t)(dir - fs_base) & (fat_bpc - 1))) {
parent = fat_fat32_1[parent];
if(!parent || parent == 0xFFFFFFF) return NULL;
dir = fat_data + parent * fat_bpc;
}
}
for(d = fat_lfn, u = uc2; *u; u++)
if(*u < 0x80) {
*d++ = *u;
} else if(*u < 0x800) {
*d++ = ((*u>>6)&0x1F)|0xC0;
*d++ = (*u&0x3F)|0x80;
} else {
*d++ = ((*u>>12)&0x0F)|0xE0;
*d++ = ((*u>>6)&0x3F)|0x80;
*d++ = (*u&0x3F)|0x80;
}
}
*clu = (dir[0x15] << 24) | (dir[0x14] << 16) | (dir[0x1B] << 8) | dir[0x1A];
*size = (dir[0x1F] << 24) | (dir[0x1E] << 16) | (dir[0x1D] << 8) | dir[0x1C];
return dir + 32;
}
/**
* Write file name
*/
unsigned char *fat_writelfn(unsigned char *dir, char *name, int type, int size, int parent, int clu)
{
uint16_t uc2[256], *u;
unsigned char *s, c = 0, sfn[12];
int i, n;
if(name[0] == '.') {
memset(dir, ' ', 11);
memcpy(dir, name, strlen(name));
} else {
memset(uc2, 0, sizeof(uc2));
for(n = 0, u = uc2, s = (unsigned char*)name; *s; n++, u++) {
if((*s & 128) != 0) {
if((*s & 32) == 0) { *u = ((*s & 0x1F)<<6)|(*(s+1) & 0x3F); s += 2; } else
if((*s & 16) == 0) { *u = ((*s & 0xF)<<12)|((*(s+1) & 0x3F)<<6)|(*(s+2) & 0x3F); s += 3; }
else { fprintf(stderr,"efidsk: unable to encode file name '%s'\r\n", name); exit(1); }
} else
*u = *s++;
}
/* don't convert "Microsoft" to "MICROS~1 ", that's patented... */
sprintf((char*)sfn, "~%07xLFN", fat_lfncnt++);
for(i = 0; i < 11; i++)
c = (((c & 1) << 7) | ((c & 0xfe) >> 1)) + sfn[i];
n = (n + 12) / 13;
u = uc2 + (n - 1) * 13;
i = 0x40;
while(n--) {
if(parent > 2 && !((uint64_t)(dir - fs_base) & (fat_bpc - 1)))
dir = fat_newclu(parent);
dir[0] = i | (n + 1);
dir[11] = 0xF;
dir[0xD] = c;
memcpy(dir + 1, (unsigned char*)u, 10);
memcpy(dir + 14, (unsigned char*)u + 10, 12);
memcpy(dir + 28, (unsigned char*)u + 22, 4);
i = 0;
u -= 13;
dir += 32;
}
if(parent > 2 && !((uint64_t)(dir - fs_base) & (fat_bpc - 1)))
dir = fat_newclu(parent);
memcpy(dir, sfn, 11);
}
if(type) {
dir[0xB] = 0x10;
} else {
dir[0x1C] = size & 0xFF; dir[0x1D] = (size >> 8) & 0xFF;
dir[0x1E] = (size >> 16) & 0xFF; dir[0x1F] = (size >> 24) & 0xFF;
}
if(!clu) clu = size > 0 || type ? fat_nextcluster : 0;
if(clu < 3) clu = 0;
dir[0x1A] = clu & 0xFF; dir[0x1B] = (clu >> 8) & 0xFF;
dir[0x14] = (clu >> 16) & 0xFF; dir[0x15] = (clu >> 24) & 0xFF;
i = (fat_ts->tm_hour << 11) | (fat_ts->tm_min << 5) | (fat_ts->tm_sec/2);
dir[0xE] = dir[0x16] = i & 0xFF; dir[0xF] = dir[0x17] = (i >> 8) & 0xFF;
i = ((fat_ts->tm_year+1900-1980) << 9) | ((fat_ts->tm_mon+1) << 5) | (fat_ts->tm_mday);
return dir + 32;
}
/**
* Create a fat file system
*/
void fat_open(int start)
{
int i;
if(fat_numclu < 67584) { fprintf(stderr,"efidsk: not enough clusters\r\n"); exit(1); }
/* "format" the partition to FAT32 */
fs_len = fat_numclu * 512 * SECTOR_PER_CLUSTER;
fs_base = realloc(fs_base, fs_len);
if(!fs_base) { fprintf(stderr,"efidsk: unable to allocate memory\r\n"); exit(1); }
memset(fs_base, 0, fs_len);
memcpy(fs_base + 3, "MSWIN4.1", 8);
fs_base[0xC] = 2; fs_base[0x10] = 2; fs_base[0x15] = 0xF8; fs_base[0x1FE] = 0x55; fs_base[0x1FF] = 0xAA;
fs_base[0x18] = 0x20; fs_base[0x1A] = 0x40;
memcpy(fs_base + 0x1C, &start, 4);
memcpy(fs_base + 0x20, &fat_numclu, 4);
fat_spf = (fat_numclu*4) / 512;
fs_base[0xD] = SECTOR_PER_CLUSTER; fs_base[0xE] = 8;
fs_base[0x24] = fat_spf & 0xFF; fs_base[0x25] = (fat_spf >> 8) & 0xFF;
fs_base[0x26] = (fat_spf >> 16) & 0xFF; fs_base[0x27] = (fat_spf >> 24) & 0xFF;
fs_base[0x2C] = 2; fs_base[0x30] = 1; fs_base[0x32] = 6; fs_base[0x40] = 0x80; fs_base[0x42] = 0x29;
memcpy(fs_base + 0x43, &pguid, 4);
memcpy(fs_base + 0x47, "EFI System FAT32 ", 19);
memcpy(fs_base + 0x200, "RRaA", 4); memcpy(fs_base + 0x3E4, "rrAa", 4);
for(i = 0; i < 8; i++) fs_base[0x3E8 + i] = 0xFF;
fs_base[0x3FE] = 0x55; fs_base[0x3FF] = 0xAA;
fat_bpc = fs_base[0xD] * 512;
fat_rootdir = fs_base + (fat_spf*fs_base[0x10]+fs_base[0xE]) * 512;
fat_data = fat_rootdir - 2*fat_bpc;
fat_fat32_1 = (uint32_t*)(&fs_base[fs_base[0xE] * 512]);
fat_fat32_2 = (uint32_t*)(&fs_base[(fs_base[0xE]+fat_spf) * 512]);
fat_fat32_1[0] = fat_fat32_2[0] = fat_fat32_1[2] = fat_fat32_2[2] = 0x0FFFFFF8;
fat_fat32_1[1] = fat_fat32_2[1] = 0x0FFFFFFF;
fat_nextcluster = 3;
}
/**
* Add a file to the file system
*/
void fat_add(struct stat *st, char *name, unsigned char *content, int size)
{
int parent = 2, clu, i, j;
unsigned char *dir = fat_rootdir;
char *end, *fn = strrchr(name, '/');
if(!fn) fn = name; else fn++;
if(!strcmp(fn, ".") || !strcmp(fn, "..")) return;
if(!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) return;
fat_ts = gmtime(&st->st_mtime);
fn = name;
end = strchr(name, '/');
if(!end) end = name + strlen(name);
fat_lfncnt = 1;
do {
dir = fat_readlfn(dir, &clu, &j, parent);
if(!dir) return;
if(!memcmp(fat_lfn, fn, end - fn) && !fat_lfn[end - fn]) {
fat_lfncnt = 1;
parent = clu;
dir = fat_data + parent * fat_bpc + 64;
fn = end + 1;
end = *end ? strchr(fn, '/') : NULL;
if(!end) { end = fn + strlen(fn); break; }
}
} while(dir[0]);
dir = fat_writelfn(dir, fn, S_ISDIR(st->st_mode), size, parent, 0);
if(S_ISDIR(st->st_mode)) {
dir = fat_newclu(fat_nextcluster);
dir = fat_writelfn(dir, ".", 1, 0, 2, fat_nextcluster - 1);
dir = fat_writelfn(dir, "..", 1, 0, 2, parent);
} else if(content && size > 0) {
if(fat_nextcluster * fat_bpc + size >= fs_len) {
fprintf(stderr,"efidsk: not enough space on partition\r\n");
exit(1);
}
memcpy(fat_data + fat_nextcluster * fat_bpc, content, size);
for(i = 0; i < ((size + fat_bpc-1) & ~(fat_bpc-1)); i += fat_bpc, fat_nextcluster++) {
fat_fat32_1[fat_nextcluster] = fat_fat32_2[fat_nextcluster] = fat_nextcluster+1;
}
fat_fat32_1[fat_nextcluster-1] = fat_fat32_2[fat_nextcluster-1] = 0xFFFFFFF;
}
}
/**
* Close the file system
*/
void fat_close()
{
int i;
if(!fs_base || fs_len < 512) return;
fat_nextcluster -= 2;
i = ((fs_len - (fat_spf*fs_base[0x10]+fs_base[0xE]) * 512)/fat_bpc) - fat_nextcluster;
fs_base[0x3E8] = i & 0xFF; fs_base[0x3E9] = (i >> 8) & 0xFF;
fs_base[0x3EA] = (i >> 16) & 0xFF; fs_base[0x3EB] = (i >> 24) & 0xFF;
fs_base[0x3EC] = fat_nextcluster & 0xFF; fs_base[0x3ED] = (fat_nextcluster >> 8) & 0xFF;
fs_base[0x3EE] = (fat_nextcluster >> 16) & 0xFF; fs_base[0x3EF] = (fat_nextcluster >> 24) & 0xFF;
/* copy backup boot sectors */
memcpy(fs_base + (fs_base[0x32]*512), fs_base, 1024);
}
/**
* Read a file entirely into memory
*/
long int read_size;
unsigned char* readfileall(char *file)
{
unsigned char *data=NULL;
FILE *f;
read_size = 0;
if(!file || !*file) return NULL;
f = fopen(file,"r");
if(f) {
fseek(f, 0L, SEEK_END);
read_size = (long int)ftell(f);
fseek(f, 0L, SEEK_SET);
data = (unsigned char*)malloc(read_size + 1);
if(!data) { fprintf(stderr, "efidsk: unable to allocate memory\r\n"); exit(1); }
memset(data, 0, read_size + 1);
fread(data, read_size, 1, f);
data[read_size] = 0;
fclose(f);
}
return data;
}
/**
* Recursively parse a directory
*/
void parsedir(char *directory, int parent)
{
DIR *dir;
struct dirent *ent;
char full[8192];
unsigned char *tmp;
struct stat st;
if(!parent && !skipbytes) skipbytes = strlen(directory) + 1;
if ((dir = opendir(directory)) != NULL) {
while ((ent = readdir(dir)) != NULL) {
if(!parent && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))) continue;
full[0] = 0;
strncat(full, directory, sizeof(full)-1);
strncat(full, "/", sizeof(full)-1);
strncat(full, ent->d_name, sizeof(full)-1);
if(stat(full, &st)) continue;
read_size = 0;
if(S_ISDIR(st.st_mode)) {
fat_add(&st, full + skipbytes, NULL, 0);
if(strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
parsedir(full, parent+1);
} else
if(S_ISREG(st.st_mode)) {
tmp = readfileall(full);
fat_add(&st, full + skipbytes, tmp, read_size);
if(tmp) free(tmp);
}
}
closedir(dir);
}
}
/**
* CRC calculation with precalculated lookup table
*/
uint32_t crc32_lookup[256]={
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832,
0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a,
0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab,
0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4,
0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074,
0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525,
0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76,
0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6,
0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7,
0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7,
0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330,
0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
uint32_t crc32_calc(unsigned char *start,int length)
{
uint32_t crc32_val=0xffffffff;
while(length--) crc32_val=(crc32_val>>8)^crc32_lookup[(crc32_val&0xff)^(unsigned char)*start++];
crc32_val^=0xffffffff;
return crc32_val;
}
/**
* Write out GUID Partitioning Table
*/
void setint(int val, unsigned char *ptr) { memcpy(ptr,&val,4); }
void writegpt(FILE *f)
{
guid_t efiguid = { 0xC12A7328, 0xF81F, 0x11D2, { 0xBA,0x4B,0x00,0xA0,0xC9,0x3E,0xC9,0x3B} };
int i;
unsigned char *gpt = malloc(FIRST_PARTITION * 512), gpt2[512], *p;
char *name;
if(!gpt) { fprintf(stderr, "efidsk: unable to allocate memory\r\n"); exit(1); }
memset(gpt, 0, FIRST_PARTITION * 512);
gpt[0x1FE]=0x55; gpt[0x1FF]=0xAA;
memcpy(gpt+0x1B8, &dguid.Data1, 4); /* WinNT disk id */
/* MBR, EFI System Partition / boot partition. */
gpt[0x1C0-2]=0x80; /* bootable flag */
setint(FIRST_PARTITION+1,gpt+0x1C0); /* start CHS */
gpt[0x1C0+2]=0xC; /* type, LBA FAT32 (0xC) */
setint(fs_len/512,gpt+0x1C0+4); /* end CHS */
setint(FIRST_PARTITION,gpt+0x1C0+6); /* start LBA */
setint(fs_len/512,gpt+0x1C0+10); /* number of sectors */
/* MBR, protective GPT entry */
setint(1,gpt+0x1D0); /* start CHS */
gpt[0x1D0+2]=0xEE; /* type */
setint(FIRST_PARTITION+1,gpt+0x1D0+4); /* end CHS */
setint(1,gpt+0x1D0+6); /* start LBA */
setint(FIRST_PARTITION,gpt+0x1D0+10); /* number of sectors */
/* GPT header */
p = gpt + 512;
memcpy(p,"EFI PART",8); /* magic */
setint(1,p+10); /* revision */
setint(92,p+12); /* size */
setint(1,p+24); /* primary LBA */
setint(2*FIRST_PARTITION+fat_numclu-2,p+32);/* secondary LBA */
setint(FIRST_PARTITION,p+40); /* first usable LBA */
setint(FIRST_PARTITION+fat_numclu,p+48); /* last usable LBA */
memcpy(p+56,&dguid,sizeof(guid_t)); /* disk UUID */
setint(2,p+72); /* partitioning table LBA */
setint((FIRST_PARTITION-2)*512/128,p+80); /* number of entries */
setint(128,p+84); /* size of one entry */
p += 512;
/* GPT, EFI System Partition (ESP) */
memcpy(p, &efiguid, sizeof(guid_t)); /* entry type */
memcpy(p+16, &pguid, sizeof(guid_t)); /* partition UUID */
setint(FIRST_PARTITION,p+32); /* start LBA */
setint(FIRST_PARTITION+fat_numclu-1,p+40); /* end LBA */
name = "EFI System Partition"; /* name */
for(i = 0; name[i]; i++) p[56+i*2] = name[i];
p += 128;
/* calculate checksums */
setint(crc32_calc(gpt+1024,((gpt[512+81]<<8)|gpt[512+80])*128),gpt+512+88);
setint(crc32_calc(gpt+512,92),gpt+512+16);
memcpy(gpt2, gpt+512, 512);
setint(1,gpt2+32); /* secondary lba */
setint(2*FIRST_PARTITION+fat_numclu-2,gpt2+24); /* primary lba */
setint(FIRST_PARTITION+fat_numclu,gpt2+72); /* partition lba */
setint(0,gpt2+16); /* calculate with zero */
setint(crc32_calc(gpt2,92),gpt2+16);
fwrite(gpt, 1, FIRST_PARTITION * 512, f);
fwrite(fs_base, 1, fs_len, f);
fwrite(gpt + 1024, 1, (FIRST_PARTITION - 2) * 512, f);
fwrite(gpt2, 1, 512, f);
free(gpt);
}
/**
* Write out ISO9660 El Torito "no emulation" Boot Catalog
*/
void setinte(int val, unsigned char *ptr) { char *v=(char*)&val; memcpy(ptr,&val,4); ptr[4]=v[3]; ptr[5]=v[2]; ptr[6]=v[1]; ptr[7]=v[0]; }
void writeetbc(FILE *f)
{
int i;
time_t t;
char isodate[128];
unsigned char *gpt = malloc(FIRST_PARTITION * 512), *iso;
if(!gpt) { fprintf(stderr, "efidsk: unable to allocate memory\r\n"); exit(1); }
memset(gpt, 0, FIRST_PARTITION * 512);
t = time(NULL);
fat_ts = gmtime(&t);
sprintf((char*)&isodate, "%04d%02d%02d%02d%02d%02d00",
fat_ts->tm_year+1900,fat_ts->tm_mon+1,fat_ts->tm_mday,fat_ts->tm_hour,fat_ts->tm_min,fat_ts->tm_sec);
iso = gpt + 16*2048;
/* 16th sector: Primary Volume Descriptor */
iso[0]=1; /* Header ID */
memcpy(&iso[1], "CD001", 5);
iso[6]=1; /* version */
for(i=8;i<72;i++) iso[i]=' ';
memcpy(&iso[40], "EFI CDROM", 9); /* Volume Identifier */
setinte((FIRST_PARTITION*512+fs_len+2047)/2048, &iso[80]);
iso[120]=iso[123]=1; /* Volume Set Size */
iso[124]=iso[127]=1; /* Volume Sequence Number */
iso[129]=iso[130]=8; /* logical blocksize (0x800) */
iso[156]=0x22; /* root directory recordsize */
setinte(20, &iso[158]); /* root directory LBA */
setinte(2048, &iso[166]); /* root directory size */
iso[174]=fat_ts->tm_year; /* root directory create date */
iso[175]=fat_ts->tm_mon+1;
iso[176]=fat_ts->tm_mday;
iso[177]=fat_ts->tm_hour;
iso[178]=fat_ts->tm_min;
iso[179]=fat_ts->tm_sec;
iso[180]=0; /* timezone UTC (GMT) */
iso[181]=2; /* root directory flags (0=hidden,1=directory) */
iso[184]=1; /* root directory number */
iso[188]=1; /* root directory filename length */
for(i=190;i<813;i++) iso[i]=' '; /* Volume data */
memcpy(&iso[318], "POSIX-UEFI <HTTPS://GITLAB.COM/BZTSRC/POSIX-UEFI>", 49);
memcpy(&iso[446], "EFIDSK", 6);
memcpy(&iso[574], "POSIX-UEFI", 11);
for(i=702;i<813;i++) iso[i]=' '; /* file descriptors */
memcpy(&iso[813], &isodate, 16); /* volume create date */
memcpy(&iso[830], &isodate, 16); /* volume modify date */
for(i=847;i<863;i++) iso[i]='0'; /* volume expiration date */
for(i=864;i<880;i++) iso[i]='0'; /* volume shown date */
iso[881]=1; /* filestructure version */
for(i=883;i<1395;i++) iso[i]=' '; /* file descriptors */
/* 17th sector: Boot Record Descriptor */
iso[2048]=0; /* Header ID */
memcpy(&iso[2049], "CD001", 5);
iso[2054]=1; /* version */
memcpy(&iso[2055], "EL TORITO SPECIFICATION", 23);
setinte(19, &iso[2048+71]); /* Boot Catalog LBA */
/* 18th sector: Volume Descritor Terminator */
iso[4096]=0xFF; /* Header ID */
memcpy(&iso[4097], "CD001", 5);
iso[4102]=1; /* version */
/* 19th sector: Boot Catalog */
/* --- UEFI, Validation Entry + Initial/Default Entry + Final --- */
iso[6144]=0x91; /* Header ID, Validation Entry, Final */
iso[6145]=0xEF; /* Platform EFI */
iso[6176]=0x88; /* Bootable, Initial/Default Entry */
iso[6182]=1; /* Sector Count */
setint(FIRST_PARTITION/4, &iso[6184]); /* ESP Start LBA */
/* 20th sector: Root Directory */
/* . */
iso[8192]=0x21 + 1; /* recordsize */
setinte(20, &iso[8194]); /* LBA */
setinte(2048, &iso[8202]); /* size */
iso[8210]=fat_ts->tm_year; /* date */
iso[8211]=fat_ts->tm_mon+1;
iso[8212]=fat_ts->tm_mday;
iso[8213]=fat_ts->tm_hour;
iso[8214]=fat_ts->tm_min;
iso[8215]=fat_ts->tm_sec;
iso[8216]=0; /* timezone UTC (GMT) */
iso[8217]=2; /* flags (0=hidden,1=directory) */
iso[8220]=1; /* serial */
iso[8224]=1; /* filename length */
iso[8225]=0; /* filename '.' */
/* .. */
iso[8226]=0x21 + 1; /* recordsize */
setinte(20, &iso[8228]); /* LBA */
setinte(2048, &iso[8236]); /* size */
iso[8244]=fat_ts->tm_year; /* date */
iso[8245]=fat_ts->tm_mon+1;
iso[8246]=fat_ts->tm_mday;
iso[8247]=fat_ts->tm_hour;
iso[8248]=fat_ts->tm_min;
iso[8249]=fat_ts->tm_sec;
iso[8250]=0; /* timezone UTC (GMT) */
iso[8251]=2; /* flags (0=hidden,1=directory) */
iso[8254]=1; /* serial */
iso[8258]=1; /* filename length */
iso[8259]='\001'; /* filename '..' */
fwrite(gpt, 1, FIRST_PARTITION * 512, f);
fwrite(fs_base, 1, fs_len, f);
free(gpt);
}
/**
* Main function
*/
int main(int argc, char **argv)
{
FILE *f;
int i, part = 0, cdrom = 0;
char *in = NULL, *out = NULL;
/* get random GUIDs */
srand(time(NULL));
i = rand(); memcpy(&((uint8_t*)&dguid)[0], &i, 4);
i = rand(); memcpy(&((uint8_t*)&dguid)[4], &i, 4);
i = rand(); memcpy(&((uint8_t*)&dguid)[8], &i, 4);
i = rand(); memcpy(&((uint8_t*)&dguid)[12], &i, 4);
i = rand(); memcpy(&((uint8_t*)&pguid)[0], &i, 4);
i = rand(); memcpy(&((uint8_t*)&pguid)[4], &i, 4);
i = rand(); memcpy(&((uint8_t*)&pguid)[8], &i, 4);
i = rand(); memcpy(&((uint8_t*)&pguid)[12], &i, 4);
/* parse command line */
for(i = 1; i < argc && argv[i]; i++)
if(argv[i][0] == '-') {
switch(argv[i][1]) {
case 'p': part++; break;
case 'c': cdrom++; break;
case 's': fat_numclu = atoi(argv[++i]) * 2048; break;
default: usage(argv[0]); break;
}
} else
if(!in) in = argv[i]; else
if(!out) out = argv[i]; else
usage(argv[0]);
if(!out) usage(argv[0]);
/* generate file system image */
remove(out);
fat_open(part ? 0 : FIRST_PARTITION);
parsedir(in, 0);
fat_close();
/* write out image */
if(fs_base) {
f = fopen(out, "wb");
if(!f) {
fprintf(stderr, "efidsk: unable to write '%s'\r\n", out);
return 2;
}
if(!part) {
if(!cdrom)
writegpt(f);
else
writeetbc(f);
} else
fwrite(fs_base, 1, fs_len, f);
fclose(f);
free(fs_base);
}
return 0;
}

View File

@ -0,0 +1,269 @@
/*
* utils/efiffs.c
*
* Copyright (C) 2022 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* This file is part of the POSIX-UEFI package.
* @brief small tool to convert an .efi file to an .ffs file
*
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} __attribute__((packed)) guid_t;
/**
* Print usage
*/
void usage(char *cmd)
{
printf("POSIX-UEFI utils - efiffs by bztsrc@gitlab MIT\r\n\r\n");
printf("%s [-g <guid>] [-n <name>] [-v <ver>] [-t <type>] [-p <type>] infile [outfile]\r\n\r\n", cmd);
printf(" -g <guid> specify the GUID (defaults to random)\r\n");
printf(" -n <name> specify the driver's name (eg 'FAT')\r\n");
printf(" -v <ver> specify the driver's version (eg '1.0')\r\n");
printf(" -t <type> specify the ffs type (defaults to 7, EFI_FV_FILETYPE_DRIVER)\r\n");
printf(" -p <type> specify the pe section type (defaults to 16, EFI_SECTION_PE32)\r\n");
printf(" infile input .efi file\r\n");
printf(" outfile output file name (default generated from infile)\r\n");
exit(1);
}
/**
* Convert hex string into integer
*/
unsigned int gethex(char *ptr, int len)
{
unsigned int ret = 0;
for(;len--;ptr++) {
if(*ptr>='0' && *ptr<='9') { ret <<= 4; ret += (unsigned int)(*ptr-'0'); }
else if(*ptr >= 'a' && *ptr <= 'f') { ret <<= 4; ret += (unsigned int)(*ptr-'a'+10); }
else if(*ptr >= 'A' && *ptr <= 'F') { ret <<= 4; ret += (unsigned int)(*ptr-'A'+10); }
else break;
}
return ret;
}
/**
* Parse a GUID in string into binary representation
*/
void getguid(char *ptr, guid_t *guid)
{
int i;
if(!ptr || !*ptr || ptr[8] != '-' || ptr[13] != '-' || ptr[18] != '-') {
fprintf(stderr, "efiffs: bad GUID format\r\n");
return;
}
guid->Data1 = gethex(ptr, 8); ptr += 9;
guid->Data2 = gethex(ptr, 4); ptr += 5;
guid->Data3 = gethex(ptr, 4); ptr += 5;
guid->Data4[0] = gethex(ptr, 2); ptr += 2;
guid->Data4[1] = gethex(ptr, 2); ptr += 2; if(*ptr == '-') ptr++;
for(i = 2; i < 8; i++, ptr += 2) guid->Data4[i] = gethex(ptr, 2);
}
/**
* Parse ffs type
*/
int gettype(char *str)
{
static const char *types[] = {
NULL, "RAW", "FREEFORM", "SECURITY_CORE", "PEI_CORE", "DXE_CORE", "PEIM", "DRIVER", "COMBINED_PEIM_DRIVER",
"APPLICATION", "SMM", "FIRMWARE_VOLUME_IMAGE", "COMBINED_SMM_DXE", "SMM_CORE", NULL };
int i;
i = atoi(str);
if(i > 0 && i < (int)(sizeof(types)/sizeof(types[0]))) return i;
if(!memcmp(str, "EFI_FV_FILETYPE_", 16)) str += 16;
for(i = 1; types[i] && strcmp(str, types[i]); i++);
if(!types[i]) {
fprintf(stderr, "efiffs: invalid ffs type, available values:\r\n");
for(i = 1; types[i]; i++)
fprintf(stderr, " EFI_FV_FILETYPE_%s\r\n", types[i]);
return 7; /* EFI_FV_FILETYPE_DRIVER */
}
return i;
}
/**
* Parse section type
*/
int getsec(char *str)
{
static const char *types[] = {
"PE32", "PIC", "TE", "DXE_DEPEX", "VERSION", "USER_INTERFACE", "COMPATIBILITY16",
"FIRMWARE_VOLUME_IMAGE", "FREEFORM_SUBTYPE_GUID", "RAW", "RESERVED", "PEI_DEPEX", "SMM_DEPEX", NULL };
int i;
i = atoi(str);
if(i == 1 || i == 2 || i >= 16) return i;
if(!memcmp(str, "EFI_SECTION_", 12)) str += 12;
if(!strcmp(str, "COMPRESSION")) return 1;
if(!strcmp(str, "GUID_DEFINED")) return 2;
for(i = 0; types[i] && strcmp(str, types[i]); i++);
if(!types[i]) {
fprintf(stderr, "efiffs: invalid section type, available values:\r\n");
fprintf(stderr, " EFI_SECTION_COMPRESSION\r\n EFI_SECTION_GUID_DEFINED\r\n");
for(i = 0; types[i]; i++)
fprintf(stderr, " EFI_SECTION_%s\r\n", types[i]);
return 16; /* EFI_SECTION_PE32 */
}
return i + 16;
}
/**
* Calculate checksum
*/
uint8_t checksum8(uint8_t *buff, int len)
{
uint8_t ret = 0;
int i;
for(i = 0; i < len; i++)
ret = (uint8_t)(ret + buff[i]);
return (uint8_t)(0x100 - ret);
}
/**
* Main function
*/
int main(int argc, char **argv)
{
FILE *f;
int i, size, len;
int filetype = 7; /* EFI_FV_FILETYPE_DRIVER */
int sectype = 16; /* EFI_SECTION_PE32 */
char *in = NULL, *out = NULL, *name = NULL, *ver = NULL;
uint8_t *buff = NULL, *sec = NULL;
guid_t guid;
/* get a random GUID */
srand(time(NULL));
i = rand(); memcpy(&((uint8_t*)&guid)[0], &i, 4);
i = rand(); memcpy(&((uint8_t*)&guid)[4], &i, 4);
i = rand(); memcpy(&((uint8_t*)&guid)[8], &i, 4);
i = rand(); memcpy(&((uint8_t*)&guid)[12], &i, 4);
/* parse command line */
for(i = 1; i < argc && argv[i]; i++)
if(argv[i][0] == '-') {
switch(argv[i][1]) {
case 'g': getguid(argv[++i], &guid); break;
case 'n': name = argv[++i]; break;
case 'v': ver = argv[++i]; break;
case 't': filetype = gettype(argv[++i]); break;
case 'p': sectype = getsec(argv[++i]); break;
default: usage(argv[0]); break;
}
} else
if(!in) in = argv[i]; else
if(!out) out = argv[i]; else
usage(argv[0]);
if(!in) usage(argv[0]);
/* get input data */
f = fopen(in, "rb");
if(!f) {
fprintf(stderr, "efiffs: unable to read '%s'\r\n", in);
return 2;
}
fseek(f, 0L, SEEK_END);
size = (int)ftell(f);
fseek(f, 0L, SEEK_SET);
len = 24 + 12 + 4 + size + (name ? 6 + 2 * strlen(name) : 0) + (ver ? 6 + 2 * strlen(ver) : 0);
buff = (uint8_t*)malloc(len);
if(!buff) { fprintf(stderr, "efiffs: unable to allocate memory\r\n"); return 2; }
memset(buff, 0, len);
sec = buff + 24;
/* add the PE section */
sec[0] = size & 0xff;
sec[1] = (size >> 8) & 0xff;
sec[2] = (size >> 16) & 0xff;
sec[3] = sectype;
fread(sec + 4, 1, size, f);
fclose(f);
sec += 4 + ((size + 3) & ~3);
/* add the name section */
if(name) {
size = 6 + 2 * strlen(name);
sec[0] = size & 0xff;
sec[1] = (size >> 8) & 0xff;
sec[2] = (size >> 16) & 0xff;
sec[3] = 0x15; /* EFI_SECTION_USER_INTERFACE */
for(i = 0; name[i]; i++)
sec[4 + 2 * i] = name[i];
sec += ((size + 3) & ~3);
}
/* add the version section */
if(ver) {
size = 6 + 2 * strlen(ver);
sec[0] = size & 0xff;
sec[1] = (size >> 8) & 0xff;
sec[2] = (size >> 16) & 0xff;
sec[3] = 0x14; /* EFI_SECTION_VERSION */
for(i = 0; ver[i]; i++)
sec[4 + 2 * i] = ver[i];
sec += ((size + 3) & ~3);
}
/* calculate ffs header fields */
len = (int)((uintptr_t)sec - (uintptr_t)buff);
memcpy(buff, &guid, 16);
buff[0x12] = filetype;
buff[0x14] = len & 0xff;
buff[0x15] = (len >> 8) & 0xff;
buff[0x16] = (len >> 16) & 0xff;
buff[0x11] = 0xAA;
buff[0x10] = checksum8(buff, 24);
/* write out image */
if(!out) {
i = strlen(in);
out = (char*)malloc(i + 5);
if(!out) { fprintf(stderr, "efiffs: unable to allocate memory\r\n"); return 2; }
strcpy(out, in);
strcpy(!strcmp(out + i - 4, ".efi") ? out + i - 4 : out + i, ".ffs");
}
f = fopen(out, "wb");
if(!f) {
fprintf(stderr, "efiffs: unable to write '%s'\r\n", out);
return 2;
}
fwrite(buff, 1, len, f);
fclose(f);
free(buff);
return 0;
}

File diff suppressed because it is too large Load Diff