diff --git a/stand/kboot/host_syscall.h b/stand/kboot/host_syscall.h index 38f0fb04aa95..75394afcde6c 100644 --- a/stand/kboot/host_syscall.h +++ b/stand/kboot/host_syscall.h @@ -1,209 +1,219 @@ /*- * Copyright (C) 2014 Nathan Whitehorn * 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 ``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 TOOLS GMBH 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$ */ #ifndef _HOST_SYSCALL_H #define _HOST_SYSCALL_H #include long host_syscall(int number, ...); /* * Sizes taken from musl's include/alltypes.h.in and expanded for LP64 hosts */ typedef uint64_t host_dev_t; typedef uint64_t host_ino_t; typedef unsigned int host_mode_t; typedef unsigned int host_uid_t; typedef unsigned int host_gid_t; typedef int64_t host_off_t; typedef long host_blksize_t; typedef int64_t host_blkcnt_t; #include "stat_arch.h" /* * stat flags * These are arch independent and match the values in nolib and uapi headers * with HOST_ prepended. */ #define HOST_S_IFMT 0170000 #define HOST_S_IFIFO 0010000 #define HOST_S_IFCHR 0020000 #define HOST_S_IFDIR 0040000 #define HOST_S_IFBLK 0060000 #define HOST_S_IFREG 0100000 #define HOST_S_IFLNK 0120000 #define HOST_S_IFSOCK 0140000 #define HOST_S_ISBLK(mode) (((mode) & HOST_S_IFMT) == HOST_S_IFBLK) #define HOST_S_ISCHR(mode) (((mode) & HOST_S_IFMT) == HOST_S_IFCHR) #define HOST_S_ISDIR(mode) (((mode) & HOST_S_IFMT) == HOST_S_IFDIR) #define HOST_S_ISFIFO(mode) (((mode) & HOST_S_IFMT) == HOST_S_IFIFO) #define HOST_S_ISLNK(mode) (((mode) & HOST_S_IFMT) == HOST_S_IFLNK) #define HOST_S_ISREG(mode) (((mode) & HOST_S_IFMT) == HOST_S_IFREG) #define HOST_S_ISSOCK(mode) (((mode) & HOST_S_IFMT) == HOST_S_IFSOCK) /* * Constants for open, fcntl, etc * * Note: Some of these are arch dependent on Linux, but are the same for * powerpc, x86, arm*, and riscv. We should be futureproof, though, since these * are the 'generic' values and only older architectures (no longer supported by * FreeBSD) vary. * * These are from tools/include/uapi/asm-generic/fcntl.h and use the octal * notation. Beware, hex is used in other places creating potential confsion. */ #define HOST_O_RDONLY 0 #define HOST_O_WRONLY 1 #define HOST_O_RDWR 2 #define HOST_O_CREAT 00100 #define HOST_O_EXCL 00200 #define HOST_O_NOCTTY 00400 #define HOST_O_TRUNC 01000 #define HOST_O_APPEND 02000 #define HOST_O_NONBLOCK 04000 #define HOST_AT_FDCWD -100 /* Relative to current directory */ /* * Data types */ struct old_utsname { char sysname[65]; char nodename[65]; char release[65]; char version[65]; char machine[65]; }; struct host_timeval { time_t tv_sec; long tv_usec; }; /* * Must match Linux's values see linux/tools/include/uapi/asm-generic/mman-common.h * and linux/tools/include/linux/mman.h * * And pre-pend HOST_ here. */ #define HOST_PROT_READ 0x1 #define HOST_PROT_WRITE 0x2 #define HOST_PROT_EXEC 0x4 #define HOST_MAP_SHARED 0x01 #define HOST_MAP_PRIVATE 0x02 #define HOST_MAP_FIXED 0x10 #define HOST_MAP_ANONYMOUS 0x20 /* Mount flags from uapi */ #define MS_RELATIME (1 << 21) #define HOST_REBOOT_MAGIC1 0xfee1dead #define HOST_REBOOT_MAGIC2 672274793 #define HOST_REBOOT_CMD_KEXEC 0x45584543 /* * Values from linux/tools/include/uapi/linux/kexec.h */ /* * Values match ELF architecture types. */ #define HOST_KEXEC_ARCH_X86_64 (62 << 16) #define HOST_KEXEC_ARCH_PPC64 (21 << 16) #define HOST_KEXEC_ARCH_ARM (40 << 16) #define HOST_KEXEC_ARCH_AARCH64 (183 << 16) #define HOST_KEXEC_ARCH_RISCV (243 << 16) /* Arbitrary cap on segments */ #define HOST_KEXEC_SEGMENT_MAX 16 struct host_kexec_segment { void *buf; int bufsz; void *mem; int memsz; }; struct host_dirent64 { uint64_t d_ino; /* 64-bit inode number */ int64_t d_off; /* 64-bit offset to next structure */ unsigned short d_reclen; /* Size of this dirent */ unsigned char d_type; /* File type */ char d_name[]; /* Filename (null-terminated) */ }; /* d_type values */ #define HOST_DT_UNKNOWN 0 #define HOST_DT_FIFO 1 #define HOST_DT_CHR 2 #define HOST_DT_DIR 4 #define HOST_DT_BLK 6 #define HOST_DT_REG 8 #define HOST_DT_LNK 10 #define HOST_DT_SOCK 12 #define HOST_DT_WHT 14 /* * System Calls */ int host_close(int fd); int host_dup(int fd); int host_exit(int code); int host_fstat(int fd, struct host_kstat *sb); int host_getdents64(int fd, void *dirp, int count); int host_getpid(void); int host_gettimeofday(struct host_timeval *a, void *b); int host_ioctl(int fd, unsigned long request, unsigned long arg); int host_kexec_load(unsigned long entry, unsigned long nsegs, struct host_kexec_segment *segs, unsigned long flags); ssize_t host_llseek(int fd, int32_t offset_high, int32_t offset_lo, uint64_t *result, int whence); int host_mkdir(const char *, host_mode_t); void *host_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off); int host_mount(const char *src, const char *target, const char *type, unsigned long flags, void *data); int host_munmap(void *addr, size_t len); int host_open(const char *path, int flags, int mode); ssize_t host_read(int fd, void *buf, size_t nbyte); int host_reboot(int, int, int, uintptr_t); int host_select(int nfds, long *readfds, long *writefds, long *exceptfds, struct host_timeval *timeout); int host_stat(const char *path, struct host_kstat *sb); int host_symlink(const char *path1, const char *path2); int host_uname(struct old_utsname *); ssize_t host_write(int fd, const void *buf, size_t nbyte); /* * Wrappers / one-liners */ #define host_getmem(size) \ host_mmap(0, size, HOST_PROT_READ | HOST_PROT_WRITE, \ HOST_MAP_PRIVATE | HOST_MAP_ANONYMOUS, -1, 0); +/* + * Translate Linux errno to FreeBSD errno. The two system have idenitcal errors + * for 1-34. After that, they differ. Linux also has errno that don't map + * exactly to FreeBSD's errno, plus the Linux errno are arch dependent > + * 34. Since we just need to do this for simple cases, use the simple mapping + * function where -1 to -34 are translated to 1 to 34 and all others are EINVAL. + * Pass the linux return value, which will be the -errno. + */ +#define host_to_stand_errno(e) ((-e) > 34 ? EINVAL : (-e)) + #endif diff --git a/stand/kboot/hostfs.c b/stand/kboot/hostfs.c index 08f532999abe..02afa885d833 100644 --- a/stand/kboot/hostfs.c +++ b/stand/kboot/hostfs.c @@ -1,280 +1,279 @@ /*- * Copyright (c) 2022 Netflix, Inc * * SPDX-License-Identifier: BSD-2-Clause */ #include #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); - } + if (sz < 0) + return (host_to_stand_errno(sz)); *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 };