Added feof, stat, dirent, time, unlink, mkdir etc.

This commit is contained in:
bzt 2021-02-02 11:49:07 +01:00
parent 1bebb7ce51
commit 604ea36b6e
8 changed files with 651 additions and 126 deletions

251
README.md
View file

@ -36,7 +36,7 @@ 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). A dozen files, about 96K in total.
1. simply copy the `uefi` directory into your source tree (or set up a git submodule). A dozen files, about 128K in total.
2. create an extremely simple **Makefile** like the one below
3. compile your code for UEFI by running `make`
@ -83,6 +83,147 @@ USE_LLVM = 1
include uefi/Makefile
```
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,
so for example your main() is NOT 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) use this wide character type too. For this reason, you
must specify your string literals with `L""` and characters with `L''`. Functions that supposed to handle characters in int
type (like `getchar`, `putchar`), do not truncate to unsigned char, rather to wchar_t. There's an additional `getchar_ifany`
function, which does not block, but returns 0 when there's no key pressed.
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 also returned (for console streams: stdin, stdout, stderr).
That's about it, everything else is the same.
List of Provided POSIX Functions
--------------------------------
### dirent.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| opendir | as usual, but accepts wide char strings |
| readdir | as usual |
| rewinddir | as usual |
| closedir | as usual |
Because UEFI has no concept of devices files nor of symlinks, only DT_DIR and DT_REG supported.
### stdlib.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| atoi | as usual, but accepts wide char strings and understands "0x" prefix |
| atol | as usual, but accepts wide char strings and understands "0x" prefix |
| strtol | as usual, but accepts wide char strings |
| malloc | as usual |
| calloc | as usual |
| realloc | as usual (needs testing) |
| 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, uses EFI_RNG_PROTOCOL if possible |
### stdio.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| fopen | as usual, but accepts wide char strings, for mode L"r", L"w" and L"a" only |
| fclose | as usual |
| fflush | as usual |
| fread | as usual, only real files accepted (no stdin) |
| fwrite | as usual, only real files accepted (no stdout nor stderr) |
| fseek | as usual, only real files accepted (no stdin, stdout, stderr) |
| ftell | as usual, only real files accepted (no stdin, stdout, stderr) |
| feof | as usual, only real files accepted (no stdin, stdout, stderr) |
| fprintf | as usual, but accepts wide char strings, max BUFSIZ, files, stdout, stderr |
| printf | as usual, but accepts wide char strings, max BUFSIZ, stdout only |
| sprintf | as usual, but accepts wide char strings, max BUFSIZ |
| vfprintf | as usual, but accepts wide char strings, max BUFSIZ, files, stdout, stderr |
| vprintf | as usual, but accepts wide char strings, max BUFSIZ |
| vsprintf | as usual, but accepts wide char strings, max BUFSIZ |
| snprintf | as usual, but accepts wide char strings |
| vsnprintf | as usual, but accepts wide char strings |
| getchar | as usual, waits for a key, blocking, stdin only (no redirects) |
| getchar_ifany | non-blocking, returns 0 if there was no key press, UNICODE otherwise |
| putchar | as usual, stdout only (no redirects) |
File open modes: `r` read, `w` write, `a` append. Because of UEFI peculiarities, `wd` creates directory.
String formating is limited; only supports padding via number prefixes, `%d`, `%x`, `%X`, `%c`, `%s`, `%q` and
`%p`. Because it operates on wchar_t, it also supports the non-standard `%C` (printing an UTF-8 character, needs
char\*), `%S` (printing an UTF-8 string), `%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).
### 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 | works on wide char strings |
| strncpy | works on wide char strings |
| strcat | works on wide char strings |
| strncat | works on wide char strings |
| strcmp | works on wide char strings |
| strncmp | works on wide char strings |
| strdup | works on wide char strings |
| strchr | works on wide char strings |
| strrchr | works on wide char strings |
| strstr | works on wide char strings |
| strtok | works on wide char strings |
| strtok_r | works on wide char strings |
| strlen | works on wide char strings |
### sys/stat.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| stat | as usual, but accepts wide char strings |
| fstat | UEFI doesn't have fd, so it uses FILE\* |
| mkdir | as usual, but accepts 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.
### 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 | the usual |
| sleep | the usual |
| unlink | as usual, but accepts wide char strings |
| rmdir | as usual, but accepts wide char strings |
Accessing UEFI Services
-----------------------
@ -126,111 +267,3 @@ naming conflicts. POSIX-UEFI itself ships the very minimum set of typedefs and s
#include <efi.h>
#include <uefi.h> /* this will work as expected! Both POSIX-UEFI and EDK II / gnu-efi typedefs available */
```
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,
so for example your main() is NOT 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) use this wide character type too. Functions that supposed
to handle characters in int type (like `getchar`, `putchar`), do not truncate to unsigned char, rather to wchar_t. For this
reason, you must specify your string literals with `L""` and characters with `L''`. There's an additional `getchar_ifany`
function, which does not block, but returns 0 when there's no key pressed.
That's about it, everything else is the same.
List of Provided POSIX Functions
--------------------------------
### stdlib.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| atoi | as usual, but accepts wide char strings and understands "0x" prefix |
| atol | as usual, but accepts wide char strings and understands "0x" prefix |
| strtol | as usual, but accepts wide char strings |
| malloc | as usual |
| calloc | as usual |
| realloc | as usual (needs testing) |
| 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) |
### stdio.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| fopen | as usual, but accepts wide char strings, for mode L"r", L"w" and L"a" only |
| fclose | as usual |
| fflush | as usual |
| fread | as usual, only real files accepted (no stdin) |
| fwrite | as usual, only real files accepted (no stdout nor stderr) |
| fseek | as usual, only real files accepted (no stdin, stdout, stderr) |
| ftell | as usual, only real files accepted (no stdin, stdout, stderr) |
| fprintf | as usual, but accepts wide char strings, max BUFSIZ, files, stdout, stderr |
| printf | as usual, but accepts wide char strings, max BUFSIZ, stdout only |
| sprintf | as usual, but accepts wide char strings, max BUFSIZ |
| vfprintf | as usual, but accepts wide char strings, max BUFSIZ, files, stdout, stderr |
| vprintf | as usual, but accepts wide char strings, max BUFSIZ |
| vsprintf | as usual, but accepts wide char strings, max BUFSIZ |
| snprintf | as usual, but accepts wide char strings |
| vsnprintf | as usual, but accepts wide char strings |
| getchar | as usual, waits for a key, blocking, stdin only (no redirects) |
| getchar_ifany | non-blocking, returns 0 if there was no key press, UNICODE otherwise |
| putchar | as usual, stdout only (no redirects) |
String formating is limited; only supports padding via number prefixes, `%d`, `%x`, `%X`, `%c`, `%s`, `%q` and
`%p`. Because it operates on wchar_t, it also supports the non-standard `%C` (printing an UTF-8 character, needs
char\*), `%S` (printing an UTF-8 string), `%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`.
### 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 | works on wide char strings |
| strncpy | works on wide char strings |
| strcat | works on wide char strings |
| strncat | works on wide char strings |
| strcmp | works on wide char strings |
| strncmp | works on wide char strings |
| strdup | works on wide char strings |
| strchr | works on wide char strings |
| strrchr | works on wide char strings |
| strstr | works on wide char strings |
| strtok | works on wide char strings |
| strtok_r | works on wide char strings |
| strlen | works on wide char strings |
### time.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| localtime | argument unused, always returns current time in struct tm |
### unistd.h
| Function | Description |
|---------------|----------------------------------------------------------------------------|
| usleep | the usual |
| sleep | the usual |

72
uefi/dirent.c Normal file
View file

@ -0,0 +1,72 @@
/*
* 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);
struct dirent __dirent;
DIR *opendir (const wchar_t *__name)
{
DIR *dp = (DIR*)fopen(__name, L"rd");
rewinddir(dp);
return dp;
}
struct dirent *readdir (DIR *__dirp)
{
efi_status_t status;
efi_file_info_t info;
uintn_t bs;
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;
__dirent.d_reclen = strlen(info.FileName);
strcpy(__dirent.d_name, info.FileName);
return &__dirent;
}
void rewinddir (DIR *__dirp)
{
if(__dirp)
__dirp->SetPosition(__dirp, 0);
}
int closedir (DIR *__dirp)
{
return fclose((FILE*)__dirp);
}

106
uefi/stat.c Normal file
View file

@ -0,0 +1,106 @@
/*
* 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>
extern void __stdio_seterrno(efi_status_t status);
extern time_t __mktime_efi(efi_time_t *t);
int fstat (FILE *__f, struct stat *__buf)
{
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);
efi_status_t status;
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;
}
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 stat (const wchar_t *__file, struct stat *__buf)
{
int ret;
FILE *f;
if(!__file || !*__file || !__buf) {
errno = EINVAL;
return -1;
}
f = fopen(__file, L"*");
if(!f) {
memset(__buf, 0, sizeof(struct stat));
return -1;
}
ret = fstat(f, __buf);
fclose(f);
return ret;
}
extern int mkdir (const wchar_t *__path, mode_t __mode)
{
FILE *f;
(void)__mode;
if(!__path || !*__path) {
errno = EINVAL;
return -1;
}
f = fopen(__path, L"wd");
if(!f) {
return -1;
}
fclose(f);
return 0;
}

View file

@ -56,12 +56,72 @@ int fflush (FILE *__stream)
return !EFI_ERROR(status);
}
FILE *fopen (const wchar_t *__filename, const char *__modes)
int __remove (const wchar_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);
FILE *f = fopen(__filename, L"r");
if(f == stdin || f == stdout || f == stderr) {
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 wchar_t *__filename)
{
return __remove(__filename, -1);
}
FILE *fopen (const wchar_t *__filename, const wchar_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);
if(!__filename || !*__filename || !__modes || !*__modes) {
errno = EINVAL;
return NULL;
}
/* fake some device names. UEFI has no concept of device files */
if(!strcmp(__filename, L"/dev/stdin")) {
if(__modes[0] == L'w' || __modes[0] == L'a') { errno = EPERM; return NULL; }
return stdin;
}
if(!strcmp(__filename, L"/dev/stdout")) {
if(__modes[0] == L'r') { errno = EPERM; return NULL; }
return stdout;
}
if(!strcmp(__filename, L"/dev/stderr")) {
if(__modes[0] == L'r') { errno = EPERM; return NULL; }
return stderr;
}
if(!__root_dir && LIP) {
status = BS->HandleProtocol(LIP->DeviceHandle, &sfsGuid, (void **)&sfs);
if(!EFI_ERROR(status))
@ -75,11 +135,20 @@ FILE *fopen (const wchar_t *__filename, const char *__modes)
ret = (FILE*)malloc(sizeof(FILE));
if(!ret) return NULL;
status = __root_dir->Open(__root_dir, &ret, (wchar_t*)__filename,
__modes[0] == L'r' ? EFI_FILE_MODE_READ : (EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE), 0);
__modes[0] == L'w' ? (EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE) : EFI_FILE_MODE_READ,
__modes[1] == L'd' ? EFI_FILE_DIRECTORY : 0);
if(EFI_ERROR(status)) {
__stdio_seterrno(status);
err: __stdio_seterrno(status);
free(ret); ret = NULL;
}
status = ret->GetInfo(ret, &infGuid, &fsiz, &info);
if(EFI_ERROR(status)) goto err;
if(__modes[1] == L'd' && !(info.Attribute & EFI_FILE_DIRECTORY)) {
free(ret); errno = ENOTDIR; return NULL;
}
if(__modes[1] != L'd' && (info.Attribute & EFI_FILE_DIRECTORY)) {
free(ret); errno = EISDIR; return NULL;
}
if(__modes[0] == L'a') fseek(ret, 0, SEEK_END);
return ret;
}
@ -87,15 +156,32 @@ FILE *fopen (const wchar_t *__filename, const char *__modes)
size_t fread (void *__ptr, size_t __size, size_t __n, FILE *__stream)
{
uintn_t bs = __size * __n;
efi_status_t status = __stream->Read(__stream, &bs, __ptr);
if(status == EFI_END_OF_FILE) bs = 0;
efi_status_t status;
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return 0;
}
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;
efi_status_t status = __stream->Write(__stream, &bs, (void *)__ptr);
efi_status_t status;
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return 0;
}
status = __stream->Write(__stream, &bs, (void *)__ptr);
if(EFI_ERROR(status)) {
__stdio_seterrno(status);
return 0;
}
return bs / __size;
}
@ -106,6 +192,10 @@ int fseek (FILE *__stream, long int __off, int __whence)
efi_guid_t infoGuid = EFI_FILE_INFO_GUID;
efi_file_info_t *info;
uintn_t infosiz = sizeof(efi_file_info_t) + 16;
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return -1;
}
switch(__whence) {
case SEEK_END:
status = __stream->GetInfo(__stream, &infoGuid, &infosiz, info);
@ -131,10 +221,38 @@ int fseek (FILE *__stream, long int __off, int __whence)
long int ftell (FILE *__stream)
{
uint64_t off = 0;
efi_status_t status = __stream->GetPosition(__stream, &off);
efi_status_t status;
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return -1;
}
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);
efi_status_t status;
int ret;
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
return 0;
}
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(wchar_t *dst, size_t maxlen, const wchar_t *fmt, __builtin_va_list args)
{
#define needsescape(a) (a==L'\"' || a==L'\\' || a==L'\a' || a==L'\b' || a==L'\033' || a=='\f' || \

View file

@ -31,6 +31,7 @@
#include <uefi.h>
int errno = 0;
static uint64_t __srand_seed = 6364136223846793005ULL;
int atoi(const wchar_t *s)
{
@ -228,3 +229,23 @@ size_t wcstombs (char *__s, const wchar_t *__pwcs, size_t __n)
};
return __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;
}

View file

@ -32,6 +32,67 @@
static struct tm __tm;
/* from musl */
uint64_t __year_to_secs(uint64_t year, int *is_leap)
{
int y, cycles, centuries, leaps, rem;
if (year-2ULL <= 136) {
y = year;
leaps = (y-68)>>2;
if (!((y-68)&3)) {
leaps--;
if (is_leap) *is_leap = 1;
} else if (is_leap) *is_leap = 0;
return 31536000*(y-70) + 86400*leaps;
}
if (!is_leap) is_leap = &(int){0};
cycles = (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 / 4U;
rem %= 4U;
*is_leap = !rem;
}
}
leaps += 97*cycles + 24*centuries - *is_leap;
return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400;
}
time_t __mktime_efi(efi_time_t *t)
{
__tm.tm_year = t->Year + 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
*/
@ -40,12 +101,45 @@ struct tm *localtime (const time_t *__timer)
efi_time_t t = {0};
(void)__timer;
ST->RuntimeServices->GetTime(&t, NULL);
__tm.tm_year = t.Year + 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;
__mktime_efi(&t);
return &__tm;
}
time_t mktime(const struct tm *tm)
{
static const int 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 = tm->tm_year, t;
int month = tm->tm_mon;
if (month >= 12 || month < 0) {
int adj = 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 += 86400LL * (tm->tm_mday-1);
t += 3600LL * tm->tm_hour;
t += 60LL * tm->tm_min;
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;
}

View file

@ -62,6 +62,9 @@ typedef uint16_t wchar_t;
typedef uint64_t uintn_t;
typedef uint64_t size_t;
typedef uint64_t time_t;
typedef uint64_t mode_t;
typedef uint64_t off_t;
typedef uint64_t blkcnt_t;
typedef uint64_t efi_status_t;
typedef uint64_t efi_tpl_t;
typedef uint64_t efi_lba_t;
@ -644,6 +647,7 @@ typedef struct {
efi_query_variable_info_t QueryVariableInfo;
} efi_runtime_services_t;
extern efi_runtime_services_t *RT;
#define gRT RT
/** Boot Services ***/
typedef struct {
@ -759,6 +763,7 @@ typedef struct {
efi_calculate_crc32_t CalculateCrc32;
} efi_boot_services_t;
extern efi_boot_services_t *BS;
#define gBS BS
/*** Loaded Image Protocol ***/
#ifndef EFI_LOADED_IMAGE_PROTOCOL_GUID
@ -814,6 +819,7 @@ typedef struct {
efi_configuration_table_t *ConfigurationTable;
} efi_system_table_t;
extern efi_system_table_t *ST;
#define gST ST
/*** Simple File System Protocol ***/
#ifndef EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID
@ -829,7 +835,7 @@ extern efi_system_table_t *ST;
#define EFI_FILE_READ_ONLY 0x0000000000000001
#define EFI_FILE_HIDDEN 0x0000000000000002
#define EFI_FILE_SYSTEM 0x0000000000000004
#define EFI_FILE_RESERVIED 0x0000000000000008
#define EFI_FILE_RESERVED 0x0000000000000008
#define EFI_FILE_DIRECTORY 0x0000000000000010
#define EFI_FILE_ARCHIVE 0x0000000000000020
#define EFI_FILE_VALID_ATTR 0x0000000000000037
@ -842,6 +848,10 @@ extern efi_system_table_t *ST;
#define EFI_FILE_INFO_GUID { 0x9576e92, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b} }
#endif
#ifndef FILENAME_MAX
#define FILENAME_MAX 262 /* from FAT spec */
#endif
typedef struct {
uint64_t Size;
uint64_t FileSize;
@ -850,7 +860,7 @@ typedef struct {
efi_time_t LastAccessTime;
efi_time_t ModificationTime;
uint64_t Attribute;
wchar_t FileName[1];
wchar_t FileName[FILENAME_MAX];
} efi_file_info_t;
typedef struct efi_file_handle_s efi_file_handle_t;
@ -918,6 +928,19 @@ typedef struct {
efi_handle_t StdErr;
} efi_shell_interface_protocol_t;
/*** Random Number Generator ***/
#ifndef EFI_RNG_PROTOCOL_GUID
#define EFI_RNG_PROTOCOL_GUID { 0x3152bca5, 0xeade, 0x433d, {0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44} }
#endif
typedef efi_status_t (EFIAPI *efi_rng_get_info_t)(void *This, uintn_t *RNGAlgorithmListSize, efi_guid_t *RNGAlgorithmList);
typedef efi_status_t (EFIAPI *efi_rng_get_rng_t)(void *This, efi_guid_t *RNGAlgorithm, uintn_t RNGValueLength, uint8_t *RNGValue);
typedef struct {
efi_rng_get_info_t GetInfo;
efi_rng_get_rng_t GetRNG;
} efi_rng_protocol_t;
/*** Serial IO Protocol (not used, but could be useful to have) ***/
#ifndef EFI_SERIAL_IO_PROTOCOL_GUID
#define EFI_SERIAL_IO_PROTOCOL_GUID { 0xBB25CF6F, 0xF1D4, 0x11D2, {0x9A, 0x0C, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0xFD} }
@ -1114,6 +1137,22 @@ typedef struct {
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
/* dirent.h */
#define IFTODT(mode) (((mode) & 0170000) >> 12)
#define DTTOIF(dirtype) ((dirtype) << 12)
#define DT_DIR 4
#define DT_REG 8
struct dirent {
unsigned short int d_reclen;
unsigned char d_type;
wchar_t d_name[FILENAME_MAX];
};
typedef struct efi_file_handle_s DIR;
extern DIR *opendir (const wchar_t *__name);
extern struct dirent *readdir (DIR *__dirp);
extern void rewinddir (DIR *__dirp);
extern int closedir (DIR *__dirp);
/* errno.h */
extern int errno;
#define EPERM 1 /* Operation not permitted */
@ -1152,6 +1191,7 @@ extern int errno;
#define ERANGE 34 /* Math result not representable */
/* stdlib.h */
#define RAND_MAX 2147483647
typedef int (*__compar_fn_t) (const void *, const void *);
extern int atoi (const wchar_t *__nptr);
extern int64_t atol (const wchar_t *__nptr);
@ -1171,6 +1211,8 @@ extern int mbtowc (wchar_t * __pwc, const char * __s, size_t __n);
extern int wctomb (char *__s, wchar_t __wchar);
extern size_t mbstowcs (wchar_t *__pwcs, const char *__s, size_t __n);
extern size_t wcstombs (char *__s, const wchar_t *__pwcs, size_t __n);
extern void srand(unsigned int __seed);
extern int rand(void);
/* stdio.h */
#ifndef BUFSIZ
@ -1185,7 +1227,8 @@ extern size_t wcstombs (char *__s, const wchar_t *__pwcs, size_t __n);
typedef struct efi_file_handle_s FILE;
extern int fclose (FILE *__stream);
extern int fflush (FILE *__stream);
extern FILE *fopen (const wchar_t *__filename, const char *__modes);
extern int remove (const wchar_t *__filename);
extern FILE *fopen (const wchar_t *__filename, const wchar_t *__modes);
extern size_t fread (void *__ptr, size_t __size, size_t __n, FILE *__stream);
extern size_t fwrite (const void *__ptr, size_t __size, size_t __n, FILE *__s);
extern int fseek (FILE *__stream, long int __off, int __whence);
@ -1236,6 +1279,29 @@ extern wchar_t *strtok (wchar_t *__s, const wchar_t *__delim);
extern wchar_t *strtok_r (wchar_t *__s, const wchar_t *__delim, wchar_t **__save_ptr);
extern size_t strlen (const wchar_t *__s);
/* sys/stat.h */
#define S_IREAD 0400 /* Read by owner. */
#define S_IWRITE 0200 /* Write by owner. */
#define S_IFMT 0170000 /* These bits determine file type. */
#define S_IFIFO 0010000 /* FIFO. */
#define S_IFDIR 0040000 /* Directory. */
#define S_IFREG 0100000 /* Regular file. */
#define S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)
#define S_ISFIFO(mode) __S_ISTYPE((mode), __S_IFIFO)
struct stat {
mode_t st_mode;
off_t st_size;
blkcnt_t st_blocks;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
};
extern int stat (const wchar_t *__file, struct stat *__buf);
extern int fstat (FILE *__f, struct stat *__buf);
extern int mkdir (const wchar_t *__path, mode_t __mode);
/* time.h */
struct tm {
int tm_sec; /* Seconds. [0-60] (1 leap second) */
@ -1249,10 +1315,13 @@ struct tm {
int tm_isdst; /* DST. [-1/0/1]*/
};
extern struct tm *localtime (const time_t *__timer);
extern time_t mktime(const struct tm *__tm);
extern time_t time(time_t *__timer);
/* unistd.h */
extern unsigned int sleep (unsigned int __seconds);
extern int usleep (unsigned long int __useconds);
extern int unlink (const wchar_t *__filename);
#ifdef __cplusplus
}

View file

@ -30,6 +30,8 @@
#include <uefi.h>
int __remove(const wchar_t *__filename, int isdir);
int usleep (unsigned long int __useconds)
{
BS->Stall(__useconds);
@ -41,3 +43,13 @@ 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);
}