aboutsummaryrefslogtreecommitdiff
path: root/stand/kboot
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2022-10-27 17:37:54 +0000
committerWarner Losh <imp@FreeBSD.org>2022-10-27 17:37:54 +0000
commit02dba4f75f86f3d5ae5a6a438b5bcfdc667929bb (patch)
tree8996cc8858eec61c31fd9818c4256fa5562cd675 /stand/kboot
parentcc9f1b4c35974972c36553d4ca07ac91dc4262a2 (diff)
downloadsrc-02dba4f75f86f3d5ae5a6a438b5bcfdc667929bb.tar.gz
src-02dba4f75f86f3d5ae5a6a438b5bcfdc667929bb.zip
Diffstat (limited to 'stand/kboot')
-rw-r--r--stand/kboot/Makefile1
-rw-r--r--stand/kboot/conf.c5
-rw-r--r--stand/kboot/hostfs.c280
-rw-r--r--stand/kboot/kboot.h2
-rw-r--r--stand/kboot/main.c4
5 files changed, 291 insertions, 1 deletions
diff --git a/stand/kboot/Makefile b/stand/kboot/Makefile
index 518e945a596e..c8bd313d1d1d 100644
--- a/stand/kboot/Makefile
+++ b/stand/kboot/Makefile
@@ -25,6 +25,7 @@ SRCS= \
host_syscalls.c \
hostcons.c \
hostdisk.c \
+ hostfs.c \
init.c \
kbootfdt.c \
main.c \
diff --git a/stand/kboot/conf.c b/stand/kboot/conf.c
index b840d008a347..26788342c4b9 100644
--- a/stand/kboot/conf.c
+++ b/stand/kboot/conf.c
@@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#endif
extern struct devsw hostdisk;
+extern struct devsw host_dev;
/*
* We could use linker sets for some or all of these, but
@@ -53,9 +54,12 @@ struct devsw *devsw[] = {
#if defined(LOADER_NET_SUPPORT)
&netdev,
#endif
+ &host_dev,
NULL
};
+extern struct fs_ops hostfs_fsops;
+
struct fs_ops *file_system[] = {
#if defined(LOADER_UFS_SUPPORT)
&ufs_fsops,
@@ -79,6 +83,7 @@ struct fs_ops *file_system[] = {
&bzipfs_fsops,
#endif
&dosfs_fsops,
+ &hostfs_fsops,
NULL
};
diff --git a/stand/kboot/hostfs.c b/stand/kboot/hostfs.c
new file mode 100644
index 000000000000..08f532999abe
--- /dev/null
+++ b/stand/kboot/hostfs.c
@@ -0,0 +1,280 @@
+/*-
+ * Copyright (c) 2022 Netflix, Inc
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/types.h>
+#include "stand.h"
+#include "host_syscall.h"
+#include "kboot.h"
+
+#define HOST_PATH_MAX 1025
+
+extern struct devsw host_dev;
+
+const char *hostfs_root = "/";
+
+enum FTYPE {
+ regular,
+ dir,
+};
+
+typedef struct _hostfs_file {
+ enum FTYPE hf_type;
+ int hf_fd;
+ /* The following are only used for FTYPE == dir */
+ char hf_dents[2048];
+ char *hf_curdent;
+ int hf_dentlen; /* Valid part of hf_dents */
+} hostfs_file;
+
+static hostfs_file *
+hostfs_alloc(void)
+{
+ hostfs_file *hf;
+
+ hf = malloc(sizeof(*hf));
+ if (hf != NULL)
+ memset(hf, 0, sizeof(*hf));
+ return (hf);
+}
+
+static void
+hostfs_free(hostfs_file *hf)
+{
+ free(hf);
+}
+
+static int
+hostfs_open(const char *fn, struct open_file *f)
+{
+ hostfs_file *hf;
+ struct host_kstat ksb;
+ char path[HOST_PATH_MAX];
+
+ if (f->f_dev != &host_dev) {
+ return (EINVAL);
+ }
+
+ /*
+ * Normally, we root everything at hostfs_root. However, there are two
+ * exceptions that make it easier to write code. First is /sys and /proc
+ * are special Linux filesystems, so we pass those paths
+ * through. Second, if the path starts with //, then we strip off the
+ * first / and pass it through (in a weird way, this is actually in
+ * POSIX: hosts are allowed to do specail things with paths that start
+ * with two //, but one or three or more are required to be treated as
+ * one).
+ */
+ if (strncmp("/sys/", fn, 5) == 0 || strncmp("/proc/", fn, 6) == 0)
+ strlcpy(path, fn, sizeof(path));
+ else if (fn[0] == '/' && fn[1] == '/' && fn[2] != '/')
+ strlcpy(path, fn + 1, sizeof(path));
+ else
+ snprintf(path, sizeof(path), "%s/%s", hostfs_root, fn);
+ hf = hostfs_alloc();
+ hf->hf_fd = host_open(path, HOST_O_RDONLY, 0);
+ if (hf->hf_fd < 0) {
+ hostfs_free(hf);
+ return (EINVAL);
+ }
+
+ if (host_fstat(hf->hf_fd, &ksb) < 0) {
+ hostfs_free(hf);
+ return (EINVAL);
+ }
+ if (S_ISDIR(hf->hf_fd)) {
+ hf->hf_type = dir;
+ } else {
+ hf->hf_type = regular;
+ }
+ f->f_fsdata = hf;
+ return (0);
+}
+
+static int
+hostfs_close(struct open_file *f)
+{
+ hostfs_file *hf = f->f_fsdata;
+
+ host_close(hf->hf_fd);
+ hostfs_free(hf);
+ f->f_fsdata = NULL;
+
+ return (0);
+}
+
+static int
+hostfs_read(struct open_file *f, void *start, size_t size, size_t *resid)
+{
+ hostfs_file *hf = f->f_fsdata;
+ ssize_t sz;
+
+ sz = host_read(hf->hf_fd, start, size);
+ if (sz < 0) {
+ return (EINVAL);
+ }
+ *resid = size - sz;
+
+ return (0);
+}
+
+static off_t
+hostfs_seek(struct open_file *f, off_t offset, int whence)
+{
+ hostfs_file *hf = f->f_fsdata;
+ uint32_t offl, offh;
+ int err;
+ uint64_t res;
+
+ /*
+ * Assumes Linux host with 'reduced' system call wrappers. Also assume
+ * host and libstand have same whence encoding (safe since it all comes
+ * from V7 later ISO-C). Also assumes we have to support powerpc still,
+ * it's interface is weird for legacy reasons....
+ */
+ offl = offset & 0xffffffff;
+ offh = (offset >> 32) & 0xffffffff;
+ err = host_llseek(hf->hf_fd, offh, offl, &res, whence);
+ if (err < 0)
+ return (err);
+ return (res);
+}
+
+static int
+hostfs_stat(struct open_file *f, struct stat *sb)
+{
+ struct host_kstat ksb;
+ hostfs_file *hf = f->f_fsdata;
+
+ if (host_fstat(hf->hf_fd, &ksb) < 0)
+ return (EINVAL);
+ /*
+ * Translate Linux stat info to lib stand's notion (which uses FreeBSD's
+ * stat structure, missing fields are zero and commented below).
+ */
+ memset(sb, 0, sizeof(*sb));
+ sb->st_dev = ksb.st_dev;
+ sb->st_ino = ksb.st_ino;
+ sb->st_nlink = ksb.st_nlink;
+ sb->st_mode = ksb.st_mode;
+ sb->st_uid = ksb.st_uid;
+ sb->st_gid = ksb.st_gid;
+ sb->st_rdev = ksb.st_rdev;
+ /* No st_?time_ext on i386 */
+ sb->st_atim.tv_sec = ksb.st_atime_sec;
+ sb->st_atim.tv_nsec = ksb.st_atime_nsec;
+ sb->st_mtim.tv_sec = ksb.st_mtime_sec;
+ sb->st_mtim.tv_nsec = ksb.st_mtime_nsec;
+ sb->st_ctim.tv_sec = ksb.st_ctime_sec;
+ sb->st_ctim.tv_nsec = ksb.st_ctime_nsec;
+ /* No st_birthtim */
+ sb->st_size = ksb.st_size;
+ sb->st_blocks = ksb.st_blocks;
+ sb->st_blksize = ksb.st_blksize;
+ /* no st_flags */
+ /* no st_get */
+
+ return (0);
+}
+
+static int
+hostfs_readdir(struct open_file *f, struct dirent *d)
+{
+ hostfs_file *hf = f->f_fsdata;
+ int dentlen;
+ struct host_dirent64 *dent;
+
+ if (hf->hf_curdent == NULL) {
+ dentlen = host_getdents64(hf->hf_fd, hf->hf_dents, sizeof(hf->hf_dents));
+ if (dentlen <= 0)
+ return (EINVAL);
+ hf->hf_dentlen = dentlen;
+ hf->hf_curdent = hf->hf_dents;
+ }
+ dent = (struct host_dirent64 *)hf->hf_curdent;
+ d->d_fileno = dent->d_ino;
+ d->d_type = dent->d_type; /* HOST_DT_XXXX == DX_XXXX for all values */
+ strlcpy(d->d_name, dent->d_name, sizeof(d->d_name)); /* d_name is NUL terminated */
+ d->d_namlen = strlen(d->d_name);
+ hf->hf_curdent += dent->d_reclen;
+ if (hf->hf_curdent >= hf->hf_dents + hf->hf_dentlen) {
+ hf->hf_curdent = NULL;
+ hf->hf_dentlen = 0;
+ }
+
+ return (0);
+}
+
+struct fs_ops hostfs_fsops = {
+ .fs_name = "host",
+ .fo_open = hostfs_open,
+ .fo_close = hostfs_close,
+ .fo_read = hostfs_read,
+ .fo_write = null_write,
+ .fo_seek = hostfs_seek,
+ .fo_stat = hostfs_stat,
+ .fo_readdir = hostfs_readdir
+};
+
+/*
+ * Generic "null" host device. This goes hand and hand with the host fs object
+ *
+ * XXXX This and userboot for bhyve both use exactly the same code, modulo some
+ * formatting nits. Make them common. We mostly use it to 'gate' the open of the
+ * filesystem to only this device.
+ */
+
+static int
+host_dev_init(void)
+{
+ return (0);
+}
+
+static int
+host_dev_print(int verbose)
+{
+ char line[80];
+
+ printf("%s devices:", host_dev.dv_name);
+ if (pager_output("\n") != 0)
+ return (1);
+
+ snprintf(line, sizeof(line), " host%d: Host filesystem\n", 0);
+ return (pager_output(line));
+}
+
+/*
+ * 'Open' the host device.
+ */
+static int
+host_dev_open(struct open_file *f, ...)
+{
+ return (0);
+}
+
+static int
+host_dev_close(struct open_file *f)
+{
+ return (0);
+}
+
+static int
+host_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
+ char *buf, size_t *rsize)
+{
+ return (ENOSYS);
+}
+
+struct devsw host_dev = {
+ .dv_name = "host",
+ .dv_type = DEVT_NET,
+ .dv_init = host_dev_init,
+ .dv_strategy = host_dev_strategy,
+ .dv_open = host_dev_open,
+ .dv_close = host_dev_close,
+ .dv_ioctl = noioctl,
+ .dv_print = host_dev_print,
+ .dv_cleanup = NULL
+};
diff --git a/stand/kboot/kboot.h b/stand/kboot/kboot.h
index 81bd18faa893..5441c90eaecc 100644
--- a/stand/kboot/kboot.h
+++ b/stand/kboot/kboot.h
@@ -11,6 +11,8 @@
void do_init(void);
+extern const char *hostfs_root;
+
/* Per-platform fdt fixup */
void fdt_arch_fixups(void *fdtp);
diff --git a/stand/kboot/main.c b/stand/kboot/main.c
index 10dd6b05194b..23211ce9df4c 100644
--- a/stand/kboot/main.c
+++ b/stand/kboot/main.c
@@ -120,8 +120,10 @@ main(int argc, const char **argv)
bootdev = argv[1];
else
bootdev = "";
+ if (argc > 2)
+ hostfs_root = argv[2];
- printf("Boot device: %s\n", bootdev);
+ printf("Boot device: %s with hostfs_root %s\n", bootdev, hostfs_root);
archsw.arch_getdev = kboot_getdev;
archsw.arch_copyin = kboot_copyin;