summaryrefslogtreecommitdiffstats
path: root/sys/boot/efi/libefi/efifs.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/boot/efi/libefi/efifs.c')
-rw-r--r--sys/boot/efi/libefi/efifs.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/sys/boot/efi/libefi/efifs.c b/sys/boot/efi/libefi/efifs.c
new file mode 100644
index 0000000..cf89a9d
--- /dev/null
+++ b/sys/boot/efi/libefi/efifs.c
@@ -0,0 +1,401 @@
+/*-
+ * Copyright (c) 2001 Doug Rabson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <stddef.h>
+#include <stand.h>
+#include <stdarg.h>
+
+#include <efi.h>
+#include <efilib.h>
+#include "efiboot.h"
+
+/* Perform I/O in blocks of size EFI_BLOCK_SIZE. */
+#define EFI_BLOCK_SIZE (1024 * 1024)
+
+static int
+efifs_open(const char *upath, struct open_file *f)
+{
+ struct efi_devdesc *dev = f->f_devdata;
+ static EFI_GUID sfsid = SIMPLE_FILE_SYSTEM_PROTOCOL;
+ EFI_FILE_IO_INTERFACE *sfs;
+ EFI_FILE *root;
+ EFI_FILE *file;
+ EFI_STATUS status;
+ CHAR16 *cp;
+ CHAR16 *path;
+
+ /*
+ * We cannot blindly assume that f->f_devdata points to a
+ * efi_devdesc structure. Before we dereference 'dev', make
+ * sure that the underlying device is ours.
+ */
+ if (f->f_dev != &efifs_dev || dev->d_handle == NULL)
+ return ENOENT;
+
+ status = BS->HandleProtocol(dev->d_handle, &sfsid, (VOID **)&sfs);
+ if (EFI_ERROR(status))
+ return ENOENT;
+
+ /*
+ * Find the root directory.
+ */
+ status = sfs->OpenVolume(sfs, &root);
+
+ /*
+ * Convert path to CHAR16, skipping leading separators.
+ */
+ while (*upath == '/')
+ upath++;
+ if (!*upath) {
+ /* Opening the root directory, */
+ f->f_fsdata = root;
+ return 0;
+ }
+ cp = path = malloc((strlen(upath) + 1) * sizeof(CHAR16));
+ if (path == NULL)
+ return ENOMEM;
+ while (*upath) {
+ if (*upath == '/')
+ *cp = '\\';
+ else
+ *cp = *upath;
+ upath++;
+ cp++;
+ }
+ *cp++ = 0;
+
+ /*
+ * Try to open it.
+ */
+ status = root->Open(root, &file, path, EFI_FILE_MODE_READ, 0);
+ free(path);
+ if (EFI_ERROR(status)) {
+ root->Close(root);
+ return ENOENT;
+ }
+
+ root->Close(root);
+ f->f_fsdata = file;
+ return 0;
+}
+
+static int
+efifs_close(struct open_file *f)
+{
+ EFI_FILE *file = f->f_fsdata;
+
+ file->Close(file);
+ return 0;
+}
+
+static int
+efifs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ EFI_FILE *file = f->f_fsdata;
+ EFI_STATUS status;
+ UINTN sz = size;
+ char *bufp;
+
+ bufp = buf;
+ while (size > 0) {
+ sz = size;
+ if (sz > EFI_BLOCK_SIZE)
+ sz = EFI_BLOCK_SIZE;
+ status = file->Read(file, &sz, bufp);
+ twiddle();
+ if (EFI_ERROR(status))
+ return EIO;
+ if (sz == 0)
+ break;
+ size -= sz;
+ bufp += sz;
+ }
+ if (resid)
+ *resid = size;
+ return 0;
+}
+
+static int
+efifs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
+{
+ EFI_FILE *file = f->f_fsdata;
+ EFI_STATUS status;
+ UINTN sz = size;
+ char *bufp;
+
+ bufp = buf;
+ while (size > 0) {
+ sz = size;
+ if (sz > EFI_BLOCK_SIZE)
+ sz = EFI_BLOCK_SIZE;
+ status = file->Write(file, &sz, bufp);
+ twiddle();
+ if (EFI_ERROR(status))
+ return EIO;
+ if (sz == 0)
+ break;
+ size -= sz;
+ bufp += sz;
+ }
+ if (resid)
+ *resid = size;
+ return 0;
+}
+
+static off_t
+efifs_seek(struct open_file *f, off_t offset, int where)
+{
+ EFI_FILE *file = f->f_fsdata;
+ EFI_STATUS status;
+ UINT64 base;
+ UINTN sz;
+ static EFI_GUID infoid = EFI_FILE_INFO_ID;
+ EFI_FILE_INFO info;
+
+ switch (where) {
+ case SEEK_SET:
+ base = 0;
+ break;
+
+ case SEEK_CUR:
+ status = file->GetPosition(file, &base);
+ if (EFI_ERROR(status))
+ return -1;
+ break;
+
+ case SEEK_END:
+ sz = sizeof(info);
+ status = file->GetInfo(file, &infoid, &sz, &info);
+ if (EFI_ERROR(status))
+ return -1;
+ base = info.FileSize;
+ break;
+ }
+
+ status = file->SetPosition(file, base + offset);
+ if (EFI_ERROR(status))
+ return -1;
+ file->GetPosition(file, &base);
+
+ return base;
+}
+
+static int
+efifs_stat(struct open_file *f, struct stat *sb)
+{
+ EFI_FILE *file = f->f_fsdata;
+ EFI_STATUS status;
+ char *buf;
+ UINTN sz;
+ static EFI_GUID infoid = EFI_FILE_INFO_ID;
+ EFI_FILE_INFO *info;
+
+ bzero(sb, sizeof(*sb));
+
+ buf = malloc(1024);
+ sz = 1024;
+
+ status = file->GetInfo(file, &infoid, &sz, buf);
+ if (EFI_ERROR(status)) {
+ free(buf);
+ return -1;
+ }
+
+ info = (EFI_FILE_INFO *) buf;
+
+ if (info->Attribute & EFI_FILE_READ_ONLY)
+ sb->st_mode = S_IRUSR;
+ else
+ sb->st_mode = S_IRUSR | S_IWUSR;
+ if (info->Attribute & EFI_FILE_DIRECTORY)
+ sb->st_mode |= S_IFDIR;
+ else
+ sb->st_mode |= S_IFREG;
+ sb->st_size = info->FileSize;
+
+ free(buf);
+ return 0;
+}
+
+static int
+efifs_readdir(struct open_file *f, struct dirent *d)
+{
+ EFI_FILE *file = f->f_fsdata;
+ EFI_STATUS status;
+ char *buf;
+ UINTN sz;
+ EFI_FILE_INFO *info;
+ int i;
+
+ buf = malloc(1024);
+ sz = 1024;
+
+ status = file->Read(file, &sz, buf);
+ if (EFI_ERROR(status) || sz < offsetof(EFI_FILE_INFO, FileName))
+ return ENOENT;
+
+ info = (EFI_FILE_INFO *) buf;
+
+ d->d_fileno = 0;
+ d->d_reclen = sizeof(*d);
+ if (info->Attribute & EFI_FILE_DIRECTORY)
+ d->d_type = DT_DIR;
+ else
+ d->d_type = DT_REG;
+ d->d_namlen = ((info->Size - offsetof(EFI_FILE_INFO, FileName))
+ / sizeof(CHAR16));
+ for (i = 0; i < d->d_namlen; i++)
+ d->d_name[i] = info->FileName[i];
+ d->d_name[i] = 0;
+
+ free(buf);
+ return 0;
+}
+
+struct fs_ops efi_fsops = {
+ "fs",
+ efifs_open,
+ efifs_close,
+ efifs_read,
+ efifs_write,
+ efifs_seek,
+ efifs_stat,
+ efifs_readdir
+};
+
+static EFI_HANDLE *fs_handles;
+UINTN fs_handle_count;
+
+int
+efifs_get_unit(EFI_HANDLE h)
+{
+ UINTN u;
+
+ u = 0;
+ while (u < fs_handle_count && fs_handles[u] != h)
+ u++;
+ return ((u < fs_handle_count) ? u : -1);
+}
+
+static int
+efifs_dev_init(void)
+{
+ EFI_STATUS status;
+ UINTN sz;
+ static EFI_GUID sfsid = SIMPLE_FILE_SYSTEM_PROTOCOL;
+
+ sz = 0;
+ status = BS->LocateHandle(ByProtocol, &sfsid, 0, &sz, 0);
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return ENOENT;
+ fs_handles = (EFI_HANDLE *) malloc(sz);
+ status = BS->LocateHandle(ByProtocol, &sfsid, 0,
+ &sz, fs_handles);
+ if (EFI_ERROR(status)) {
+ free(fs_handles);
+ return ENOENT;
+ }
+ fs_handle_count = sz / sizeof(EFI_HANDLE);
+
+ return 0;
+}
+
+/*
+ * Print information about disks
+ */
+static void
+efifs_dev_print(int verbose)
+{
+ int i;
+ char line[80];
+
+ for (i = 0; i < fs_handle_count; i++) {
+ sprintf(line, " fs%d: EFI filesystem", i);
+ pager_output(line);
+ /* XXX more detail? */
+ pager_output("\n");
+ }
+}
+
+/*
+ * Attempt to open the disk described by (dev) for use by (f).
+ *
+ * Note that the philosophy here is "give them exactly what
+ * they ask for". This is necessary because being too "smart"
+ * about what the user might want leads to complications.
+ * (eg. given no slice or partition value, with a disk that is
+ * sliced - are they after the first BSD slice, or the DOS
+ * slice before it?)
+ */
+static int
+efifs_dev_open(struct open_file *f, ...)
+{
+ va_list args;
+ struct efi_devdesc *dev;
+ int unit;
+
+ va_start(args, f);
+ dev = va_arg(args, struct efi_devdesc*);
+ va_end(args);
+
+ unit = dev->d_kind.efidisk.unit;
+ if (unit < 0 || unit >= fs_handle_count) {
+ printf("attempt to open nonexistent EFI filesystem\n");
+ return(ENXIO);
+ }
+
+ dev->d_handle = fs_handles[unit];
+
+ return 0;
+}
+
+static int
+efifs_dev_close(struct open_file *f)
+{
+
+ return 0;
+}
+
+static int
+efifs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
+{
+ return 0;
+}
+
+struct devsw efifs_dev = {
+ "fs",
+ DEVT_DISK,
+ efifs_dev_init,
+ efifs_dev_strategy,
+ efifs_dev_open,
+ efifs_dev_close,
+ noioctl,
+ efifs_dev_print
+};
OpenPOWER on IntegriCloud