Added POSIX-UEFI as dependency
This commit is contained in:
parent
61a8ca90e8
commit
8bb56b3246
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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) }
|
||||
}
|
|
@ -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) }
|
||||
}
|
|
@ -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) }
|
||||
}
|
|
@ -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);*/
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
```
|
|
@ -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
|
||||
```
|
|
@ -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;
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue