Added fseek and fstat support for Block IO

merge-requests/1/head
bzt 3 years ago
parent 6209a3e459
commit 7860764ff7

@ -39,7 +39,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 128K in total.
1. simply copy the `uefi` directory into your source tree (or set up a git submodule). 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`
@ -175,16 +175,16 @@ Sets an environment variable by `name` with `data` of length `len`. On success r
| fopen | as usual, but might accept wide char strings, also for mode |
| 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, might be wide char strings, max BUFSIZ, files, stdout, stderr |
| 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, max BUFSIZ, files, stdout, stderr |
| vprintf | 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 |
@ -204,19 +204,20 @@ example `%5D` gives you 5 lines (80 dumped bytes).
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, fread, fwrite |
With disk devices, `fread` and `fwrite` arguments look like this (because UEFI can't handle position internally), and returned
size is rounded to block size:
| 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
size_t fread(ptr, buffer size, lba number, stream);
size_t fwrite(ptr, buffer size, lba number, stream);
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.
@ -256,6 +257,8 @@ to the read data.
| 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.
For Block IO S_IFBLK is returned, for Serial IO, S_IFCHR. The actual implementation of `fstat` is in stdio.c,
because it needs to access static variables defined there.
### time.h

@ -30,45 +30,7 @@
#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;
}
/* fstat is in stdio.c because we can't access static variables otherwise... */
int stat (const char_t *__file, struct stat *__buf)
{

@ -32,8 +32,9 @@
static efi_file_handle_t *__root_dir = NULL;
static efi_serial_io_protocol_t *__ser = NULL;
static efi_block_io_t **__blk_devs = 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()
{
@ -59,19 +60,65 @@ void __stdio_seterrno(efi_status_t status)
}
}
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;
int 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(__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;
__buf->st_blocks = __blk_devs[i].bio->Media->LastBlock;
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 == stdin || __stream == stdout || __stream == stderr || (__ser && __stream == (FILE*)__ser)) {
free(__stream);
return 1;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i]) {
free(__stream);
if(__stream == (FILE*)__blk_devs[i].bio)
return 1;
}
status = __stream->Close(__stream);
free(__stream);
return !EFI_ERROR(status);
@ -85,7 +132,7 @@ int fflush (FILE *__stream)
return 1;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i]) {
if(__stream == (FILE*)__blk_devs[i].bio) {
return 1;
}
status = __stream->Flush(__stream);
@ -104,7 +151,7 @@ int __remove (const char_t *__filename, int isdir)
return 1;
}
for(i = 0; i < __blk_ndevs; i++)
if(f == (FILE*)__blk_devs[i]) {
if(f == (FILE*)__blk_devs[i].bio) {
errno = EBADF;
return 1;
}
@ -188,12 +235,13 @@ FILE *fopen (const char_t *__filename, const char_t *__modes)
} while(status == EFI_BUFFER_TOO_SMALL);
if(!EFI_ERROR(status) && handles) {
handle_size /= (uintn_t)sizeof(efi_handle_t);
__blk_devs = (efi_block_io_t**)malloc(handle_size * sizeof(efi_block_io_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(!EFI_ERROR(BS->HandleProtocol(handles[i], &bioGuid, (void **) &__blk_devs[__blk_ndevs])) &&
__blk_devs[__blk_ndevs] && __blk_devs[__blk_ndevs]->Media &&
__blk_devs[__blk_ndevs]->Media->BlockSize > 0)
if(!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;
@ -201,7 +249,7 @@ FILE *fopen (const char_t *__filename, const char_t *__modes)
}
}
if(par >= 0 && par < __blk_ndevs)
return (FILE*)__blk_devs[par];
return (FILE*)__blk_devs[par].bio;
errno = ENOENT;
return NULL;
}
@ -243,7 +291,7 @@ err: __stdio_seterrno(status);
size_t fread (void *__ptr, size_t __size, size_t __n, FILE *__stream)
{
uintn_t bs = __size * __n, i;
uintn_t bs = __size * __n, i, n;
efi_status_t status;
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
@ -253,14 +301,16 @@ size_t fread (void *__ptr, size_t __size, size_t __n, FILE *__stream)
status = __ser->Read(__ser, &bs, __ptr);
} else {
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i]) {
status = __blk_devs[i]->ReadBlocks(__blk_devs[i], __blk_devs[i]->Media->MediaId, __n, __size, __ptr);
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;
}
return ((__size + __blk_devs[i]->Media->BlockSize - 1) / __blk_devs[i]->Media->BlockSize) *
__blk_devs[i]->Media->BlockSize;
__blk_devs[i].offset += bs;
return bs / __size;
}
status = __stream->Read(__stream, &bs, __ptr);
}
@ -273,7 +323,7 @@ size_t fread (void *__ptr, size_t __size, size_t __n, FILE *__stream)
size_t fwrite (const void *__ptr, size_t __size, size_t __n, FILE *__stream)
{
uintn_t bs = __size * __n, i;
uintn_t bs = __size * __n, n, i;
efi_status_t status;
if(__stream == stdin || __stream == stdout || __stream == stderr) {
errno = ESPIPE;
@ -283,14 +333,16 @@ size_t fwrite (const void *__ptr, size_t __size, size_t __n, FILE *__stream)
status = __ser->Write(__ser, &bs, (void*)__ptr);
} else {
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i]) {
status = __blk_devs[i]->WriteBlocks(__blk_devs[i], __blk_devs[i]->Media->MediaId, __n, __size, (void*)__ptr);
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;
}
return ((__size + __blk_devs[i]->Media->BlockSize - 1) / __blk_devs[i]->Media->BlockSize) *
__blk_devs[i]->Media->BlockSize;
__blk_devs[i].offset += bs;
return bs / __size;
}
status = __stream->Write(__stream, &bs, (void *)__ptr);
}
@ -303,7 +355,7 @@ size_t fwrite (const void *__ptr, size_t __size, size_t __n, FILE *__stream)
int fseek (FILE *__stream, long int __off, int __whence)
{
uint64_t off = 0;
off_t off = 0;
efi_status_t status;
efi_guid_t infoGuid = EFI_FILE_INFO_GUID;
efi_file_info_t *info;
@ -317,9 +369,24 @@ int fseek (FILE *__stream, long int __off, int __whence)
return -1;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i]) {
errno = EBADF;
return -1;
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:
@ -357,9 +424,8 @@ long int ftell (FILE *__stream)
return -1;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i]) {
errno = EBADF;
return -1;
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;
@ -382,9 +448,9 @@ int feof (FILE *__stream)
return 0;
}
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i]) {
if(__stream == (FILE*)__blk_devs[i].bio) {
errno = EBADF;
return 0;
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)) {
@ -641,7 +707,7 @@ int vfprintf (FILE *__stream, const char_t *__format, __builtin_va_list args)
#endif
if(ret < 1 || __stream == stdin) return 0;
for(i = 0; i < __blk_ndevs; i++)
if(__stream == (FILE*)__blk_devs[i]) {
if(__stream == (FILE*)__blk_devs[i].bio) {
errno = EBADF;
return -1;
}

@ -1075,6 +1075,11 @@ typedef struct {
efi_block_flush_t FlushBlocks;
} efi_block_io_t;
typedef struct {
off_t offset;
efi_block_io_t *bio;
} block_file_t;
/*** Graphics Output Protocol (not used, but could be useful to have) ***/
#ifndef EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID { 0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } }
@ -1140,6 +1145,37 @@ typedef struct {
efi_gop_mode_t *Mode;
} efi_gop_t;
/*** Simple Pointer Protocol (not used, but could be useful to have) ***/
#ifndef EFI_SIMPLE_POINTER_PROTOCOL_GUID
#define EFI_SIMPLE_POINTER_PROTOCOL_GUID { 0x31878c87, 0xb75, 0x11d5, { 0x9a, 0x4f, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } }
#endif
typedef struct {
int32_t RelativeMovementX;
int32_t RelativeMovementY;
int32_t RelativeMovementZ;
boolean_t LeftButton;
boolean_t RightButton;
} efi_simple_pointer_state_t;
typedef struct {
uint64_t ResolutionX;
uint64_t ResolutionY;
uint64_t ResolutionZ;
boolean_t LeftButton;
boolean_t RightButton;
} efi_simple_pointer_mode_t;
typedef efi_status_t (EFIAPI *efi_simple_pointer_reset_t) (void *This, boolean_t ExtendedVerification);
typedef efi_status_t (EFIAPI *efi_simple_pointer_get_state_t) (void *This, efi_simple_pointer_state_t *State);
typedef struct {
efi_simple_pointer_reset_t Reset;
efi_simple_pointer_get_state_t GetState;
efi_event_t WaitForInput;
efi_simple_pointer_mode_t *Mode;
} efi_simple_pointer_protocol_t;
/*** Option ROM Protocol (not used, but could be useful to have) ***/
#ifndef EFI_PCI_OPTION_ROM_TABLE_GUID
#define EFI_PCI_OPTION_ROM_TABLE_GUID { 0x7462660f, 0x1cbd, 0x48da, {0xad, 0x11, 0x91, 0x71, 0x79, 0x13, 0x83, 0x1c} }
@ -1339,12 +1375,16 @@ extern size_t strlen (const char_t *__s);
#define S_IWRITE 0200 /* Write by owner. */
#define S_IFMT 0170000 /* These bits determine file type. */
#define S_IFIFO 0010000 /* FIFO. */
#define S_IFCHR 0020000 /* Character device. */
#define S_IFDIR 0040000 /* Directory. */
#define S_IFBLK 0060000 /* Block device. */
#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)
#define S_ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask))
#define S_ISCHR(mode) S_ISTYPE((mode), S_IFCHR)
#define S_ISDIR(mode) S_ISTYPE((mode), S_IFDIR)
#define S_ISBLK(mode) S_ISTYPE((mode), S_IFBLK)
#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;

Loading…
Cancel
Save