Index: stable/12/lib/libsecureboot/h/libsecureboot.h =================================================================== --- stable/12/lib/libsecureboot/h/libsecureboot.h (revision 359734) +++ stable/12/lib/libsecureboot/h/libsecureboot.h (revision 359735) @@ -1,102 +1,97 @@ /*- * Copyright (c) 2017-2018, Juniper Networks, Inc. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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$ */ #ifndef _LIBSECUREBOOT_H_ #define _LIBSECUREBOOT_H_ #include #ifdef _STANDALONE #include #else #include #include #include #include #include #include #endif #include unsigned char * read_fd(int, size_t); #ifndef NEED_BRSSL_H unsigned char * read_file(const char *, size_t *); #endif extern int DebugVe; #define DEBUG_PRINTF(n, x) if (DebugVe >= n) printf x int ve_trust_init(void); size_t ve_trust_anchors_add_buf(unsigned char *, size_t); size_t ve_trust_anchors_revoke(unsigned char *, size_t); int ve_trust_add(const char *); void ve_debug_set(int); void ve_anchor_verbose_set(int); int ve_anchor_verbose_get(void); void ve_utc_set(time_t utc); char *ve_error_get(void); int ve_error_set(const char *, ...) __printflike(1,2); int ve_self_tests(void); void fingerprint_info_add(const char *, const char *, const char *, const char *, struct stat *); int ve_check_hash(br_hash_compat_context *, const br_hash_class *, const char *, const char *, size_t); -struct vectx; -struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *); -ssize_t vectx_read(struct vectx *, void *, size_t); -off_t vectx_lseek(struct vectx *, off_t, int); -int vectx_close(struct vectx *); - char * hexdigest(char *, size_t, unsigned char *, size_t); int verify_fd(int, const char *, off_t, struct stat *); int verify_open(const char *, int); unsigned char *verify_signed(const char *, int); unsigned char *verify_sig(const char *, int); unsigned char *verify_asc(const char *, int); /* OpenPGP */ void ve_pcr_init(void); -void ve_pcr_update(unsigned char *, size_t); +void ve_pcr_update(const char *, unsigned char *, size_t); ssize_t ve_pcr_get(unsigned char *, size_t); int ve_pcr_updating_get(void); void ve_pcr_updating_set(int); +char * ve_pcr_hashed_get(int); /* flags for verify_{asc,sig,signed} */ #define VEF_VERBOSE 1 #define VE_FINGERPRINT_OK 1 #define VE_FINGERPRINT_IGNORE 2 /* errors from verify_fd */ #define VE_FINGERPRINT_NONE -2 #define VE_FINGERPRINT_WRONG -3 #define VE_FINGERPRINT_UNKNOWN -4 /* may not be an error */ #endif /* _LIBSECUREBOOT_H_ */ Index: stable/12/lib/libsecureboot/h/verify_file.h =================================================================== --- stable/12/lib/libsecureboot/h/verify_file.h (revision 359734) +++ stable/12/lib/libsecureboot/h/verify_file.h (revision 359735) @@ -1,51 +1,59 @@ /*- * Copyright (c) 2017-2018, Juniper Networks, Inc. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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$ */ #ifndef _VERIFY_FILE_H_ #define _VERIFY_FILE_H_ #define VE_GUESS -1 /* let verify_file work it out */ #define VE_TRY 0 /* we don't mind if unverified */ #define VE_WANT 1 /* we want this verified */ #define VE_MUST 2 /* this must be verified */ #define VE_NOT_CHECKED -42 #define VE_VERIFIED 1 /* all good */ #define VE_UNVERIFIED_OK 0 /* not verified but that's ok */ #define VE_NOT_VERIFYING 2 /* we are not verifying */ struct stat; -void ve_debug_set(int); -int ve_status_get(int); -void ve_efi_init(void); -int load_manifest(const char *, const char *, const char *, struct stat *); -int pass_manifest(const char *, const char *); -int pass_manifest_export_envs(void); -int verify_file(int, const char *, off_t, int); -void verify_pcr_export(void); +int verify_prep(int, const char *, off_t, struct stat *, const char *); +void ve_debug_set(int); +char *ve_error_get(void); +void ve_efi_init(void); +int ve_status_get(int); +int load_manifest(const char *, const char *, const char *, struct stat *); +int pass_manifest(const char *, const char *); +int pass_manifest_export_envs(void); +int verify_file(int, const char *, off_t, int, const char *); +void verify_pcr_export(void); + +struct vectx; +struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *, const char *); +ssize_t vectx_read(struct vectx *, void *, size_t); +off_t vectx_lseek(struct vectx *, off_t, int); +int vectx_close(struct vectx *, int, const char *); #endif /* _VERIFY_FILE_H_ */ Index: stable/12/lib/libsecureboot/tests/tvo.c =================================================================== --- stable/12/lib/libsecureboot/tests/tvo.c (revision 359734) +++ stable/12/lib/libsecureboot/tests/tvo.c (revision 359735) @@ -1,188 +1,198 @@ /* * Copyright (c) 2017-2018, Juniper Networks, Inc. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ #include __FBSDID("$FreeBSD$"); #include "../libsecureboot-priv.h" #include #include #include +size_t DestdirLen; +char *Destdir; char *Skip; int main(int argc, char *argv[]) { int n; int fd; int c; int Vflag; char *cp; char *prefix; + char *destdir; + Destdir = NULL; + DestdirLen = 0; prefix = NULL; Skip = NULL; n = ve_trust_init(); printf("Trust %d\n", n); Vflag = 0; - while ((c = getopt(argc, argv, "dp:s:T:V")) != -1) { + while ((c = getopt(argc, argv, "D:dp:s:T:V")) != -1) { switch (c) { + case 'D': + Destdir = optarg; + DestdirLen = strlen(optarg); + break; case 'd': DebugVe++; break; case 'p': prefix = optarg; break; case 's': Skip = optarg; break; case 'T': n = ve_trust_add(optarg); printf("Local trust %s: %d\n", optarg, n); break; case 'V': Vflag = 1; break; default: errx(1, "unknown option: -%c", c); break; } } #ifdef VE_PCR_SUPPORT ve_pcr_updating_set(1); #endif ve_self_tests(); for ( ; optind < argc; optind++) { if (Vflag) { /* * Simulate what loader does. * verify_file should "just work" */ fd = open(argv[optind], O_RDONLY); if (fd > 0) { /* * See if verify_file is happy */ int x; - x = verify_file(fd, argv[optind], 0, VE_GUESS); + x = verify_file(fd, argv[optind], 0, VE_GUESS, __func__); printf("verify_file(%s) = %d\n", argv[optind], x); close(fd); } continue; } #ifdef VE_OPENPGP_SUPPORT if (strstr(argv[optind], "asc")) { cp = (char *)verify_asc(argv[optind], 1); if (cp) { printf("Verified: %s: %.28s...\n", argv[optind], cp); fingerprint_info_add(argv[optind], prefix, Skip, cp, NULL); } else { fprintf(stderr, "%s: %s\n", argv[optind], ve_error_get()); } } else #endif if (strstr(argv[optind], "sig")) { cp = (char *)verify_sig(argv[optind], 1); if (cp) { printf("Verified: %s: %.28s...\n", argv[optind], cp); fingerprint_info_add(argv[optind], prefix, Skip, cp, NULL); } else { fprintf(stderr, "%s: %s\n", argv[optind], ve_error_get()); } } else if (strstr(argv[optind], "manifest")) { cp = (char *)read_file(argv[optind], NULL); if (cp) { fingerprint_info_add(argv[optind], prefix, Skip, cp, NULL); } } else { fd = verify_open(argv[optind], O_RDONLY); printf("verify_open(%s) = %d %s\n", argv[optind], fd, (fd < 0) ? ve_error_get() : ""); if (fd > 0) { /* * Check that vectx_* can also verify the file. */ void *vp; char buf[BUFSIZ]; struct stat st; int error; size_t off, n; fstat(fd, &st); lseek(fd, 0, SEEK_SET); off = st.st_size % 512; vp = vectx_open(fd, argv[optind], off, - &st, &error); + &st, &error, __func__); if (!vp) { printf("vectx_open(%s) failed: %d %s\n", argv[optind], error, ve_error_get()); } else { off = vectx_lseek(vp, (st.st_size % 1024), SEEK_SET); - + /* we can seek backwards! */ + off = vectx_lseek(vp, off/2, SEEK_SET); if (off < st.st_size) { n = vectx_read(vp, buf, sizeof(buf)); if (n > 0) off += n; } off = vectx_lseek(vp, 0, SEEK_END); /* repeating that should be harmless */ off = vectx_lseek(vp, 0, SEEK_END); - error = vectx_close(vp); + error = vectx_close(vp, VE_MUST, __func__); if (error) { printf("vectx_close(%s) == %d %s\n", argv[optind], error, ve_error_get()); } else { printf("vectx_close: Verified: %s\n", argv[optind]); } } close(fd); } } } #ifdef VE_PCR_SUPPORT verify_pcr_export(); printf("pcr=%s\n", getenv("loader.ve.pcr")); #endif return (0); } Index: stable/12/lib/libsecureboot/vectx.c =================================================================== --- stable/12/lib/libsecureboot/vectx.c (revision 359734) +++ stable/12/lib/libsecureboot/vectx.c (revision 359735) @@ -1,296 +1,364 @@ /*- * Copyright (c) 2018, Juniper Networks, Inc. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ #include __FBSDID("$FreeBSD$"); #ifndef _STANDALONE /* Avoid unwanted userlandish components */ #define _KERNEL #include #undef _KERNEL #endif #include "libsecureboot-priv.h" +#include /** * @file vectx.c * @brief api to verify file while reading * * This API allows the hash of a file to be computed as it is read. * Key to this is seeking by reading. * * On close an indication of the verification result is returned. */ struct vectx { br_hash_compat_context vec_ctx; /* hash ctx */ const br_hash_class *vec_md; /* hash method */ const char *vec_path; /* path we are verifying */ const char *vec_want; /* hash value we want */ off_t vec_off; /* current offset */ + off_t vec_hashed; /* where we have hashed to */ size_t vec_size; /* size of path */ size_t vec_hashsz; /* size of hash */ int vec_fd; /* file descriptor */ int vec_status; /* verification status */ }; + /** * @brief * verify an open file as we read it * * If the file has no fingerprint to match, we will still return a * verification context containing little more than the file * descriptor, and an error code in @c error. * * @param[in] fd * open descriptor * * @param[in] path * pathname to open * * @param[in] off * current offset * * @param[in] stp * pointer to struct stat * * @param[out] error * @li 0 all is good * @li ENOMEM out of memory * @li VE_FINGERPRINT_NONE no entry found * @li VE_FINGERPRINT_UNKNOWN no fingerprint in entry * * @return ctx or NULL on error. * NULL is only returned for non-files or out-of-memory. */ struct vectx * -vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error) +vectx_open(int fd, const char *path, off_t off, struct stat *stp, + int *error, const char *caller) { struct vectx *ctx; struct stat st; size_t hashsz; char *cp; + int rc; - if (!stp) { - if (fstat(fd, &st) == 0) - stp = &st; - } + if (!stp) + stp = &st; - /* we *should* only get called for files */ - if (stp && !S_ISREG(stp->st_mode)) { - *error = 0; + rc = verify_prep(fd, path, off, stp, __func__); + + DEBUG_PRINTF(2, + ("vectx_open: caller=%s,fd=%d,name='%s',prep_rc=%d\n", + caller, fd, path, rc)); + + switch (rc) { + case VE_FINGERPRINT_NONE: + case VE_FINGERPRINT_UNKNOWN: + case VE_FINGERPRINT_WRONG: + *error = rc; return (NULL); } - ctx = malloc(sizeof(struct vectx)); if (!ctx) goto enomem; ctx->vec_fd = fd; ctx->vec_path = path; ctx->vec_size = stp->st_size; ctx->vec_off = 0; + ctx->vec_hashed = 0; ctx->vec_want = NULL; ctx->vec_status = 0; - hashsz = 0; + ctx->vec_hashsz = hashsz = 0; + if (rc == 0) { + /* we are not verifying this */ + *error = 0; + return (ctx); + } cp = fingerprint_info_lookup(fd, path); if (!cp) { ctx->vec_status = VE_FINGERPRINT_NONE; ve_error_set("%s: no entry", path); } else { if (strncmp(cp, "no_hash", 7) == 0) { ctx->vec_status = VE_FINGERPRINT_IGNORE; hashsz = 0; } else if (strncmp(cp, "sha256=", 7) == 0) { ctx->vec_md = &br_sha256_vtable; hashsz = br_sha256_SIZE; cp += 7; #ifdef VE_SHA1_SUPPORT } else if (strncmp(cp, "sha1=", 5) == 0) { ctx->vec_md = &br_sha1_vtable; hashsz = br_sha1_SIZE; cp += 5; #endif #ifdef VE_SHA384_SUPPORT } else if (strncmp(cp, "sha384=", 7) == 0) { ctx->vec_md = &br_sha384_vtable; hashsz = br_sha384_SIZE; cp += 7; #endif #ifdef VE_SHA512_SUPPORT } else if (strncmp(cp, "sha512=", 7) == 0) { ctx->vec_md = &br_sha512_vtable; hashsz = br_sha512_SIZE; cp += 7; #endif } else { ctx->vec_status = VE_FINGERPRINT_UNKNOWN; ve_error_set("%s: no supported fingerprint", path); } } *error = ctx->vec_status; ctx->vec_hashsz = hashsz; ctx->vec_want = cp; if (hashsz > 0) { ctx->vec_md->init(&ctx->vec_ctx.vtable); if (off > 0) { lseek(fd, 0, SEEK_SET); vectx_lseek(ctx, off, SEEK_SET); } } + DEBUG_PRINTF(2, + ("vectx_open: caller=%s,name='%s',hashsz=%lu,status=%d\n", + caller, path, (unsigned long)ctx->vec_hashsz, + ctx->vec_status)); return (ctx); enomem: /* unlikely */ *error = ENOMEM; free(ctx); return (NULL); } /** * @brief * read bytes from file and update hash * * It is critical that all file I/O comes through here. * We keep track of current offset. + * We also track what offset we have hashed to, + * so we won't replay data if we seek backwards. * * @param[in] pctx * pointer to ctx * * @param[in] buf * * @param[in] nbytes * * @return bytes read or error. */ ssize_t vectx_read(struct vectx *ctx, void *buf, size_t nbytes) { unsigned char *bp = buf; int n; + int delta; + int x; size_t off; if (ctx->vec_hashsz == 0) /* nothing to do */ return (read(ctx->vec_fd, buf, nbytes)); off = 0; do { n = read(ctx->vec_fd, &bp[off], nbytes - off); if (n < 0) return (n); if (n > 0) { - ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n); - off += n; - ctx->vec_off += n; + /* we may have seeked backwards! */ + delta = ctx->vec_hashed - ctx->vec_off; + if (delta > 0) { + x = MIN(delta, n); + off += x; + n -= x; + ctx->vec_off += x; + } + if (n > 0) { + ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n); + off += n; + ctx->vec_off += n; + ctx->vec_hashed += n; + } } } while (n > 0 && off < nbytes); return (off); } /** * @brief * vectx equivalent of lseek * - * We do not actually, seek, but call vectx_read + * When seeking forwards we actually call vectx_read * to reach the desired offset. * - * We do not support seeking backwards. + * We support seeking backwards. * * @param[in] pctx * pointer to ctx * * @param[in] off * desired offset * * @param[in] whence + * We try to convert whence to ``SEEK_SET``. + * We do not support ``SEEK_DATA`` or ``SEEK_HOLE``. * * @return offset or error. */ off_t vectx_lseek(struct vectx *ctx, off_t off, int whence) { unsigned char buf[PAGE_SIZE]; size_t delta; ssize_t n; if (ctx->vec_hashsz == 0) /* nothing to do */ return (lseek(ctx->vec_fd, off, whence)); /* - * Try to convert whence to SEEK_SET - * but we cannot support seeking backwards! - * Nor beyond end of file. + * Convert whence to SEEK_SET */ if (whence == SEEK_END && off <= 0) { whence = SEEK_SET; off += ctx->vec_size; - } else if (whence == SEEK_CUR && off >= 0) { + } else if (whence == SEEK_CUR) { whence = SEEK_SET; off += ctx->vec_off; } - if (whence != SEEK_SET || off < ctx->vec_off || + if (whence != SEEK_SET || (size_t)off > ctx->vec_size) { - printf("ERROR: %s: unsupported operation\n", __func__); + printf("ERROR: %s: unsupported operation: whence=%d off=%lld -> %lld\n", + __func__, whence, (long long)ctx->vec_off, (long long)off); return (-1); } + if (off < ctx->vec_hashed) { + /* seeking backwards! just do it */ + ctx->vec_off = lseek(ctx->vec_fd, off, whence); + return (ctx->vec_off); + } n = 0; do { delta = off - ctx->vec_off; if (delta > 0) { delta = MIN(PAGE_SIZE, delta); n = vectx_read(ctx, buf, delta); if (n < 0) return (n); } } while (ctx->vec_off < off && n > 0); return (ctx->vec_off); } /** * @brief * check that hashes match and cleanup * * We have finished reading file, compare the hash with what * we wanted. * + * Be sure to call this before closing the file, since we may + * need to seek to the end to ensure hashing is complete. + * * @param[in] pctx * pointer to ctx * * @return 0 or an error. */ int -vectx_close(struct vectx *ctx) +vectx_close(struct vectx *ctx, int severity, const char *caller) { int rc; if (ctx->vec_hashsz == 0) { rc = ctx->vec_status; } else { +#ifdef VE_PCR_SUPPORT + /* + * Only update pcr with things that must verify + * these tend to be processed in a more deterministic + * order, which makes our pseudo pcr more useful. + */ + ve_pcr_updating_set((severity == VE_MUST)); +#endif + /* make sure we have hashed it all */ + vectx_lseek(ctx, 0, SEEK_END); rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md, ctx->vec_path, ctx->vec_want, ctx->vec_hashsz); + } + DEBUG_PRINTF(2, + ("vectx_close: caller=%s,name='%s',rc=%d,severity=%d\n", + caller,ctx->vec_path, rc, severity)); + if (rc == VE_FINGERPRINT_WRONG) { + printf("Unverified: %s\n", ve_error_get()); +#if !defined(UNIT_TEST) && !defined(DEBUG_VECTX) + /* we are generally called with VE_MUST */ + if (severity > VE_WANT) + panic("cannot continue"); +#endif + } else if (severity > VE_WANT) { + printf("%serified %s\n", (rc <= 0) ? "Unv" : "V", + ctx->vec_path); } free(ctx); return ((rc < 0) ? rc : 0); } Index: stable/12/lib/libsecureboot/veopen.c =================================================================== --- stable/12/lib/libsecureboot/veopen.c (revision 359734) +++ stable/12/lib/libsecureboot/veopen.c (revision 359735) @@ -1,461 +1,463 @@ /*- * Copyright (c) 2017-2018, Juniper Networks, Inc. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ #include __FBSDID("$FreeBSD$"); #include #include "libsecureboot-priv.h" struct fingerprint_info { char *fi_prefix; /**< manifest entries relative to */ char *fi_skip; /**< manifest entries prefixed with */ const char *fi_data; /**< manifest data */ size_t fi_prefix_len; /**< length of prefix */ size_t fi_skip_len; /**< length of skip */ dev_t fi_dev; /**< device id */ LIST_ENTRY(fingerprint_info) entries; }; static LIST_HEAD(, fingerprint_info) fi_list; static void fingerprint_info_init(void) { static int once; if (once) return; LIST_INIT(&fi_list); once = 1; } /** * @brief * add manifest data to list * * list is kept sorted by longest prefix. * * @param[in] prefix * path that all manifest entries are resolved via * * @param[in] skip * optional prefix within manifest entries which should be skipped * * @param[in] data * manifest data */ void fingerprint_info_add(const char *filename, const char *prefix, const char *skip, const char *data, struct stat *stp) { struct fingerprint_info *fip, *nfip, *lfip; char *cp; int n; fingerprint_info_init(); nfip = malloc(sizeof(struct fingerprint_info)); if (prefix) { nfip->fi_prefix = strdup(prefix); } else { if (!filename) { free(nfip); return; } nfip->fi_prefix = strdup(filename); cp = strrchr(nfip->fi_prefix, '/'); - if (cp) + if (cp == nfip->fi_prefix) { + cp[1] = '\0'; + } else if (cp) { *cp = '\0'; - else { + } else { free(nfip->fi_prefix); free(nfip); return; } } /* collapse any trailing ..[/] */ n = 0; - while ((cp = strrchr(nfip->fi_prefix, '/')) != NULL) { + while ((cp = strrchr(nfip->fi_prefix, '/')) > nfip->fi_prefix) { if (cp[1] == '\0') { /* trailing "/" */ *cp = '\0'; continue; } if (strcmp(&cp[1], "..") == 0) { n++; *cp = '\0'; continue; } if (n > 0) { n--; *cp = '\0'; } if (n == 0) break; } #ifdef UNIT_TEST nfip->fi_dev = 0; #else nfip->fi_dev = stp->st_dev; #endif nfip->fi_data = data; nfip->fi_prefix_len = strlen(nfip->fi_prefix); if (skip) { nfip->fi_skip_len = strlen(skip); if (nfip->fi_skip_len) nfip->fi_skip = strdup(skip); else nfip->fi_skip = NULL; } else { nfip->fi_skip = NULL; nfip->fi_skip_len = 0; } if (LIST_EMPTY(&fi_list)) { LIST_INSERT_HEAD(&fi_list, nfip, entries); DEBUG_PRINTF(4, ("inserted %zu %s at head\n", nfip->fi_prefix_len, nfip->fi_prefix)); return; } LIST_FOREACH(fip, &fi_list, entries) { if (nfip->fi_prefix_len >= fip->fi_prefix_len) { LIST_INSERT_BEFORE(fip, nfip, entries); DEBUG_PRINTF(4, ("inserted %zu %s before %zu %s\n", nfip->fi_prefix_len, nfip->fi_prefix, fip->fi_prefix_len, fip->fi_prefix)); return; } lfip = fip; } LIST_INSERT_AFTER(lfip, nfip, entries); DEBUG_PRINTF(4, ("inserted %zu %s after %zu %s\n", nfip->fi_prefix_len, nfip->fi_prefix, lfip->fi_prefix_len, lfip->fi_prefix)); } #ifdef MANIFEST_SKIP_MAYBE /* * Deal with old incompatible boot/manifest * if fp[-1] is '/' and start of entry matches * MANIFEST_SKIP_MAYBE, we want it. */ static char * maybe_skip(char *fp, struct fingerprint_info *fip, size_t *nplenp) { char *tp; tp = fp - sizeof(MANIFEST_SKIP_MAYBE); if (tp >= fip->fi_data) { DEBUG_PRINTF(3, ("maybe: %.48s\n", tp)); if ((tp == fip->fi_data || tp[-1] == '\n') && strncmp(tp, MANIFEST_SKIP_MAYBE, sizeof(MANIFEST_SKIP_MAYBE) - 1) == 0) { fp = tp; *nplenp += sizeof(MANIFEST_SKIP_MAYBE); } } return (fp); } #endif char * fingerprint_info_lookup(int fd, const char *path) { char pbuf[MAXPATHLEN+1]; char nbuf[MAXPATHLEN+1]; struct stat st; struct fingerprint_info *fip; char *cp, *ep, *fp, *np; const char *prefix; size_t n, plen, nlen, nplen; dev_t dev = 0; fingerprint_info_init(); n = strlcpy(pbuf, path, sizeof(pbuf)); if (n >= sizeof(pbuf)) return (NULL); #ifndef UNIT_TEST if (fstat(fd, &st) == 0) dev = st.st_dev; #endif /* * get the first entry - it will have longest prefix * so we can can work out how to initially split path */ fip = LIST_FIRST(&fi_list); if (!fip) return (NULL); prefix = pbuf; ep = NULL; cp = &pbuf[fip->fi_prefix_len]; do { if (ep) { *ep = '/'; cp -= 2; if (cp < pbuf) break; } nlen = plen = 0; /* keep gcc quiet */ if (cp > pbuf) { for ( ; cp >= pbuf && *cp != '/'; cp--) ; /* nothing */ if (cp > pbuf) { ep = cp++; *ep = '\0'; } else { cp = pbuf; } if (ep) { plen = ep - pbuf; nlen = n - plen - 1; } } if (cp == pbuf) { prefix = "/"; plen = 1; if (*cp == '/') { nlen = n - 1; cp++; } else nlen = n; ep = NULL; } DEBUG_PRINTF(2, ("looking for %s %zu %s\n", prefix, plen, cp)); LIST_FOREACH(fip, &fi_list, entries) { DEBUG_PRINTF(4, ("at %zu %s\n", fip->fi_prefix_len, fip->fi_prefix)); if (fip->fi_prefix_len < plen) { DEBUG_PRINTF(3, ("skipping prefix=%s %zu %zu\n", fip->fi_prefix, fip->fi_prefix_len, plen)); break; } if (fip->fi_prefix_len == plen) { if (fip->fi_dev != 0 && fip->fi_dev != dev) { DEBUG_PRINTF(3, ( "skipping dev=%ld != %ld\n", (long)fip->fi_dev, (long)dev)); continue; } if (strcmp(prefix, fip->fi_prefix)) { DEBUG_PRINTF(3, ( "skipping prefix=%s\n", fip->fi_prefix)); continue; } DEBUG_PRINTF(3, ("checking prefix=%s\n", fip->fi_prefix)); if (fip->fi_skip_len) { np = nbuf; nplen = snprintf(nbuf, sizeof(nbuf), "%s/%s", fip->fi_skip, cp); nplen = MIN(nplen, sizeof(nbuf) - 1); } else { np = cp; nplen = nlen; } DEBUG_PRINTF(3, ("lookup: '%s'\n", np)); if (!(fp = strstr(fip->fi_data, np))) continue; #ifdef MANIFEST_SKIP_MAYBE if (fip->fi_skip_len == 0 && fp > fip->fi_data && fp[-1] == '/') { fp = maybe_skip(fp, fip, &nplen); } #endif /* * when we find a match: * fp[nplen] will be space and * fp will be fip->fi_data or * fp[-1] will be \n */ if (!((fp == fip->fi_data || fp[-1] == '\n') && fp[nplen] == ' ')) { do { fp++; fp = strstr(fp, np); if (fp) { #ifdef MANIFEST_SKIP_MAYBE if (fip->fi_skip_len == 0 && fp > fip->fi_data && fp[-1] == '/') { fp = maybe_skip(fp, fip, &nplen); } #endif DEBUG_PRINTF(3, ("fp[-1]=%#x fp[%zu]=%#x fp=%.78s\n", fp[-1], nplen, fp[nplen], fp)); } } while (fp != NULL && !(fp[-1] == '\n' && fp[nplen] == ' ')); if (!fp) continue; } DEBUG_PRINTF(2, ("found %.78s\n", fp)); /* we have a match! */ for (cp = &fp[nplen]; *cp == ' '; cp++) ; /* nothing */ return (cp); } else { DEBUG_PRINTF(3, ("Ignoring prefix=%s\n", fip->fi_prefix)); } } } while (cp > &pbuf[1]); return (NULL); } static int verify_fingerprint(int fd, const char *path, const char *cp, off_t off) { unsigned char buf[PAGE_SIZE]; const br_hash_class *md; br_hash_compat_context mctx; size_t hlen; int n; if (strncmp(cp, "no_hash", 7) == 0) { return (VE_FINGERPRINT_IGNORE); } else if (strncmp(cp, "sha256=", 7) == 0) { md = &br_sha256_vtable; hlen = br_sha256_SIZE; cp += 7; #ifdef VE_SHA1_SUPPORT } else if (strncmp(cp, "sha1=", 5) == 0) { md = &br_sha1_vtable; hlen = br_sha1_SIZE; cp += 5; #endif #ifdef VE_SHA384_SUPPORT } else if (strncmp(cp, "sha384=", 7) == 0) { md = &br_sha384_vtable; hlen = br_sha384_SIZE; cp += 7; #endif #ifdef VE_SHA512_SUPPORT } else if (strncmp(cp, "sha512=", 7) == 0) { md = &br_sha512_vtable; hlen = br_sha512_SIZE; cp += 7; #endif } else { ve_error_set("%s: no supported fingerprint", path); return (VE_FINGERPRINT_UNKNOWN); } md->init(&mctx.vtable); if (off) lseek(fd, 0, SEEK_SET); do { n = read(fd, buf, sizeof(buf)); if (n < 0) return (n); if (n > 0) md->update(&mctx.vtable, buf, n); } while (n > 0); lseek(fd, off, SEEK_SET); return (ve_check_hash(&mctx, md, path, cp, hlen)); } /** * @brief * verify an open file * * @param[in] fd * open descriptor * * @param[in] path * pathname to open * * @param[in] off * current offset * * @return 0, VE_FINGERPRINT_OK or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG */ int verify_fd(int fd, const char *path, off_t off, struct stat *stp) { struct stat st; char *cp; int rc; if (!stp) { if (fstat(fd, &st) == 0) stp = &st; } if (stp && !S_ISREG(stp->st_mode)) return (0); /* not relevant */ cp = fingerprint_info_lookup(fd, path); if (!cp) { ve_error_set("%s: no entry", path); return (VE_FINGERPRINT_NONE); } rc = verify_fingerprint(fd, path, cp, off); switch (rc) { case VE_FINGERPRINT_OK: case VE_FINGERPRINT_IGNORE: case VE_FINGERPRINT_UNKNOWN: return (rc); default: return (VE_FINGERPRINT_WRONG); } } /** * @brief * open a file if it can be verified * * @param[in] path * pathname to open * * @param[in] flags * flags for open * * @return fd or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG */ int verify_open(const char *path, int flags) { int fd; int rc; if ((fd = open(path, flags)) >= 0) { if ((rc = verify_fd(fd, path, 0, NULL)) < 0) { close(fd); fd = rc; } } return (fd); } Index: stable/12/lib/libsecureboot/vepcr.c =================================================================== --- stable/12/lib/libsecureboot/vepcr.c (revision 359734) +++ stable/12/lib/libsecureboot/vepcr.c (revision 359735) @@ -1,104 +1,169 @@ /*- * Copyright (c) 2018, Juniper Networks, Inc. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ #include __FBSDID("$FreeBSD$"); +#include #include "libsecureboot-priv.h" /* * To support measured boot without putting a ton * of extra code in the loader, we just maintain * a hash of all the hashes we (attempt to) verify. * The loader can export this for kernel or rc script * to feed to a TPM pcr register - hence the name ve_pcr. * * NOTE: in the current standard the TPM pcr register size is for SHA1, * the fact that we provide a SHA256 hash should not matter * as long as we are consistent - it can be truncated or hashed * before feeding to TPM. */ static const br_hash_class *pcr_md = NULL; static br_hash_compat_context pcr_ctx; static size_t pcr_hlen = 0; -static int pcr_updating; +static int pcr_updating = -1; +struct hashed_info { + const char *hi_path; + const char *hi_basename; + STAILQ_ENTRY(hashed_info) entries; +}; + +static STAILQ_HEAD(, hashed_info) hi_list; + + /** * @brief initialize pcr context * * Real TPM registers only hold a SHA1 hash * but we use SHA256 */ void ve_pcr_init(void) { - pcr_updating = 0; - pcr_hlen = br_sha256_SIZE; - pcr_md = &br_sha256_vtable; - pcr_md->init(&pcr_ctx.vtable); + if (pcr_updating < 0) { + pcr_updating = 0; + pcr_hlen = br_sha256_SIZE; + pcr_md = &br_sha256_vtable; + pcr_md->init(&pcr_ctx.vtable); + STAILQ_INIT(&hi_list); + } } /** * @brief get pcr_updating state */ int ve_pcr_updating_get(void) { return (pcr_updating); } /** * @brief set pcr_updating state */ void ve_pcr_updating_set(int updating) { pcr_updating = updating; } /** * @brief update pcr context */ void -ve_pcr_update(unsigned char *data, size_t dlen) +ve_pcr_update(const char *path, unsigned char *data, size_t dlen) { - if (pcr_updating != 0 && pcr_md != NULL) + struct hashed_info *hip; + + if (pcr_updating > 0 && pcr_md != NULL) { pcr_md->update(&pcr_ctx.vtable, data, dlen); + /* if mallocs fail, measured boot will likely fail too */ + if ((hip = malloc(sizeof(struct hashed_info)))) { + hip->hi_path = strdup(path); + if (!hip->hi_path) { + free(hip); + return; + } + hip->hi_basename = strrchr(hip->hi_path, '/'); + if (hip->hi_basename) { + hip->hi_basename++; + } else { + hip->hi_basename = hip->hi_path; + } + STAILQ_INSERT_TAIL(&hi_list, hip, entries); + } + } } /** * @brief get pcr result */ ssize_t ve_pcr_get(unsigned char *buf, size_t sz) { if (!pcr_md) return (-1); if (sz < pcr_hlen) return (-1); pcr_md->out(&pcr_ctx.vtable, buf); return (pcr_hlen); } +/** + * @brief get list of paths in prc + */ +char * +ve_pcr_hashed_get(int flags) +{ + const char *cp; + char *hinfo; + struct hashed_info *hip; + size_t nbytes; + size_t x; + int n; + + n = 0; + nbytes = x = 0; + hinfo = NULL; + STAILQ_FOREACH(hip, &hi_list, entries) { + nbytes += 1 + strlen(flags ? hip->hi_basename : hip->hi_path); + } + if (nbytes > 1) { + hinfo = malloc(nbytes + 2); + if (hinfo) { + STAILQ_FOREACH(hip, &hi_list, entries) { + cp = flags ? hip->hi_basename : hip->hi_path; + n = snprintf(&hinfo[x], nbytes - x, "%s,", cp); + x += n; + } + if (x > 0) { + hinfo[x-1] = '\0'; + } + } + } + return hinfo; +} Index: stable/12/lib/libsecureboot/verify_file.c =================================================================== --- stable/12/lib/libsecureboot/verify_file.c (revision 359734) +++ stable/12/lib/libsecureboot/verify_file.c (revision 359735) @@ -1,475 +1,540 @@ /*- * Copyright (c) 2017-2018, Juniper Networks, Inc. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ /* * Routines to verify files loaded. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include "libsecureboot.h" #include #include #ifdef UNIT_TEST # include # define panic warn /* * define MANIFEST_SKIP to Skip - in tests/tvo.c so that * tvo can control the value we use in find_manifest() */ +extern char *Destdir; +extern size_t DestdirLen; extern char *Skip; # undef MANIFEST_SKIP # define MANIFEST_SKIP Skip # undef VE_DEBUG_LEVEL #endif /* * We sometimes need to know if input is verified or not. * The extra slot is for tracking most recently opened. */ static int ve_status[SOPEN_MAX+1]; static int ve_status_state; struct verify_status; struct verify_status *verified_files = NULL; static int loaded_manifests = 0; /* have we loaded anything? */ #define VE_STATUS_NONE 1 #define VE_STATUS_VALID 2 /** * @brief set ve status for fd */ static void ve_status_set(int fd, int ves) { if (fd >= 0 && fd < SOPEN_MAX) { ve_status[fd] = ves; ve_status_state = VE_STATUS_VALID; } ve_status[SOPEN_MAX] = ves; } /** * @brief get ve status of fd * * What we return depends on ve_status_state. * * @return * @li ve_status[fd] if ve_status_state is valid * @li ve_status[SOPEN_MAX] if ve_status_state is none * @li VE_NOT_CHECKED if ve_status_state uninitialized */ int ve_status_get(int fd) { if (!ve_status_state) { return (VE_NOT_CHECKED); } if (ve_status_state == VE_STATUS_VALID && fd >= 0 && fd < SOPEN_MAX) return (ve_status[fd]); return (ve_status[SOPEN_MAX]); /* most recent */ } /** * @brief track verify status * * occasionally loader will make multiple calls * for the same file, we need only check it once. */ struct verify_status { dev_t vs_dev; ino_t vs_ino; int vs_status; struct verify_status *vs_next; }; int is_verified(struct stat *stp) { struct verify_status *vsp; - for (vsp = verified_files; vsp != NULL; vsp = vsp->vs_next) { - if (stp->st_dev == vsp->vs_dev && - stp->st_ino == vsp->vs_ino) - return (vsp->vs_status); + if (stp->st_ino > 0) { + for (vsp = verified_files; vsp != NULL; vsp = vsp->vs_next) { + if (stp->st_dev == vsp->vs_dev && + stp->st_ino == vsp->vs_ino) + return (vsp->vs_status); + } } return (VE_NOT_CHECKED); } /* most recent first, since most likely to see repeated calls. */ void add_verify_status(struct stat *stp, int status) { struct verify_status *vsp; vsp = malloc(sizeof(struct verify_status)); vsp->vs_next = verified_files; vsp->vs_dev = stp->st_dev; vsp->vs_ino = stp->st_ino; vsp->vs_status = status; verified_files = vsp; } /** * @brief * load specified manifest if verified */ int load_manifest(const char *name, const char *prefix, const char *skip, struct stat *stp) { struct stat st; size_t n; int rc; char *content; rc = VE_FINGERPRINT_NONE; n = strlen(name); if (n > 4) { if (!stp) { stp = &st; if (stat(name, &st) < 0 || !S_ISREG(st.st_mode)) return (rc); } rc = is_verified(stp); if (rc != VE_NOT_CHECKED) { return (rc); } /* loader has no sense of time */ ve_utc_set(stp->st_mtime); content = (char *)verify_signed(name, VEF_VERBOSE); if (content) { +#ifdef UNIT_TEST + if (DestdirLen > 0 && + strncmp(name, Destdir, DestdirLen) == 0) { + name += DestdirLen; + if (prefix && + strncmp(prefix, Destdir, DestdirLen) == 0) + prefix += DestdirLen; + } +#endif fingerprint_info_add(name, prefix, skip, content, stp); add_verify_status(stp, VE_VERIFIED); loaded_manifests = 1; /* we are verifying! */ DEBUG_PRINTF(3, ("loaded: %s %s %s\n", name, prefix, skip)); - rc = 0; + rc = VE_VERIFIED; } else { rc = VE_FINGERPRINT_WRONG; add_verify_status(stp, rc); /* remember */ } } return (rc); } static int find_manifest(const char *name) { struct stat st; char buf[MAXPATHLEN]; char *prefix; char *skip; const char **tp; int rc; strncpy(buf, name, MAXPATHLEN - 1); if (!(prefix = strrchr(buf, '/'))) return (-1); *prefix = '\0'; prefix = strdup(buf); rc = VE_FINGERPRINT_NONE; for (tp = manifest_names; *tp; tp++) { snprintf(buf, sizeof(buf), "%s/%s", prefix, *tp); DEBUG_PRINTF(5, ("looking for %s\n", buf)); if (stat(buf, &st) == 0 && st.st_size > 0) { #ifdef MANIFEST_SKIP_ALWAYS /* very unlikely */ skip = MANIFEST_SKIP_ALWAYS; #else #ifdef MANIFEST_SKIP /* rare */ if (*tp[0] == '.') { skip = MANIFEST_SKIP; } else #endif skip = NULL; #endif rc = load_manifest(buf, skip ? prefix : NULL, skip, &st); break; } } free(prefix); return (rc); } #ifdef LOADER_VERIEXEC_TESTING # define ACCEPT_NO_FP_DEFAULT VE_MUST + 1 #else # define ACCEPT_NO_FP_DEFAULT VE_MUST #endif #ifndef VE_VERBOSE_DEFAULT # define VE_VERBOSE_DEFAULT 0 #endif static int severity_guess(const char *filename) { const char *cp; /* Some files like *.conf and *.hints may be unsigned */ if ((cp = strrchr(filename, '.'))) { if (strcmp(cp, ".conf") == 0 || strcmp(cp, ".cookie") == 0 || strcmp(cp, ".hints") == 0) return (VE_TRY); } return (VE_WANT); } +static int Verifying = -1; /* 0 if not verifying */ + static void verify_tweak(int fd, off_t off, struct stat *stp, char *tweak, int *accept_no_fp, - int *verbose, int *verifying) + int *verbose) { if (strcmp(tweak, "off") == 0) { - *verifying = 0; + Verifying = 0; } else if (strcmp(tweak, "strict") == 0) { /* anything caller wants verified must be */ *accept_no_fp = VE_WANT; *verbose = 1; /* warn of anything unverified */ /* treat self test failure as fatal */ if (!ve_self_tests()) { panic("verify self tests failed"); } } else if (strcmp(tweak, "modules") == 0) { /* modules/kernel must be verified */ *accept_no_fp = VE_MUST; } else if (strcmp(tweak, "try") == 0) { /* best effort: always accept no fp */ *accept_no_fp = VE_MUST + 1; } else if (strcmp(tweak, "verbose") == 0) { *verbose = 1; } else if (strcmp(tweak, "quiet") == 0) { *verbose = 0; } else if (strncmp(tweak, "trust", 5) == 0) { /* content is trust anchor to add or revoke */ unsigned char *ucp; size_t num; if (off > 0) lseek(fd, 0, SEEK_SET); ucp = read_fd(fd, stp->st_size); if (ucp == NULL) return; if (strstr(tweak, "revoke")) { num = ve_trust_anchors_revoke(ucp, stp->st_size); DEBUG_PRINTF(3, ("revoked %d trust anchors\n", (int) num)); } else { num = ve_trust_anchors_add_buf(ucp, stp->st_size); DEBUG_PRINTF(3, ("added %d trust anchors\n", (int) num)); } } } #ifndef VE_DEBUG_LEVEL # define VE_DEBUG_LEVEL 0 #endif static int getenv_int(const char *var, int def) { const char *cp; char *ep; long val; val = def; cp = getenv(var); if (cp && *cp) { val = strtol(cp, &ep, 0); if ((ep && *ep) || val != (int)val) { val = def; } } return (int)val; } + /** + * @brief prepare to verify an open file + * + * @param[in] fd + * open descriptor + * + * @param[in] filename + * path we opened and will use to lookup fingerprint + * + * @param[in] stp + * stat pointer so we can check file type + */ +int +verify_prep(int fd, const char *filename, off_t off, struct stat *stp, + const char *caller) +{ + int rc; + + if (Verifying < 0) { + Verifying = ve_trust_init(); +#ifndef UNIT_TEST + ve_debug_set(getenv_int("VE_DEBUG_LEVEL", VE_DEBUG_LEVEL)); +#endif + /* initialize ve_status with default result */ + rc = Verifying ? VE_NOT_CHECKED : VE_NOT_VERIFYING; + ve_status_set(0, rc); + ve_status_state = VE_STATUS_NONE; + if (Verifying) { + ve_self_tests(); + ve_anchor_verbose_set(1); + } + } + if (!Verifying || fd < 0) + return (0); + if (stp) { + if (fstat(fd, stp) < 0 || !S_ISREG(stp->st_mode)) + return (0); + } + DEBUG_PRINTF(2, + ("verify_prep: caller=%s,fd=%d,name='%s',off=%lld,dev=%lld,ino=%lld\n", + caller, fd, filename, (long long)off, (long long)stp->st_dev, + (long long)stp->st_ino)); + rc = is_verified(stp); + DEBUG_PRINTF(4,("verify_prep: is_verified()->%d\n", rc)); + if (rc == VE_NOT_CHECKED) { + rc = find_manifest(filename); + } else { + ve_status_set(fd, rc); + } + return (rc); +} + +/** * @brief verify an open file * * @param[in] fd * open descriptor * * @param[in] filename * path we opened and will use to lookup fingerprint * * @param[in] off * current offset in fd, must be restored on return * * @param[in] severity * indicator of how to handle case of missing fingerprint * * We look for a signed manifest relative to the filename * just opened and verify/load it if needed. * * We then use verify_fd() in libve to actually verify that hash for * open file. If it returns < 0 we look at the severity arg to decide * what to do about it. * * If verify_fd() returns VE_FINGERPRINT_NONE we accept it if severity * is < accept_no_fp. * * @return >= 0 on success < 0 on failure */ int -verify_file(int fd, const char *filename, off_t off, int severity) +verify_file(int fd, const char *filename, off_t off, int severity, + const char *caller) { - static int verifying = -1; + static int once; static int accept_no_fp = ACCEPT_NO_FP_DEFAULT; static int verbose = VE_VERBOSE_DEFAULT; struct stat st; char *cp; int rc; - if (verifying < 0) { - verifying = ve_trust_init(); - verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT); - ve_debug_set(getenv_int("VE_DEBUG_LEVEL", VE_DEBUG_LEVEL)); - /* initialize ve_status with default result */ - rc = verifying ? VE_NOT_CHECKED : VE_NOT_VERIFYING; - ve_status_set(0, rc); - ve_status_state = VE_STATUS_NONE; - if (verifying) { - ve_self_tests(); - ve_anchor_verbose_set(1); - } - } - if (!verifying) - return (0); + rc = verify_prep(fd, filename, off, &st, caller); - if (fd < 0 || fstat(fd, &st) < 0 || !S_ISREG(st.st_mode)) + if (!rc) return (0); - DEBUG_PRINTF(3, ("fd=%d,name='%s',off=%lld,dev=%lld,ino=%lld\n", - fd, filename, (long long)off, (long long)st.st_dev, - (long long)st.st_ino)); - - - rc = is_verified(&st); - if (rc != VE_NOT_CHECKED) { - ve_status_set(fd, rc); - return (rc); + if (!once) { + once++; + verbose = getenv_int("VE_VERBOSE", VE_VERBOSE_DEFAULT); } - rc = find_manifest(filename); + if (rc != VE_FINGERPRINT_WRONG && loaded_manifests) { if (severity <= VE_GUESS) severity = severity_guess(filename); #ifdef VE_PCR_SUPPORT /* * Only update pcr with things that must verify * these tend to be processed in a more deterministic * order, which makes our pseudo pcr more useful. */ ve_pcr_updating_set((severity == VE_MUST)); #endif +#ifdef UNIT_TEST + if (DestdirLen > 0 && + strncmp(filename, Destdir, DestdirLen) == 0) { + filename += DestdirLen; + } +#endif if ((rc = verify_fd(fd, filename, off, &st)) >= 0) { if (verbose || severity > VE_WANT) { #if defined(VE_DEBUG_LEVEL) && VE_DEBUG_LEVEL > 0 printf("%serified %s %llu,%llu\n", (rc == VE_FINGERPRINT_IGNORE) ? "Unv" : "V", filename, (long long)st.st_dev, (long long)st.st_ino); #else printf("%serified %s\n", (rc == VE_FINGERPRINT_IGNORE) ? "Unv" : "V", filename); #endif } if (severity < VE_MUST) { /* not a kernel or module */ - if ((cp = strrchr(filename, '/'))) { cp++; if (strncmp(cp, "loader.ve.", 10) == 0) { cp += 10; verify_tweak(fd, off, &st, cp, - &accept_no_fp, &verbose, - &verifying); + &accept_no_fp, &verbose); } } } add_verify_status(&st, rc); ve_status_set(fd, rc); return (rc); } if (severity || verbose || rc == VE_FINGERPRINT_WRONG) printf("Unverified: %s\n", ve_error_get()); if (rc == VE_FINGERPRINT_UNKNOWN && severity < VE_MUST) rc = VE_UNVERIFIED_OK; else if (rc == VE_FINGERPRINT_NONE && severity < accept_no_fp) rc = VE_UNVERIFIED_OK; add_verify_status(&st, rc); } #ifdef LOADER_VERIEXEC_TESTING else if (rc != VE_FINGERPRINT_WRONG) { /* * We have not loaded any manifest and * not because of verication failure. * Most likely reason is we have none. * Allow boot to proceed if we are just testing. */ return (VE_UNVERIFIED_OK); } #endif if (rc == VE_FINGERPRINT_WRONG && severity > accept_no_fp) panic("cannot continue"); ve_status_set(fd, rc); return (rc); } /** * @brief get hex string for pcr value and export * * In case we are doing measured boot, provide * value of the "pcr" data we have accumulated. */ void verify_pcr_export(void) { #ifdef VE_PCR_SUPPORT char hexbuf[br_sha256_SIZE * 2 + 2]; unsigned char hbuf[br_sha256_SIZE]; + char *hinfo; char *hex; ssize_t hlen; hlen = ve_pcr_get(hbuf, sizeof(hbuf)); if (hlen > 0) { hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen); if (hex) { hex[hlen*2] = '\0'; /* clobber newline */ setenv("loader.ve.pcr", hex, 1); + DEBUG_PRINTF(1, + ("%s: setenv(loader.ve.pcr, %s\n", __func__, + hex)); + hinfo = ve_pcr_hashed_get(1); + if (hinfo) { + setenv("loader.ve.hashed", hinfo, 1); + DEBUG_PRINTF(1, + ("%s: setenv(loader.ve.hashed, %s\n", + __func__, hinfo)); + free(hinfo); + } } } #endif } Index: stable/12/lib/libsecureboot/vets.c =================================================================== --- stable/12/lib/libsecureboot/vets.c (revision 359734) +++ stable/12/lib/libsecureboot/vets.c (revision 359735) @@ -1,1016 +1,1023 @@ /*- * Copyright (c) 2017-2018, Juniper Networks, Inc. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ #include __FBSDID("$FreeBSD$"); /** * @file vets.c - trust store * @brief verify signatures * * We leverage code from BearSSL www.bearssl.org */ #include #include #define NEED_BRSSL_H #include "libsecureboot-priv.h" #include #include #ifndef TRUST_ANCHOR_STR # define TRUST_ANCHOR_STR ta_PEM #endif #define SECONDS_PER_DAY 86400 +#define SECONDS_PER_YEAR 365 * SECONDS_PER_DAY +#ifndef VE_UTC_MAX_JUMP +# define VE_UTC_MAX_JUMP 20 * SECONDS_PER_YEAR +#endif #define X509_DAYS_TO_UTC0 719528 int DebugVe = 0; typedef VECTOR(br_x509_certificate) cert_list; typedef VECTOR(hash_data) digest_list; static anchor_list trust_anchors = VEC_INIT; static anchor_list forbidden_anchors = VEC_INIT; static digest_list forbidden_digests = VEC_INIT; static int anchor_verbose = 0; void ve_anchor_verbose_set(int n) { anchor_verbose = n; } int ve_anchor_verbose_get(void) { return (anchor_verbose); } void ve_debug_set(int n) { DebugVe = n; } static char ebuf[512]; char * ve_error_get(void) { return (ebuf); } int ve_error_set(const char *fmt, ...) { int rc; va_list ap; va_start(ap, fmt); ebuf[0] = '\0'; rc = 0; if (fmt) { #ifdef STAND_H vsprintf(ebuf, fmt, ap); /* no vsnprintf in libstand */ ebuf[sizeof(ebuf) - 1] = '\0'; rc = strlen(ebuf); #else rc = vsnprintf(ebuf, sizeof(ebuf), fmt, ap); #endif } va_end(ap); return (rc); } /* this is the time we use for verifying certs */ static time_t ve_utc = 0; /** * @brief * set ve_utc used for certificate verification * * @param[in] utc - * time - ignored unless greater than current value. + * time - ignored unless greater than current value + * and not a leap of 20 years or more. */ void ve_utc_set(time_t utc) { - if (utc > ve_utc) { + if (utc > ve_utc && + (ve_utc == 0 || (utc - ve_utc) < VE_UTC_MAX_JUMP)) { DEBUG_PRINTF(2, ("Set ve_utc=%jd\n", (intmax_t)utc)); ve_utc = utc; } } static void free_cert_contents(br_x509_certificate *xc) { xfree(xc->data); } /* * a bit of a dance to get commonName from a certificate */ static char * x509_cn_get(br_x509_certificate *xc, char *buf, size_t len) { br_x509_minimal_context mc; br_name_element cn; unsigned char cn_oid[4]; int err; if (buf == NULL) return (buf); /* * We want the commonName field * the OID we want is 2,5,4,3 - but DER encoded */ cn_oid[0] = 3; cn_oid[1] = 0x55; cn_oid[2] = 4; cn_oid[3] = 3; cn.oid = cn_oid; cn.buf = buf; cn.len = len; cn.buf[0] = '\0'; br_x509_minimal_init(&mc, &br_sha256_vtable, NULL, 0); br_x509_minimal_set_name_elements(&mc, &cn, 1); /* the below actually does the work - updates cn.status */ mc.vtable->start_chain(&mc.vtable, NULL); mc.vtable->start_cert(&mc.vtable, xc->data_len); mc.vtable->append(&mc.vtable, xc->data, xc->data_len); mc.vtable->end_cert(&mc.vtable); /* we don' actually care about cert status - just its name */ err = mc.vtable->end_chain(&mc.vtable); if (!cn.status) buf = NULL; return (buf); } /* ASN parsing related defines */ #define ASN1_PRIMITIVE_TAG 0x1F #define ASN1_INF_LENGTH 0x80 #define ASN1_LENGTH_MASK 0x7F /* * Get TBS part of certificate. * Since BearSSL doesn't provide any API to do this, * it has to be implemented here. */ static void* X509_to_tbs(unsigned char* cert, size_t* output_size) { unsigned char *result; size_t tbs_size; int size, i; if (cert == NULL) return (NULL); /* Strip two sequences to get to the TBS section */ for (i = 0; i < 2; i++) { /* * XXX: We don't need to support extended tags since * they should not be present in certificates. */ if ((*cert & ASN1_PRIMITIVE_TAG) == ASN1_PRIMITIVE_TAG) return (NULL); cert++; if (*cert == ASN1_INF_LENGTH) return (NULL); size = *cert & ASN1_LENGTH_MASK; tbs_size = 0; /* Size can either be stored on a single or multiple bytes */ if (*cert & (ASN1_LENGTH_MASK + 1)) { cert++; while (*cert == 0 && size > 0) { cert++; size--; } while (size-- > 0) { tbs_size <<= 8; tbs_size |= *(cert++); } } if (i == 0) result = cert; } tbs_size += (cert - result); if (output_size != NULL) *output_size = tbs_size; return (result); } void ve_forbidden_digest_add(hash_data *digest, size_t num) { while (num--) VEC_ADD(forbidden_digests, digest[num]); } static size_t ve_anchors_add(br_x509_certificate *xcs, size_t num, anchor_list *anchors, const char *anchors_name) { br_x509_trust_anchor ta; size_t u; for (u = 0; u < num; u++) { if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) { break; } VEC_ADD(*anchors, ta); if (anchor_verbose && anchors_name) { char buf[64]; char *cp; cp = x509_cn_get(&xcs[u], buf, sizeof(buf)); if (cp) { printf("x509_anchor(%s) %s\n", cp, anchors_name); } } } return (u); } /** * @brief * add certs to our trust store */ size_t ve_trust_anchors_add(br_x509_certificate *xcs, size_t num) { return (ve_anchors_add(xcs, num, &trust_anchors, "trusted")); } size_t ve_forbidden_anchors_add(br_x509_certificate *xcs, size_t num) { return (ve_anchors_add(xcs, num, &forbidden_anchors, "forbidden")); } /** * @brief add trust anchors in buf * * Assume buf contains x509 certificates, but if not and * we support OpenPGP try adding as that. * * @return number of anchors added */ size_t ve_trust_anchors_add_buf(unsigned char *buf, size_t len) { br_x509_certificate *xcs; size_t num; num = 0; xcs = parse_certificates(buf, len, &num); if (xcs != NULL) { num = ve_trust_anchors_add(xcs, num); #ifdef VE_OPENPGP_SUPPORT } else { num = openpgp_trust_add_buf(buf, len); #endif } return (num); } /** * @brief revoke trust anchors in buf * * Assume buf contains x509 certificates, but if not and * we support OpenPGP try revoking keyId * * @return number of anchors revoked */ size_t ve_trust_anchors_revoke(unsigned char *buf, size_t len) { br_x509_certificate *xcs; size_t num; num = 0; xcs = parse_certificates(buf, len, &num); if (xcs != NULL) { num = ve_forbidden_anchors_add(xcs, num); #ifdef VE_OPENPGP_SUPPORT } else { if (buf[len - 1] == '\n') buf[len - 1] = '\0'; num = openpgp_trust_revoke((char *)buf); #endif } return (num); } /** * @brief * initialize our trust_anchors from ta_PEM */ int ve_trust_init(void) { static int once = -1; if (once >= 0) return (once); - - ve_utc_set(time(NULL)); + once = 0; /* to be sure */ #ifdef BUILD_UTC - ve_utc_set(BUILD_UTC); /* just in case */ + ve_utc_set(BUILD_UTC); /* ensure sanity */ #endif + ve_utc_set(time(NULL)); ve_error_set(NULL); /* make sure it is empty */ #ifdef VE_PCR_SUPPORT ve_pcr_init(); #endif #ifdef TRUST_ANCHOR_STR ve_trust_anchors_add_buf(__DECONST(unsigned char *, TRUST_ANCHOR_STR), sizeof(TRUST_ANCHOR_STR)); #endif once = (int) VEC_LEN(trust_anchors); #ifdef VE_OPENPGP_SUPPORT once += openpgp_trust_init(); #endif return (once); } /** * if we can verify the certificate chain in "certs", * return the public key and if "xcp" is !NULL the associated * certificate */ static br_x509_pkey * verify_signer_xcs(br_x509_certificate *xcs, size_t num, br_name_element *elts, size_t num_elts, anchor_list *anchors) { br_x509_minimal_context mc; br_x509_certificate *xc; size_t u; cert_list chain = VEC_INIT; const br_x509_pkey *tpk; br_x509_pkey *pk; unsigned int usages; int err; DEBUG_PRINTF(5, ("verify_signer: %zu certs in chain\n", num)); VEC_ADDMANY(chain, xcs, num); if (VEC_LEN(chain) == 0) { ve_error_set("ERROR: no/invalid certificate chain\n"); return (NULL); } DEBUG_PRINTF(5, ("verify_signer: %zu trust anchors\n", VEC_LEN(*anchors))); br_x509_minimal_init(&mc, &br_sha256_vtable, &VEC_ELT(*anchors, 0), VEC_LEN(*anchors)); #ifdef VE_ECDSA_SUPPORT br_x509_minimal_set_ecdsa(&mc, &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1); #endif #ifdef VE_RSA_SUPPORT br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy); #endif #if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT) /* This is deprecated! do not enable unless you absoultely have to */ br_x509_minimal_set_hash(&mc, br_sha1_ID, &br_sha1_vtable); #endif br_x509_minimal_set_hash(&mc, br_sha256_ID, &br_sha256_vtable); #ifdef VE_SHA384_SUPPORT br_x509_minimal_set_hash(&mc, br_sha384_ID, &br_sha384_vtable); #endif #ifdef VE_SHA512_SUPPORT br_x509_minimal_set_hash(&mc, br_sha512_ID, &br_sha512_vtable); #endif br_x509_minimal_set_name_elements(&mc, elts, num_elts); #ifdef _STANDALONE /* * Clock is probably bogus so we use ve_utc. */ mc.days = (ve_utc / SECONDS_PER_DAY) + X509_DAYS_TO_UTC0; mc.seconds = (ve_utc % SECONDS_PER_DAY); #endif mc.vtable->start_chain(&mc.vtable, NULL); for (u = 0; u < VEC_LEN(chain); u ++) { xc = &VEC_ELT(chain, u); mc.vtable->start_cert(&mc.vtable, xc->data_len); mc.vtable->append(&mc.vtable, xc->data, xc->data_len); mc.vtable->end_cert(&mc.vtable); switch (mc.err) { case 0: case BR_ERR_X509_OK: case BR_ERR_X509_EXPIRED: break; default: printf("u=%zu mc.err=%d\n", u, mc.err); break; } } err = mc.vtable->end_chain(&mc.vtable); pk = NULL; if (err) { ve_error_set("Validation failed, err = %d", err); } else { tpk = mc.vtable->get_pkey(&mc.vtable, &usages); if (tpk != NULL) { pk = xpkeydup(tpk); } } VEC_CLEAR(chain); return (pk); } /* * Check if digest of one of the certificates from verified chain * is present in the forbidden database. * Since UEFI allows to store three types of digests * all of them have to be checked separately. */ static int check_forbidden_digests(br_x509_certificate *xcs, size_t num) { unsigned char sha256_digest[br_sha256_SIZE]; unsigned char sha384_digest[br_sha384_SIZE]; unsigned char sha512_digest[br_sha512_SIZE]; void *tbs; hash_data *digest; br_hash_compat_context ctx; const br_hash_class *md; size_t tbs_len, i; int have_sha256, have_sha384, have_sha512; if (VEC_LEN(forbidden_digests) == 0) return (0); /* * Iterate through certificates, extract their To-Be-Signed section, * and compare its digest against the ones in the forbidden database. */ while (num--) { tbs = X509_to_tbs(xcs[num].data, &tbs_len); if (tbs == NULL) { printf("Failed to obtain TBS part of certificate\n"); return (1); } have_sha256 = have_sha384 = have_sha512 = 0; for (i = 0; i < VEC_LEN(forbidden_digests); i++) { digest = &VEC_ELT(forbidden_digests, i); switch (digest->hash_size) { case br_sha256_SIZE: if (!have_sha256) { have_sha256 = 1; md = &br_sha256_vtable; md->init(&ctx.vtable); md->update(&ctx.vtable, tbs, tbs_len); md->out(&ctx.vtable, sha256_digest); } if (!memcmp(sha256_digest, digest->data, br_sha256_SIZE)) return (1); break; case br_sha384_SIZE: if (!have_sha384) { have_sha384 = 1; md = &br_sha384_vtable; md->init(&ctx.vtable); md->update(&ctx.vtable, tbs, tbs_len); md->out(&ctx.vtable, sha384_digest); } if (!memcmp(sha384_digest, digest->data, br_sha384_SIZE)) return (1); break; case br_sha512_SIZE: if (!have_sha512) { have_sha512 = 1; md = &br_sha512_vtable; md->init(&ctx.vtable); md->update(&ctx.vtable, tbs, tbs_len); md->out(&ctx.vtable, sha512_digest); } if (!memcmp(sha512_digest, digest->data, br_sha512_SIZE)) return (1); break; } } } return (0); } static br_x509_pkey * verify_signer(const char *certs, br_name_element *elts, size_t num_elts) { br_x509_certificate *xcs; br_x509_pkey *pk; size_t num; pk = NULL; ve_trust_init(); xcs = read_certificates(certs, &num); if (xcs == NULL) { ve_error_set("cannot read certificates\n"); return (NULL); } /* * Check if either * 1. There is a direct match between cert from forbidden_anchors * and a cert from chain. * 2. CA that signed the chain is found in forbidden_anchors. */ if (VEC_LEN(forbidden_anchors) > 0) pk = verify_signer_xcs(xcs, num, elts, num_elts, &forbidden_anchors); if (pk != NULL) { ve_error_set("Certificate is on forbidden list\n"); xfreepkey(pk); pk = NULL; goto out; } pk = verify_signer_xcs(xcs, num, elts, num_elts, &trust_anchors); if (pk == NULL) goto out; /* * Check if hash of tbs part of any certificate in chain * is on the forbidden list. */ if (check_forbidden_digests(xcs, num)) { ve_error_set("Certificate hash is on forbidden list\n"); xfreepkey(pk); pk = NULL; } out: free_certificates(xcs, num); return (pk); } /** * we need a hex digest including trailing newline below */ char * hexdigest(char *buf, size_t bufsz, unsigned char *foo, size_t foo_len) { char const hex2ascii[] = "0123456789abcdef"; size_t i; /* every binary byte is 2 chars in hex + newline + null */ if (bufsz < (2 * foo_len) + 2) return (NULL); for (i = 0; i < foo_len; i++) { buf[i * 2] = hex2ascii[foo[i] >> 4]; buf[i * 2 + 1] = hex2ascii[foo[i] & 0x0f]; } buf[i * 2] = 0x0A; /* we also want a newline */ buf[i * 2 + 1] = '\0'; return (buf); } /** * @brief * verify file against sigfile using pk * * When we generated the signature in sigfile, * we hashed (sha256) file, and sent that to signing server * which hashed (sha256) that hash. * * To verify we need to replicate that result. * * @param[in] pk * br_x509_pkey * * @paramp[in] file * file to be verified * * @param[in] sigfile * signature (PEM encoded) * * @return NULL on error, otherwise content of file. */ #ifdef VE_ECDSA_SUPPORT static unsigned char * verify_ec(br_x509_pkey *pk, const char *file, const char *sigfile) { - char hexbuf[br_sha512_SIZE * 2 + 2]; +#ifdef VE_ECDSA_HASH_AGAIN + char *hex, hexbuf[br_sha512_SIZE * 2 + 2]; +#endif unsigned char rhbuf[br_sha512_SIZE]; - char *hex; br_sha256_context ctx; unsigned char *fcp, *scp; size_t flen, slen, plen; pem_object *po; const br_ec_impl *ec; br_ecdsa_vrfy vrfy; if ((fcp = read_file(file, &flen)) == NULL) return (NULL); if ((scp = read_file(sigfile, &slen)) == NULL) { free(fcp); return (NULL); } if ((po = decode_pem(scp, slen, &plen)) == NULL) { free(fcp); free(scp); return (NULL); } br_sha256_init(&ctx); br_sha256_update(&ctx, fcp, flen); br_sha256_out(&ctx, rhbuf); #ifdef VE_ECDSA_HASH_AGAIN hex = hexdigest(hexbuf, sizeof(hexbuf), rhbuf, br_sha256_SIZE); /* now hash that */ if (hex) { br_sha256_init(&ctx); br_sha256_update(&ctx, hex, strlen(hex)); br_sha256_out(&ctx, rhbuf); } #endif ec = br_ec_get_default(); vrfy = br_ecdsa_vrfy_asn1_get_default(); if (!vrfy(ec, rhbuf, br_sha256_SIZE, &pk->key.ec, po->data, po->data_len)) { free(fcp); fcp = NULL; } free(scp); return (fcp); } #endif #if defined(VE_RSA_SUPPORT) || defined(VE_OPENPGP_SUPPORT) /** * @brief verify an rsa digest * * @return 0 on failure */ int verify_rsa_digest (br_rsa_public_key *pkey, const unsigned char *hash_oid, unsigned char *mdata, size_t mlen, unsigned char *sdata, size_t slen) { br_rsa_pkcs1_vrfy vrfy; unsigned char vhbuf[br_sha512_SIZE]; vrfy = br_rsa_pkcs1_vrfy_get_default(); if (!vrfy(sdata, slen, hash_oid, mlen, pkey, vhbuf) || memcmp(vhbuf, mdata, mlen) != 0) { return (0); /* fail */ } return (1); /* ok */ } #endif /** * @brief * verify file against sigfile using pk * * When we generated the signature in sigfile, * we hashed (sha256) file, and sent that to signing server * which hashed (sha256) that hash. * * Or (deprecated) we simply used sha1 hash directly. * * To verify we need to replicate that result. * * @param[in] pk * br_x509_pkey * * @paramp[in] file * file to be verified * * @param[in] sigfile * signature (PEM encoded) * * @return NULL on error, otherwise content of file. */ #ifdef VE_RSA_SUPPORT static unsigned char * verify_rsa(br_x509_pkey *pk, const char *file, const char *sigfile) { unsigned char rhbuf[br_sha512_SIZE]; const unsigned char *hash_oid; const br_hash_class *md; br_hash_compat_context mctx; unsigned char *fcp, *scp; size_t flen, slen, plen, hlen; pem_object *po; if ((fcp = read_file(file, &flen)) == NULL) return (NULL); if ((scp = read_file(sigfile, &slen)) == NULL) { free(fcp); return (NULL); } if ((po = decode_pem(scp, slen, &plen)) == NULL) { free(fcp); free(scp); return (NULL); } switch (po->data_len) { #if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT) case 256: // this is our old deprecated sig method md = &br_sha1_vtable; hlen = br_sha1_SIZE; hash_oid = BR_HASH_OID_SHA1; break; #endif default: md = &br_sha256_vtable; hlen = br_sha256_SIZE; hash_oid = BR_HASH_OID_SHA256; break; } md->init(&mctx.vtable); md->update(&mctx.vtable, fcp, flen); md->out(&mctx.vtable, rhbuf); if (!verify_rsa_digest(&pk->key.rsa, hash_oid, rhbuf, hlen, po->data, po->data_len)) { free(fcp); fcp = NULL; } free(scp); return (fcp); } #endif /** * @brief * verify a signature and return content of signed file * * @param[in] sigfile * file containing signature * we derrive path of signed file and certificate change from * this. * * @param[in] flags * only bit 1 significant so far * * @return NULL on error otherwise content of signed file */ unsigned char * verify_sig(const char *sigfile, int flags) { br_x509_pkey *pk; br_name_element cn; char cn_buf[80]; unsigned char cn_oid[4]; char pbuf[MAXPATHLEN]; char *cp; unsigned char *ucp; size_t n; DEBUG_PRINTF(5, ("verify_sig: %s\n", sigfile)); n = strlcpy(pbuf, sigfile, sizeof(pbuf)); if (n > (sizeof(pbuf) - 5) || strcmp(&sigfile[n - 3], "sig") != 0) return (NULL); cp = strcpy(&pbuf[n - 3], "certs"); /* * We want the commonName field * the OID we want is 2,5,4,3 - but DER encoded */ cn_oid[0] = 3; cn_oid[1] = 0x55; cn_oid[2] = 4; cn_oid[3] = 3; cn.oid = cn_oid; cn.buf = cn_buf; cn.len = sizeof(cn_buf); pk = verify_signer(pbuf, &cn, 1); if (!pk) { printf("cannot verify: %s: %s\n", pbuf, ve_error_get()); return (NULL); } for (; cp > pbuf; cp--) { if (*cp == '.') { *cp = '\0'; break; } } switch (pk->key_type) { #ifdef VE_ECDSA_SUPPORT case BR_KEYTYPE_EC: ucp = verify_ec(pk, pbuf, sigfile); break; #endif #ifdef VE_RSA_SUPPORT case BR_KEYTYPE_RSA: ucp = verify_rsa(pk, pbuf, sigfile); break; #endif default: ucp = NULL; /* not supported */ } xfreepkey(pk); if (!ucp) { printf("Unverified %s (%s)\n", pbuf, cn.status ? cn_buf : "unknown"); } else if ((flags & 1) != 0) { printf("Verified %s signed by %s\n", pbuf, cn.status ? cn_buf : "someone we trust"); } return (ucp); } /** * @brief verify hash matches * * We have finished hashing a file, * see if we got the desired result. * * @param[in] ctx * pointer to hash context * * @param[in] md * pointer to hash class * * @param[in] path * name of the file we are checking * * @param[in] want * the expected result * * @param[in] hlen * size of hash output * * @return 0 on success */ int ve_check_hash(br_hash_compat_context *ctx, const br_hash_class *md, const char *path, const char *want, size_t hlen) { char hexbuf[br_sha512_SIZE * 2 + 2]; unsigned char hbuf[br_sha512_SIZE]; char *hex; int rc; int n; md->out(&ctx->vtable, hbuf); #ifdef VE_PCR_SUPPORT - ve_pcr_update(hbuf, hlen); + ve_pcr_update(path, hbuf, hlen); #endif hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen); if (!hex) return (VE_FINGERPRINT_WRONG); n = 2*hlen; if ((rc = strncmp(hex, want, n))) { ve_error_set("%s: %.*s != %.*s", path, n, hex, n, want); rc = VE_FINGERPRINT_WRONG; } return (rc ? rc : VE_FINGERPRINT_OK); } #ifdef VE_HASH_KAT_STR static int test_hash(const br_hash_class *md, size_t hlen, const char *hname, const char *s, size_t slen, const char *want) { br_hash_compat_context mctx; md->init(&mctx.vtable); md->update(&mctx.vtable, s, slen); return (ve_check_hash(&mctx, md, hname, want, hlen) != VE_FINGERPRINT_OK); } #endif #define ve_test_hash(n, N) \ printf("Testing hash: " #n "\t\t\t\t%s\n", \ test_hash(&br_ ## n ## _vtable, br_ ## n ## _SIZE, #n, \ VE_HASH_KAT_STR, VE_HASH_KAT_STRLEN(VE_HASH_KAT_STR), \ vh_ ## N) ? "Failed" : "Passed") /** * @brief * run self tests on hash and signature verification * * Test that the hash methods (SHA1 and SHA256) work. * Test that we can verify a certificate for each supported * Root CA. * * @return cached result. */ int ve_self_tests(void) { static int once = -1; #ifdef VERIFY_CERTS_STR br_x509_certificate *xcs; br_x509_pkey *pk; br_name_element cn; char cn_buf[80]; unsigned char cn_oid[4]; size_t num; size_t u; #endif if (once >= 0) return (once); once = 0; DEBUG_PRINTF(5, ("Self tests...\n")); #ifdef VE_HASH_KAT_STR #ifdef VE_SHA1_SUPPORT ve_test_hash(sha1, SHA1); #endif #ifdef VE_SHA256_SUPPORT ve_test_hash(sha256, SHA256); #endif #ifdef VE_SHA384_SUPPORT ve_test_hash(sha384, SHA384); #endif #ifdef VE_SHA512_SUPPORT ve_test_hash(sha512, SHA512); #endif #endif #ifdef VERIFY_CERTS_STR xcs = parse_certificates(__DECONST(unsigned char *, VERIFY_CERTS_STR), sizeof(VERIFY_CERTS_STR), &num); if (xcs != NULL) { /* * We want the commonName field * the OID we want is 2,5,4,3 - but DER encoded */ cn_oid[0] = 3; cn_oid[1] = 0x55; cn_oid[2] = 4; cn_oid[3] = 3; cn.oid = cn_oid; cn.buf = cn_buf; for (u = 0; u < num; u ++) { cn.len = sizeof(cn_buf); if ((pk = verify_signer_xcs(&xcs[u], 1, &cn, 1, &trust_anchors)) != NULL) { free_cert_contents(&xcs[u]); once++; printf("Testing verify certificate: %s\tPassed\n", cn.status ? cn_buf : ""); xfreepkey(pk); } } if (!once) printf("Testing verify certificate:\t\t\tFailed\n"); xfree(xcs); } #endif /* VERIFY_CERTS_STR */ #ifdef VE_OPENPGP_SUPPORT if (!openpgp_self_tests()) once++; #endif return (once); } Index: stable/12/share/mk/src.opts.mk =================================================================== --- stable/12/share/mk/src.opts.mk (revision 359734) +++ stable/12/share/mk/src.opts.mk (revision 359735) @@ -1,601 +1,602 @@ # $FreeBSD$ # # Option file for FreeBSD /usr/src builds. # # Users define WITH_FOO and WITHOUT_FOO on the command line or in /etc/src.conf # and /etc/make.conf files. These translate in the build system to MK_FOO={yes,no} # with sensible (usually) defaults. # # Makefiles must include bsd.opts.mk after defining specific MK_FOO options that # are applicable for that Makefile (typically there are none, but sometimes there # are exceptions). Recursive makes usually add MK_FOO=no for options that they wish # to omit from that make. # # Makefiles must include bsd.mkopt.mk before they test the value of any MK_FOO # variable. # # Makefiles may also assume that this file is included by src.opts.mk should it # need variables defined there prior to the end of the Makefile where # bsd.{subdir,lib.bin}.mk is traditionally included. # # The old-style YES_FOO and NO_FOO are being phased out. No new instances of them # should be added. Old instances should be removed since they were just to # bridge the gap between FreeBSD 4 and FreeBSD 5. # # Makefiles should never test WITH_FOO or WITHOUT_FOO directly (although an # exception is made for _WITHOUT_SRCONF which turns off this mechanism # completely inside bsd.*.mk files). # .if !target(____) ____: .include # # Define MK_* variables (which are either "yes" or "no") for users # to set via WITH_*/WITHOUT_* in /etc/src.conf and override in the # make(1) environment. # These should be tested with `== "no"' or `!= "no"' in makefiles. # The NO_* variables should only be set by makefiles for variables # that haven't been converted over. # # These options are used by the src builds. Those listed in # __DEFAULT_YES_OPTIONS default to 'yes' and will build unless turned # off. __DEFAULT_NO_OPTIONS will default to 'no' and won't build # unless turned on. Any options listed in 'BROKEN_OPTIONS' will be # hard-wired to 'no'. "Broken" here means not working or # not-appropriate and/or not supported. It doesn't imply something is # wrong with the code. There's not a single good word for this, so # BROKEN was selected as the least imperfect one considered at the # time. Options are added to BROKEN_OPTIONS list on a per-arch basis. # At this time, there's no provision for mutually incompatible options. __DEFAULT_YES_OPTIONS = \ ACCT \ ACPI \ AMD \ APM \ AT \ ATM \ AUDIT \ AUTHPF \ AUTOFS \ BHYVE \ BINUTILS \ BINUTILS_BOOTSTRAP \ BLACKLIST \ BLUETOOTH \ BOOT \ BOOTPARAMD \ BOOTPD \ BSD_CPIO \ BSDINSTALL \ BSNMP \ BZIP2 \ CALENDAR \ CAPSICUM \ CAROOT \ CASPER \ CCD \ CDDL \ CPP \ CROSS_COMPILER \ CRYPT \ CTM \ CUSE \ CXX \ DIALOG \ DICT \ DMAGENT \ DYNAMICROOT \ ED_CRYPTO \ EE \ EFI \ ELFTOOLCHAIN_BOOTSTRAP \ EXAMPLES \ FDT \ FILE \ FINGER \ FLOPPY \ FMTREE \ FORTH \ FP_LIBC \ FREEBSD_UPDATE \ FTP \ GAMES \ GCOV \ GDB \ GNU_DIFF \ GNU_GREP \ GOOGLETEST \ GPIO \ HAST \ HTML \ ICONV \ INET \ INET6 \ INETD \ IPFILTER \ IPFW \ ISCSI \ JAIL \ KDUMP \ KVM \ LDNS \ LDNS_UTILS \ LEGACY_CONSOLE \ LIB32 \ LIBPTHREAD \ LIBTHR \ LLVM_COV \ LLVM_TARGET_ALL \ LOADER_GELI \ LOADER_LUA \ LOADER_OFW \ LOADER_UBOOT \ LOCALES \ LOCATE \ LPR \ LS_COLORS \ LZMA_SUPPORT \ MAIL \ MAILWRAPPER \ MAKE \ NDIS \ NETCAT \ NETGRAPH \ NLS_CATALOGS \ NS_CACHING \ NTP \ OFED \ OPENSSL \ PAM \ PC_SYSINSTALL \ PF \ PKGBOOTSTRAP \ PMC \ PORTSNAP \ PPP \ QUOTAS \ RADIUS_SUPPORT \ RBOOTD \ REPRODUCIBLE_BUILD \ RESCUE \ ROUTED \ SENDMAIL \ SERVICESDB \ SETUID_LOGIN \ SHAREDOCS \ SOURCELESS \ SOURCELESS_HOST \ SOURCELESS_UCODE \ SVNLITE \ SYSCONS \ SYSTEM_COMPILER \ SYSTEM_LINKER \ TALK \ TCP_WRAPPERS \ TCSH \ TELNET \ TEXTPROC \ TFTP \ TIMED \ UNBOUND \ USB \ UTMPX \ VI \ VT \ WIRELESS \ WPA_SUPPLICANT_EAPOL \ ZFS \ LOADER_ZFS \ ZONEINFO __DEFAULT_NO_OPTIONS = \ BEARSSL \ BSD_CRTBEGIN \ BSD_GREP \ CLANG_EXTRAS \ DTRACE_TESTS \ GNU_GREP_COMPAT \ HESIOD \ LIBSOFT \ LOADER_FIREWIRE \ LOADER_FORCE_LE \ LOADER_VERIEXEC_PASS_MANIFEST \ NAND \ OFED_EXTRA \ OPENLDAP \ RPCBIND_WARMSTART_SUPPORT \ SHARED_TOOLCHAIN \ SORT_THREADS \ SVN \ ZONEINFO_LEAPSECONDS_SUPPORT \ ZONEINFO_OLD_TIMEZONES_SUPPORT \ # LEFT/RIGHT. Left options which default to "yes" unless their corresponding # RIGHT option is disabled. __DEFAULT_DEPENDENT_OPTIONS= \ CLANG_FULL/CLANG \ LOADER_VERIEXEC/BEARSSL \ LOADER_EFI_SECUREBOOT/LOADER_VERIEXEC \ + LOADER_VERIEXEC_VECTX/LOADER_VERIEXEC \ VERIEXEC/BEARSSL \ # MK_*_SUPPORT options which default to "yes" unless their corresponding # MK_* variable is set to "no". # .for var in \ BLACKLIST \ BZIP2 \ INET \ INET6 \ KERBEROS \ KVM \ NETGRAPH \ PAM \ TESTS \ WIRELESS __DEFAULT_DEPENDENT_OPTIONS+= ${var}_SUPPORT/${var} .endfor # # Default behaviour of some options depends on the architecture. Unfortunately # this means that we have to test TARGET_ARCH (the buildworld case) as well # as MACHINE_ARCH (the non-buildworld case). Normally TARGET_ARCH is not # used at all in bsd.*.mk, but we have to make an exception here if we want # to allow defaults for some things like clang to vary by target architecture. # Additional, per-target behavior should be rarely added only after much # gnashing of teeth and grinding of gears. # .if defined(TARGET_ARCH) __T=${TARGET_ARCH} .else __T=${MACHINE_ARCH} .endif .if defined(TARGET) __TT=${TARGET} .else __TT=${MACHINE} .endif # All supported backends for LLVM_TARGET_XXX __LLVM_TARGETS= \ aarch64 \ arm \ mips \ powerpc \ sparc \ x86 __LLVM_TARGET_FILT= C/(amd64|i386)/x86/:S/sparc64/sparc/:S/arm64/aarch64/ .for __llt in ${__LLVM_TARGETS} # Default enable the given TARGET's LLVM_TARGET support .if ${__TT:${__LLVM_TARGET_FILT}} == ${__llt} __DEFAULT_YES_OPTIONS+= LLVM_TARGET_${__llt:${__LLVM_TARGET_FILT}:tu} # Disable other targets for arm and armv6, to work around "relocation truncated # to fit" errors with BFD ld, since libllvm.a will get too large to link. .elif ${__T} == "arm" || ${__T} == "armv6" __DEFAULT_NO_OPTIONS+=LLVM_TARGET_${__llt:tu} # aarch64 needs arm for -m32 support. .elif ${__TT} == "arm64" && ${__llt} == "arm" __DEFAULT_DEPENDENT_OPTIONS+= LLVM_TARGET_ARM/LLVM_TARGET_AARCH64 # Default the rest of the LLVM_TARGETs to the value of MK_LLVM_TARGET_ALL. .else __DEFAULT_DEPENDENT_OPTIONS+= LLVM_TARGET_${__llt:${__LLVM_TARGET_FILT}:tu}/LLVM_TARGET_ALL .endif .endfor __DEFAULT_NO_OPTIONS+=LLVM_TARGET_BPF __DEFAULT_NO_OPTIONS+=LLVM_TARGET_RISCV .include # If the compiler is not C++11 capable, disable Clang and use GCC instead. # This means that architectures that have GCC 4.2 as default can not # build Clang without using an external compiler. .if ${COMPILER_FEATURES:Mc++11} && (${__T} == "aarch64" || \ ${__T} == "amd64" || ${__TT} == "arm" || ${__T} == "i386") # Clang is enabled, and will be installed as the default /usr/bin/cc. __DEFAULT_YES_OPTIONS+=CLANG CLANG_BOOTSTRAP CLANG_IS_CC LLD __DEFAULT_NO_OPTIONS+=GCC GCC_BOOTSTRAP GNUCXX GPL_DTC .elif ${COMPILER_FEATURES:Mc++11} && ${__T:Mriscv*} == "" && ${__T} != "sparc64" # If an external compiler that supports C++11 is used as ${CC} and Clang # supports the target, then Clang is enabled but GCC is installed as the # default /usr/bin/cc. __DEFAULT_YES_OPTIONS+=CLANG GCC GCC_BOOTSTRAP GNUCXX GPL_DTC LLD __DEFAULT_NO_OPTIONS+=CLANG_BOOTSTRAP CLANG_IS_CC .else # Everything else disables Clang, and uses GCC instead. __DEFAULT_YES_OPTIONS+=GCC GCC_BOOTSTRAP GNUCXX GPL_DTC __DEFAULT_NO_OPTIONS+=CLANG CLANG_BOOTSTRAP CLANG_IS_CC LLD .endif # In-tree binutils/gcc are older versions without modern architecture support. .if ${__T} == "aarch64" || ${__T:Mriscv*} != "" BROKEN_OPTIONS+=BINUTILS BINUTILS_BOOTSTRAP GCC GCC_BOOTSTRAP GDB .endif .if ${__T:Mriscv*} != "" BROKEN_OPTIONS+=OFED .endif .if ${__T} == "aarch64" || ${__T} == "amd64" || ${__T} == "i386" || \ ${__T:Mriscv*} != "" || ${__TT} == "mips" __DEFAULT_YES_OPTIONS+=LLVM_LIBUNWIND .else __DEFAULT_NO_OPTIONS+=LLVM_LIBUNWIND .endif .if ${__T} == "aarch64" || ${__T} == "amd64" || ${__T} == "armv7" || \ ${__T} == "i386" __DEFAULT_YES_OPTIONS+=LLD_BOOTSTRAP LLD_IS_LD .else __DEFAULT_NO_OPTIONS+=LLD_BOOTSTRAP LLD_IS_LD .endif .if ${__T} == "aarch64" || ${__T} == "amd64" || ${__T} == "i386" __DEFAULT_YES_OPTIONS+=LLDB .else __DEFAULT_NO_OPTIONS+=LLDB .endif # LLVM lacks support for FreeBSD 64-bit atomic operations for ARMv4/ARMv5 .if ${__T} == "arm" BROKEN_OPTIONS+=LLDB .endif # GDB in base is generally less functional than GDB in ports. Ports GDB # sparc64 kernel support has not been tested. .if ${__T} == "sparc64" __DEFAULT_NO_OPTIONS+=GDB_LIBEXEC .else __DEFAULT_YES_OPTIONS+=GDB_LIBEXEC .endif # Only doing soft float API stuff on armv6 and armv7 .if ${__T} != "armv6" && ${__T} != "armv7" BROKEN_OPTIONS+=LIBSOFT .endif .if ${__T:Mmips*} BROKEN_OPTIONS+=SSP .endif # EFI doesn't exist on mips, powerpc, sparc or riscv. .if ${__T:Mmips*} || ${__T:Mpowerpc*} || ${__T:Msparc64} || ${__T:Mriscv*} BROKEN_OPTIONS+=EFI .endif # OFW is only for powerpc and sparc64, exclude others .if ${__T:Mpowerpc*} == "" && ${__T:Msparc64} == "" BROKEN_OPTIONS+=LOADER_OFW .endif # UBOOT is only for arm, mips and powerpc, exclude others .if ${__T:Marm*} == "" && ${__T:Mmips*} == "" && ${__T:Mpowerpc*} == "" BROKEN_OPTIONS+=LOADER_UBOOT .endif # GELI and Lua in loader currently cause boot failures on sparc64 and powerpc. # Further debugging is required -- probably they are just broken on big # endian systems generically (they jump to null pointers or try to read # crazy high addresses, which is typical of endianness problems). .if ${__T} == "sparc64" || ${__T:Mpowerpc*} BROKEN_OPTIONS+=LOADER_GELI LOADER_LUA .endif .if ${__T:Mmips64*} # profiling won't work on MIPS64 because there is only assembly for o32 BROKEN_OPTIONS+=PROFILE .endif .if ${__T} == "aarch64" || ${__T} == "amd64" || ${__T} == "i386" || \ ${__T} == "powerpc64" || ${__T} == "sparc64" __DEFAULT_YES_OPTIONS+=CXGBETOOL __DEFAULT_YES_OPTIONS+=MLX5TOOL .else __DEFAULT_NO_OPTIONS+=CXGBETOOL __DEFAULT_NO_OPTIONS+=MLX5TOOL .endif # HyperV is currently x86-only .if ${__T} == "amd64" || ${__T} == "i386" __DEFAULT_YES_OPTIONS+=HYPERV .else __DEFAULT_NO_OPTIONS+=HYPERV .endif # NVME is only x86 and powerpc64 .if ${__T} == "amd64" || ${__T} == "i386" || ${__T} == "powerpc64" __DEFAULT_YES_OPTIONS+=NVME .else __DEFAULT_NO_OPTIONS+=NVME .endif # Sparc64 need extra crt*.o files .if ${__T:Msparc64} BROKEN_OPTIONS+=BSD_CRTBEGIN .endif .if ${COMPILER_FEATURES:Mc++11} && \ (${__T} == "amd64" || ${__T} == "i386" || ${__T} == "powerpc64") __DEFAULT_YES_OPTIONS+=OPENMP .else __DEFAULT_NO_OPTIONS+=OPENMP .endif .include # # MK_* options that default to "yes" if the compiler is a C++11 compiler. # .for var in \ LIBCPLUSPLUS .if !defined(MK_${var}) .if ${COMPILER_FEATURES:Mc++11} .if defined(WITHOUT_${var}) MK_${var}:= no .else MK_${var}:= yes .endif .else .if defined(WITH_${var}) MK_${var}:= yes .else MK_${var}:= no .endif .endif .endif .endfor # # Force some options off if their dependencies are off. # Order is somewhat important. # .if !${COMPILER_FEATURES:Mc++11} MK_GOOGLETEST:= no MK_LLVM_LIBUNWIND:= no .endif .if ${MK_BINUTILS} == "no" MK_GDB:= no .endif .if ${MK_CAPSICUM} == "no" MK_CASPER:= no .endif .if ${MK_LIBPTHREAD} == "no" MK_LIBTHR:= no .endif .if ${MK_SOURCELESS} == "no" MK_SOURCELESS_HOST:= no MK_SOURCELESS_UCODE:= no .endif .if ${MK_CDDL} == "no" MK_ZFS:= no MK_LOADER_ZFS:= no MK_CTF:= no .endif .if ${MK_CRYPT} == "no" MK_OPENSSL:= no MK_OPENSSH:= no MK_KERBEROS:= no .endif .if ${MK_CXX} == "no" MK_CLANG:= no MK_GNUCXX:= no MK_TESTS:= no .endif .if ${MK_DIALOG} == "no" MK_BSDINSTALL:= no .endif .if ${MK_MAIL} == "no" MK_MAILWRAPPER:= no MK_SENDMAIL:= no MK_DMAGENT:= no .endif .if ${MK_NETGRAPH} == "no" MK_ATM:= no MK_BLUETOOTH:= no .endif .if ${MK_NLS} == "no" MK_NLS_CATALOGS:= no .endif .if ${MK_OPENSSL} == "no" MK_DMAGENT:= no MK_OPENSSH:= no MK_KERBEROS:= no MK_LDNS:= no .endif .if ${MK_LDNS} == "no" MK_LDNS_UTILS:= no MK_UNBOUND:= no .endif .if ${MK_PF} == "no" MK_AUTHPF:= no .endif .if ${MK_OFED} == "no" MK_OFED_EXTRA:= no .endif .if ${MK_PORTSNAP} == "no" # freebsd-update depends on phttpget from portsnap MK_FREEBSD_UPDATE:= no .endif .if ${MK_TESTS} == "no" MK_DTRACE_TESTS:= no .endif .if ${MK_TESTS_SUPPORT} == "no" MK_GOOGLETEST:= no .endif .if ${MK_ZONEINFO} == "no" MK_ZONEINFO_LEAPSECONDS_SUPPORT:= no MK_ZONEINFO_OLD_TIMEZONES_SUPPORT:= no .endif .if ${MK_CROSS_COMPILER} == "no" MK_BINUTILS_BOOTSTRAP:= no MK_CLANG_BOOTSTRAP:= no MK_ELFTOOLCHAIN_BOOTSTRAP:= no MK_GCC_BOOTSTRAP:= no MK_LLD_BOOTSTRAP:= no .endif .if ${MK_TOOLCHAIN} == "no" MK_BINUTILS:= no MK_CLANG:= no MK_GCC:= no MK_GDB:= no MK_INCLUDES:= no MK_LLD:= no MK_LLDB:= no .endif .if ${MK_CLANG} == "no" MK_CLANG_EXTRAS:= no MK_CLANG_FULL:= no MK_LLVM_COV:= no .endif .if ${MK_LOADER_VERIEXEC} == "no" MK_LOADER_VERIEXEC_PASS_MANIFEST := no .endif # # MK_* options whose default value depends on another option. # .for vv in \ GSSAPI/KERBEROS \ MAN_UTILS/MAN .if defined(WITH_${vv:H}) MK_${vv:H}:= yes .elif defined(WITHOUT_${vv:H}) MK_${vv:H}:= no .else MK_${vv:H}:= ${MK_${vv:T}} .endif .endfor # # Set defaults for the MK_*_SUPPORT variables. # .if !${COMPILER_FEATURES:Mc++11} MK_LLDB:= no .endif # gcc 4.8 and newer supports libc++, so suppress gnuc++ in that case. # while in theory we could build it with that, we don't want to do # that since it creates too much confusion for too little gain. # XXX: This is incomplete and needs X_COMPILER_TYPE/VERSION checks too # to prevent Makefile.inc1 from bootstrapping unneeded dependencies # and to support 'make delete-old' when supplying an external toolchain. .if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 40800 MK_GNUCXX:=no MK_GCC:=no .endif .endif # !target(____) Index: stable/12/stand/common/bootstrap.h =================================================================== --- stable/12/stand/common/bootstrap.h (revision 359734) +++ stable/12/stand/common/bootstrap.h (revision 359735) @@ -1,356 +1,354 @@ /*- * Copyright (c) 1998 Michael Smith * 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$ */ #ifndef _BOOTSTRAP_H_ #define _BOOTSTRAP_H_ #include #include #include +#include "readin.h" + /* Commands and return values; nonzero return sets command_errmsg != NULL */ typedef int (bootblk_cmd_t)(int argc, char *argv[]); #define COMMAND_ERRBUFSZ (256) extern const char *command_errmsg; extern char command_errbuf[COMMAND_ERRBUFSZ]; #define CMD_OK 0 #define CMD_WARN 1 #define CMD_ERROR 2 #define CMD_CRIT 3 #define CMD_FATAL 4 /* interp.c */ void interact(void); void interp_emit_prompt(void); int interp_builtin_cmd(int argc, char *argv[]); /* Called by interp.c for interp_*.c embedded interpreters */ int interp_include(const char *filename); /* Execute commands from filename */ void interp_init(void); /* Initialize interpreater */ int interp_run(const char *line); /* Run a single command */ /* interp_backslash.c */ char *backslash(const char *str); /* interp_parse.c */ int parse(int *argc, char ***argv, const char *str); /* boot.c */ void autoboot_maybe(void); int getrootmount(char *rootdev); /* misc.c */ char *unargv(int argc, char *argv[]); void hexdump(caddr_t region, size_t len); size_t strlenout(vm_offset_t str); char *strdupout(vm_offset_t str); void kern_bzero(vm_offset_t dest, size_t len); -int kern_pread(int fd, vm_offset_t dest, size_t len, off_t off); -void *alloc_pread(int fd, off_t off, size_t len); +int kern_pread(readin_handle_t fd, vm_offset_t dest, size_t len, off_t off); +void *alloc_pread(readin_handle_t fd, off_t off, size_t len); /* bcache.c */ void bcache_init(size_t nblks, size_t bsize); void bcache_add_dev(int); void *bcache_allocate(void); void bcache_free(void *); int bcache_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); /* * Disk block cache */ struct bcache_devdata { int (*dv_strategy)(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); void *dv_devdata; void *dv_cache; }; /* * Modular console support. */ struct console { const char *c_name; const char *c_desc; int c_flags; #define C_PRESENTIN (1<<0) /* console can provide input */ #define C_PRESENTOUT (1<<1) /* console can provide output */ #define C_ACTIVEIN (1<<2) /* user wants input from console */ #define C_ACTIVEOUT (1<<3) /* user wants output to console */ #define C_WIDEOUT (1<<4) /* c_out routine groks wide chars */ void (* c_probe)(struct console *cp); /* set c_flags to match hardware */ int (* c_init)(int arg); /* reinit XXX may need more args */ void (* c_out)(int c); /* emit c */ int (* c_in)(void); /* wait for and return input */ int (* c_ready)(void); /* return nonzer if input waiting */ }; extern struct console *consoles[]; void cons_probe(void); /* * Plug-and-play enumerator/configurator interface. */ struct pnphandler { const char *pp_name; /* handler/bus name */ void (* pp_enumerate)(void); /* enumerate PnP devices, add to chain */ }; struct pnpident { char *id_ident; /* ASCII identifier, actual format varies with bus/handler */ STAILQ_ENTRY(pnpident) id_link; }; struct pnpinfo { char *pi_desc; /* ASCII description, optional */ int pi_revision; /* optional revision (or -1) if not supported */ char *pi_module; /* module/args nominated to handle device */ int pi_argc; /* module arguments */ char **pi_argv; struct pnphandler *pi_handler; /* handler which detected this device */ STAILQ_HEAD(,pnpident) pi_ident; /* list of identifiers */ STAILQ_ENTRY(pnpinfo) pi_link; }; STAILQ_HEAD(pnpinfo_stql, pnpinfo); extern struct pnphandler *pnphandlers[]; /* provided by MD code */ void pnp_addident(struct pnpinfo *pi, char *ident); struct pnpinfo *pnp_allocinfo(void); void pnp_freeinfo(struct pnpinfo *pi); void pnp_addinfo(struct pnpinfo *pi); char *pnp_eisaformat(uint8_t *data); /* * < 0 - No ISA in system * == 0 - Maybe ISA, search for read data port * > 0 - ISA in system, value is read data port address */ extern int isapnp_readport; /* * Version information */ extern char bootprog_info[]; /* * Interpreter information */ extern const char bootprog_interp[]; #define INTERP_DEFINE(interpstr) \ const char bootprog_interp[] = "$Interpreter:" interpstr /* * Preloaded file metadata header. * * Metadata are allocated on our heap, and copied into kernel space * before executing the kernel. */ struct file_metadata { size_t md_size; uint16_t md_type; struct file_metadata *md_next; char md_data[1]; /* data are immediately appended */ }; struct preloaded_file; struct mod_depend; struct kernel_module { char *m_name; /* module name */ int m_version; /* module version */ /* char *m_args;*/ /* arguments for the module */ struct preloaded_file *m_fp; struct kernel_module *m_next; }; /* * Preloaded file information. Depending on type, file can contain * additional units called 'modules'. * * At least one file (the kernel) must be loaded in order to boot. * The kernel is always loaded first. * * String fields (m_name, m_type) should be dynamically allocated. */ struct preloaded_file { char *f_name; /* file name */ char *f_type; /* verbose file type, eg 'ELF kernel', 'pnptable', etc. */ char *f_args; /* arguments for the file */ struct file_metadata *f_metadata; /* metadata that will be placed in the module directory */ int f_loader; /* index of the loader that read the file */ vm_offset_t f_addr; /* load address */ size_t f_size; /* file size */ struct kernel_module *f_modules; /* list of modules if any */ struct preloaded_file *f_next; /* next file */ }; struct file_format { /* Load function must return EFTYPE if it can't handle the module supplied */ int (* l_load)(char *filename, uint64_t dest, struct preloaded_file **result); /* Only a loader that will load a kernel (first module) should have an exec handler */ int (* l_exec)(struct preloaded_file *mp); }; extern struct file_format *file_formats[]; /* supplied by consumer */ extern struct preloaded_file *preloaded_files; int mod_load(char *name, struct mod_depend *verinfo, int argc, char *argv[]); int mod_loadkld(const char *name, int argc, char *argv[]); void unload(void); struct preloaded_file *file_alloc(void); struct preloaded_file *file_findfile(const char *name, const char *type); struct file_metadata *file_findmetadata(struct preloaded_file *fp, int type); struct preloaded_file *file_loadraw(const char *name, char *type, int insert); void file_discard(struct preloaded_file *fp); void file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p); int file_addmodule(struct preloaded_file *fp, char *modname, int version, struct kernel_module **newmp); void file_removemetadata(struct preloaded_file *fp); /* MI module loaders */ #ifdef __elfN /* Relocation types. */ #define ELF_RELOC_REL 1 #define ELF_RELOC_RELA 2 /* Relocation offset for some architectures */ extern uint64_t __elfN(relocation_offset); struct elf_file; typedef Elf_Addr (symaddr_fn)(struct elf_file *ef, Elf_Size symidx); int __elfN(loadfile)(char *filename, uint64_t dest, struct preloaded_file **result); int __elfN(obj_loadfile)(char *filename, uint64_t dest, struct preloaded_file **result); int __elfN(reloc)(struct elf_file *ef, symaddr_fn *symaddr, const void *reldata, int reltype, Elf_Addr relbase, Elf_Addr dataaddr, void *data, size_t len); int __elfN(loadfile_raw)(char *filename, uint64_t dest, struct preloaded_file **result, int multiboot); int __elfN(load_modmetadata)(struct preloaded_file *fp, uint64_t dest); #endif /* * Support for commands */ struct bootblk_command { const char *c_name; const char *c_desc; bootblk_cmd_t *c_fn; }; #define COMMAND_SET(tag, key, desc, func) \ static bootblk_cmd_t func; \ static struct bootblk_command _cmd_ ## tag = { key, desc, func }; \ DATA_SET(Xcommand_set, _cmd_ ## tag) SET_DECLARE(Xcommand_set, struct bootblk_command); /* * The intention of the architecture switch is to provide a convenient * encapsulation of the interface between the bootstrap MI and MD code. * MD code may selectively populate the switch at runtime based on the * actual configuration of the target system. */ struct arch_switch { /* Automatically load modules as required by detected hardware */ int (*arch_autoload)(void); /* Locate the device for (name), return pointer to tail in (*path) */ int (*arch_getdev)(void **dev, const char *name, const char **path); /* Copy from local address space to module address space, similar to bcopy() */ ssize_t (*arch_copyin)(const void *src, vm_offset_t dest, const size_t len); /* Copy to local address space from module address space, similar to bcopy() */ ssize_t (*arch_copyout)(const vm_offset_t src, void *dest, const size_t len); /* Read from file to module address space, same semantics as read() */ - ssize_t (*arch_readin)(const int fd, vm_offset_t dest, + ssize_t (*arch_readin)(readin_handle_t fd, vm_offset_t dest, const size_t len); /* Perform ISA byte port I/O (only for systems with ISA) */ int (*arch_isainb)(int port); void (*arch_isaoutb)(int port, int value); /* * Interface to adjust the load address according to the "object" * being loaded. */ uint64_t (*arch_loadaddr)(u_int type, void *data, uint64_t addr); #define LOAD_ELF 1 /* data points to the ELF header. */ #define LOAD_RAW 2 /* data points to the file name. */ /* * Interface to inform MD code about a loaded (ELF) segment. This * can be used to flush caches and/or set up translations. */ #ifdef __elfN void (*arch_loadseg)(Elf_Ehdr *eh, Elf_Phdr *ph, uint64_t delta); #else void (*arch_loadseg)(void *eh, void *ph, uint64_t delta); #endif /* Probe ZFS pool(s), if needed. */ void (*arch_zfs_probe)(void); /* Return the hypervisor name/type or NULL if not virtualized. */ const char *(*arch_hypervisor)(void); /* For kexec-type loaders, get ksegment structure */ void (*arch_kexec_kseg_get)(int *nseg, void **kseg); }; extern struct arch_switch archsw; /* This must be provided by the MD code, but should it be in the archsw? */ void delay(int delay); void dev_cleanup(void); time_t time(time_t *tloc); #ifndef CTASSERT #define CTASSERT(x) _Static_assert(x, "compile-time assertion failed") -#endif - -#ifdef LOADER_VERIEXEC -#include #endif #endif /* !_BOOTSTRAP_H_ */ Index: stable/12/stand/common/install.c =================================================================== --- stable/12/stand/common/install.c (revision 359734) +++ stable/12/stand/common/install.c (revision 359735) @@ -1,355 +1,366 @@ /*- * Copyright (c) 2008-2014, Juniper Networks, Inc. * 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 THE AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "bootstrap.h" extern struct in_addr servip; extern int pkgfs_init(const char *, struct fs_ops *); extern void pkgfs_cleanup(void); COMMAND_SET(install, "install", "install software package", command_install); static char *inst_kernel; static char **inst_modules; static char *inst_rootfs; static char *inst_loader_rc; static int setpath(char **what, char *val) { char *path; size_t len; int rel; len = strlen(val) + 1; rel = (val[0] != '/') ? 1 : 0; path = malloc(len + rel); if (path == NULL) return (ENOMEM); path[0] = '/'; strcpy(path + rel, val); *what = path; return (0); } static int setmultipath(char ***what, char *val) { char *s, *v; int count, error, idx; count = 0; v = val; do { count++; s = strchr(v, ','); v = (s == NULL) ? NULL : s + 1; } while (v != NULL); *what = calloc(count + 1, sizeof(char *)); if (*what == NULL) return (ENOMEM); for (idx = 0; idx < count; idx++) { s = strchr(val, ','); if (s != NULL) *s++ = '\0'; error = setpath(*what + idx, val); if (error) return (error); val = s; } return (0); } static int read_metatags(int fd) { char buf[1024]; char *p, *tag, *val; ssize_t fsize; int error; fsize = read(fd, buf, sizeof(buf)); if (fsize == -1) return (errno); /* * Assume that if we read a whole buffer worth of data, we * haven't read the entire file. In other words, the buffer * size must always be larger than the file size. That way * we can append a '\0' and use standard string operations. * Return an error if this is not possible. */ if (fsize == sizeof(buf)) return (ENOMEM); buf[fsize] = '\0'; error = 0; tag = buf; while (!error && *tag != '\0') { val = strchr(tag, '='); if (val == NULL) { error = EINVAL; break; } *val++ = '\0'; p = strchr(val, '\n'); if (p == NULL) { error = EINVAL; break; } *p++ = '\0'; if (strcmp(tag, "KERNEL") == 0) error = setpath(&inst_kernel, val); else if (strcmp(tag, "MODULES") == 0) error = setmultipath(&inst_modules, val); else if (strcmp(tag, "ROOTFS") == 0) error = setpath(&inst_rootfs, val); else if (strcmp(tag, "LOADER_RC") == 0) error = setpath(&inst_loader_rc, val); tag = p; } return (error); } static void cleanup(void) { u_int i; if (inst_kernel != NULL) { free(inst_kernel); inst_kernel = NULL; } if (inst_modules != NULL) { i = 0; while (inst_modules[i] != NULL) free(inst_modules[i++]); free(inst_modules); inst_modules = NULL; } if (inst_rootfs != NULL) { free(inst_rootfs); inst_rootfs = NULL; } if (inst_loader_rc != NULL) { free(inst_loader_rc); inst_loader_rc = NULL; } pkgfs_cleanup(); } /* * usage: install URL * where: URL = (tftp|file)://[host]/ */ static int install(char *pkgname) { static char buf[256]; struct fs_ops *proto; struct preloaded_file *fp; char *s, *currdev; const char *devname; int error, fd, i, local; s = strstr(pkgname, "://"); if (s == NULL) goto invalid_url; i = s - pkgname; if (i == 4 && !strncasecmp(pkgname, "tftp", i)) { devname = "net0"; proto = &tftp_fsops; local = 0; } else if (i == 4 && !strncasecmp(pkgname, "file", i)) { currdev = getenv("currdev"); if (currdev != NULL && strcmp(currdev, "pxe0:") == 0) { devname = "pxe0"; proto = NULL; +#ifdef HOSTPROG + } else if (currdev != NULL && strcmp(currdev, "host0:") == 0) { + extern struct fs_ops host_fsops; + + devname = "host0"; + proto = &host_fsops; +#endif } else { devname = "disk1"; proto = &dosfs_fsops; } local = 1; } else goto invalid_url; s += 3; if (*s == '\0') goto invalid_url; if (*s != '/' ) { if (local) goto invalid_url; pkgname = strchr(s, '/'); if (pkgname == NULL) goto invalid_url; *pkgname = '\0'; servip.s_addr = inet_addr(s); if (servip.s_addr == htonl(INADDR_NONE)) goto invalid_url; setenv("serverip", inet_ntoa(servip), 1); + + if (proto == &tftp_fsops) { + tftpip.s_addr = servip.s_addr; + } *pkgname = '/'; } else pkgname = s; if (strlen(devname) + strlen(pkgname) + 2 > sizeof(buf)) { command_errmsg = "package name too long"; return (CMD_ERROR); } sprintf(buf, "%s:%s", devname, pkgname); setenv("install_package", buf, 1); error = pkgfs_init(buf, proto); if (error) { command_errmsg = "cannot open package"; goto fail; } /* * Point of no return: unload anything that may have been * loaded and prune the environment from harmful variables. */ unload(); unsetenv("vfs.root.mountfrom"); /* * read the metatags file. */ fd = open("/metatags", O_RDONLY); if (fd != -1) { error = read_metatags(fd); close(fd); if (error) { command_errmsg = "cannot load metatags"; goto fail; } } s = (inst_kernel == NULL) ? "/kernel" : inst_kernel; error = mod_loadkld(s, 0, NULL); if (error) { command_errmsg = "cannot load kernel from package"; goto fail; } /* If there is a loader.rc in the package, execute it */ s = (inst_loader_rc == NULL) ? "/loader.rc" : inst_loader_rc; fd = open(s, O_RDONLY); if (fd != -1) { close(fd); error = inter_include(s); if (error == CMD_ERROR) goto fail; } i = 0; while (inst_modules != NULL && inst_modules[i] != NULL) { error = mod_loadkld(inst_modules[i], 0, NULL); if (error) { command_errmsg = "cannot load module(s) from package"; goto fail; } i++; } s = (inst_rootfs == NULL) ? "/install.iso" : inst_rootfs; if (file_loadraw(s, "mfs_root", 1) == NULL) { error = errno; command_errmsg = "cannot load root file system"; goto fail; } cleanup(); fp = file_findfile(NULL, NULL); if (fp != NULL) file_formats[fp->f_loader]->l_exec(fp); error = CMD_ERROR; command_errmsg = "unable to start installation"; fail: sprintf(buf, "%s (error %d)", command_errmsg, error); cleanup(); unload(); exclusive_file_system = NULL; command_errmsg = buf; /* buf is static. */ return (CMD_ERROR); invalid_url: command_errmsg = "invalid URL"; return (CMD_ERROR); } static int command_install(int argc, char *argv[]) { int argidx; unsetenv("install_format"); argidx = 1; while (1) { if (argc == argidx) { command_errmsg = "usage: install [--format] "; return (CMD_ERROR); } if (!strcmp(argv[argidx], "--format")) { setenv("install_format", "yes", 1); argidx++; continue; } break; } return (install(argv[argidx])); } Index: stable/12/stand/common/interp_forth.c =================================================================== --- stable/12/stand/common/interp_forth.c (revision 359734) +++ stable/12/stand/common/interp_forth.c (revision 359735) @@ -1,458 +1,458 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include __FBSDID("$FreeBSD$"); #include /* to pick up __FreeBSD_version */ #include #include #include "bootstrap.h" #include "ficl.h" extern unsigned bootprog_rev; INTERP_DEFINE("4th"); /* #define BFORTH_DEBUG */ #ifdef BFORTH_DEBUG #define DPRINTF(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) #else #define DPRINTF(fmt, args...) ((void)0) #endif /* * Eventually, all builtin commands throw codes must be defined * elsewhere, possibly bootstrap.h. For now, just this code, used * just in this file, it is getting defined. */ #define BF_PARSE 100 /* * FreeBSD loader default dictionary cells */ #ifndef BF_DICTSIZE #define BF_DICTSIZE 10000 #endif /* * BootForth Interface to Ficl Forth interpreter. */ FICL_SYSTEM *bf_sys; FICL_VM *bf_vm; /* * Shim for taking commands from BF and passing them out to 'standard' * argv/argc command functions. */ static void bf_command(FICL_VM *vm) { char *name, *line, *tail, *cp; size_t len; struct bootblk_command **cmdp; bootblk_cmd_t *cmd; int nstrings, i; int argc, result; char **argv; /* Get the name of the current word */ name = vm->runningWord->name; /* Find our command structure */ cmd = NULL; SET_FOREACH(cmdp, Xcommand_set) { if (((*cmdp)->c_name != NULL) && !strcmp(name, (*cmdp)->c_name)) cmd = (*cmdp)->c_fn; } if (cmd == NULL) panic("callout for unknown command '%s'", name); /* Check whether we have been compiled or are being interpreted */ if (stackPopINT(vm->pStack)) { /* * Get parameters from stack, in the format: * an un ... a2 u2 a1 u1 n -- * Where n is the number of strings, a/u are pairs of * address/size for strings, and they will be concatenated * in LIFO order. */ nstrings = stackPopINT(vm->pStack); for (i = 0, len = 0; i < nstrings; i++) len += stackFetch(vm->pStack, i * 2).i + 1; line = malloc(strlen(name) + len + 1); strcpy(line, name); if (nstrings) for (i = 0; i < nstrings; i++) { len = stackPopINT(vm->pStack); cp = stackPopPtr(vm->pStack); strcat(line, " "); strncat(line, cp, len); } } else { /* Get remainder of invocation */ tail = vmGetInBuf(vm); for (cp = tail, len = 0; cp != vm->tib.end && *cp != 0 && *cp != '\n'; cp++, len++) ; line = malloc(strlen(name) + len + 2); strcpy(line, name); if (len > 0) { strcat(line, " "); strncat(line, tail, len); vmUpdateTib(vm, tail + len); } } DPRINTF("cmd '%s'", line); command_errmsg = command_errbuf; command_errbuf[0] = 0; if (!parse(&argc, &argv, line)) { result = (cmd)(argc, argv); free(argv); } else { result=BF_PARSE; } switch (result) { case CMD_CRIT: printf("%s\n", command_errmsg); command_errmsg = NULL; break; case CMD_FATAL: panic("%s", command_errmsg); } free(line); /* * If there was error during nested ficlExec(), we may no longer have * valid environment to return. Throw all exceptions from here. */ if (result != CMD_OK) vmThrow(vm, result); /* This is going to be thrown!!! */ stackPushINT(vm->pStack,result); } /* * Replace a word definition (a builtin command) with another * one that: * * - Throw error results instead of returning them on the stack * - Pass a flag indicating whether the word was compiled or is * being interpreted. * * There is one major problem with builtins that cannot be overcome * in anyway, except by outlawing it. We want builtins to behave * differently depending on whether they have been compiled or they * are being interpreted. Notice that this is *not* the interpreter's * current state. For example: * * : example ls ; immediate * : problem example ; \ "ls" gets executed while compiling * example \ "ls" gets executed while interpreting * * Notice that, though the current state is different in the two * invocations of "example", in both cases "ls" has been * *compiled in*, which is what we really want. * * The problem arises when you tick the builtin. For example: * * : example-1 ['] ls postpone literal ; immediate * : example-2 example-1 execute ; immediate * : problem example-2 ; * example-2 * * We have no way, when we get EXECUTEd, of knowing what our behavior * should be. Thus, our only alternative is to "outlaw" this. See RFI * 0007, and ANS Forth Standard's appendix D, item 6.7 for a related * problem, concerning compile semantics. * * The problem is compounded by the fact that "' builtin CATCH" is valid * and desirable. The only solution is to create an intermediary word. * For example: * * : my-ls ls ; * : example ['] my-ls catch ; * * So, with the below implementation, here is a summary of the behavior * of builtins: * * ls -l \ "interpret" behavior, ie, * \ takes parameters from TIB * : ex-1 s" -l" 1 ls ; \ "compile" behavior, ie, * \ takes parameters from the stack * : ex-2 ['] ls catch ; immediate \ undefined behavior * : ex-3 ['] ls catch ; \ undefined behavior * ex-2 ex-3 \ "interpret" behavior, * \ catch works * : ex-4 ex-2 ; \ "compile" behavior, * \ catch does not work * : ex-5 ex-3 ; immediate \ same as ex-2 * : ex-6 ex-3 ; \ same as ex-3 * : ex-7 ['] ex-1 catch ; \ "compile" behavior, * \ catch works * : ex-8 postpone ls ; immediate \ same as ex-2 * : ex-9 postpone ls ; \ same as ex-3 * * As the definition below is particularly tricky, and it's side effects * must be well understood by those playing with it, I'll be heavy on * the comments. * * (if you edit this definition, pay attention to trailing spaces after * each word -- I warned you! :-) ) */ #define BUILTIN_CONSTRUCTOR \ ": builtin: " \ ">in @ " /* save the tib index pointer */ \ "' " /* get next word's xt */ \ "swap >in ! " /* point again to next word */ \ "create " /* create a new definition of the next word */ \ ", " /* save previous definition's xt */ \ "immediate " /* make the new definition an immediate word */ \ \ "does> " /* Now, the *new* definition will: */ \ "state @ if " /* if in compiling state: */ \ "1 postpone literal " /* pass 1 flag to indicate compile */ \ "@ compile, " /* compile in previous definition */ \ "postpone throw " /* throw stack-returned result */ \ "else " /* if in interpreting state: */ \ "0 swap " /* pass 0 flag to indicate interpret */ \ "@ execute " /* call previous definition */ \ "throw " /* throw stack-returned result */ \ "then ; " /* * Initialise the Forth interpreter, create all our commands as words. */ void bf_init(void) { struct bootblk_command **cmdp; char create_buf[41]; /* 31 characters-long builtins */ int fd; bf_sys = ficlInitSystem(BF_DICTSIZE); bf_vm = ficlNewVM(bf_sys); /* Put all private definitions in a "builtins" vocabulary */ ficlExec(bf_vm, "vocabulary builtins also builtins definitions"); /* Builtin constructor word */ ficlExec(bf_vm, BUILTIN_CONSTRUCTOR); /* make all commands appear as Forth words */ SET_FOREACH(cmdp, Xcommand_set) { ficlBuild(bf_sys, (char *)(*cmdp)->c_name, bf_command, FW_DEFAULT); ficlExec(bf_vm, "forth definitions builtins"); sprintf(create_buf, "builtin: %s", (*cmdp)->c_name); ficlExec(bf_vm, create_buf); ficlExec(bf_vm, "builtins definitions"); } ficlExec(bf_vm, "only forth definitions"); /* Export some version numbers so that code can detect the loader/host version */ ficlSetEnv(bf_sys, "FreeBSD_version", __FreeBSD_version); ficlSetEnv(bf_sys, "loader_version", bootprog_rev); /* try to load and run init file if present */ if ((fd = open("/boot/boot.4th", O_RDONLY)) != -1) { #ifdef LOADER_VERIEXEC - if (verify_file(fd, "/boot/boot.4th", 0, VE_GUESS) < 0) { + if (verify_file(fd, "/boot/boot.4th", 0, VE_GUESS, __func__) < 0) { close(fd); return; } #endif (void)ficlExecFD(bf_vm, fd); close(fd); } } /* * Feed a line of user input to the Forth interpreter */ static int bf_run(const char *line) { int result; /* * ficl would require extensive changes to accept a const char * * interface. Instead, cast it away here and hope for the best. * We know at the present time the caller for us in the boot * forth loader can tolerate the string being modified because * the string is passed in here and then not touched again. */ result = ficlExec(bf_vm, __DECONST(char *, line)); DPRINTF("ficlExec '%s' = %d", line, result); switch (result) { case VM_OUTOFTEXT: case VM_ABORTQ: case VM_QUIT: case VM_ERREXIT: break; case VM_USEREXIT: printf("No where to leave to!\n"); break; case VM_ABORT: printf("Aborted!\n"); break; case BF_PARSE: printf("Parse error!\n"); break; default: if (command_errmsg != NULL) { printf("%s\n", command_errmsg); command_errmsg = NULL; } } if (result == VM_USEREXIT) panic("interpreter exit"); setenv("interpret", bf_vm->state ? "" : "OK", 1); return (result); } void interp_init(void) { setenv("script.lang", "forth", 1); bf_init(); /* Read our default configuration. */ interp_include("/boot/loader.rc"); } int interp_run(const char *input) { bf_vm->sourceID.i = 0; return bf_run(input); } /* * Header prepended to each line. The text immediately follows the header. * We try to make this short in order to save memory -- the loader has * limited memory available, and some of the forth files are very long. */ struct includeline { struct includeline *next; char text[0]; }; int interp_include(const char *filename) { struct includeline *script, *se, *sp; char input[256]; /* big enough? */ int res; char *cp; int prevsrcid, fd, line; if (((fd = open(filename, O_RDONLY)) == -1)) { snprintf(command_errbuf, sizeof(command_errbuf), "can't open '%s': %s", filename, strerror(errno)); return(CMD_ERROR); } #ifdef LOADER_VERIEXEC - if (verify_file(fd, filename, 0, VE_GUESS) < 0) { + if (verify_file(fd, filename, 0, VE_GUESS, __func__) < 0) { close(fd); sprintf(command_errbuf,"can't verify '%s'", filename); return(CMD_ERROR); } #endif /* * Read the script into memory. */ script = se = NULL; line = 0; while (fgetstr(input, sizeof(input), fd) >= 0) { line++; cp = input; /* Allocate script line structure and copy line, flags */ if (*cp == '\0') continue; /* ignore empty line, save memory */ sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); /* On malloc failure (it happens!), free as much as possible and exit */ if (sp == NULL) { while (script != NULL) { se = script; script = script->next; free(se); } snprintf(command_errbuf, sizeof(command_errbuf), "file '%s' line %d: memory allocation failure - aborting", filename, line); close(fd); return (CMD_ERROR); } strcpy(sp->text, cp); sp->next = NULL; if (script == NULL) { script = sp; } else { se->next = sp; } se = sp; } close(fd); /* * Execute the script */ prevsrcid = bf_vm->sourceID.i; bf_vm->sourceID.i = fd; res = CMD_OK; for (sp = script; sp != NULL; sp = sp->next) { res = bf_run(sp->text); if (res != VM_OUTOFTEXT) { snprintf(command_errbuf, sizeof(command_errbuf), "Error while including %s, in the line:\n%s", filename, sp->text); res = CMD_ERROR; break; } else res = CMD_OK; } bf_vm->sourceID.i = prevsrcid; while (script != NULL) { se = script; script = script->next; free(se); } return(res); } Index: stable/12/stand/common/interp_simple.c =================================================================== --- stable/12/stand/common/interp_simple.c (revision 359734) +++ stable/12/stand/common/interp_simple.c (revision 359735) @@ -1,202 +1,202 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include __FBSDID("$FreeBSD$"); /* * Simple commandline interpreter, toplevel and misc. */ #include #include #include "bootstrap.h" INTERP_DEFINE("simp"); void interp_init(void) { setenv("script.lang", "simple", 1); /* Read our default configuration. */ interp_include("/boot/loader.rc"); } int interp_run(const char *input) { int argc; char **argv; if (parse(&argc, &argv, input)) { printf("parse error\n"); return CMD_ERROR; } if (interp_builtin_cmd(argc, argv)) { printf("%s: %s\n", argv[0], command_errmsg); free(argv); return CMD_ERROR; } free(argv); return CMD_OK; } /* * Header prepended to each line. The text immediately follows the header. * We try to make this short in order to save memory -- the loader has * limited memory available, and some of the forth files are very long. */ struct includeline { struct includeline *next; int flags; int line; #define SL_QUIET (1<<0) #define SL_IGNOREERR (1<<1) char text[0]; }; int interp_include(const char *filename) { struct includeline *script, *se, *sp; char input[256]; /* big enough? */ int argc,res; char **argv, *cp; int fd, flags, line; if (((fd = open(filename, O_RDONLY)) == -1)) { snprintf(command_errbuf, sizeof(command_errbuf), "can't open '%s': %s", filename, strerror(errno)); return(CMD_ERROR); } #ifdef LOADER_VERIEXEC - if (verify_file(fd, filename, 0, VE_GUESS) < 0) { + if (verify_file(fd, filename, 0, VE_GUESS, __func__) < 0) { close(fd); sprintf(command_errbuf,"can't verify '%s'", filename); return(CMD_ERROR); } #endif /* * Read the script into memory. */ script = se = NULL; line = 0; while (fgetstr(input, sizeof(input), fd) >= 0) { line++; flags = 0; /* Discard comments */ if (strncmp(input+strspn(input, " "), "\\", 1) == 0) continue; cp = input; /* Echo? */ if (input[0] == '@') { cp++; flags |= SL_QUIET; } /* Error OK? */ if (input[0] == '-') { cp++; flags |= SL_IGNOREERR; } /* Allocate script line structure and copy line, flags */ if (*cp == '\0') continue; /* ignore empty line, save memory */ sp = malloc(sizeof(struct includeline) + strlen(cp) + 1); /* On malloc failure (it happens!), free as much as possible and exit */ if (sp == NULL) { while (script != NULL) { se = script; script = script->next; free(se); } snprintf(command_errbuf, sizeof(command_errbuf), "file '%s' line %d: memory allocation failure - aborting", filename, line); close(fd); return (CMD_ERROR); } strcpy(sp->text, cp); sp->flags = flags; sp->line = line; sp->next = NULL; if (script == NULL) { script = sp; } else { se->next = sp; } se = sp; } close(fd); /* * Execute the script */ argv = NULL; res = CMD_OK; for (sp = script; sp != NULL; sp = sp->next) { /* print if not being quiet */ if (!(sp->flags & SL_QUIET)) { interp_emit_prompt(); printf("%s\n", sp->text); } /* Parse the command */ if (!parse(&argc, &argv, sp->text)) { if ((argc > 0) && (interp_builtin_cmd(argc, argv) != 0)) { /* normal command */ printf("%s: %s\n", argv[0], command_errmsg); if (!(sp->flags & SL_IGNOREERR)) { res=CMD_ERROR; break; } } free(argv); argv = NULL; } else { printf("%s line %d: parse error\n", filename, sp->line); res=CMD_ERROR; break; } } if (argv != NULL) free(argv); while (script != NULL) { se = script; script = script->next; free(se); } return(res); } Index: stable/12/stand/common/load_elf.c =================================================================== --- stable/12/stand/common/load_elf.c (revision 359734) +++ stable/12/stand/common/load_elf.c (revision 359735) @@ -1,1224 +1,1274 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #define FREEBSD_ELF #include #include "bootstrap.h" #define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) #if defined(__i386__) && __ELF_WORD_SIZE == 64 #undef ELF_TARG_CLASS #undef ELF_TARG_MACH #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_MACH EM_X86_64 #endif typedef struct elf_file { Elf_Phdr *ph; Elf_Ehdr *ehdr; Elf_Sym *symtab; Elf_Hashelt *hashtab; Elf_Hashelt nbuckets; Elf_Hashelt nchains; Elf_Hashelt *buckets; Elf_Hashelt *chains; Elf_Rel *rel; size_t relsz; Elf_Rela *rela; size_t relasz; char *strtab; size_t strsz; int fd; caddr_t firstpage; size_t firstlen; int kernel; uint64_t off; +#ifdef LOADER_VERIEXEC_VECTX + struct vectx *vctx; +#endif } *elf_file_t; +#ifdef LOADER_VERIEXEC_VECTX +#define VECTX_HANDLE(ef) (ef)->vctx +#else +#define VECTX_HANDLE(ef) (ef)->fd +#endif + static int __elfN(loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(lookup_symbol)(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); static int __elfN(parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end); static symaddr_fn __elfN(symaddr); static char *fake_modname(const char *name); const char *__elfN(kerneltype) = "elf kernel"; const char *__elfN(moduletype) = "elf module"; uint64_t __elfN(relocation_offset) = 0; extern void elf_wrong_field_size(void); #define CONVERT_FIELD(b, f, e) \ switch (sizeof((b)->f)) { \ case 2: \ (b)->f = e ## 16toh((b)->f); \ break; \ case 4: \ (b)->f = e ## 32toh((b)->f); \ break; \ case 8: \ (b)->f = e ## 64toh((b)->f); \ break; \ default: \ /* Force a link time error. */ \ elf_wrong_field_size(); \ break; \ } #define CONVERT_SWITCH(h, d, f) \ switch ((h)->e_ident[EI_DATA]) { \ case ELFDATA2MSB: \ f(d, be); \ break; \ case ELFDATA2LSB: \ f(d, le); \ break; \ default: \ return (EINVAL); \ } static int elf_header_convert(Elf_Ehdr *ehdr) { /* * Fixup ELF header endianness. * * The Xhdr structure was loaded using block read call to optimize file * accesses. It might happen, that the endianness of the system memory * is different that endianness of the ELF header. Swap fields here to * guarantee that Xhdr always contain valid data regardless of * architecture. */ #define HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, e_type, e); \ CONVERT_FIELD(b, e_machine, e); \ CONVERT_FIELD(b, e_version, e); \ CONVERT_FIELD(b, e_entry, e); \ CONVERT_FIELD(b, e_phoff, e); \ CONVERT_FIELD(b, e_shoff, e); \ CONVERT_FIELD(b, e_flags, e); \ CONVERT_FIELD(b, e_ehsize, e); \ CONVERT_FIELD(b, e_phentsize, e); \ CONVERT_FIELD(b, e_phnum, e); \ CONVERT_FIELD(b, e_shentsize, e); \ CONVERT_FIELD(b, e_shnum, e); \ CONVERT_FIELD(b, e_shstrndx, e) CONVERT_SWITCH(ehdr, ehdr, HEADER_FIELDS); #undef HEADER_FIELDS return (0); } static int elf_program_header_convert(const Elf_Ehdr *ehdr, Elf_Phdr *phdr) { #define PROGRAM_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, p_type, e); \ CONVERT_FIELD(b, p_flags, e); \ CONVERT_FIELD(b, p_offset, e); \ CONVERT_FIELD(b, p_vaddr, e); \ CONVERT_FIELD(b, p_paddr, e); \ CONVERT_FIELD(b, p_filesz, e); \ CONVERT_FIELD(b, p_memsz, e); \ CONVERT_FIELD(b, p_align, e) CONVERT_SWITCH(ehdr, phdr, PROGRAM_HEADER_FIELDS); #undef PROGRAM_HEADER_FIELDS return (0); } static int elf_section_header_convert(const Elf_Ehdr *ehdr, Elf_Shdr *shdr) { #define SECTION_HEADER_FIELDS(b, e) \ CONVERT_FIELD(b, sh_name, e); \ CONVERT_FIELD(b, sh_type, e); \ CONVERT_FIELD(b, sh_link, e); \ CONVERT_FIELD(b, sh_info, e); \ CONVERT_FIELD(b, sh_flags, e); \ CONVERT_FIELD(b, sh_addr, e); \ CONVERT_FIELD(b, sh_offset, e); \ CONVERT_FIELD(b, sh_size, e); \ CONVERT_FIELD(b, sh_addralign, e); \ CONVERT_FIELD(b, sh_entsize, e) CONVERT_SWITCH(ehdr, shdr, SECTION_HEADER_FIELDS); #undef SECTION_HEADER_FIELDS return (0); } #undef CONVERT_SWITCH #undef CONVERT_FIELD static int __elfN(load_elf_header)(char *filename, elf_file_t ef) { ssize_t bytes_read; Elf_Ehdr *ehdr; int err; /* * Open the image, read and validate the ELF header */ if (filename == NULL) /* can't handle nameless */ return (EFTYPE); if ((ef->fd = open(filename, O_RDONLY)) == -1) return (errno); ef->firstpage = malloc(PAGE_SIZE); if (ef->firstpage == NULL) { close(ef->fd); return (ENOMEM); } - bytes_read = read(ef->fd, ef->firstpage, PAGE_SIZE); +#ifdef LOADER_VERIEXEC_VECTX + { + int verror; + + ef->vctx = vectx_open(ef->fd, filename, 0L, NULL, &verror, __func__); + if (verror) { + printf("Unverified %s: %s\n", filename, ve_error_get()); + close(ef->fd); + free(ef->vctx); + return (EAUTH); + } + } +#endif + bytes_read = VECTX_READ(VECTX_HANDLE(ef), ef->firstpage, PAGE_SIZE); ef->firstlen = (size_t)bytes_read; if (bytes_read < 0 || ef->firstlen <= sizeof(Elf_Ehdr)) { err = EFTYPE; /* could be EIO, but may be small file */ goto error; } ehdr = ef->ehdr = (Elf_Ehdr *)ef->firstpage; /* Is it ELF? */ if (!IS_ELF(*ehdr)) { err = EFTYPE; goto error; } if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || ehdr->e_ident[EI_VERSION] != EV_CURRENT) /* Version ? */ { err = EFTYPE; goto error; } err = elf_header_convert(ehdr); if (err) goto error; if (ehdr->e_version != EV_CURRENT || ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ err = EFTYPE; goto error; } -#ifdef LOADER_VERIEXEC - if (verify_file(ef->fd, filename, bytes_read, VE_MUST) < 0) { - err = EAUTH; - goto error; +#if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) + if (verify_file(ef->fd, filename, bytes_read, VE_MUST, __func__) < 0) { + err = EAUTH; + goto error; } #endif return (0); error: if (ef->firstpage != NULL) { free(ef->firstpage); ef->firstpage = NULL; } if (ef->fd != -1) { +#ifdef LOADER_VERIEXEC_VECTX + free(ef->vctx); +#endif close(ef->fd); ef->fd = -1; } return (err); } /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object * will be saved in (result). */ int __elfN(loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { return (__elfN(loadfile_raw)(filename, dest, result, 0)); } int __elfN(loadfile_raw)(char *filename, uint64_t dest, struct preloaded_file **result, int multiboot) { struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *ehdr; int err; fp = NULL; bzero(&ef, sizeof(struct elf_file)); ef.fd = -1; err = __elfN(load_elf_header)(filename, &ef); if (err != 0) return (err); ehdr = ef.ehdr; /* * Check to see what sort of module we are. */ kfp = file_findfile(NULL, __elfN(kerneltype)); #ifdef __powerpc__ /* * Kernels can be ET_DYN, so just assume the first loaded object is the * kernel. This assumption will be checked later. */ if (kfp == NULL) ef.kernel = 1; #endif if (ef.kernel || ehdr->e_type == ET_EXEC) { /* Looks like a kernel */ if (kfp != NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: kernel already loaded\n"); err = EPERM; goto oerr; } /* * Calculate destination address based on kernel entrypoint. * * For ARM, the destination address is independent of any values * in the elf header (an ARM kernel can be loaded at any 2MB * boundary), so we leave dest set to the value calculated by * archsw.arch_loadaddr() and passed in to this function. */ #ifndef __arm__ if (ehdr->e_type == ET_EXEC) dest = (ehdr->e_entry & ~PAGE_MASK); #endif if ((ehdr->e_entry & ~PAGE_MASK) == 0) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: not a kernel (maybe static binary?)\n"); err = EPERM; goto oerr; } ef.kernel = 1; } else if (ehdr->e_type == ET_DYN) { /* Looks like a kld module */ if (multiboot != 0) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module as multiboot\n"); err = EPERM; goto oerr; } if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module before kernel\n"); err = EPERM; goto oerr; } if (strcmp(__elfN(kerneltype), kfp->f_type)) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); err = EPERM; goto oerr; } /* Looks OK, got ahead */ ef.kernel = 0; } else { err = EFTYPE; goto oerr; } if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_ELF, ehdr, dest); else dest = roundup(dest, PAGE_SIZE); /* * Ok, we think we should handle this. */ fp = file_alloc(); if (fp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadfile: cannot allocate module info\n"); err = EPERM; goto out; } if (ef.kernel == 1 && multiboot == 0) setenv("kernelname", filename, 1); fp->f_name = strdup(filename); if (multiboot == 0) fp->f_type = strdup(ef.kernel ? __elfN(kerneltype) : __elfN(moduletype)); else fp->f_type = strdup("elf multiboot kernel"); #ifdef ELF_VERBOSE if (ef.kernel) printf("%s entry at 0x%jx\n", filename, (uintmax_t)ehdr->e_entry); #else printf("%s ", filename); #endif fp->f_size = __elfN(loadimage)(fp, &ef, dest); if (fp->f_size == 0 || fp->f_addr == 0) goto ioerr; /* save exec header as metadata */ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); /* Load OK, return module pointer */ *result = (struct preloaded_file *)fp; err = 0; goto out; ioerr: err = EIO; oerr: file_discard(fp); out: if (ef.firstpage) free(ef.firstpage); - if (ef.fd != -1) + if (ef.fd != -1) { +#ifdef LOADER_VERIEXEC_VECTX + if (!err && ef.vctx) { + int verror; + + verror = vectx_close(ef.vctx, VE_MUST, __func__); + if (verror) { + err = EAUTH; + file_discard(fp); + } + } +#endif close(ef.fd); + } return (err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off) { int i; u_int j; Elf_Ehdr *ehdr; Elf_Phdr *phdr, *php; Elf_Shdr *shdr; char *shstr; int ret; vm_offset_t firstaddr; vm_offset_t lastaddr; size_t chunk; ssize_t result; Elf_Addr ssym, esym; Elf_Dyn *dp; Elf_Addr adp; Elf_Addr ctors; int ndp; int symstrindex; int symtabindex; Elf_Size size; u_int fpcopy; Elf_Sym sym; Elf_Addr p_start, p_end; dp = NULL; shdr = NULL; ret = 0; firstaddr = lastaddr = 0; ehdr = ef->ehdr; if (ehdr->e_type == ET_EXEC) { #if defined(__i386__) || defined(__amd64__) #if __ELF_WORD_SIZE == 64 /* x86_64 relocates after locore */ off = - (off & 0xffffffffff000000ull); #else /* i386 relocates after locore */ off = - (off & 0xff000000u); #endif #elif defined(__powerpc__) /* * On the purely virtual memory machines like e500, the kernel * is linked against its final VA range, which is most often * not available at the loader stage, but only after kernel * initializes and completes its VM settings. In such cases we * cannot use p_vaddr field directly to load ELF segments, but * put them at some 'load-time' locations. */ if (off & 0xf0000000u) { off = -(off & 0xf0000000u); /* * XXX the physical load address should not be * hardcoded. Note that the Book-E kernel assumes that * it's loaded at a 16MB boundary for now... */ off += 0x01000000; ehdr->e_entry += off; #ifdef ELF_VERBOSE printf("Converted entry 0x%jx\n", (uintmax_t)ehdr->e_entry); #endif } else off = 0; #elif defined(__arm__) && !defined(EFI) /* * The elf headers in arm kernels specify virtual addresses in * all header fields, even the ones that should be physical * addresses. We assume the entry point is in the first page, * and masking the page offset will leave us with the virtual * address the kernel was linked at. We subtract that from the * load offset, making 'off' into the value which, when added * to a virtual address in an elf header, translates it to a * physical address. We do the va->pa conversion on the entry * point address in the header now, so that later we can launch * the kernel by just jumping to that address. * * When booting from UEFI the copyin and copyout functions * handle adjusting the location relative to the first virtual * address. Because of this there is no need to adjust the * offset or entry point address as these will both be handled * by the efi code. */ off -= ehdr->e_entry & ~PAGE_MASK; ehdr->e_entry += off; #ifdef ELF_VERBOSE printf("ehdr->e_entry 0x%jx, va<->pa off %llx\n", (uintmax_t)ehdr->e_entry, off); #endif #else off = 0; /* other archs use direct mapped kernels */ #endif } ef->off = off; if (ef->kernel) __elfN(relocation_offset) = off; if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: program header not within first page\n"); goto out; } phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { if (elf_program_header_convert(ehdr, phdr)) continue; /* We want to load PT_LOAD segments only.. */ if (phdr[i].p_type != PT_LOAD) continue; #ifdef ELF_VERBOSE printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", (long)phdr[i].p_filesz, (long)phdr[i].p_offset, (long)(phdr[i].p_vaddr + off), (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); #else if ((phdr[i].p_flags & PF_W) == 0) { printf("text=0x%lx ", (long)phdr[i].p_filesz); } else { printf("data=0x%lx", (long)phdr[i].p_filesz); if (phdr[i].p_filesz < phdr[i].p_memsz) printf("+0x%lx", (long)(phdr[i].p_memsz - phdr[i].p_filesz)); printf(" "); } #endif fpcopy = 0; if (ef->firstlen > phdr[i].p_offset) { fpcopy = ef->firstlen - phdr[i].p_offset; archsw.arch_copyin(ef->firstpage + phdr[i].p_offset, phdr[i].p_vaddr + off, fpcopy); } if (phdr[i].p_filesz > fpcopy) { - if (kern_pread(ef->fd, phdr[i].p_vaddr + off + fpcopy, + if (kern_pread(VECTX_HANDLE(ef), + phdr[i].p_vaddr + off + fpcopy, phdr[i].p_filesz - fpcopy, phdr[i].p_offset + fpcopy) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: read failed\n"); goto out; } } /* clear space from oversized segments; eg: bss */ if (phdr[i].p_filesz < phdr[i].p_memsz) { #ifdef ELF_VERBOSE printf(" (bss: 0x%lx-0x%lx)", (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz -1)); #endif kern_bzero(phdr[i].p_vaddr + off + phdr[i].p_filesz, phdr[i].p_memsz - phdr[i].p_filesz); } #ifdef ELF_VERBOSE printf("\n"); #endif if (archsw.arch_loadseg != NULL) archsw.arch_loadseg(ehdr, phdr + i, off); if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) firstaddr = phdr[i].p_vaddr + off; if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; } lastaddr = roundup(lastaddr, sizeof(long)); /* * Get the section headers. We need this for finding the .ctors * section as well as for loading any symbols. Both may be hard * to do if reading from a .gz file as it involves seeking. I * think the rule is going to have to be that you must strip a * file to remove symbols before gzipping it. */ chunk = (size_t)ehdr->e_shnum * (size_t)ehdr->e_shentsize; if (chunk == 0 || ehdr->e_shoff == 0) goto nosyms; - shdr = alloc_pread(ef->fd, ehdr->e_shoff, chunk); + shdr = alloc_pread(VECTX_HANDLE(ef), ehdr->e_shoff, chunk); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: failed to read section headers"); goto nosyms; } for (i = 0; i < ehdr->e_shnum; i++) elf_section_header_convert(ehdr, &shdr[i]); file_addmetadata(fp, MODINFOMD_SHDR, chunk, shdr); /* * Read the section string table and look for the .ctors section. * We need to tell the kernel where it is so that it can call the * ctors. */ chunk = shdr[ehdr->e_shstrndx].sh_size; if (chunk) { - shstr = alloc_pread(ef->fd, shdr[ehdr->e_shstrndx].sh_offset, - chunk); + shstr = alloc_pread(VECTX_HANDLE(ef), + shdr[ehdr->e_shstrndx].sh_offset, chunk); if (shstr) { for (i = 0; i < ehdr->e_shnum; i++) { if (strcmp(shstr + shdr[i].sh_name, ".ctors") != 0) continue; ctors = shdr[i].sh_addr; file_addmetadata(fp, MODINFOMD_CTORS_ADDR, sizeof(ctors), &ctors); size = shdr[i].sh_size; file_addmetadata(fp, MODINFOMD_CTORS_SIZE, sizeof(size), &size); break; } free(shstr); } } /* * Now load any symbols. */ symtabindex = -1; symstrindex = -1; for (i = 0; i < ehdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_SYMTAB) continue; for (j = 0; j < ehdr->e_phnum; j++) { if (phdr[j].p_type != PT_LOAD) continue; if (shdr[i].sh_offset >= phdr[j].p_offset && (shdr[i].sh_offset + shdr[i].sh_size <= phdr[j].p_offset + phdr[j].p_filesz)) { shdr[i].sh_offset = 0; shdr[i].sh_size = 0; break; } } if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) continue; /* alread loaded in a PT_LOAD above */ /* Save it for loading below */ symtabindex = i; symstrindex = shdr[i].sh_link; } if (symtabindex < 0 || symstrindex < 0) goto nosyms; /* Ok, committed to a load. */ #ifndef ELF_VERBOSE printf("syms=["); #endif ssym = lastaddr; for (i = symtabindex; i >= 0; i = symstrindex) { #ifdef ELF_VERBOSE char *secname; switch(shdr[i].sh_type) { case SHT_SYMTAB: /* Symbol table */ secname = "symtab"; break; case SHT_STRTAB: /* String table */ secname = "strtab"; break; default: secname = "WHOA!!"; break; } #endif size = shdr[i].sh_size; #if defined(__powerpc__) #if __ELF_WORD_SIZE == 64 size = htobe64(size); #else size = htobe32(size); #endif #endif archsw.arch_copyin(&size, lastaddr, sizeof(size)); lastaddr += sizeof(size); #ifdef ELF_VERBOSE printf("\n%s: 0x%jx@0x%jx -> 0x%jx-0x%jx", secname, (uintmax_t)shdr[i].sh_size, (uintmax_t)shdr[i].sh_offset, (uintmax_t)lastaddr, (uintmax_t)(lastaddr + shdr[i].sh_size)); #else if (i == symstrindex) printf("+"); printf("0x%lx+0x%lx", (long)sizeof(size), (long)size); #endif - if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { + if (VECTX_LSEEK(VECTX_HANDLE(ef), (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not seek for symbols - skipped!"); lastaddr = ssym; ssym = 0; goto nosyms; } - result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size); + result = archsw.arch_readin(VECTX_HANDLE(ef), lastaddr, shdr[i].sh_size); if (result < 0 || (size_t)result != shdr[i].sh_size) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_loadimage: could not read symbols - skipped! " "(%ju != %ju)", (uintmax_t)result, (uintmax_t)shdr[i].sh_size); lastaddr = ssym; ssym = 0; goto nosyms; } /* Reset offsets relative to ssym */ lastaddr += shdr[i].sh_size; lastaddr = roundup(lastaddr, sizeof(size)); if (i == symtabindex) symtabindex = -1; else if (i == symstrindex) symstrindex = -1; } esym = lastaddr; #ifndef ELF_VERBOSE printf("]"); #endif #if defined(__powerpc__) /* On PowerPC we always need to provide BE data to the kernel */ #if __ELF_WORD_SIZE == 64 ssym = htobe64((uint64_t)ssym); esym = htobe64((uint64_t)esym); #else ssym = htobe32((uint32_t)ssym); esym = htobe32((uint32_t)esym); #endif #endif file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); nosyms: printf("\n"); ret = lastaddr - firstaddr; fp->f_addr = firstaddr; php = NULL; for (i = 0; i < ehdr->e_phnum; i++) { if (phdr[i].p_type == PT_DYNAMIC) { php = phdr + i; adp = php->p_vaddr; file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(adp), &adp); break; } } if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ goto out; ndp = php->p_filesz / sizeof(Elf_Dyn); if (ndp == 0) goto out; dp = malloc(php->p_filesz); if (dp == NULL) goto out; archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); ef->strsz = 0; for (i = 0; i < ndp; i++) { if (dp[i].d_tag == 0) break; switch (dp[i].d_tag) { case DT_HASH: ef->hashtab = (Elf_Hashelt*)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_STRTAB: ef->strtab = (char *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_STRSZ: ef->strsz = dp[i].d_un.d_val; break; case DT_SYMTAB: ef->symtab = (Elf_Sym *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_REL: ef->rel = (Elf_Rel *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_RELSZ: ef->relsz = dp[i].d_un.d_val; break; case DT_RELA: ef->rela = (Elf_Rela *)(uintptr_t)(dp[i].d_un.d_ptr + off); break; case DT_RELASZ: ef->relasz = dp[i].d_un.d_val; break; default: break; } } if (ef->hashtab == NULL || ef->symtab == NULL || ef->strtab == NULL || ef->strsz == 0) goto out; COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); ef->buckets = ef->hashtab + 2; ef->chains = ef->buckets + ef->nbuckets; if (__elfN(lookup_symbol)(fp, ef, "__start_set_modmetadata_set", &sym) != 0) return 0; p_start = sym.st_value + ef->off; if (__elfN(lookup_symbol)(fp, ef, "__stop_set_modmetadata_set", &sym) != 0) return ENOENT; p_end = sym.st_value + ef->off; if (__elfN(parse_modmetadata)(fp, ef, p_start, p_end) == 0) goto out; if (ef->kernel) /* kernel must not depend on anything */ goto out; out: if (dp) free(dp); if (shdr) free(shdr); return ret; } static char invalid_name[] = "bad"; char * fake_modname(const char *name) { const char *sp, *ep; char *fp; size_t len; sp = strrchr(name, '/'); if (sp) sp++; else sp = name; ep = strrchr(sp, '.'); if (ep == NULL) { ep = sp + strlen(sp); } if (ep == sp) { sp = invalid_name; ep = invalid_name + sizeof(invalid_name) - 1; } len = ep - sp; fp = malloc(len + 1); if (fp == NULL) return NULL; memcpy(fp, sp, len); fp[len] = '\0'; return fp; } #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint64_t md_data; /* specific data */ uint64_t md_cval; /* common string label */ }; #endif #if defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint32_t md_data; /* specific data */ uint32_t md_cval; /* common string label */ }; #endif int __elfN(load_modmetadata)(struct preloaded_file *fp, uint64_t dest) { struct elf_file ef; int err, i, j; Elf_Shdr *sh_meta, *shdr = NULL; Elf_Shdr *sh_data[2]; char *shstrtab = NULL; size_t size; Elf_Addr p_start, p_end; bzero(&ef, sizeof(struct elf_file)); ef.fd = -1; err = __elfN(load_elf_header)(fp->f_name, &ef); if (err != 0) goto out; if (ef.kernel == 1 || ef.ehdr->e_type == ET_EXEC) { ef.kernel = 1; } else if (ef.ehdr->e_type != ET_DYN) { err = EFTYPE; goto out; } size = (size_t)ef.ehdr->e_shnum * (size_t)ef.ehdr->e_shentsize; - shdr = alloc_pread(ef.fd, ef.ehdr->e_shoff, size); + shdr = alloc_pread(VECTX_HANDLE(&ef), ef.ehdr->e_shoff, size); if (shdr == NULL) { err = ENOMEM; goto out; } /* Load shstrtab. */ - shstrtab = alloc_pread(ef.fd, shdr[ef.ehdr->e_shstrndx].sh_offset, + shstrtab = alloc_pread(VECTX_HANDLE(&ef), shdr[ef.ehdr->e_shstrndx].sh_offset, shdr[ef.ehdr->e_shstrndx].sh_size); if (shstrtab == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load shstrtab\n"); err = EFTYPE; goto out; } /* Find set_modmetadata_set and data sections. */ sh_data[0] = sh_data[1] = sh_meta = NULL; for (i = 0, j = 0; i < ef.ehdr->e_shnum; i++) { if (strcmp(&shstrtab[shdr[i].sh_name], "set_modmetadata_set") == 0) { sh_meta = &shdr[i]; } if ((strcmp(&shstrtab[shdr[i].sh_name], ".data") == 0) || (strcmp(&shstrtab[shdr[i].sh_name], ".rodata") == 0)) { sh_data[j++] = &shdr[i]; } } if (sh_meta == NULL || sh_data[0] == NULL || sh_data[1] == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to find set_modmetadata_set or data sections\n"); err = EFTYPE; goto out; } /* Load set_modmetadata_set into memory */ - err = kern_pread(ef.fd, dest, sh_meta->sh_size, sh_meta->sh_offset); + err = kern_pread(VECTX_HANDLE(&ef), dest, sh_meta->sh_size, sh_meta->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load set_modmetadata_set: %d\n", err); goto out; } p_start = dest; p_end = dest + sh_meta->sh_size; dest += sh_meta->sh_size; /* Load data sections into memory. */ - err = kern_pread(ef.fd, dest, sh_data[0]->sh_size, + err = kern_pread(VECTX_HANDLE(&ef), dest, sh_data[0]->sh_size, sh_data[0]->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load data: %d\n", err); goto out; } /* * We have to increment the dest, so that the offset is the same into * both the .rodata and .data sections. */ ef.off = -(sh_data[0]->sh_addr - dest); dest += (sh_data[1]->sh_addr - sh_data[0]->sh_addr); - err = kern_pread(ef.fd, dest, sh_data[1]->sh_size, + err = kern_pread(VECTX_HANDLE(&ef), dest, sh_data[1]->sh_size, sh_data[1]->sh_offset); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to load data: %d\n", err); goto out; } err = __elfN(parse_modmetadata)(fp, &ef, p_start, p_end); if (err != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "load_modmetadata: unable to parse metadata: %d\n", err); goto out; } out: if (shstrtab != NULL) free(shstrtab); if (shdr != NULL) free(shdr); if (ef.firstpage != NULL) free(ef.firstpage); - if (ef.fd != -1) + if (ef.fd != -1) { +#ifdef LOADER_VERIEXEC_VECTX + if (!err && ef.vctx) { + int verror; + + verror = vectx_close(ef.vctx, VE_MUST, __func__); + if (verror) { + err = EAUTH; + file_discard(fp); + } + } +#endif close(ef.fd); + } return (err); } int __elfN(parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef, Elf_Addr p_start, Elf_Addr p_end) { struct mod_metadata md; #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 struct mod_metadata32 md32; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p; modcnt = 0; p = p_start; while (p < p_end) { COPYOUT(p, &v, sizeof(v)); error = __elfN(reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error == EOPNOTSUPP) v += ef->off; else if (error != 0) return (error); #if (defined(__i386__) || defined(__powerpc__)) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error == EOPNOTSUPP) { md64.md_cval += ef->off; md64.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #elif defined(__amd64__) && __ELF_WORD_SIZE == 32 COPYOUT(v, &md32, sizeof(md32)); error = __elfN(reloc_ptr)(fp, ef, v, &md32, sizeof(md32)); if (error == EOPNOTSUPP) { md32.md_cval += ef->off; md32.md_data += ef->off; } else if (error != 0) return (error); md.md_version = md32.md_version; md.md_type = md32.md_type; md.md_cval = (const char *)(uintptr_t)md32.md_cval; md.md_data = (void *)(uintptr_t)md32.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error == EOPNOTSUPP) { md.md_cval += ef->off; md.md_data = (void *)((uintptr_t)md.md_data + (uintptr_t)ef->off); } else if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: if (ef->kernel) /* kernel must not depend on anything */ break; s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; } } if (modcnt == 0) { s = fake_modname(fp->f_name); file_addmodule(fp, s, 1, NULL); free(s); } return 0; } static unsigned long elf_hash(const char *name) { const unsigned char *p = (const unsigned char *) name; unsigned long h = 0; unsigned long g; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xf0000000) != 0) h ^= g >> 24; h &= ~g; } return h; } static const char __elfN(bad_symtable)[] = "elf" __XSTRING(__ELF_WORD_SIZE) "_lookup_symbol: corrupt symbol table\n"; int __elfN(lookup_symbol)(struct preloaded_file *fp, elf_file_t ef, const char* name, Elf_Sym *symp) { Elf_Hashelt symnum; Elf_Sym sym; char *strp; unsigned long hash; hash = elf_hash(name); COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); while (symnum != STN_UNDEF) { if (symnum >= ef->nchains) { printf(__elfN(bad_symtable)); return ENOENT; } COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); if (sym.st_name == 0) { printf(__elfN(bad_symtable)); return ENOENT; } strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); if (strcmp(name, strp) == 0) { free(strp); if (sym.st_shndx != SHN_UNDEF || (sym.st_value != 0 && ELF_ST_TYPE(sym.st_info) == STT_FUNC)) { *symp = sym; return 0; } return ENOENT; } free(strp); COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); } return ENOENT; } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. * * Returns EOPNOTSUPP if no relocation method is supplied. */ static int __elfN(reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { size_t n; Elf_Rela a; Elf_Rel r; int error; /* * The kernel is already relocated, but we still want to apply * offset adjustments. */ if (ef->kernel) return (EOPNOTSUPP); for (n = 0; n < ef->relsz / sizeof(r); n++) { COPYOUT(ef->rel + n, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(symaddr), &r, ELF_RELOC_REL, ef->off, p, val, len); if (error != 0) return (error); } for (n = 0; n < ef->relasz / sizeof(a); n++) { COPYOUT(ef->rela + n, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(symaddr), &a, ELF_RELOC_RELA, ef->off, p, val, len); if (error != 0) return (error); } return (0); } static Elf_Addr __elfN(symaddr)(struct elf_file *ef, Elf_Size symidx) { /* Symbol lookup by index not required here. */ return (0); } Index: stable/12/stand/common/load_elf_obj.c =================================================================== --- stable/12/stand/common/load_elf_obj.c (revision 359734) +++ stable/12/stand/common/load_elf_obj.c (revision 359735) @@ -1,548 +1,581 @@ /*- * Copyright (c) 2004 Ian Dowse * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Peter Wemm * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #define FREEBSD_ELF #include #include "bootstrap.h" #define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) #if defined(__i386__) && __ELF_WORD_SIZE == 64 #undef ELF_TARG_CLASS #undef ELF_TARG_MACH #define ELF_TARG_CLASS ELFCLASS64 #define ELF_TARG_MACH EM_X86_64 #endif typedef struct elf_file { Elf_Ehdr hdr; Elf_Shdr *e_shdr; int symtabindex; /* Index of symbol table */ int shstrindex; /* Index of section name string table */ int fd; vm_offset_t off; +#ifdef LOADER_VERIEXEC_VECTX + struct vectx *vctx; +#endif } *elf_file_t; +#ifdef LOADER_VERIEXEC_VECTX +#define VECTX_HANDLE(ef) (ef)->vctx +#else +#define VECTX_HANDLE(ef) (ef)->fd +#endif + static int __elfN(obj_loadimage)(struct preloaded_file *mp, elf_file_t ef, uint64_t loadaddr); static int __elfN(obj_lookup_set)(struct preloaded_file *mp, elf_file_t ef, const char *name, Elf_Addr *startp, Elf_Addr *stopp, int *countp); static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len); static int __elfN(obj_parse_modmetadata)(struct preloaded_file *mp, elf_file_t ef); static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx); const char *__elfN(obj_kerneltype) = "elf kernel"; const char *__elfN(obj_moduletype) = "elf obj module"; /* * Attempt to load the file (file) as an ELF module. It will be stored at * (dest), and a pointer to a module structure describing the loaded object * will be saved in (result). */ int __elfN(obj_loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { struct preloaded_file *fp, *kfp; struct elf_file ef; Elf_Ehdr *hdr; int err; ssize_t bytes_read; fp = NULL; bzero(&ef, sizeof(struct elf_file)); /* * Open the image, read and validate the ELF header */ if (filename == NULL) /* can't handle nameless */ return(EFTYPE); if ((ef.fd = open(filename, O_RDONLY)) == -1) return(errno); +#ifdef LOADER_VERIEXEC_VECTX + { + int verror; + ef.vctx = vectx_open(ef.fd, filename, 0L, NULL, &verror, __func__); + if (verror) { + printf("Unverified %s: %s\n", filename, ve_error_get()); + close(ef.fd); + free(ef.vctx); + return (EAUTH); + } + } +#endif + hdr = &ef.hdr; - bytes_read = read(ef.fd, hdr, sizeof(*hdr)); + bytes_read = VECTX_READ(VECTX_HANDLE(&ef), hdr, sizeof(*hdr)); if (bytes_read != sizeof(*hdr)) { err = EFTYPE; /* could be EIO, but may be small file */ goto oerr; } /* Is it ELF? */ if (!IS_ELF(*hdr)) { err = EFTYPE; goto oerr; } if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ hdr->e_ident[EI_DATA] != ELF_TARG_DATA || hdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH || /* Machine ? */ hdr->e_type != ET_REL) { err = EFTYPE; goto oerr; } if (hdr->e_shnum * hdr->e_shentsize == 0 || hdr->e_shoff == 0 || hdr->e_shentsize != sizeof(Elf_Shdr)) { err = EFTYPE; goto oerr; } -#ifdef LOADER_VERIEXEC - if (verify_file(ef.fd, filename, bytes_read, VE_MUST) < 0) { - err = EAUTH; - goto oerr; +#if defined(LOADER_VERIEXEC) && !defined(LOADER_VERIEXEC_VECTX) + if (verify_file(ef.fd, filename, bytes_read, VE_MUST, __func__) < 0) { + err = EAUTH; + goto oerr; } #endif kfp = file_findfile(NULL, __elfN(obj_kerneltype)); if (kfp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: can't load module before kernel\n"); err = EPERM; goto oerr; } if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_ELF, hdr, dest); else dest = roundup(dest, PAGE_SIZE); /* * Ok, we think we should handle this. */ fp = file_alloc(); if (fp == NULL) { printf("elf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadfile: cannot allocate module info\n"); err = EPERM; goto out; } fp->f_name = strdup(filename); fp->f_type = strdup(__elfN(obj_moduletype)); printf("%s ", filename); fp->f_size = __elfN(obj_loadimage)(fp, &ef, dest); if (fp->f_size == 0 || fp->f_addr == 0) goto ioerr; /* save exec header as metadata */ file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*hdr), hdr); /* Load OK, return module pointer */ *result = (struct preloaded_file *)fp; err = 0; goto out; ioerr: err = EIO; oerr: file_discard(fp); out: +#ifdef LOADER_VERIEXEC_VECTX + if (!err && ef.vctx) { + int verror; + + verror = vectx_close(ef.vctx, VE_MUST, __func__); + if (verror) { + err = EAUTH; + file_discard(fp); + } + } +#endif close(ef.fd); if (ef.e_shdr != NULL) free(ef.e_shdr); return(err); } /* * With the file (fd) open on the image, and (ehdr) containing * the Elf header, load the image at (off) */ static int __elfN(obj_loadimage)(struct preloaded_file *fp, elf_file_t ef, uint64_t off) { Elf_Ehdr *hdr; Elf_Shdr *shdr, *cshdr, *lshdr; vm_offset_t firstaddr, lastaddr; int i, nsym, res, ret, shdrbytes, symstrindex; ret = 0; firstaddr = lastaddr = (vm_offset_t)off; hdr = &ef->hdr; ef->off = (vm_offset_t)off; /* Read in the section headers. */ shdrbytes = hdr->e_shnum * hdr->e_shentsize; - shdr = alloc_pread(ef->fd, (off_t)hdr->e_shoff, shdrbytes); + shdr = alloc_pread(VECTX_HANDLE(ef), (off_t)hdr->e_shoff, shdrbytes); if (shdr == NULL) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: read section headers failed\n"); goto out; } ef->e_shdr = shdr; /* * Decide where to load everything, but don't read it yet. * We store the load address as a non-zero sh_addr value. * Start with the code/data and bss. */ for (i = 0; i < hdr->e_shnum; i++) shdr[i].sh_addr = 0; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_size == 0) continue; switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: #if defined(__i386__) || defined(__amd64__) case SHT_X86_64_UNWIND: #endif if ((shdr[i].sh_flags & SHF_ALLOC) == 0) break; lastaddr = roundup(lastaddr, shdr[i].sh_addralign); shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } /* Symbols. */ nsym = 0; for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_SYMTAB: nsym++; ef->symtabindex = i; shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } if (nsym != 1) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has no valid symbol table\n"); goto out; } lastaddr = roundup(lastaddr, shdr[ef->symtabindex].sh_addralign); shdr[ef->symtabindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[ef->symtabindex].sh_size; symstrindex = shdr[ef->symtabindex].sh_link; if (symstrindex < 0 || symstrindex >= hdr->e_shnum || shdr[symstrindex].sh_type != SHT_STRTAB) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has invalid symbol strings\n"); goto out; } lastaddr = roundup(lastaddr, shdr[symstrindex].sh_addralign); shdr[symstrindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[symstrindex].sh_size; /* Section names. */ if (hdr->e_shstrndx == 0 || hdr->e_shstrndx >= hdr->e_shnum || shdr[hdr->e_shstrndx].sh_type != SHT_STRTAB) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: file has no section names\n"); goto out; } ef->shstrindex = hdr->e_shstrndx; lastaddr = roundup(lastaddr, shdr[ef->shstrindex].sh_addralign); shdr[ef->shstrindex].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[ef->shstrindex].sh_size; /* Relocation tables. */ for (i = 0; i < hdr->e_shnum; i++) { switch (shdr[i].sh_type) { case SHT_REL: case SHT_RELA: if ((shdr[shdr[i].sh_info].sh_flags & SHF_ALLOC) == 0) break; lastaddr = roundup(lastaddr, shdr[i].sh_addralign); shdr[i].sh_addr = (Elf_Addr)lastaddr; lastaddr += shdr[i].sh_size; break; } } /* Clear the whole area, including bss regions. */ kern_bzero(firstaddr, lastaddr - firstaddr); /* Figure section with the lowest file offset we haven't loaded yet. */ for (cshdr = NULL; /* none */; /* none */) { /* * Find next section to load. The complexity of this loop is * O(n^2), but with the number of sections being typically * small, we do not care. */ lshdr = cshdr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_addr == 0 || shdr[i].sh_type == SHT_NOBITS) continue; /* Skip sections that were loaded already. */ if (lshdr != NULL && lshdr->sh_offset >= shdr[i].sh_offset) continue; /* Find section with smallest offset. */ if (cshdr == lshdr || cshdr->sh_offset > shdr[i].sh_offset) cshdr = &shdr[i]; } if (cshdr == lshdr) break; - if (kern_pread(ef->fd, (vm_offset_t)cshdr->sh_addr, + if (kern_pread(VECTX_HANDLE(ef), (vm_offset_t)cshdr->sh_addr, cshdr->sh_size, (off_t)cshdr->sh_offset) != 0) { printf("\nelf" __XSTRING(__ELF_WORD_SIZE) "_obj_loadimage: read failed\n"); goto out; } } file_addmetadata(fp, MODINFOMD_SHDR, shdrbytes, shdr); res = __elfN(obj_parse_modmetadata)(fp, ef); if (res != 0) goto out; ret = lastaddr - firstaddr; fp->f_addr = firstaddr; printf("size 0x%lx at 0x%lx", (u_long)ret, (u_long)firstaddr); out: printf("\n"); return ret; } #if defined(__i386__) && __ELF_WORD_SIZE == 64 struct mod_metadata64 { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ uint64_t md_data; /* specific data */ uint64_t md_cval; /* common string label */ }; #endif int __elfN(obj_parse_modmetadata)(struct preloaded_file *fp, elf_file_t ef) { struct mod_metadata md; #if defined(__i386__) && __ELF_WORD_SIZE == 64 struct mod_metadata64 md64; #endif struct mod_depend *mdepend; struct mod_version mver; char *s; int error, modcnt, minfolen; Elf_Addr v, p, p_stop; if (__elfN(obj_lookup_set)(fp, ef, "modmetadata_set", &p, &p_stop, &modcnt) != 0) return 0; modcnt = 0; while (p < p_stop) { COPYOUT(p, &v, sizeof(v)); error = __elfN(obj_reloc_ptr)(fp, ef, p, &v, sizeof(v)); if (error != 0) return (error); #if defined(__i386__) && __ELF_WORD_SIZE == 64 COPYOUT(v, &md64, sizeof(md64)); error = __elfN(obj_reloc_ptr)(fp, ef, v, &md64, sizeof(md64)); if (error != 0) return (error); md.md_version = md64.md_version; md.md_type = md64.md_type; md.md_cval = (const char *)(uintptr_t)md64.md_cval; md.md_data = (void *)(uintptr_t)md64.md_data; #else COPYOUT(v, &md, sizeof(md)); error = __elfN(obj_reloc_ptr)(fp, ef, v, &md, sizeof(md)); if (error != 0) return (error); #endif p += sizeof(Elf_Addr); switch(md.md_type) { case MDT_DEPEND: s = strdupout((vm_offset_t)md.md_cval); minfolen = sizeof(*mdepend) + strlen(s) + 1; mdepend = malloc(minfolen); if (mdepend == NULL) return ENOMEM; COPYOUT((vm_offset_t)md.md_data, mdepend, sizeof(*mdepend)); strcpy((char*)(mdepend + 1), s); free(s); file_addmetadata(fp, MODINFOMD_DEPLIST, minfolen, mdepend); free(mdepend); break; case MDT_VERSION: s = strdupout((vm_offset_t)md.md_cval); COPYOUT((vm_offset_t)md.md_data, &mver, sizeof(mver)); file_addmodule(fp, s, mver.mv_version, NULL); free(s); modcnt++; break; case MDT_MODULE: case MDT_PNP_INFO: break; default: printf("unknown type %d\n", md.md_type); break; } } return 0; } static int __elfN(obj_lookup_set)(struct preloaded_file *fp, elf_file_t ef, const char* name, Elf_Addr *startp, Elf_Addr *stopp, int *countp) { Elf_Ehdr *hdr; Elf_Shdr *shdr; char *p; vm_offset_t shstrtab; int i; hdr = &ef->hdr; shdr = ef->e_shdr; shstrtab = shdr[ef->shstrindex].sh_addr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_PROGBITS) continue; if (shdr[i].sh_name == 0) continue; p = strdupout(shstrtab + shdr[i].sh_name); if (strncmp(p, "set_", 4) == 0 && strcmp(p + 4, name) == 0) { *startp = shdr[i].sh_addr; *stopp = shdr[i].sh_addr + shdr[i].sh_size; *countp = (*stopp - *startp) / sizeof(Elf_Addr); free(p); return (0); } free(p); } return (ESRCH); } /* * Apply any intra-module relocations to the value. p is the load address * of the value and val/len is the value to be modified. This does NOT modify * the image in-place, because this is done by kern_linker later on. */ static int __elfN(obj_reloc_ptr)(struct preloaded_file *mp, elf_file_t ef, Elf_Addr p, void *val, size_t len) { Elf_Ehdr *hdr; Elf_Shdr *shdr; Elf_Addr off = p; Elf_Addr base; Elf_Rela a, *abase; Elf_Rel r, *rbase; int error, i, j, nrel, nrela; hdr = &ef->hdr; shdr = ef->e_shdr; for (i = 0; i < hdr->e_shnum; i++) { if (shdr[i].sh_type != SHT_RELA && shdr[i].sh_type != SHT_REL) continue; base = shdr[shdr[i].sh_info].sh_addr; if (base == 0 || shdr[i].sh_addr == 0) continue; if (off < base || off + len > base + shdr[shdr[i].sh_info].sh_size) continue; switch (shdr[i].sh_type) { case SHT_RELA: abase = (Elf_Rela *)(intptr_t)shdr[i].sh_addr; nrela = shdr[i].sh_size / sizeof(Elf_Rela); for (j = 0; j < nrela; j++) { COPYOUT(abase + j, &a, sizeof(a)); error = __elfN(reloc)(ef, __elfN(obj_symaddr), &a, ELF_RELOC_RELA, base, off, val, len); if (error != 0) return (error); } break; case SHT_REL: rbase = (Elf_Rel *)(intptr_t)shdr[i].sh_addr; nrel = shdr[i].sh_size / sizeof(Elf_Rel); for (j = 0; j < nrel; j++) { COPYOUT(rbase + j, &r, sizeof(r)); error = __elfN(reloc)(ef, __elfN(obj_symaddr), &r, ELF_RELOC_REL, base, off, val, len); if (error != 0) return (error); } break; } } return (0); } /* Look up the address of a specified symbol. */ static Elf_Addr __elfN(obj_symaddr)(struct elf_file *ef, Elf_Size symidx) { Elf_Sym sym; Elf_Addr base; if (symidx >= ef->e_shdr[ef->symtabindex].sh_size / sizeof(Elf_Sym)) return (0); COPYOUT(ef->e_shdr[ef->symtabindex].sh_addr + symidx * sizeof(Elf_Sym), &sym, sizeof(sym)); if (sym.st_shndx == SHN_UNDEF || sym.st_shndx >= ef->hdr.e_shnum) return (0); base = ef->e_shdr[sym.st_shndx].sh_addr; if (base == 0) return (0); return (base + sym.st_value); } Index: stable/12/stand/common/misc.c =================================================================== --- stable/12/stand/common/misc.c (revision 359734) +++ stable/12/stand/common/misc.c (revision 359735) @@ -1,220 +1,220 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include /* * Concatenate the (argc) elements of (argv) into a single string, and return * a copy of same. */ char * unargv(int argc, char *argv[]) { size_t hlong; int i; char *cp; for (i = 0, hlong = 0; i < argc; i++) hlong += strlen(argv[i]) + 2; if(hlong == 0) return(NULL); cp = malloc(hlong); cp[0] = 0; for (i = 0; i < argc; i++) { strcat(cp, argv[i]); if (i < (argc - 1)) strcat(cp, " "); } return(cp); } /* * Get the length of a string in kernel space */ size_t strlenout(vm_offset_t src) { char c; size_t len; for (len = 0; ; len++) { archsw.arch_copyout(src++, &c, 1); if (c == 0) break; } return(len); } /* * Make a duplicate copy of a string in kernel space */ char * strdupout(vm_offset_t str) { char *result, *cp; result = malloc(strlenout(str) + 1); for (cp = result; ;cp++) { archsw.arch_copyout(str++, cp, 1); if (*cp == 0) break; } return(result); } /* Zero a region in kernel space. */ void kern_bzero(vm_offset_t dest, size_t len) { char buf[256]; size_t chunk, resid; bzero(buf, sizeof(buf)); resid = len; while (resid > 0) { chunk = min(sizeof(buf), resid); archsw.arch_copyin(buf, dest, chunk); resid -= chunk; dest += chunk; } } /* * Read the specified part of a file to kernel space. Unlike regular * pread, the file pointer is advanced to the end of the read data, * and it just returns 0 if successful. */ int -kern_pread(int fd, vm_offset_t dest, size_t len, off_t off) +kern_pread(readin_handle_t fd, vm_offset_t dest, size_t len, off_t off) { - if (lseek(fd, off, SEEK_SET) == -1) { + if (VECTX_LSEEK(fd, off, SEEK_SET) == -1) { #ifdef DEBUG printf("\nlseek failed\n"); #endif return (-1); } if ((size_t)archsw.arch_readin(fd, dest, len) != len) { #ifdef DEBUG printf("\nreadin failed\n"); #endif return (-1); } return (0); } /* * Read the specified part of a file to a malloced buffer. The file * pointer is advanced to the end of the read data. */ /* coverity[ -tainted_data_return ] */ void * -alloc_pread(int fd, off_t off, size_t len) +alloc_pread(readin_handle_t fd, off_t off, size_t len) { void *buf; buf = malloc(len); if (buf == NULL) { #ifdef DEBUG printf("\nmalloc(%d) failed\n", (int)len); #endif return (NULL); } - if (lseek(fd, off, SEEK_SET) == -1) { + if (VECTX_LSEEK(fd, off, SEEK_SET) == -1) { #ifdef DEBUG printf("\nlseek failed\n"); #endif free(buf); return (NULL); } - if ((size_t)read(fd, buf, len) != len) { + if ((size_t)VECTX_READ(fd, buf, len) != len) { #ifdef DEBUG printf("\nread failed\n"); #endif free(buf); return (NULL); } return (buf); } /* * Display a region in traditional hexdump format. */ void hexdump(caddr_t region, size_t len) { caddr_t line; int x, c; char lbuf[80]; #define emit(fmt, args...) {sprintf(lbuf, fmt , ## args); pager_output(lbuf);} pager_open(); for (line = region; line < (region + len); line += 16) { emit("%08lx ", (long) line); for (x = 0; x < 16; x++) { if ((line + x) < (region + len)) { emit("%02x ", *(uint8_t *)(line + x)); } else { emit("-- "); } if (x == 7) emit(" "); } emit(" |"); for (x = 0; x < 16; x++) { if ((line + x) < (region + len)) { c = *(uint8_t *)(line + x); if ((c < ' ') || (c > '~')) /* !isprint(c) */ c = '.'; emit("%c", c); } else { emit(" "); } } emit("|\n"); } pager_close(); } void dev_cleanup(void) { int i; /* Call cleanup routines */ for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); } Index: stable/12/stand/common/module.c =================================================================== --- stable/12/stand/common/module.c (revision 359734) +++ stable/12/stand/common/module.c (revision 359735) @@ -1,1646 +1,1683 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include __FBSDID("$FreeBSD$"); /* * file/module function dispatcher, support, etc. */ #include #include #include #include #include #include #include #if defined(LOADER_FDT_SUPPORT) #include #endif #include "bootstrap.h" #define MDIR_REMOVED 0x0001 #define MDIR_NOHINTS 0x0002 struct moduledir { char *d_path; /* path of modules directory */ u_char *d_hints; /* content of linker.hints file */ int d_hintsz; /* size of hints data */ int d_flags; STAILQ_ENTRY(moduledir) d_link; }; static int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result); static int file_load_dependencies(struct preloaded_file *base_mod); static char * file_search(const char *name, char **extlist); static struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo); static int file_havepath(const char *name); static char *mod_searchmodule(char *name, struct mod_depend *verinfo); static char * mod_searchmodule_pnpinfo(const char *bus, const char *pnpinfo); static void file_insert_tail(struct preloaded_file *mp); static void file_remove(struct preloaded_file *fp); struct file_metadata* metadata_next(struct file_metadata *base_mp, int type); static void moduledir_readhints(struct moduledir *mdp); static void moduledir_rebuild(void); /* load address should be tweaked by first module loaded (kernel) */ static vm_offset_t loadaddr = 0; #if defined(LOADER_FDT_SUPPORT) static const char *default_searchpath = "/boot/kernel;/boot/modules;/boot/dtb"; #else static const char *default_searchpath = "/boot/kernel;/boot/modules"; #endif static STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list); struct preloaded_file *preloaded_files = NULL; static char *kld_ext_list[] = { ".ko", "", ".debug", NULL }; /* * load an object, either a disk file or code module. * * To load a file, the syntax is: * * load -t * * code modules are loaded as: * * load */ COMMAND_SET(load, "load", "load a kernel or module", command_load); static int command_load(int argc, char *argv[]) { struct preloaded_file *fp; char *typestr; char *prefix; char *skip; int dflag, dofile, dokld, ch, error; dflag = dokld = dofile = 0; optind = 1; optreset = 1; typestr = NULL; if (argc == 1) { command_errmsg = "no filename specified"; return (CMD_CRIT); } prefix = skip = NULL; while ((ch = getopt(argc, argv, "dkp:s:t:")) != -1) { switch(ch) { case 'd': dflag++; break; case 'k': dokld = 1; break; case 'p': prefix = optarg; break; case 's': skip = optarg; break; case 't': typestr = optarg; dofile = 1; break; case '?': default: /* getopt has already reported an error */ return (CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); /* * Request to load a raw file? */ if (dofile) { if ((argc != 2) || (typestr == NULL) || (*typestr == 0)) { command_errmsg = "invalid load type"; return (CMD_CRIT); } #ifdef LOADER_VERIEXEC if (strncmp(typestr, "manifest", 8) == 0) { if (dflag > 0) ve_debug_set(dflag); return (load_manifest(argv[1], prefix, skip, NULL)); } #ifdef LOADER_VERIEXEC_PASS_MANIFEST if (strncmp(typestr, "pass_manifest", 13) == 0) { if (dflag > 0) ve_debug_set(dflag); return (pass_manifest(argv[1], prefix)); } #endif #endif fp = file_findfile(argv[1], typestr); if (fp) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: file '%s' already loaded", argv[1]); return (CMD_WARN); } if (file_loadraw(argv[1], typestr, 1) != NULL) return (CMD_OK); /* Failing to load mfs_root is never going to end well! */ if (strcmp("mfs_root", typestr) == 0) return (CMD_FATAL); return (CMD_ERROR); } /* * Do we have explicit KLD load ? */ if (dokld || file_havepath(argv[1])) { error = mod_loadkld(argv[1], argc - 2, argv + 2); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: KLD '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } /* * Looks like a request for a module. */ error = mod_load(argv[1], NULL, argc - 2, argv + 2); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } #ifdef LOADER_GELI_SUPPORT COMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli); static int command_load_geli(int argc, char *argv[]) { char typestr[80]; char *cp; int ch, num; if (argc < 3) { command_errmsg = "usage is [-n key#] "; return(CMD_ERROR); } num = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "n:")) != -1) { switch(ch) { case 'n': num = strtol(optarg, &cp, 0); if (cp == optarg) { snprintf(command_errbuf, sizeof(command_errbuf), "bad key index '%s'", optarg); return(CMD_ERROR); } break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); sprintf(typestr, "%s:geli_keyfile%d", argv[1], num); return (file_loadraw(argv[2], typestr, 1) ? CMD_OK : CMD_ERROR); } #endif void unload(void) { struct preloaded_file *fp; while (preloaded_files != NULL) { fp = preloaded_files; preloaded_files = preloaded_files->f_next; file_discard(fp); } loadaddr = 0; unsetenv("kernelname"); } COMMAND_SET(unload, "unload", "unload all modules", command_unload); static int command_unload(int argc, char *argv[]) { unload(); return(CMD_OK); } COMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod); static int command_lsmod(int argc, char *argv[]) { struct preloaded_file *fp; struct kernel_module *mp; struct file_metadata *md; char lbuf[80]; int ch, verbose, ret = 0; verbose = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "v")) != -1) { switch(ch) { case 'v': verbose = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } pager_open(); for (fp = preloaded_files; fp; fp = fp->f_next) { snprintf(lbuf, sizeof(lbuf), " %p: ", (void *) fp->f_addr); pager_output(lbuf); pager_output(fp->f_name); snprintf(lbuf, sizeof(lbuf), " (%s, 0x%lx)\n", fp->f_type, (long)fp->f_size); if (pager_output(lbuf)) break; if (fp->f_args != NULL) { pager_output(" args: "); pager_output(fp->f_args); if (pager_output("\n")) break; } if (fp->f_modules) { pager_output(" modules: "); for (mp = fp->f_modules; mp; mp = mp->m_next) { snprintf(lbuf, sizeof(lbuf), "%s.%d ", mp->m_name, mp->m_version); pager_output(lbuf); } if (pager_output("\n")) break; } if (verbose) { /* XXX could add some formatting smarts here to display some better */ for (md = fp->f_metadata; md != NULL; md = md->md_next) { snprintf(lbuf, sizeof(lbuf), " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size); if (pager_output(lbuf)) break; } } if (ret) break; } pager_close(); return(CMD_OK); } COMMAND_SET(pnpmatch, "pnpmatch", "list matched modules based on pnpinfo", command_pnpmatch); static int pnp_dump_flag = 0; static int pnp_unbound_flag = 0; static int pnp_verbose_flag = 0; static int command_pnpmatch(int argc, char *argv[]) { char *module; int ch; pnp_verbose_flag = 0; pnp_dump_flag = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "vd")) != -1) { switch(ch) { case 'v': pnp_verbose_flag = 1; break; case 'd': pnp_dump_flag = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); module = mod_searchmodule_pnpinfo(argv[1], argv[2]); if (module) printf("Matched module: %s\n", module); else if(argv[1]) printf("No module matches %s\n", argv[1]); return (CMD_OK); } COMMAND_SET(pnpload, "pnpload", "load matched modules based on pnpinfo", command_pnpload); static int command_pnpload(int argc, char *argv[]) { char *module; int ch, error; pnp_verbose_flag = 0; pnp_dump_flag = 0; optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "vd")) != -1) { switch(ch) { case 'v': pnp_verbose_flag = 1; break; case 'd': pnp_dump_flag = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); if (argc != 2) return (CMD_ERROR); module = mod_searchmodule_pnpinfo(argv[1], argv[2]); error = mod_load(module, NULL, 0, NULL); if (error == EEXIST) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", argv[1]); return (CMD_WARN); } return (error == 0 ? CMD_OK : CMD_CRIT); } #if defined(LOADER_FDT_SUPPORT) static void pnpautoload_simplebus(void) { const char *pnpstring; const char *compatstr; char *pnpinfo = NULL; char *module = NULL; int tag = 0, len, pnplen; int error; while (1) { pnpstring = fdt_devmatch_next(&tag, &len); if (pnpstring == NULL) return; compatstr = pnpstring; for (pnplen = 0; pnplen != len; compatstr = pnpstring + pnplen) { pnplen += strlen(compatstr) + 1; asprintf(&pnpinfo, "compat=%s", compatstr); module = mod_searchmodule_pnpinfo("simplebus", pnpinfo); if (module) { error = mod_loadkld(module, 0, NULL); if (error) printf("Cannot load module %s\n", module); break; } } free(pnpinfo); free(module); } } #endif struct pnp_bus { const char *name; void (*load)(void); }; struct pnp_bus pnp_buses[] = { #if defined(LOADER_FDT_SUPPORT) {"simplebus", pnpautoload_simplebus}, #endif }; COMMAND_SET(pnpautoload, "pnpautoload", "auto load modules based on pnpinfo", command_pnpautoload); static int command_pnpautoload(int argc, char *argv[]) { int i; int verbose; int ch, match; pnp_verbose_flag = 0; pnp_dump_flag = 0; verbose = 0; optind = 1; optreset = 1; match = 0; while ((ch = getopt(argc, argv, "v")) != -1) { switch(ch) { case 'v': verbose = 1; break; case '?': default: /* getopt has already reported an error */ return(CMD_OK); } } argv += (optind - 1); argc -= (optind - 1); if (argc > 2) return (CMD_ERROR); for (i = 0; i < nitems(pnp_buses); i++) { if (argc == 2 && strcmp(argv[1], pnp_buses[i].name) != 0) { if (verbose) printf("Skipping bus %s\n", pnp_buses[i].name); continue; } if (verbose) printf("Autoloading modules for simplebus\n"); pnp_buses[i].load(); match = 1; } if (match == 0) printf("Unsupported bus %s\n", argv[1]); return (CMD_OK); } /* * File level interface, functions file_* */ int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result) { static int last_file_format = 0; struct preloaded_file *fp; int error; int i; if (archsw.arch_loadaddr != NULL) dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest); error = EFTYPE; for (i = last_file_format, fp = NULL; file_formats[i] && fp == NULL; i++) { error = (file_formats[i]->l_load)(filename, dest, &fp); if (error == 0) { fp->f_loader = last_file_format = i; /* remember the loader */ *result = fp; break; } else if (last_file_format == i && i != 0) { /* Restart from the beginning */ i = -1; last_file_format = 0; fp = NULL; continue; } if (error == EFTYPE) continue; /* Unknown to this handler? */ if (error) { snprintf(command_errbuf, sizeof(command_errbuf), "can't load file '%s': %s", filename, strerror(error)); break; } } return (error); } static int file_load_dependencies(struct preloaded_file *base_file) { struct file_metadata *md; struct preloaded_file *fp; struct mod_depend *verinfo; struct kernel_module *mp; char *dmodname; int error; md = file_findmetadata(base_file, MODINFOMD_DEPLIST); if (md == NULL) return (0); error = 0; do { verinfo = (struct mod_depend*)md->md_data; dmodname = (char *)(verinfo + 1); if (file_findmodule(NULL, dmodname, verinfo) == NULL) { printf("loading required module '%s'\n", dmodname); error = mod_load(dmodname, verinfo, 0, NULL); if (error) break; /* * If module loaded via kld name which isn't listed * in the linker.hints file, we should check if it have * required version. */ mp = file_findmodule(NULL, dmodname, verinfo); if (mp == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "module '%s' exists but with wrong version", dmodname); error = ENOENT; break; } } md = metadata_next(md, MODINFOMD_DEPLIST); } while (md); if (!error) return (0); /* Load failed; discard everything */ while (base_file != NULL) { fp = base_file; base_file = base_file->f_next; file_discard(fp); } return (error); } + +#ifdef LOADER_VERIEXEC_VECTX +#define VECTX_HANDLE(fd) vctx +#else +#define VECTX_HANDLE(fd) fd +#endif + + /* * We've been asked to load (fname) as (type), so just suck it in, * no arguments or anything. */ struct preloaded_file * file_loadraw(const char *fname, char *type, int insert) { struct preloaded_file *fp; char *name; int fd, got; vm_offset_t laddr; +#ifdef LOADER_VERIEXEC_VECTX + struct vectx *vctx; + int verror; +#endif /* We can't load first */ if ((file_findfile(NULL, NULL)) == NULL) { command_errmsg = "can't load file before kernel"; return(NULL); } /* locate the file on the load path */ name = file_search(fname, NULL); if (name == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", fname); return(NULL); } if ((fd = open(name, O_RDONLY)) < 0) { snprintf(command_errbuf, sizeof(command_errbuf), "can't open '%s': %s", name, strerror(errno)); free(name); return(NULL); } +#ifdef LOADER_VERIEXEC_VECTX + vctx = vectx_open(fd, name, 0L, NULL, &verror, __func__); + if (verror) { + sprintf(command_errbuf, "can't verify '%s': %s", + name, ve_error_get()); + free(name); + free(vctx); + close(fd); + return(NULL); + } +#else #ifdef LOADER_VERIEXEC - if (verify_file(fd, name, 0, VE_MUST) < 0) { - sprintf(command_errbuf, "can't verify '%s'", name); + if (verify_file(fd, name, 0, VE_MUST, __func__) < 0) { + sprintf(command_errbuf, "can't verify '%s': %s", + name, ve_error_get()); free(name); close(fd); return(NULL); } #endif +#endif if (archsw.arch_loadaddr != NULL) loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr); printf("%s ", name); laddr = loadaddr; for (;;) { /* read in 4k chunks; size is not really important */ - got = archsw.arch_readin(fd, laddr, 4096); + got = archsw.arch_readin(VECTX_HANDLE(fd), laddr, 4096); if (got == 0) /* end of file */ break; if (got < 0) { /* error */ snprintf(command_errbuf, sizeof(command_errbuf), "error reading '%s': %s", name, strerror(errno)); free(name); close(fd); +#ifdef LOADER_VERIEXEC_VECTX + free(vctx); +#endif return(NULL); } laddr += got; } printf("size=%#jx\n", (uintmax_t)(laddr - loadaddr)); +#ifdef LOADER_VERIEXEC_VECTX + verror = vectx_close(vctx, VE_MUST, __func__); + if (verror) { + free(name); + close(fd); + free(vctx); + return(NULL); + } +#endif /* Looks OK so far; create & populate control structure */ fp = file_alloc(); if (fp == NULL) { snprintf(command_errbuf, sizeof (command_errbuf), "no memory to load %s", name); free(name); close(fd); return (NULL); } fp->f_name = name; fp->f_type = strdup(type); fp->f_args = NULL; fp->f_metadata = NULL; fp->f_loader = -1; fp->f_addr = loadaddr; fp->f_size = laddr - loadaddr; if (fp->f_type == NULL) { snprintf(command_errbuf, sizeof (command_errbuf), "no memory to load %s", name); free(name); close(fd); return (NULL); } /* recognise space consumption */ loadaddr = laddr; /* Add to the list of loaded files */ if (insert != 0) file_insert_tail(fp); close(fd); return(fp); } /* * Load the module (name), pass it (argc),(argv), add container file * to the list of loaded files. * If module is already loaded just assign new argc/argv. */ int mod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[]) { struct kernel_module *mp; int err; char *filename; if (file_havepath(modname)) { printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname); return (mod_loadkld(modname, argc, argv)); } /* see if module is already loaded */ mp = file_findmodule(NULL, modname, verinfo); if (mp) { #ifdef moduleargs free(mp->m_args); mp->m_args = unargv(argc, argv); #endif snprintf(command_errbuf, sizeof(command_errbuf), "warning: module '%s' already loaded", mp->m_name); return (0); } /* locate file with the module on the search path */ filename = mod_searchmodule(modname, verinfo); if (filename == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", modname); return (ENOENT); } err = mod_loadkld(filename, argc, argv); free(filename); return (err); } /* * Load specified KLD. If path is omitted, then try to locate it via * search path. */ int mod_loadkld(const char *kldname, int argc, char *argv[]) { struct preloaded_file *fp; int err; char *filename; vm_offset_t loadaddr_saved; /* * Get fully qualified KLD name */ filename = file_search(kldname, kld_ext_list); if (filename == NULL) { snprintf(command_errbuf, sizeof(command_errbuf), "can't find '%s'", kldname); return (ENOENT); } /* * Check if KLD already loaded */ fp = file_findfile(filename, NULL); if (fp) { snprintf(command_errbuf, sizeof(command_errbuf), "warning: KLD '%s' already loaded", filename); free(filename); return (0); } do { err = file_load(filename, loadaddr, &fp); if (err) break; fp->f_args = unargv(argc, argv); loadaddr_saved = loadaddr; loadaddr = fp->f_addr + fp->f_size; file_insert_tail(fp); /* Add to the list of loaded files */ if (file_load_dependencies(fp) != 0) { err = ENOENT; file_remove(fp); loadaddr = loadaddr_saved; fp = NULL; break; } } while(0); if (err == EFTYPE) { snprintf(command_errbuf, sizeof(command_errbuf), "don't know how to load module '%s'", filename); } if (err) file_discard(fp); free(filename); return (err); } /* * Find a file matching (name) and (type). * NULL may be passed as a wildcard to either. */ struct preloaded_file * file_findfile(const char *name, const char *type) { struct preloaded_file *fp; for (fp = preloaded_files; fp != NULL; fp = fp->f_next) { if (((name == NULL) || !strcmp(name, fp->f_name)) && ((type == NULL) || !strcmp(type, fp->f_type))) break; } return (fp); } /* * Find a module matching (name) inside of given file. * NULL may be passed as a wildcard. */ struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo) { struct kernel_module *mp, *best; int bestver, mver; if (fp == NULL) { for (fp = preloaded_files; fp; fp = fp->f_next) { mp = file_findmodule(fp, modname, verinfo); if (mp) return (mp); } return (NULL); } best = NULL; bestver = 0; for (mp = fp->f_modules; mp; mp = mp->m_next) { if (strcmp(modname, mp->m_name) == 0) { if (verinfo == NULL) return (mp); mver = mp->m_version; if (mver == verinfo->md_ver_preferred) return (mp); if (mver >= verinfo->md_ver_minimum && mver <= verinfo->md_ver_maximum && mver > bestver) { best = mp; bestver = mver; } } } return (best); } /* * Make a copy of (size) bytes of data from (p), and associate them as * metadata of (type) to the module (mp). */ void file_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p) { struct file_metadata *md; md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size); if (md != NULL) { md->md_size = size; md->md_type = type; bcopy(p, md->md_data, size); md->md_next = fp->f_metadata; } fp->f_metadata = md; } /* * Find a metadata object of (type) associated with the file (fp) */ struct file_metadata * file_findmetadata(struct preloaded_file *fp, int type) { struct file_metadata *md; for (md = fp->f_metadata; md != NULL; md = md->md_next) if (md->md_type == type) break; return(md); } /* * Remove all metadata from the file. */ void file_removemetadata(struct preloaded_file *fp) { struct file_metadata *md, *next; for (md = fp->f_metadata; md != NULL; md = next) { next = md->md_next; free(md); } fp->f_metadata = NULL; } struct file_metadata * metadata_next(struct file_metadata *md, int type) { if (md == NULL) return (NULL); while((md = md->md_next) != NULL) if (md->md_type == type) break; return (md); } static char *emptyextlist[] = { "", NULL }; /* * Check if the given file is in place and return full path to it. */ static char * file_lookup(const char *path, const char *name, int namelen, char **extlist) { struct stat st; char *result, *cp, **cpp; int pathlen, extlen, len; pathlen = strlen(path); extlen = 0; if (extlist == NULL) extlist = emptyextlist; for (cpp = extlist; *cpp; cpp++) { len = strlen(*cpp); if (len > extlen) extlen = len; } result = malloc(pathlen + namelen + extlen + 2); if (result == NULL) return (NULL); bcopy(path, result, pathlen); if (pathlen > 0 && result[pathlen - 1] != '/') result[pathlen++] = '/'; cp = result + pathlen; bcopy(name, cp, namelen); cp += namelen; for (cpp = extlist; *cpp; cpp++) { strcpy(cp, *cpp); if (stat(result, &st) == 0 && S_ISREG(st.st_mode)) return result; } free(result); return NULL; } /* * Check if file name have any qualifiers */ static int file_havepath(const char *name) { const char *cp; archsw.arch_getdev(NULL, name, &cp); return (cp != name || strchr(name, '/') != NULL); } /* * Attempt to find the file (name) on the module searchpath. * If (name) is qualified in any way, we simply check it and * return it or NULL. If it is not qualified, then we attempt * to construct a path using entries in the environment variable * module_path. * * The path we return a pointer to need never be freed, as we manage * it internally. */ static char * file_search(const char *name, char **extlist) { struct moduledir *mdp; struct stat sb; char *result; int namelen; /* Don't look for nothing */ if (name == NULL) return(NULL); if (*name == 0) return(strdup(name)); if (file_havepath(name)) { /* Qualified, so just see if it exists */ if (stat(name, &sb) == 0) return(strdup(name)); return(NULL); } moduledir_rebuild(); result = NULL; namelen = strlen(name); STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = file_lookup(mdp->d_path, name, namelen, extlist); if (result) break; } return(result); } #define INT_ALIGN(base, ptr) ptr = \ (base) + roundup2((ptr) - (base), sizeof(int)) static char * mod_search_hints(struct moduledir *mdp, const char *modname, struct mod_depend *verinfo) { u_char *cp, *recptr, *bufend, *best; char *result; int *intp, bestver, blen, clen, found, ival, modnamelen, reclen; moduledir_readhints(mdp); modnamelen = strlen(modname); found = 0; result = NULL; bestver = 0; if (mdp->d_hints == NULL) goto bad; recptr = mdp->d_hints; bufend = recptr + mdp->d_hintsz; clen = blen = 0; best = cp = NULL; while (recptr < bufend && !found) { intp = (int*)recptr; reclen = *intp++; ival = *intp++; cp = (u_char*)intp; switch (ival) { case MDT_VERSION: clen = *cp++; if (clen != modnamelen || bcmp(cp, modname, clen) != 0) break; cp += clen; INT_ALIGN(mdp->d_hints, cp); ival = *(int*)cp; cp += sizeof(int); clen = *cp++; if (verinfo == NULL || ival == verinfo->md_ver_preferred) { found = 1; break; } if (ival >= verinfo->md_ver_minimum && ival <= verinfo->md_ver_maximum && ival > bestver) { bestver = ival; best = cp; blen = clen; } break; default: break; } recptr += reclen + sizeof(int); } /* * Finally check if KLD is in the place */ if (found) result = file_lookup(mdp->d_path, (const char *)cp, clen, NULL); else if (best) result = file_lookup(mdp->d_path, (const char *)best, blen, NULL); bad: /* * If nothing found or hints is absent - fallback to the old way * by using "kldname[.ko]" as module name. */ if (!found && !bestver && result == NULL) result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list); return result; } static int getint(void **ptr) { int *p = *ptr; int rv; p = (int *)roundup2((intptr_t)p, sizeof(int)); rv = *p++; *ptr = p; return rv; } static void getstr(void **ptr, char *val) { int *p = *ptr; char *c = (char *)p; int len = *(uint8_t *)c; memcpy(val, c + 1, len); val[len] = 0; c += len + 1; *ptr = (void *)c; } static int pnpval_as_int(const char *val, const char *pnpinfo) { int rv; char key[256]; char *cp; if (pnpinfo == NULL) return -1; cp = strchr(val, ';'); key[0] = ' '; if (cp == NULL) strlcpy(key + 1, val, sizeof(key) - 1); else { memcpy(key + 1, val, cp - val); key[cp - val + 1] = '\0'; } strlcat(key, "=", sizeof(key)); if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) rv = strtol(pnpinfo + strlen(key + 1), NULL, 0); else { cp = strstr(pnpinfo, key); if (cp == NULL) rv = -1; else rv = strtol(cp + strlen(key), NULL, 0); } return rv; } static void quoted_strcpy(char *dst, const char *src) { char q = ' '; if (*src == '\'' || *src == '"') q = *src++; while (*src && *src != q) *dst++ = *src++; // XXX backtick quoting *dst++ = '\0'; // XXX overflow } static char * pnpval_as_str(const char *val, const char *pnpinfo) { static char retval[256]; char key[256]; char *cp; if (pnpinfo == NULL) { *retval = '\0'; return retval; } cp = strchr(val, ';'); key[0] = ' '; if (cp == NULL) strlcpy(key + 1, val, sizeof(key) - 1); else { memcpy(key + 1, val, cp - val); key[cp - val + 1] = '\0'; } strlcat(key, "=", sizeof(key)); if (strncmp(key + 1, pnpinfo, strlen(key + 1)) == 0) quoted_strcpy(retval, pnpinfo + strlen(key + 1)); else { cp = strstr(pnpinfo, key); if (cp == NULL) strcpy(retval, "MISSING"); else quoted_strcpy(retval, cp + strlen(key)); } return retval; } static char * devmatch_search_hints(struct moduledir *mdp, const char *bus, const char *dev, const char *pnpinfo) { char val1[256], val2[256]; int ival, len, ents, i, notme, mask, bit, v, found; void *ptr, *walker, *hints_end; char *lastmod = NULL, *cp, *s; moduledir_readhints(mdp); found = 0; if (mdp->d_hints == NULL) goto bad; walker = mdp->d_hints; hints_end = walker + mdp->d_hintsz; while (walker < hints_end && !found) { len = getint(&walker); ival = getint(&walker); ptr = walker; switch (ival) { case MDT_VERSION: getstr(&ptr, val1); ival = getint(&ptr); getstr(&ptr, val2); if (pnp_dump_flag || pnp_verbose_flag) printf("Version: if %s.%d kmod %s\n", val1, ival, val2); break; case MDT_MODULE: getstr(&ptr, val1); getstr(&ptr, val2); if (lastmod) free(lastmod); lastmod = strdup(val2); if (pnp_dump_flag || pnp_verbose_flag) printf("module %s in %s\n", val1, val1); break; case MDT_PNP_INFO: if (!pnp_dump_flag && !pnp_unbound_flag && lastmod && strcmp(lastmod, "kernel") == 0) break; getstr(&ptr, val1); getstr(&ptr, val2); ents = getint(&ptr); if (pnp_dump_flag || pnp_verbose_flag) printf("PNP info for bus %s format %s %d entries (%s)\n", val1, val2, ents, lastmod); if (strcmp(val1, "usb") == 0) { if (pnp_verbose_flag) printf("Treating usb as uhub -- bug in source table still?\n"); strcpy(val1, "uhub"); } if (bus && strcmp(val1, bus) != 0) { if (pnp_verbose_flag) printf("Skipped because table for bus %s, looking for %s\n", val1, bus); break; } for (i = 0; i < ents; i++) { if (pnp_verbose_flag) printf("---------- Entry %d ----------\n", i); if (pnp_dump_flag) printf(" "); cp = val2; notme = 0; mask = -1; bit = -1; do { switch (*cp) { /* All integer fields */ case 'I': case 'J': case 'G': case 'L': case 'M': ival = getint(&ptr); if (pnp_dump_flag) { printf("%#x:", ival); break; } if (bit >= 0 && ((1 << bit) & mask) == 0) break; v = pnpval_as_int(cp + 2, pnpinfo); if (pnp_verbose_flag) printf("Matching %s (%c) table=%#x tomatch=%#x\n", cp + 2, *cp, v, ival); switch (*cp) { case 'J': if (ival == -1) break; /*FALLTHROUGH*/ case 'I': if (v != ival) notme++; break; case 'G': if (v < ival) notme++; break; case 'L': if (v > ival) notme++; break; case 'M': mask = ival; break; } break; /* String fields */ case 'D': case 'Z': getstr(&ptr, val1); if (pnp_dump_flag) { printf("'%s':", val1); break; } if (*cp == 'D') break; s = pnpval_as_str(cp + 2, pnpinfo); if (strcmp(s, val1) != 0) notme++; break; /* Key override fields, required to be last in the string */ case 'T': /* * This is imperfect and only does one key and will be redone * to be more general for multiple keys. Currently, nothing * does that. */ if (pnp_dump_flag) /* No per-row data stored */ break; if (cp[strlen(cp) - 1] == ';') /* Skip required ; at end */ cp[strlen(cp) - 1] = '\0'; /* in case it's not there */ if ((s = strstr(pnpinfo, cp + 2)) == NULL) notme++; else if (s > pnpinfo && s[-1] != ' ') notme++; break; default: printf("Unknown field type %c\n:", *cp); break; } bit++; cp = strchr(cp, ';'); if (cp) cp++; } while (cp && *cp); if (pnp_dump_flag) printf("\n"); else if (!notme) { if (!pnp_unbound_flag) { if (pnp_verbose_flag) printf("Matches --- %s ---\n", lastmod); } found++; } } break; default: break; } walker = (void *)(len - sizeof(int) + (intptr_t)walker); } if (pnp_unbound_flag && found == 0 && *pnpinfo) { if (pnp_verbose_flag) printf("------------------------- "); printf("%s on %s pnpinfo %s", *dev ? dev : "unattached", bus, pnpinfo); if (pnp_verbose_flag) printf(" -------------------------"); printf("\n"); } if (found != 0) return (lastmod); free(lastmod); bad: return (NULL); } /* * Attempt to locate the file containing the module (name) */ static char * mod_searchmodule(char *name, struct mod_depend *verinfo) { struct moduledir *mdp; char *result; moduledir_rebuild(); /* * Now we ready to lookup module in the given directories */ result = NULL; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = mod_search_hints(mdp, name, verinfo); if (result) break; } return(result); } static char * mod_searchmodule_pnpinfo(const char *bus, const char *pnpinfo) { struct moduledir *mdp; char *result; moduledir_rebuild(); /* * Now we ready to lookup module in the given directories */ result = NULL; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { result = devmatch_search_hints(mdp, bus, NULL, pnpinfo); if (result) break; } return(result); } int file_addmodule(struct preloaded_file *fp, char *modname, int version, struct kernel_module **newmp) { struct kernel_module *mp; struct mod_depend mdepend; bzero(&mdepend, sizeof(mdepend)); mdepend.md_ver_preferred = version; mp = file_findmodule(fp, modname, &mdepend); if (mp) return (EEXIST); mp = calloc(1, sizeof(struct kernel_module)); if (mp == NULL) return (ENOMEM); mp->m_name = strdup(modname); if (mp->m_name == NULL) { free(mp); return (ENOMEM); } mp->m_version = version; mp->m_fp = fp; mp->m_next = fp->f_modules; fp->f_modules = mp; if (newmp) *newmp = mp; return (0); } /* * Throw a file away */ void file_discard(struct preloaded_file *fp) { struct file_metadata *md, *md1; struct kernel_module *mp, *mp1; if (fp == NULL) return; md = fp->f_metadata; while (md) { md1 = md; md = md->md_next; free(md1); } mp = fp->f_modules; while (mp) { free(mp->m_name); mp1 = mp; mp = mp->m_next; free(mp1); } free(fp->f_name); free(fp->f_type); free(fp->f_args); free(fp); } /* * Allocate a new file; must be used instead of malloc() * to ensure safe initialisation. */ struct preloaded_file * file_alloc(void) { return (calloc(1, sizeof(struct preloaded_file))); } /* * Add a module to the chain */ static void file_insert_tail(struct preloaded_file *fp) { struct preloaded_file *cm; /* Append to list of loaded file */ fp->f_next = NULL; if (preloaded_files == NULL) { preloaded_files = fp; } else { for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) ; cm->f_next = fp; } } /* * Remove module from the chain */ static void file_remove(struct preloaded_file *fp) { struct preloaded_file *cm; if (preloaded_files == NULL) return; if (preloaded_files == fp) { preloaded_files = fp->f_next; return; } for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next) { if (cm->f_next == fp) { cm->f_next = fp->f_next; return; } } } static char * moduledir_fullpath(struct moduledir *mdp, const char *fname) { char *cp; cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2); if (cp == NULL) return NULL; strcpy(cp, mdp->d_path); strcat(cp, "/"); strcat(cp, fname); return (cp); } /* * Read linker.hints file into memory performing some sanity checks. */ static void moduledir_readhints(struct moduledir *mdp) { struct stat st; char *path; int fd, size, version; if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS)) return; path = moduledir_fullpath(mdp, "linker.hints"); if (stat(path, &st) != 0 || st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) || st.st_size > LINKER_HINTS_MAX || (fd = open(path, O_RDONLY)) < 0) { free(path); mdp->d_flags |= MDIR_NOHINTS; return; } free(path); size = read(fd, &version, sizeof(version)); if (size != sizeof(version) || version != LINKER_HINTS_VERSION) goto bad; size = st.st_size - size; mdp->d_hints = malloc(size); if (mdp->d_hints == NULL) goto bad; if (read(fd, mdp->d_hints, size) != size) goto bad; mdp->d_hintsz = size; close(fd); return; bad: close(fd); free(mdp->d_hints); mdp->d_hints = NULL; mdp->d_flags |= MDIR_NOHINTS; return; } /* * Extract directories from the ';' separated list, remove duplicates. */ static void moduledir_rebuild(void) { struct moduledir *mdp, *mtmp; const char *path, *cp, *ep; size_t cplen; path = getenv("module_path"); if (path == NULL) path = default_searchpath; /* * Rebuild list of module directories if it changed */ STAILQ_FOREACH(mdp, &moduledir_list, d_link) mdp->d_flags |= MDIR_REMOVED; for (ep = path; *ep != 0; ep++) { cp = ep; for (; *ep != 0 && *ep != ';'; ep++) ; /* * Ignore trailing slashes */ for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--) ; STAILQ_FOREACH(mdp, &moduledir_list, d_link) { if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0) continue; mdp->d_flags &= ~MDIR_REMOVED; break; } if (mdp == NULL) { mdp = malloc(sizeof(*mdp) + cplen + 1); if (mdp == NULL) return; mdp->d_path = (char*)(mdp + 1); bcopy(cp, mdp->d_path, cplen); mdp->d_path[cplen] = 0; mdp->d_hints = NULL; mdp->d_flags = 0; STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link); } if (*ep == 0) break; } /* * Delete unused directories if any */ mdp = STAILQ_FIRST(&moduledir_list); while (mdp) { if ((mdp->d_flags & MDIR_REMOVED) == 0) { mdp = STAILQ_NEXT(mdp, d_link); } else { free(mdp->d_hints); mtmp = mdp; mdp = STAILQ_NEXT(mdp, d_link); STAILQ_REMOVE(&moduledir_list, mtmp, moduledir, d_link); free(mtmp); } } return; } Index: stable/12/stand/common/readin.h =================================================================== --- stable/12/stand/common/readin.h (nonexistent) +++ stable/12/stand/common/readin.h (revision 359735) @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2020, Juniper Networks, Inc. + * + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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$ + */ +#ifndef _READIN_H_ +#define _READIN_H_ + +#ifdef LOADER_VERIEXEC +#include +#endif +#ifdef LOADER_VERIEXEC_VECTX +typedef struct vectx * readin_handle_t; +#define VECTX_READ vectx_read +#define VECTX_LSEEK vectx_lseek +#else +typedef int readin_handle_t; +#define VECTX_READ read +#define VECTX_LSEEK lseek +#endif + +#endif /* !_READIN_H_ */ Property changes on: stable/12/stand/common/readin.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/12/stand/efi/loader/arch/i386/i386_copy.c =================================================================== --- stable/12/stand/efi/loader/arch/i386/i386_copy.c (revision 359734) +++ stable/12/stand/efi/loader/arch/i386/i386_copy.c (revision 359735) @@ -1,59 +1,58 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include __FBSDID("$FreeBSD$"); /* * MD primitives supporting placement of module data * * XXX should check load address/size against memory top. */ #include #include "libi386.h" #include "btxv86.h" ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len) { bcopy(src, PTOV(dest), len); return(len); } ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len) { bcopy(PTOV(src), dest, len); return(len); } - ssize_t -i386_readin(const int fd, vm_offset_t dest, const size_t len) +i386_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { - return (read(fd, PTOV(dest), len)); + return (VECTX_READ(fd, PTOV(dest), len)); } Index: stable/12/stand/efi/loader/copy.c =================================================================== --- stable/12/stand/efi/loader/copy.c (revision 359734) +++ stable/12/stand/efi/loader/copy.c (revision 359735) @@ -1,304 +1,304 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Benno Rice under sponsorship from * the FreeBSD Foundation. * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "loader_efi.h" #if defined(__i386__) || defined(__amd64__) #include #include /* * The code is excerpted from sys/x86/x86/identcpu.c: identify_cpu(), * identify_hypervisor(), and dev/hyperv/vmbus/hyperv.c: hyperv_identify(). */ #define CPUID_LEAF_HV_MAXLEAF 0x40000000 #define CPUID_LEAF_HV_INTERFACE 0x40000001 #define CPUID_LEAF_HV_FEATURES 0x40000003 #define CPUID_LEAF_HV_LIMITS 0x40000005 #define CPUID_HV_IFACE_HYPERV 0x31237648 /* HV#1 */ #define CPUID_HV_MSR_TIME_REFCNT 0x0002 /* MSR_HV_TIME_REF_COUNT */ #define CPUID_HV_MSR_HYPERCALL 0x0020 static int running_on_hyperv(void) { char hv_vendor[16]; uint32_t regs[4]; do_cpuid(1, regs); if ((regs[2] & CPUID2_HV) == 0) return (0); do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs); if (regs[0] < CPUID_LEAF_HV_LIMITS) return (0); ((uint32_t *)&hv_vendor)[0] = regs[1]; ((uint32_t *)&hv_vendor)[1] = regs[2]; ((uint32_t *)&hv_vendor)[2] = regs[3]; hv_vendor[12] = '\0'; if (strcmp(hv_vendor, "Microsoft Hv") != 0) return (0); do_cpuid(CPUID_LEAF_HV_INTERFACE, regs); if (regs[0] != CPUID_HV_IFACE_HYPERV) return (0); do_cpuid(CPUID_LEAF_HV_FEATURES, regs); if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0) return (0); if ((regs[0] & CPUID_HV_MSR_TIME_REFCNT) == 0) return (0); return (1); } #define KERNEL_PHYSICAL_BASE (2*1024*1024) static void efi_verify_staging_size(unsigned long *nr_pages) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map = NULL, *p; EFI_PHYSICAL_ADDRESS start, end; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; unsigned long available_pages = 0; sz = 0; for (;;) { status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (!EFI_ERROR(status)) break; if (status != EFI_BUFFER_TOO_SMALL) { printf("Can't read memory map: %lu\n", EFI_ERROR_CODE(status)); goto out; } free(map); /* Allocate 10 descriptors more than the size reported, * to allow for any fragmentation caused by calling * malloc */ map = malloc(sz + (10 * dsz)); if (map == NULL) { printf("Unable to allocate memory\n"); goto out; } } ndesc = sz / dsz; for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { start = p->PhysicalStart; end = start + p->NumberOfPages * EFI_PAGE_SIZE; if (KERNEL_PHYSICAL_BASE < start || KERNEL_PHYSICAL_BASE >= end) continue; available_pages = p->NumberOfPages - ((KERNEL_PHYSICAL_BASE - start) >> EFI_PAGE_SHIFT); break; } if (available_pages == 0) { printf("Can't find valid memory map for staging area!\n"); goto out; } i++; p = NextMemoryDescriptor(p, dsz); for ( ; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { if (p->Type != EfiConventionalMemory && p->Type != EfiLoaderData) break; if (p->PhysicalStart != end) break; end = p->PhysicalStart + p->NumberOfPages * EFI_PAGE_SIZE; available_pages += p->NumberOfPages; } if (*nr_pages > available_pages) { printf("Staging area's size is reduced: %ld -> %ld!\n", *nr_pages, available_pages); *nr_pages = available_pages; } out: free(map); } #endif /* __i386__ || __amd64__ */ #ifndef EFI_STAGING_SIZE #if defined(__amd64__) #define EFI_STAGING_SIZE 100 #elif defined(__arm__) #define EFI_STAGING_SIZE 32 #else #define EFI_STAGING_SIZE 64 #endif #endif EFI_PHYSICAL_ADDRESS staging, staging_end; int stage_offset_set = 0; ssize_t stage_offset; int efi_copy_init(void) { EFI_STATUS status; unsigned long nr_pages; nr_pages = EFI_SIZE_TO_PAGES((EFI_STAGING_SIZE) * 1024 * 1024); #if defined(__i386__) || defined(__amd64__) /* * We'll decrease nr_pages, if it's too big. Currently we only * apply this to FreeBSD VM running on Hyper-V. Why? Please see * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211746#c28 */ if (running_on_hyperv()) efi_verify_staging_size(&nr_pages); /* * The staging area must reside in the the first 1GB physical * memory: see elf64_exec() in * boot/efi/loader/arch/amd64/elf64_freebsd.c. */ staging = 1024*1024*1024; status = BS->AllocatePages(AllocateMaxAddress, EfiLoaderData, nr_pages, &staging); #else status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, nr_pages, &staging); #endif if (EFI_ERROR(status)) { printf("failed to allocate staging area: %lu\n", EFI_ERROR_CODE(status)); return (status); } staging_end = staging + nr_pages * EFI_PAGE_SIZE; #if defined(__aarch64__) || defined(__arm__) /* * Round the kernel load address to a 2MiB value. This is needed * because the kernel builds a page table based on where it has * been loaded in physical address space. As the kernel will use * either a 1MiB or 2MiB page for this we need to make sure it * is correctly aligned for both cases. */ staging = roundup2(staging, 2 * 1024 * 1024); #endif return (0); } void * efi_translate(vm_offset_t ptr) { return ((void *)(ptr + stage_offset)); } ssize_t efi_copyin(const void *src, vm_offset_t dest, const size_t len) { if (!stage_offset_set) { stage_offset = (vm_offset_t)staging - dest; stage_offset_set = 1; } /* XXX: Callers do not check for failure. */ if (dest + stage_offset + len > staging_end) { errno = ENOMEM; return (-1); } bcopy(src, (void *)(dest + stage_offset), len); return (len); } ssize_t efi_copyout(const vm_offset_t src, void *dest, const size_t len) { /* XXX: Callers do not check for failure. */ if (src + stage_offset + len > staging_end) { errno = ENOMEM; return (-1); } bcopy((void *)(src + stage_offset), dest, len); return (len); } ssize_t -efi_readin(const int fd, vm_offset_t dest, const size_t len) +efi_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { if (dest + stage_offset + len > staging_end) { errno = ENOMEM; return (-1); } - return (read(fd, (void *)(dest + stage_offset), len)); + return (VECTX_READ(fd, (void *)(dest + stage_offset), len)); } void efi_copy_finish(void) { uint64_t *src, *dst, *last; src = (uint64_t *)(uintptr_t)staging; dst = (uint64_t *)(uintptr_t)(staging - stage_offset); last = (uint64_t *)(uintptr_t)staging_end; while (src < last) *dst++ = *src++; } Index: stable/12/stand/efi/loader/loader_efi.h =================================================================== --- stable/12/stand/efi/loader/loader_efi.h (revision 359734) +++ stable/12/stand/efi/loader/loader_efi.h (revision 359735) @@ -1,47 +1,48 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Benno Rice under sponsorship from * the FreeBSD Foundation. * 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$ */ #ifndef _LOADER_EFI_COPY_H_ #define _LOADER_EFI_COPY_H_ #include +#include int efi_autoload(void); int efi_copy_init(void); ssize_t efi_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t efi_copyout(const vm_offset_t src, void *dest, const size_t len); -ssize_t efi_readin(const int fd, vm_offset_t dest, const size_t len); +ssize_t efi_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); void * efi_translate(vm_offset_t ptr); void efi_copy_finish(void); #endif /* _LOADER_EFI_COPY_H_ */ Index: stable/12/stand/efi/loader/main.c =================================================================== --- stable/12/stand/efi/loader/main.c (revision 359734) +++ stable/12/stand/efi/loader/main.c (revision 359735) @@ -1,1557 +1,1565 @@ /*- * Copyright (c) 2008-2010 Rui Paulo * Copyright (c) 2006 Marcel Moolenaar * All rights reserved. * * Copyright (c) 2016-2019 Netflix, Inc. written by M. Warner Losh * * 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 THE AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "efizfs.h" #include "loader_efi.h" struct arch_switch archsw; /* MI/MD interface boundary */ EFI_GUID acpi = ACPI_TABLE_GUID; EFI_GUID acpi20 = ACPI_20_TABLE_GUID; EFI_GUID devid = DEVICE_PATH_PROTOCOL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID mps = MPS_TABLE_GUID; EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID smbios3 = SMBIOS3_TABLE_GUID; EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; EFI_GUID hoblist = HOB_LIST_TABLE_GUID; EFI_GUID lzmadecomp = LZMA_DECOMPRESSION_GUID; EFI_GUID mpcore = ARM_MP_CORE_INFO_TABLE_GUID; EFI_GUID esrt = ESRT_TABLE_GUID; EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; /* * Number of seconds to wait for a keystroke before exiting with failure * in the event no currdev is found. -2 means always break, -1 means * never break, 0 means poll once and then reboot, > 0 means wait for * that many seconds. "fail_timeout" can be set in the environment as * well. */ static int fail_timeout = 5; /* * Current boot variable */ UINT16 boot_current; /* * Image that we booted from. */ EFI_LOADED_IMAGE *boot_img; static bool has_keyboard(void) { EFI_STATUS status; EFI_DEVICE_PATH *path; EFI_HANDLE *hin, *hin_end, *walker; UINTN sz; bool retval = false; /* * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and * do the typical dance to get the right sized buffer. */ sz = 0; hin = NULL; status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { hin = (EFI_HANDLE *)malloc(sz); status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return retval; /* * Look at each of the handles. If it supports the device path protocol, * use it to get the device path for this handle. Then see if that * device path matches either the USB device path for keyboards or the * legacy device path for keyboards. */ hin_end = &hin[sz / sizeof(*hin)]; for (walker = hin; walker < hin_end; walker++) { status = OpenProtocolByHandle(*walker, &devid, (void **)&path); if (EFI_ERROR(status)) continue; while (!IsDevicePathEnd(path)) { /* * Check for the ACPI keyboard node. All PNP3xx nodes * are keyboards of different flavors. Note: It is * unclear of there's always a keyboard node when * there's a keyboard controller, or if there's only one * when a keyboard is detected at boot. */ if (DevicePathType(path) == ACPI_DEVICE_PATH && (DevicePathSubType(path) == ACPI_DP || DevicePathSubType(path) == ACPI_EXTENDED_DP)) { ACPI_HID_DEVICE_PATH *acpi; acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { retval = true; goto out; } /* * Check for USB keyboard node, if present. Unlike a * PS/2 keyboard, these definitely only appear when * connected to the system. */ } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && DevicePathSubType(path) == MSG_USB_CLASS_DP) { USB_CLASS_DEVICE_PATH *usb; usb = (USB_CLASS_DEVICE_PATH *)(void *)path; if (usb->DeviceClass == 3 && /* HID */ usb->DeviceSubClass == 1 && /* Boot devices */ usb->DeviceProtocol == 1) { /* Boot keyboards */ retval = true; goto out; } } path = NextDevicePathNode(path); } } out: free(hin); return retval; } static void set_currdev(const char *devname) { /* * Don't execute hooks here; we may need to try setting these more than * once here if we're probing for the ZFS pool we're supposed to boot. * The currdev hook is intended to just validate user input anyways, * while the loaddev hook makes it immutable once we've determined what * the proper currdev is. */ env_setenv("currdev", EV_VOLATILE | EV_NOHOOK, devname, efi_setcurrdev, env_nounset); env_setenv("loaddev", EV_VOLATILE | EV_NOHOOK, devname, env_noset, env_nounset); } static void set_currdev_devdesc(struct devdesc *currdev) { const char *devname; devname = efi_fmtdev(currdev); printf("Setting currdev to %s\n", devname); set_currdev(devname); } static void set_currdev_devsw(struct devsw *dev, int unit) { struct devdesc currdev; currdev.d_dev = dev; currdev.d_unit = unit; set_currdev_devdesc(&currdev); } static void set_currdev_pdinfo(pdinfo_t *dp) { /* * Disks are special: they have partitions. if the parent * pointer is non-null, we're a partition not a full disk * and we need to adjust currdev appropriately. */ if (dp->pd_devsw->dv_type == DEVT_DISK) { struct disk_devdesc currdev; currdev.dd.d_dev = dp->pd_devsw; if (dp->pd_parent == NULL) { currdev.dd.d_unit = dp->pd_unit; currdev.d_slice = D_SLICENONE; currdev.d_partition = D_PARTNONE; } else { currdev.dd.d_unit = dp->pd_parent->pd_unit; currdev.d_slice = dp->pd_unit; currdev.d_partition = D_PARTISGPT; /* XXX Assumes GPT */ } set_currdev_devdesc((struct devdesc *)&currdev); } else { set_currdev_devsw(dp->pd_devsw, dp->pd_unit); } } static bool sanity_check_currdev(void) { struct stat st; return (stat(PATH_DEFAULTS_LOADER_CONF, &st) == 0 || #ifdef PATH_BOOTABLE_TOKEN stat(PATH_BOOTABLE_TOKEN, &st) == 0 || /* non-standard layout */ #endif stat(PATH_KERNEL, &st) == 0); } #ifdef EFI_ZFS_BOOT static bool probe_zfs_currdev(uint64_t guid) { char *devname; struct zfs_devdesc currdev; currdev.dd.d_dev = &zfs_dev; currdev.dd.d_unit = 0; currdev.pool_guid = guid; currdev.root_guid = 0; set_currdev_devdesc((struct devdesc *)&currdev); devname = efi_fmtdev(&currdev); init_zfs_bootenv(devname); return (sanity_check_currdev()); } #endif static bool try_as_currdev(pdinfo_t *hd, pdinfo_t *pp) { uint64_t guid; #ifdef EFI_ZFS_BOOT /* * If there's a zpool on this device, try it as a ZFS * filesystem, which has somewhat different setup than all * other types of fs due to imperfect loader integration. * This all stems from ZFS being both a device (zpool) and * a filesystem, plus the boot env feature. */ if (efizfs_get_guid_by_handle(pp->pd_handle, &guid)) return (probe_zfs_currdev(guid)); #endif /* * All other filesystems just need the pdinfo * initialized in the standard way. */ set_currdev_pdinfo(pp); return (sanity_check_currdev()); } /* * Sometimes we get filenames that are all upper case * and/or have backslashes in them. Filter all this out * if it looks like we need to do so. */ static void fix_dosisms(char *p) { while (*p) { if (isupper(*p)) *p = tolower(*p); else if (*p == '\\') *p = '/'; p++; } } #define SIZE(dp, edp) (size_t)((intptr_t)(void *)edp - (intptr_t)(void *)dp) enum { BOOT_INFO_OK = 0, BAD_CHOICE = 1, NOT_SPECIFIC = 2 }; static int match_boot_info(char *boot_info, size_t bisz) { uint32_t attr; uint16_t fplen; size_t len; char *walker, *ep; EFI_DEVICE_PATH *dp, *edp, *first_dp, *last_dp; pdinfo_t *pp; CHAR16 *descr; char *kernel = NULL; FILEPATH_DEVICE_PATH *fp; struct stat st; CHAR16 *text; /* * FreeBSD encodes it's boot loading path into the boot loader * BootXXXX variable. We look for the last one in the path * and use that to load the kernel. However, if we only fine * one DEVICE_PATH, then there's nothing specific and we should * fall back. * * In an ideal world, we'd look at the image handle we were * passed, match up with the loader we are and then return the * next one in the path. This would be most flexible and cover * many chain booting scenarios where you need to use this * boot loader to get to the next boot loader. However, that * doesn't work. We rarely have the path to the image booted * (just the device) so we can't count on that. So, we do the * enxt best thing, we look through the device path(s) passed * in the BootXXXX varaible. If there's only one, we return * NOT_SPECIFIC. Otherwise, we look at the last one and try to * load that. If we can, we return BOOT_INFO_OK. Otherwise we * return BAD_CHOICE for the caller to sort out. */ if (bisz < sizeof(attr) + sizeof(fplen) + sizeof(CHAR16)) return NOT_SPECIFIC; walker = boot_info; ep = walker + bisz; memcpy(&attr, walker, sizeof(attr)); walker += sizeof(attr); memcpy(&fplen, walker, sizeof(fplen)); walker += sizeof(fplen); descr = (CHAR16 *)(intptr_t)walker; len = ucs2len(descr); walker += (len + 1) * sizeof(CHAR16); last_dp = first_dp = dp = (EFI_DEVICE_PATH *)walker; edp = (EFI_DEVICE_PATH *)(walker + fplen); if ((char *)edp > ep) return NOT_SPECIFIC; while (dp < edp && SIZE(dp, edp) > sizeof(EFI_DEVICE_PATH)) { text = efi_devpath_name(dp); if (text != NULL) { printf(" BootInfo Path: %S\n", text); efi_free_devpath_name(text); } last_dp = dp; dp = (EFI_DEVICE_PATH *)((char *)dp + efi_devpath_length(dp)); } /* * If there's only one item in the list, then nothing was * specified. Or if the last path doesn't have a media * path in it. Those show up as various VenHw() nodes * which are basically opaque to us. Don't count those * as something specifc. */ if (last_dp == first_dp) { printf("Ignoring Boot%04x: Only one DP found\n", boot_current); return NOT_SPECIFIC; } if (efi_devpath_to_media_path(last_dp) == NULL) { printf("Ignoring Boot%04x: No Media Path\n", boot_current); return NOT_SPECIFIC; } /* * OK. At this point we either have a good path or a bad one. * Let's check. */ pp = efiblk_get_pdinfo_by_device_path(last_dp); if (pp == NULL) { printf("Ignoring Boot%04x: Device Path not found\n", boot_current); return BAD_CHOICE; } set_currdev_pdinfo(pp); if (!sanity_check_currdev()) { printf("Ignoring Boot%04x: sanity check failed\n", boot_current); return BAD_CHOICE; } /* * OK. We've found a device that matches, next we need to check the last * component of the path. If it's a file, then we set the default kernel * to that. Otherwise, just use this as the default root. * * Reminder: we're running very early, before we've parsed the defaults * file, so we may need to have a hack override. */ dp = efi_devpath_last_node(last_dp); if (DevicePathType(dp) != MEDIA_DEVICE_PATH || DevicePathSubType(dp) != MEDIA_FILEPATH_DP) { printf("Using Boot%04x for root partition\n", boot_current); return (BOOT_INFO_OK); /* use currdir, default kernel */ } fp = (FILEPATH_DEVICE_PATH *)dp; ucs2_to_utf8(fp->PathName, &kernel); if (kernel == NULL) { printf("Not using Boot%04x: can't decode kernel\n", boot_current); return (BAD_CHOICE); } if (*kernel == '\\' || isupper(*kernel)) fix_dosisms(kernel); if (stat(kernel, &st) != 0) { free(kernel); printf("Not using Boot%04x: can't find %s\n", boot_current, kernel); return (BAD_CHOICE); } setenv("kernel", kernel, 1); free(kernel); text = efi_devpath_name(last_dp); if (text) { printf("Using Boot%04x %S + %s\n", boot_current, text, kernel); efi_free_devpath_name(text); } return (BOOT_INFO_OK); } /* * Look at the passed-in boot_info, if any. If we find it then we need * to see if we can find ourselves in the boot chain. If we can, and * there's another specified thing to boot next, assume that the file * is loaded from / and use that for the root filesystem. If can't * find the specified thing, we must fail the boot. If we're last on * the list, then we fallback to looking for the first available / * candidate (ZFS, if there's a bootable zpool, otherwise a UFS * partition that has either /boot/defaults/loader.conf on it or * /boot/kernel/kernel (the default kernel) that we can use. * * We always fail if we can't find the right thing. However, as * a concession to buggy UEFI implementations, like u-boot, if * we have determined that the host is violating the UEFI boot * manager protocol, we'll signal the rest of the program that * a drop to the OK boot loader prompt is possible. */ static int find_currdev(bool do_bootmgr, bool is_last, char *boot_info, size_t boot_info_sz) { pdinfo_t *dp, *pp; EFI_DEVICE_PATH *devpath, *copy; EFI_HANDLE h; CHAR16 *text; struct devsw *dev; int unit; uint64_t extra; int rv; char *rootdev; /* * First choice: if rootdev is already set, use that, even if * it's wrong. */ rootdev = getenv("rootdev"); if (rootdev != NULL) { printf(" Setting currdev to configured rootdev %s\n", rootdev); set_currdev(rootdev); return (0); } /* * Second choice: If uefi_rootdev is set, translate that UEFI device * path to the loader's internal name and use that. */ do { rootdev = getenv("uefi_rootdev"); if (rootdev == NULL) break; devpath = efi_name_to_devpath(rootdev); if (devpath == NULL) break; dp = efiblk_get_pdinfo_by_device_path(devpath); efi_devpath_free(devpath); if (dp == NULL) break; printf(" Setting currdev to UEFI path %s\n", rootdev); set_currdev_pdinfo(dp); return (0); } while (0); /* * Third choice: If we can find out image boot_info, and there's * a follow-on boot image in that boot_info, use that. In this * case root will be the partition specified in that image and * we'll load the kernel specified by the file path. Should there * not be a filepath, we use the default. This filepath overrides * loader.conf. */ if (do_bootmgr) { rv = match_boot_info(boot_info, boot_info_sz); switch (rv) { case BOOT_INFO_OK: /* We found it */ return (0); case BAD_CHOICE: /* specified file not found -> error */ /* XXX do we want to have an escape hatch for last in boot order? */ return (ENOENT); } /* Nothing specified, try normal match */ } #ifdef EFI_ZFS_BOOT /* * Did efi_zfs_probe() detect the boot pool? If so, use the zpool * it found, if it's sane. ZFS is the only thing that looks for * disks and pools to boot. This may change in the future, however, * if we allow specifying which pool to boot from via UEFI variables * rather than the bootenv stuff that FreeBSD uses today. */ if (pool_guid != 0) { printf("Trying ZFS pool\n"); if (probe_zfs_currdev(pool_guid)) return (0); } #endif /* EFI_ZFS_BOOT */ /* * Try to find the block device by its handle based on the * image we're booting. If we can't find a sane partition, * search all the other partitions of the disk. We do not * search other disks because it's a violation of the UEFI * boot protocol to do so. We fail and let UEFI go on to * the next candidate. */ dp = efiblk_get_pdinfo_by_handle(boot_img->DeviceHandle); if (dp != NULL) { text = efi_devpath_name(dp->pd_devpath); if (text != NULL) { printf("Trying ESP: %S\n", text); efi_free_devpath_name(text); } set_currdev_pdinfo(dp); if (sanity_check_currdev()) return (0); if (dp->pd_parent != NULL) { pdinfo_t *espdp = dp; dp = dp->pd_parent; STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { /* Already tried the ESP */ if (espdp == pp) continue; /* * Roll up the ZFS special case * for those partitions that have * zpools on them. */ text = efi_devpath_name(pp->pd_devpath); if (text != NULL) { printf("Trying: %S\n", text); efi_free_devpath_name(text); } if (try_as_currdev(dp, pp)) return (0); } } } /* * Try the device handle from our loaded image first. If that * fails, use the device path from the loaded image and see if * any of the nodes in that path match one of the enumerated * handles. Currently, this handle list is only for netboot. */ if (efi_handle_lookup(boot_img->DeviceHandle, &dev, &unit, &extra) == 0) { set_currdev_devsw(dev, unit); if (sanity_check_currdev()) return (0); } copy = NULL; devpath = efi_lookup_image_devpath(IH); while (devpath != NULL) { h = efi_devpath_handle(devpath); if (h == NULL) break; free(copy); copy = NULL; if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) { set_currdev_devsw(dev, unit); if (sanity_check_currdev()) return (0); } devpath = efi_lookup_devpath(h); if (devpath != NULL) { copy = efi_devpath_trim(devpath); devpath = copy; } } free(copy); return (ENOENT); } static bool interactive_interrupt(const char *msg) { time_t now, then, last; last = 0; now = then = getsecs(); printf("%s\n", msg); if (fail_timeout == -2) /* Always break to OK */ return (true); if (fail_timeout == -1) /* Never break to OK */ return (false); do { if (last != now) { printf("press any key to interrupt reboot in %d seconds\r", fail_timeout - (int)(now - then)); last = now; } /* XXX no pause or timeout wait for char */ if (ischar()) return (true); now = getsecs(); } while (now - then < fail_timeout); return (false); } static int parse_args(int argc, CHAR16 *argv[]) { int i, j, howto; bool vargood; char var[128]; /* * Parse the args to set the console settings, etc * boot1.efi passes these in, if it can read /boot.config or /boot/config * or iPXE may be setup to pass these in. Or the optional argument in the * boot environment was used to pass these arguments in (in which case * neither /boot.config nor /boot/config are consulted). * * Loop through the args, and for each one that contains an '=' that is * not the first character, add it to the environment. This allows * loader and kernel env vars to be passed on the command line. Convert * args from UCS-2 to ASCII (16 to 8 bit) as they are copied (though this * method is flawed for non-ASCII characters). */ howto = 0; for (i = 1; i < argc; i++) { cpy16to8(argv[i], var, sizeof(var)); howto |= boot_parse_arg(var); } return (howto); } static void setenv_int(const char *key, int val) { char buf[20]; snprintf(buf, sizeof(buf), "%d", val); setenv(key, buf, 1); } /* * Parse ConOut (the list of consoles active) and see if we can find a * serial port and/or a video port. It would be nice to also walk the * ACPI name space to map the UID for the serial port to a port. The * latter is especially hard. */ static int parse_uefi_con_out(void) { int how, rv; int vid_seen = 0, com_seen = 0, seen = 0; size_t sz; char buf[4096], *ep; EFI_DEVICE_PATH *node; ACPI_HID_DEVICE_PATH *acpi; UART_DEVICE_PATH *uart; bool pci_pending; how = 0; sz = sizeof(buf); rv = efi_global_getenv("ConOut", buf, &sz); if (rv != EFI_SUCCESS) goto out; ep = buf + sz; node = (EFI_DEVICE_PATH *)buf; while ((char *)node < ep) { pci_pending = false; if (DevicePathType(node) == ACPI_DEVICE_PATH && DevicePathSubType(node) == ACPI_DP) { /* Check for Serial node */ acpi = (void *)node; if (EISA_ID_TO_NUM(acpi->HID) == 0x501) { setenv_int("efi_8250_uid", acpi->UID); com_seen = ++seen; } } else if (DevicePathType(node) == MESSAGING_DEVICE_PATH && DevicePathSubType(node) == MSG_UART_DP) { uart = (void *)node; setenv_int("efi_com_speed", uart->BaudRate); } else if (DevicePathType(node) == ACPI_DEVICE_PATH && DevicePathSubType(node) == ACPI_ADR_DP) { /* Check for AcpiAdr() Node for video */ vid_seen = ++seen; } else if (DevicePathType(node) == HARDWARE_DEVICE_PATH && DevicePathSubType(node) == HW_PCI_DP) { /* * Note, vmware fusion has a funky console device * PciRoot(0x0)/Pci(0xf,0x0) * which we can only detect at the end since we also * have to cope with: * PciRoot(0x0)/Pci(0x1f,0x0)/Serial(0x1) * so only match it if it's last. */ pci_pending = true; } node = NextDevicePathNode(node); /* Skip the end node */ } if (pci_pending && vid_seen == 0) vid_seen = ++seen; /* * Truth table for RB_MULTIPLE | RB_SERIAL * Value Result * 0 Use only video console * RB_SERIAL Use only serial console * RB_MULTIPLE Use both video and serial console * (but video is primary so gets rc messages) * both Use both video and serial console * (but serial is primary so gets rc messages) * * Try to honor this as best we can. If only one of serial / video * found, then use that. Otherwise, use the first one we found. * This also implies if we found nothing, default to video. */ how = 0; if (vid_seen && com_seen) { how |= RB_MULTIPLE; if (com_seen < vid_seen) how |= RB_SERIAL; } else if (com_seen) how |= RB_SERIAL; out: return (how); } void parse_loader_efi_config(EFI_HANDLE h, const char *env_fn) { pdinfo_t *dp; struct stat st; int fd = -1; char *env = NULL; dp = efiblk_get_pdinfo_by_handle(h); if (dp == NULL) return; set_currdev_pdinfo(dp); if (stat(env_fn, &st) != 0) return; fd = open(env_fn, O_RDONLY); if (fd == -1) return; env = malloc(st.st_size + 1); if (env == NULL) goto out; if (read(fd, env, st.st_size) != st.st_size) goto out; env[st.st_size] = '\0'; boot_parse_cmdline(env); out: free(env); close(fd); } static void read_loader_env(const char *name, char *def_fn, bool once) { UINTN len; char *fn, *freeme = NULL; len = 0; fn = def_fn; if (efi_freebsd_getenv(name, NULL, &len) == EFI_BUFFER_TOO_SMALL) { freeme = fn = malloc(len + 1); if (fn != NULL) { if (efi_freebsd_getenv(name, fn, &len) != EFI_SUCCESS) { free(fn); fn = NULL; printf( "Can't fetch FreeBSD::%s we know is there\n", name); } else { /* * if tagged as 'once' delete the env variable so we * only use it once. */ if (once) efi_freebsd_delenv(name); /* * We malloced 1 more than len above, then redid the call. * so now we have room at the end of the string to NUL terminate * it here, even if the typical idium would have '- 1' here to * not overflow. len should be the same on return both times. */ fn[len] = '\0'; } } else { printf( "Can't allocate %d bytes to fetch FreeBSD::%s env var\n", len, name); } } if (fn) { printf(" Reading loader env vars from %s\n", fn); parse_loader_efi_config(boot_img->DeviceHandle, fn); } } EFI_STATUS main(int argc, CHAR16 *argv[]) { EFI_GUID *guid; int howto, i, uhowto; UINTN k; bool has_kbd, is_last; char *s; EFI_DEVICE_PATH *imgpath; CHAR16 *text; EFI_STATUS rv; size_t sz, bosz = 0, bisz = 0; UINT16 boot_order[100]; char boot_info[4096]; char buf[32]; bool uefi_boot_mgr; archsw.arch_autoload = efi_autoload; archsw.arch_getdev = efi_getdev; archsw.arch_copyin = efi_copyin; archsw.arch_copyout = efi_copyout; #ifdef __amd64__ archsw.arch_hypervisor = x86_hypervisor; #endif archsw.arch_readin = efi_readin; archsw.arch_zfs_probe = efi_zfs_probe; /* Get our loaded image protocol interface structure. */ (void) OpenProtocolByHandle(IH, &imgid, (void **)&boot_img); /* * Chicken-and-egg problem; we want to have console output early, but * some console attributes may depend on reading from eg. the boot * device, which we can't do yet. We can use printf() etc. once this is * done. So, we set it to the efi console, then call console init. This * gets us printf early, but also primes the pump for all future console * changes to take effect, regardless of where they come from. */ setenv("console", "efi", 1); cons_probe(); /* Init the time source */ efi_time_init(); /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); /* * Scan the BLOCK IO MEDIA handles then * march through the device switch probing for things. */ i = efipart_inithandles(); if (i != 0 && i != ENOENT) { printf("efipart_inithandles failed with ERRNO %d, expect " "failures\n", i); } for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (devsw[i]->dv_init)(); /* * Detect console settings two different ways: one via the command * args (eg -h) or via the UEFI ConOut variable. */ has_kbd = has_keyboard(); howto = parse_args(argc, argv); if (!has_kbd && (howto & RB_PROBE)) howto |= RB_SERIAL | RB_MULTIPLE; howto &= ~RB_PROBE; uhowto = parse_uefi_con_out(); /* * Read additional environment variables from the boot device's * "LoaderEnv" file. Any boot loader environment variable may be set * there, which are subtly different than loader.conf variables. Only * the 'simple' ones may be set so things like foo_load="YES" won't work * for two reasons. First, the parser is simplistic and doesn't grok * quotes. Second, because the variables that cause an action to happen * are parsed by the lua, 4th or whatever code that's not yet * loaded. This is relative to the root directory when loader.efi is * loaded off the UFS root drive (when chain booted), or from the ESP * when directly loaded by the BIOS. * * We also read in NextLoaderEnv if it was specified. This allows next boot * functionality to be implemented and to override anything in LoaderEnv. */ read_loader_env("LoaderEnv", "/efi/freebsd/loader.env", false); read_loader_env("NextLoaderEnv", NULL, true); /* * We now have two notions of console. howto should be viewed as * overrides. If console is already set, don't set it again. */ #define VIDEO_ONLY 0 #define SERIAL_ONLY RB_SERIAL #define VID_SER_BOTH RB_MULTIPLE #define SER_VID_BOTH (RB_SERIAL | RB_MULTIPLE) #define CON_MASK (RB_SERIAL | RB_MULTIPLE) if (strcmp(getenv("console"), "efi") == 0) { if ((howto & CON_MASK) == 0) { /* No override, uhowto is controlling and efi cons is perfect */ howto = howto | (uhowto & CON_MASK); } else if ((howto & CON_MASK) == (uhowto & CON_MASK)) { /* override matches what UEFI told us, efi console is perfect */ } else if ((uhowto & (CON_MASK)) != 0) { /* * We detected a serial console on ConOut. All possible * overrides include serial. We can't really override what efi * gives us, so we use it knowing it's the best choice. */ /* Do nothing */ } else { /* * We detected some kind of serial in the override, but ConOut * has no serial, so we have to sort out which case it really is. */ switch (howto & CON_MASK) { case SERIAL_ONLY: setenv("console", "comconsole", 1); break; case VID_SER_BOTH: setenv("console", "efi comconsole", 1); break; case SER_VID_BOTH: setenv("console", "comconsole efi", 1); break; /* case VIDEO_ONLY can't happen -- it's the first if above */ } } } /* * howto is set now how we want to export the flags to the kernel, so * set the env based on it. */ boot_howto_to_env(howto); if (efi_copy_init()) { printf("failed to allocate staging area\n"); return (EFI_BUFFER_TOO_SMALL); } if ((s = getenv("fail_timeout")) != NULL) fail_timeout = strtol(s, NULL, 10); printf("%s\n", bootprog_info); printf(" Command line arguments:"); for (i = 0; i < argc; i++) printf(" %S", argv[i]); printf("\n"); printf(" EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); printf(" EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); printf(" Console: %s (%#x)\n", getenv("console"), howto); /* Determine the devpath of our image so we can prefer it. */ text = efi_devpath_name(boot_img->FilePath); if (text != NULL) { printf(" Load Path: %S\n", text); efi_setenv_freebsd_wcs("LoaderPath", text); efi_free_devpath_name(text); } rv = OpenProtocolByHandle(boot_img->DeviceHandle, &devid, (void **)&imgpath); if (rv == EFI_SUCCESS) { text = efi_devpath_name(imgpath); if (text != NULL) { printf(" Load Device: %S\n", text); efi_setenv_freebsd_wcs("LoaderDev", text); efi_free_devpath_name(text); } } if (getenv("uefi_ignore_boot_mgr") != NULL) { printf(" Ignoring UEFI boot manager\n"); uefi_boot_mgr = false; } else { uefi_boot_mgr = true; boot_current = 0; sz = sizeof(boot_current); rv = efi_global_getenv("BootCurrent", &boot_current, &sz); if (rv == EFI_SUCCESS) printf(" BootCurrent: %04x\n", boot_current); else { boot_current = 0xffff; uefi_boot_mgr = false; } sz = sizeof(boot_order); rv = efi_global_getenv("BootOrder", &boot_order, &sz); if (rv == EFI_SUCCESS) { printf(" BootOrder:"); for (i = 0; i < sz / sizeof(boot_order[0]); i++) printf(" %04x%s", boot_order[i], boot_order[i] == boot_current ? "[*]" : ""); printf("\n"); is_last = boot_order[(sz / sizeof(boot_order[0])) - 1] == boot_current; bosz = sz; } else if (uefi_boot_mgr) { /* * u-boot doesn't set BootOrder, but otherwise participates in the * boot manager protocol. So we fake it here and don't consider it * a failure. */ bosz = sizeof(boot_order[0]); boot_order[0] = boot_current; is_last = true; } } /* * Next, find the boot info structure the UEFI boot manager is * supposed to setup. We need this so we can walk through it to * find where we are in the booting process and what to try to * boot next. */ if (uefi_boot_mgr) { snprintf(buf, sizeof(buf), "Boot%04X", boot_current); sz = sizeof(boot_info); rv = efi_global_getenv(buf, &boot_info, &sz); if (rv == EFI_SUCCESS) bisz = sz; else uefi_boot_mgr = false; } /* * Disable the watchdog timer. By default the boot manager sets * the timer to 5 minutes before invoking a boot option. If we * want to return to the boot manager, we have to disable the * watchdog timer and since we're an interactive program, we don't * want to wait until the user types "quit". The timer may have * fired by then. We don't care if this fails. It does not prevent * normal functioning in any way... */ BS->SetWatchdogTimer(0, 0, 0, NULL); /* * Initialize the trusted/forbidden certificates from UEFI. * They will be later used to verify the manifest(s), * which should contain hashes of verified files. * This needs to be initialized before any configuration files * are loaded. */ #ifdef EFI_SECUREBOOT ve_efi_init(); #endif /* * Try and find a good currdev based on the image that was booted. * It might be desirable here to have a short pause to allow falling * through to the boot loader instead of returning instantly to follow * the boot protocol and also allow an escape hatch for users wishing * to try something different. */ if (find_currdev(uefi_boot_mgr, is_last, boot_info, bisz) != 0) if (uefi_boot_mgr && !interactive_interrupt("Failed to find bootable partition")) return (EFI_NOT_FOUND); efi_init_environment(); #if !defined(__arm__) for (k = 0; k < ST->NumberOfTableEntries; k++) { guid = &ST->ConfigurationTable[k].VendorGuid; if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { char buf[40]; snprintf(buf, sizeof(buf), "%p", ST->ConfigurationTable[k].VendorTable); setenv("hint.smbios.0.mem", buf, 1); smbios_detect(ST->ConfigurationTable[k].VendorTable); break; } } #endif interact(); /* doesn't return */ return (EFI_SUCCESS); /* keep compiler happy */ } COMMAND_SET(poweroff, "poweroff", "power off the system", command_poweroff); static int command_poweroff(int argc __unused, char *argv[] __unused) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(quit, "quit", "exit the loader", command_quit); static int command_quit(int argc, char *argv[]) { exit(0); return (CMD_OK); } COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); static int command_memmap(int argc __unused, char *argv[] __unused) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map, *p; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; char line[80]; sz = 0; status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); if (status != EFI_BUFFER_TOO_SMALL) { printf("Can't determine memory map size\n"); return (CMD_ERROR); } map = malloc(sz); status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (EFI_ERROR(status)) { printf("Can't read memory map\n"); return (CMD_ERROR); } ndesc = sz / dsz; snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n", "Type", "Physical", "Virtual", "#Pages", "Attr"); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { snprintf(line, sizeof(line), "%23s %012jx %012jx %08jx ", efi_memory_type(p->Type), (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, (uintmax_t)p->NumberOfPages); if (pager_output(line)) break; if (p->Attribute & EFI_MEMORY_UC) printf("UC "); if (p->Attribute & EFI_MEMORY_WC) printf("WC "); if (p->Attribute & EFI_MEMORY_WT) printf("WT "); if (p->Attribute & EFI_MEMORY_WB) printf("WB "); if (p->Attribute & EFI_MEMORY_UCE) printf("UCE "); if (p->Attribute & EFI_MEMORY_WP) printf("WP "); if (p->Attribute & EFI_MEMORY_RP) printf("RP "); if (p->Attribute & EFI_MEMORY_XP) printf("XP "); if (p->Attribute & EFI_MEMORY_NV) printf("NV "); if (p->Attribute & EFI_MEMORY_MORE_RELIABLE) printf("MR "); if (p->Attribute & EFI_MEMORY_RO) printf("RO "); if (pager_output("\n")) break; } pager_close(); return (CMD_OK); } COMMAND_SET(configuration, "configuration", "print configuration tables", command_configuration); static int command_configuration(int argc, char *argv[]) { UINTN i; char *name; printf("NumberOfTableEntries=%lu\n", (unsigned long)ST->NumberOfTableEntries); for (i = 0; i < ST->NumberOfTableEntries; i++) { EFI_GUID *guid; printf(" "); guid = &ST->ConfigurationTable[i].VendorGuid; if (efi_guid_to_name(guid, &name) == true) { printf(name); free(name); } else { printf("Error while translating UUID to name"); } printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); } return (CMD_OK); } COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); static int command_mode(int argc, char *argv[]) { UINTN cols, rows; unsigned int mode; int i; char *cp; char rowenv[8]; EFI_STATUS status; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; extern void HO(void); conout = ST->ConOut; if (argc > 1) { mode = strtol(argv[1], &cp, 0); if (cp[0] != '\0') { printf("Invalid mode\n"); return (CMD_ERROR); } status = conout->QueryMode(conout, mode, &cols, &rows); if (EFI_ERROR(status)) { printf("invalid mode %d\n", mode); return (CMD_ERROR); } status = conout->SetMode(conout, mode); if (EFI_ERROR(status)) { printf("couldn't set mode %d\n", mode); return (CMD_ERROR); } sprintf(rowenv, "%u", (unsigned)rows); setenv("LINES", rowenv, 1); HO(); /* set cursor */ return (CMD_OK); } printf("Current mode: %d\n", conout->Mode->Mode); for (i = 0; i <= conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, (unsigned)rows); } if (i != 0) printf("Select a mode with the command \"mode \"\n"); return (CMD_OK); } COMMAND_SET(lsefi, "lsefi", "list EFI handles", command_lsefi); static int command_lsefi(int argc __unused, char *argv[] __unused) { char *name; EFI_HANDLE *buffer = NULL; EFI_HANDLE handle; UINTN bufsz = 0, i, j; EFI_STATUS status; int ret = 0; status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer); if (status != EFI_BUFFER_TOO_SMALL) { snprintf(command_errbuf, sizeof (command_errbuf), "unexpected error: %lld", (long long)status); return (CMD_ERROR); } if ((buffer = malloc(bufsz)) == NULL) { sprintf(command_errbuf, "out of memory"); return (CMD_ERROR); } status = BS->LocateHandle(AllHandles, NULL, NULL, &bufsz, buffer); if (EFI_ERROR(status)) { free(buffer); snprintf(command_errbuf, sizeof (command_errbuf), "LocateHandle() error: %lld", (long long)status); return (CMD_ERROR); } pager_open(); for (i = 0; i < (bufsz / sizeof (EFI_HANDLE)); i++) { UINTN nproto = 0; EFI_GUID **protocols = NULL; handle = buffer[i]; printf("Handle %p", handle); if (pager_output("\n")) break; /* device path */ status = BS->ProtocolsPerHandle(handle, &protocols, &nproto); if (EFI_ERROR(status)) { snprintf(command_errbuf, sizeof (command_errbuf), "ProtocolsPerHandle() error: %lld", (long long)status); continue; } for (j = 0; j < nproto; j++) { if (efi_guid_to_name(protocols[j], &name) == true) { printf(" %s", name); free(name); } else { printf("Error while translating UUID to name"); } if ((ret = pager_output("\n")) != 0) break; } BS->FreePool(protocols); if (ret != 0) break; } pager_close(); free(buffer); return (CMD_OK); } #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif /* * Chain load another efi loader. */ static int command_chain(int argc, char *argv[]) { EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; EFI_HANDLE loaderhandle; EFI_LOADED_IMAGE *loaded_image; EFI_STATUS status; struct stat st; struct devdesc *dev; char *name, *path; void *buf; int fd; if (argc < 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } name = argv[1]; if ((fd = open(name, O_RDONLY)) < 0) { command_errmsg = "no such file"; return (CMD_ERROR); } +#ifdef LOADER_VERIEXEC + if (verify_file(fd, name, 0, VE_MUST, __func__) < 0) { + sprintf(command_errbuf, "can't verify: %s", name); + close(fd); + return (CMD_ERROR); + } +#endif + if (fstat(fd, &st) < -1) { command_errmsg = "stat failed"; close(fd); return (CMD_ERROR); } status = BS->AllocatePool(EfiLoaderCode, (UINTN)st.st_size, &buf); if (status != EFI_SUCCESS) { command_errmsg = "failed to allocate buffer"; close(fd); return (CMD_ERROR); } if (read(fd, buf, st.st_size) != st.st_size) { command_errmsg = "error while reading the file"; (void)BS->FreePool(buf); close(fd); return (CMD_ERROR); } close(fd); status = BS->LoadImage(FALSE, IH, NULL, buf, st.st_size, &loaderhandle); (void)BS->FreePool(buf); if (status != EFI_SUCCESS) { command_errmsg = "LoadImage failed"; return (CMD_ERROR); } status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID, (void **)&loaded_image); if (argc > 2) { int i, len = 0; CHAR16 *argp; for (i = 2; i < argc; i++) len += strlen(argv[i]) + 1; len *= sizeof (*argp); loaded_image->LoadOptions = argp = malloc (len); loaded_image->LoadOptionsSize = len; for (i = 2; i < argc; i++) { char *ptr = argv[i]; while (*ptr) *(argp++) = *(ptr++); *(argp++) = ' '; } *(--argv) = 0; } if (efi_getdev((void **)&dev, name, (const char **)&path) == 0) { #ifdef EFI_ZFS_BOOT struct zfs_devdesc *z_dev; #endif struct disk_devdesc *d_dev; pdinfo_t *hd, *pd; switch (dev->d_dev->dv_type) { #ifdef EFI_ZFS_BOOT case DEVT_ZFS: z_dev = (struct zfs_devdesc *)dev; loaded_image->DeviceHandle = efizfs_get_handle_by_guid(z_dev->pool_guid); break; #endif case DEVT_NET: loaded_image->DeviceHandle = efi_find_handle(dev->d_dev, dev->d_unit); break; default: hd = efiblk_get_pdinfo(dev); if (STAILQ_EMPTY(&hd->pd_part)) { loaded_image->DeviceHandle = hd->pd_handle; break; } d_dev = (struct disk_devdesc *)dev; STAILQ_FOREACH(pd, &hd->pd_part, pd_link) { /* * d_partition should be 255 */ if (pd->pd_unit == (uint32_t)d_dev->d_slice) { loaded_image->DeviceHandle = pd->pd_handle; break; } } break; } } dev_cleanup(); status = BS->StartImage(loaderhandle, NULL, NULL); if (status != EFI_SUCCESS) { command_errmsg = "StartImage failed"; free(loaded_image->LoadOptions); loaded_image->LoadOptions = NULL; status = BS->UnloadImage(loaded_image); return (CMD_ERROR); } return (CMD_ERROR); /* not reached */ } COMMAND_SET(chain, "chain", "chain load file", command_chain); Index: stable/12/stand/ficl/loader.c =================================================================== --- stable/12/stand/ficl/loader.c (revision 359734) +++ stable/12/stand/ficl/loader.c (revision 359735) @@ -1,885 +1,885 @@ /*- * Copyright (c) 2000 Daniel Capo Sobral * 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$ */ /******************************************************************* ** l o a d e r . c ** Additional FICL words designed for FreeBSD's loader ** *******************************************************************/ #ifdef TESTMAIN #include #include #include #include #include #include #include #else #include #endif #include "bootstrap.h" #include #include #include "ficl.h" /* FreeBSD's loader interaction words and extras * * setenv ( value n name n' -- ) * setenv? ( value n name n' flag -- ) * getenv ( addr n -- addr' n' | -1 ) * unsetenv ( addr n -- ) * copyin ( addr addr' len -- ) * copyout ( addr addr' len -- ) * findfile ( name len type len' -- addr ) * pnpdevices ( -- addr ) * pnphandlers ( -- addr ) * ccall ( [[...[p10] p9] ... p1] n addr -- result ) * uuid-from-string ( addr n -- addr' ) * uuid-to-string ( addr' -- addr n ) * .# ( value -- ) */ void ficlSetenv(FICL_VM *pVM) { #ifndef TESTMAIN char *name, *value; #endif char *namep, *valuep; int names, values; #if FICL_ROBUST > 1 vmCheckStack(pVM, 4, 0); #endif names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); values = stackPopINT(pVM->pStack); valuep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; value = (char*) ficlMalloc(values+1); if (!value) vmThrowErr(pVM, "Error: out of memory"); strncpy(value, valuep, values); value[values] = '\0'; setenv(name, value, 1); ficlFree(name); ficlFree(value); #endif return; } void ficlSetenvq(FICL_VM *pVM) { #ifndef TESTMAIN char *name, *value; #endif char *namep, *valuep; int names, values, overwrite; #if FICL_ROBUST > 1 vmCheckStack(pVM, 5, 0); #endif overwrite = stackPopINT(pVM->pStack); names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); values = stackPopINT(pVM->pStack); valuep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; value = (char*) ficlMalloc(values+1); if (!value) vmThrowErr(pVM, "Error: out of memory"); strncpy(value, valuep, values); value[values] = '\0'; setenv(name, value, overwrite); ficlFree(name); ficlFree(value); #endif return; } void ficlGetenv(FICL_VM *pVM) { #ifndef TESTMAIN char *name, *value; #endif char *namep; int names; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 2); #endif names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; value = getenv(name); ficlFree(name); if(value != NULL) { stackPushPtr(pVM->pStack, value); stackPushINT(pVM->pStack, strlen(value)); } else #endif stackPushINT(pVM->pStack, -1); return; } void ficlUnsetenv(FICL_VM *pVM) { #ifndef TESTMAIN char *name; #endif char *namep; int names; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; unsetenv(name); ficlFree(name); #endif return; } void ficlCopyin(FICL_VM *pVM) { void* src; vm_offset_t dest; size_t len; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 0); #endif len = stackPopINT(pVM->pStack); dest = stackPopINT(pVM->pStack); src = stackPopPtr(pVM->pStack); #ifndef TESTMAIN archsw.arch_copyin(src, dest, len); #endif return; } void ficlCopyout(FICL_VM *pVM) { void* dest; vm_offset_t src; size_t len; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 0); #endif len = stackPopINT(pVM->pStack); dest = stackPopPtr(pVM->pStack); src = stackPopINT(pVM->pStack); #ifndef TESTMAIN archsw.arch_copyout(src, dest, len); #endif return; } void ficlFindfile(FICL_VM *pVM) { #ifndef TESTMAIN char *name, *type; #endif char *namep, *typep; struct preloaded_file* fp; int names, types; #if FICL_ROBUST > 1 vmCheckStack(pVM, 4, 1); #endif types = stackPopINT(pVM->pStack); typep = (char*) stackPopPtr(pVM->pStack); names = stackPopINT(pVM->pStack); namep = (char*) stackPopPtr(pVM->pStack); #ifndef TESTMAIN name = (char*) ficlMalloc(names+1); if (!name) vmThrowErr(pVM, "Error: out of memory"); strncpy(name, namep, names); name[names] = '\0'; type = (char*) ficlMalloc(types+1); if (!type) vmThrowErr(pVM, "Error: out of memory"); strncpy(type, typep, types); type[types] = '\0'; fp = file_findfile(name, type); #else fp = NULL; #endif stackPushPtr(pVM->pStack, fp); return; } #ifndef TESTMAIN /* isvirtualized? - Return whether the loader runs under a * hypervisor. * * isvirtualized? ( -- flag ) */ static void ficlIsvirtualizedQ(FICL_VM *pVM) { FICL_INT flag; const char *hv; #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif hv = (archsw.arch_hypervisor != NULL) ? (*archsw.arch_hypervisor)() : NULL; flag = (hv != NULL) ? FICL_TRUE : FICL_FALSE; stackPushINT(pVM->pStack, flag); } #endif /* ndef TESTMAIN */ void ficlCcall(FICL_VM *pVM) { int (*func)(int, ...); int result, p[10]; int nparam, i; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif func = stackPopPtr(pVM->pStack); nparam = stackPopINT(pVM->pStack); #if FICL_ROBUST > 1 vmCheckStack(pVM, nparam, 1); #endif for (i = 0; i < nparam; i++) p[i] = stackPopINT(pVM->pStack); result = func(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); stackPushINT(pVM->pStack, result); return; } void ficlUuidFromString(FICL_VM *pVM) { #ifndef TESTMAIN char *uuid; uint32_t status; #endif char *uuidp; int uuids; uuid_t *u; #if FICL_ROBUST > 1 vmCheckStack(pVM, 2, 0); #endif uuids = stackPopINT(pVM->pStack); uuidp = (char *) stackPopPtr(pVM->pStack); #ifndef TESTMAIN uuid = (char *)ficlMalloc(uuids + 1); if (!uuid) vmThrowErr(pVM, "Error: out of memory"); strncpy(uuid, uuidp, uuids); uuid[uuids] = '\0'; u = (uuid_t *)ficlMalloc(sizeof (*u)); uuid_from_string(uuid, u, &status); ficlFree(uuid); if (status != uuid_s_ok) { ficlFree(u); u = NULL; } #else u = NULL; #endif stackPushPtr(pVM->pStack, u); return; } void ficlUuidToString(FICL_VM *pVM) { #ifndef TESTMAIN char *uuid; uint32_t status; #endif uuid_t *u; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif u = (uuid_t *)stackPopPtr(pVM->pStack); #ifndef TESTMAIN uuid_to_string(u, &uuid, &status); if (status != uuid_s_ok) { stackPushPtr(pVM->pStack, uuid); stackPushINT(pVM->pStack, strlen(uuid)); } else #endif stackPushINT(pVM->pStack, -1); return; } /************************************************************************** f i c l E x e c F D ** reads in text from file fd and passes it to ficlExec() * returns VM_OUTOFTEXT on success or the ficlExec() error code on * failure. */ #define nLINEBUF 256 int ficlExecFD(FICL_VM *pVM, int fd) { char cp[nLINEBUF]; int nLine = 0, rval = VM_OUTOFTEXT; char ch; CELL id; id = pVM->sourceID; pVM->sourceID.i = fd; /* feed each line to ficlExec */ while (1) { int status, i; i = 0; while ((status = read(fd, &ch, 1)) > 0 && ch != '\n') cp[i++] = ch; nLine++; if (!i) { if (status < 1) break; continue; } rval = ficlExecC(pVM, cp, i); if(rval != VM_QUIT && rval != VM_USEREXIT && rval != VM_OUTOFTEXT) { pVM->sourceID = id; return rval; } } /* ** Pass an empty line with SOURCE-ID == -1 to flush ** any pending REFILLs (as required by FILE wordset) */ pVM->sourceID.i = -1; ficlExec(pVM, ""); pVM->sourceID = id; return rval; } static void displayCellNoPad(FICL_VM *pVM) { CELL c; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif c = stackPop(pVM->pStack); ltoa((c).i, pVM->pad, pVM->base); vmTextOut(pVM, pVM->pad, 0); return; } /* isdir? - Return whether an fd corresponds to a directory. * * isdir? ( fd -- bool ) */ static void isdirQuestion(FICL_VM *pVM) { struct stat sb; FICL_INT flag; int fd; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif fd = stackPopINT(pVM->pStack); flag = FICL_FALSE; do { if (fd < 0) break; if (fstat(fd, &sb) < 0) break; if (!S_ISDIR(sb.st_mode)) break; flag = FICL_TRUE; } while (0); stackPushINT(pVM->pStack, flag); } /* fopen - open a file and return new fd on stack. * * fopen ( ptr count mode -- fd ) */ static void pfopen(FICL_VM *pVM) { int mode, fd, count; char *ptr, *name; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif mode = stackPopINT(pVM->pStack); /* get mode */ count = stackPopINT(pVM->pStack); /* get count */ ptr = stackPopPtr(pVM->pStack); /* get ptr */ if ((count < 0) || (ptr == NULL)) { stackPushINT(pVM->pStack, -1); return; } /* ensure that the string is null terminated */ name = (char *)malloc(count+1); bcopy(ptr,name,count); name[count] = 0; /* open the file */ fd = open(name, mode); #ifdef LOADER_VERIEXEC if (fd >= 0) { - if (verify_file(fd, name, 0, VE_GUESS) < 0) { + if (verify_file(fd, name, 0, VE_GUESS, __func__) < 0) { /* not verified writing ok but reading is not */ if ((mode & O_ACCMODE) != O_WRONLY) { close(fd); fd = -1; } } else { /* verified reading ok but writing is not */ if ((mode & O_ACCMODE) != O_RDONLY) { close(fd); fd = -1; } } } #endif free(name); stackPushINT(pVM->pStack, fd); return; } /* fclose - close a file who's fd is on stack. * * fclose ( fd -- ) */ static void pfclose(FICL_VM *pVM) { int fd; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif fd = stackPopINT(pVM->pStack); /* get fd */ if (fd != -1) close(fd); return; } /* fread - read file contents * * fread ( fd buf nbytes -- nread ) */ static void pfread(FICL_VM *pVM) { int fd, len; char *buf; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif len = stackPopINT(pVM->pStack); /* get number of bytes to read */ buf = stackPopPtr(pVM->pStack); /* get buffer */ fd = stackPopINT(pVM->pStack); /* get fd */ if (len > 0 && buf && fd != -1) stackPushINT(pVM->pStack, read(fd, buf, len)); else stackPushINT(pVM->pStack, -1); return; } /* freaddir - read directory contents * * freaddir ( fd -- ptr len TRUE | FALSE ) */ static void pfreaddir(FICL_VM *pVM) { #ifdef TESTMAIN static struct dirent dirent; struct stat sb; char *buf; off_t off, ptr; u_int blksz; int bufsz; #endif struct dirent *d; int fd; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 3); #endif fd = stackPopINT(pVM->pStack); #if TESTMAIN /* * The readdirfd() function is specific to the loader environment. * We do the best we can to make freaddir work, but it's not at * all guaranteed. */ d = NULL; buf = NULL; do { if (fd == -1) break; if (fstat(fd, &sb) == -1) break; blksz = (sb.st_blksize) ? sb.st_blksize : getpagesize(); if ((blksz & (blksz - 1)) != 0) break; buf = malloc(blksz); if (buf == NULL) break; off = lseek(fd, 0LL, SEEK_CUR); if (off == -1) break; ptr = off; if (lseek(fd, 0, SEEK_SET) == -1) break; bufsz = getdents(fd, buf, blksz); while (bufsz > 0 && bufsz <= ptr) { ptr -= bufsz; bufsz = getdents(fd, buf, blksz); } if (bufsz <= 0) break; d = (void *)(buf + ptr); dirent = *d; off += d->d_reclen; d = (lseek(fd, off, SEEK_SET) != off) ? NULL : &dirent; } while (0); if (buf != NULL) free(buf); #else d = readdirfd(fd); #endif if (d != NULL) { stackPushPtr(pVM->pStack, d->d_name); stackPushINT(pVM->pStack, strlen(d->d_name)); stackPushINT(pVM->pStack, FICL_TRUE); } else { stackPushINT(pVM->pStack, FICL_FALSE); } } /* fload - interpret file contents * * fload ( fd -- ) */ static void pfload(FICL_VM *pVM) { int fd; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 0); #endif fd = stackPopINT(pVM->pStack); /* get fd */ if (fd != -1) ficlExecFD(pVM, fd); return; } /* fwrite - write file contents * * fwrite ( fd buf nbytes -- nwritten ) */ static void pfwrite(FICL_VM *pVM) { int fd, len; char *buf; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif len = stackPopINT(pVM->pStack); /* get number of bytes to read */ buf = stackPopPtr(pVM->pStack); /* get buffer */ fd = stackPopINT(pVM->pStack); /* get fd */ if (len > 0 && buf && fd != -1) stackPushINT(pVM->pStack, write(fd, buf, len)); else stackPushINT(pVM->pStack, -1); return; } /* fseek - seek to a new position in a file * * fseek ( fd ofs whence -- pos ) */ static void pfseek(FICL_VM *pVM) { int fd, pos, whence; #if FICL_ROBUST > 1 vmCheckStack(pVM, 3, 1); #endif whence = stackPopINT(pVM->pStack); pos = stackPopINT(pVM->pStack); fd = stackPopINT(pVM->pStack); stackPushINT(pVM->pStack, lseek(fd, pos, whence)); return; } /* key - get a character from stdin * * key ( -- char ) */ static void key(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif stackPushINT(pVM->pStack, getchar()); return; } /* key? - check for a character from stdin (FACILITY) * * key? ( -- flag ) */ static void keyQuestion(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM, 0, 1); #endif #ifdef TESTMAIN /* XXX Since we don't fiddle with termios, let it always succeed... */ stackPushINT(pVM->pStack, FICL_TRUE); #else /* But here do the right thing. */ stackPushINT(pVM->pStack, ischar()? FICL_TRUE : FICL_FALSE); #endif return; } /* seconds - gives number of seconds since beginning of time * * beginning of time is defined as: * * BTX - number of seconds since midnight * FreeBSD - number of seconds since Jan 1 1970 * * seconds ( -- u ) */ static void pseconds(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM,0,1); #endif stackPushUNS(pVM->pStack, (FICL_UNS) time(NULL)); return; } /* ms - wait at least that many milliseconds (FACILITY) * * ms ( u -- ) * */ static void ms(FICL_VM *pVM) { #if FICL_ROBUST > 1 vmCheckStack(pVM,1,0); #endif #ifdef TESTMAIN usleep(stackPopUNS(pVM->pStack)*1000); #else delay(stackPopUNS(pVM->pStack)*1000); #endif return; } /* fkey - get a character from a file * * fkey ( file -- char ) */ static void fkey(FICL_VM *pVM) { int i, fd; char ch; #if FICL_ROBUST > 1 vmCheckStack(pVM, 1, 1); #endif fd = stackPopINT(pVM->pStack); i = read(fd, &ch, 1); stackPushINT(pVM->pStack, i > 0 ? ch : -1); return; } /* ** Retrieves free space remaining on the dictionary */ static void freeHeap(FICL_VM *pVM) { stackPushINT(pVM->pStack, dictCellsAvail(ficlGetDict(pVM->pSys))); } /******************* Increase dictionary size on-demand ******************/ static void ficlDictThreshold(FICL_VM *pVM) { stackPushPtr(pVM->pStack, &dictThreshold); } static void ficlDictIncrease(FICL_VM *pVM) { stackPushPtr(pVM->pStack, &dictIncrease); } /************************************************************************** f i c l C o m p i l e P l a t f o r m ** Build FreeBSD platform extensions into the system dictionary **************************************************************************/ void ficlCompilePlatform(FICL_SYSTEM *pSys) { ficlCompileFcn **fnpp; FICL_DICT *dp = pSys->dp; assert (dp); dictAppendWord(dp, ".#", displayCellNoPad, FW_DEFAULT); dictAppendWord(dp, "isdir?", isdirQuestion, FW_DEFAULT); dictAppendWord(dp, "fopen", pfopen, FW_DEFAULT); dictAppendWord(dp, "fclose", pfclose, FW_DEFAULT); dictAppendWord(dp, "fread", pfread, FW_DEFAULT); dictAppendWord(dp, "freaddir", pfreaddir, FW_DEFAULT); dictAppendWord(dp, "fload", pfload, FW_DEFAULT); dictAppendWord(dp, "fkey", fkey, FW_DEFAULT); dictAppendWord(dp, "fseek", pfseek, FW_DEFAULT); dictAppendWord(dp, "fwrite", pfwrite, FW_DEFAULT); dictAppendWord(dp, "key", key, FW_DEFAULT); dictAppendWord(dp, "key?", keyQuestion, FW_DEFAULT); dictAppendWord(dp, "ms", ms, FW_DEFAULT); dictAppendWord(dp, "seconds", pseconds, FW_DEFAULT); dictAppendWord(dp, "heap?", freeHeap, FW_DEFAULT); dictAppendWord(dp, "dictthreshold", ficlDictThreshold, FW_DEFAULT); dictAppendWord(dp, "dictincrease", ficlDictIncrease, FW_DEFAULT); dictAppendWord(dp, "setenv", ficlSetenv, FW_DEFAULT); dictAppendWord(dp, "setenv?", ficlSetenvq, FW_DEFAULT); dictAppendWord(dp, "getenv", ficlGetenv, FW_DEFAULT); dictAppendWord(dp, "unsetenv", ficlUnsetenv, FW_DEFAULT); dictAppendWord(dp, "copyin", ficlCopyin, FW_DEFAULT); dictAppendWord(dp, "copyout", ficlCopyout, FW_DEFAULT); dictAppendWord(dp, "findfile", ficlFindfile, FW_DEFAULT); dictAppendWord(dp, "ccall", ficlCcall, FW_DEFAULT); dictAppendWord(dp, "uuid-from-string", ficlUuidFromString, FW_DEFAULT); dictAppendWord(dp, "uuid-to-string", ficlUuidToString, FW_DEFAULT); #ifndef TESTMAIN dictAppendWord(dp, "isvirtualized?",ficlIsvirtualizedQ, FW_DEFAULT); #endif SET_FOREACH(fnpp, Xficl_compile_set) (*fnpp)(pSys); #if defined(__i386__) ficlSetEnv(pSys, "arch-i386", FICL_TRUE); ficlSetEnv(pSys, "arch-powerpc", FICL_FALSE); #elif defined(__powerpc__) ficlSetEnv(pSys, "arch-i386", FICL_FALSE); ficlSetEnv(pSys, "arch-powerpc", FICL_TRUE); #endif return; } Index: stable/12/stand/i386/libi386/i386_copy.c =================================================================== --- stable/12/stand/i386/libi386/i386_copy.c (revision 359734) +++ stable/12/stand/i386/libi386/i386_copy.c (revision 359735) @@ -1,75 +1,75 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include __FBSDID("$FreeBSD$"); /* * MD primitives supporting placement of module data * * XXX should check load address/size against memory top. */ #include #include "libi386.h" #include "btxv86.h" ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len) { if (dest + len >= memtop) { errno = EFBIG; return(-1); } bcopy(src, PTOV(dest), len); return(len); } ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len) { if (src + len >= memtop) { errno = EFBIG; return(-1); } bcopy(PTOV(src), dest, len); return(len); } ssize_t -i386_readin(const int fd, vm_offset_t dest, const size_t len) +i386_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { if (dest + len >= memtop_copyin) { errno = EFBIG; return(-1); } - return (read(fd, PTOV(dest), len)); + return (VECTX_READ(fd, PTOV(dest), len)); } Index: stable/12/stand/i386/libi386/libi386.h =================================================================== --- stable/12/stand/i386/libi386/libi386.h (revision 359734) +++ stable/12/stand/i386/libi386/libi386.h (revision 359735) @@ -1,154 +1,156 @@ /*- * Copyright (c) 1998 Michael Smith * 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$ */ /* * i386 fully-qualified device descriptor. */ struct i386_devdesc { struct devdesc dd; /* Must be first. */ union { struct { int slice; int partition; off_t offset; } biosdisk; struct { uint64_t pool_guid; uint64_t root_guid; } zfs; } d_kind; }; /* * relocater trampoline support. */ struct relocate_data { uint32_t src; uint32_t dest; uint32_t size; }; extern void relocater(void); /* * The relocater_data[] is fixed size array allocated in relocater_tramp.S */ extern struct relocate_data relocater_data[]; extern uint32_t relocater_size; extern uint16_t relocator_ip; extern uint16_t relocator_cs; extern uint16_t relocator_ds; extern uint16_t relocator_es; extern uint16_t relocator_fs; extern uint16_t relocator_gs; extern uint16_t relocator_ss; extern uint16_t relocator_sp; extern uint32_t relocator_esi; extern uint32_t relocator_eax; extern uint32_t relocator_ebx; extern uint32_t relocator_edx; extern uint32_t relocator_ebp; extern uint16_t relocator_a20_enabled; int i386_getdev(void **vdev, const char *devspec, const char **path); char *i386_fmtdev(void *vdev); int i386_setcurrdev(struct env_var *ev, int flags, const void *value); extern struct devdesc currdev; /* our current device */ #define MAXDEV 31 /* maximum number of distinct devices */ #define MAXBDDEV MAXDEV +#include + /* exported devices XXX rename? */ extern struct devsw bioscd; extern struct devsw biosfd; extern struct devsw bioshd; extern struct devsw pxedisk; extern struct fs_ops pxe_fsops; int bc_add(int biosdev); /* Register CD booted from. */ uint32_t bd_getbigeom(int bunit); /* return geometry in bootinfo format */ int bd_bios2unit(int biosdev); /* xlate BIOS device -> biosdisk unit */ int bd_unit2bios(struct i386_devdesc *); /* xlate biosdisk -> BIOS device */ int bd_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ ssize_t i386_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t i386_copyout(const vm_offset_t src, void *dest, const size_t len); -ssize_t i386_readin(const int fd, vm_offset_t dest, const size_t len); +ssize_t i386_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); struct preloaded_file; void bios_addsmapdata(struct preloaded_file *); void bios_getsmap(void); void bios_getmem(void); extern uint32_t bios_basemem; /* base memory in bytes */ extern uint32_t bios_extmem; /* extended memory in bytes */ extern vm_offset_t memtop; /* last address of physical memory + 1 */ extern vm_offset_t memtop_copyin; /* memtop less heap size for the cases */ /* when heap is at the top of */ /* extended memory; for other cases */ /* just the same as memtop */ extern uint32_t high_heap_size; /* extended memory region available */ extern vm_offset_t high_heap_base; /* for use as the heap */ /* 16KB buffer space for real mode data transfers. */ #define BIO_BUFFER_SIZE 0x4000 void *bio_alloc(size_t size); void bio_free(void *ptr, size_t size); /* * Values for width parameter to biospci_{read,write}_config */ #define BIOSPCI_8BITS 0 #define BIOSPCI_16BITS 1 #define BIOSPCI_32BITS 2 void biospci_detect(void); int biospci_find_devclass(uint32_t class, int index, uint32_t *locator); int biospci_read_config(uint32_t locator, int offset, int width, uint32_t *val); uint32_t biospci_locator(int8_t bus, uint8_t device, uint8_t function); int biospci_write_config(uint32_t locator, int offset, int width, uint32_t val); void biosacpi_detect(void); int i386_autoload(void); int bi_getboothowto(char *kargs); void bi_setboothowto(int howto); vm_offset_t bi_copyenv(vm_offset_t addr); int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernend); int bi_load64(char *args, vm_offset_t addr, vm_offset_t *modulep, vm_offset_t *kernend, int add_smap); void pxe_enable(void *pxeinfo); Index: stable/12/stand/i386/loader/chain.c =================================================================== --- stable/12/stand/i386/loader/chain.c (revision 359734) +++ stable/12/stand/i386/loader/chain.c (revision 359735) @@ -1,136 +1,169 @@ /*- * Copyright 2015 Toomas Soome * 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. */ /* * Chain loader to load BIOS boot block either from MBR or PBR. * * Note the boot block location 0000:7c000 conflicts with loader, so we need to * read in to temporary space and relocate on exec, when btx is stopped. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "bootstrap.h" #include "libi386/libi386.h" #include "btxv86.h" +#ifdef LOADER_VERIEXEC_VECTX +#define VECTX_HANDLE(x) vctx +#else +#define VECTX_HANDLE(x) x +#endif + /* * The MBR/VBR is located in first sector of disk/partition. * Read 512B to temporary location and set up relocation. Then * exec relocator. */ #define SECTOR_SIZE (512) COMMAND_SET(chain, "chain", "chain load boot block from device", command_chain); static int command_chain(int argc, char *argv[]) { int fd, len, size = SECTOR_SIZE; struct stat st; vm_offset_t mem = 0x100000; struct i386_devdesc *rootdev; +#ifdef LOADER_VERIEXEC_VECTX + struct vectx *vctx; + int verror; +#endif if (argc == 1) { command_errmsg = "no device or file name specified"; return (CMD_ERROR); } if (argc != 2) { command_errmsg = "invalid trailing arguments"; return (CMD_ERROR); } fd = open(argv[1], O_RDONLY); if (fd == -1) { command_errmsg = "open failed"; return (CMD_ERROR); } +#ifdef LOADER_VERIEXEC_VECTX + vctx = vectx_open(fd, argv[1], 0L, NULL, &verror, __func__); + if (verror) { + sprintf(command_errbuf, "can't verify: %s", argv[1]); + close(fd); + free(vctx); + return (CMD_ERROR); + } +#else +#ifdef LOADER_VERIEXEC + if (verify_file(fd, argv[1], 0, VE_MUST, __func__) < 0) { + sprintf(command_errbuf, "can't verify: %s", argv[1]); + close(fd); + return (CMD_ERROR); + } +#endif +#endif len = strlen(argv[1]); if (argv[1][len-1] != ':') { if (fstat(fd, &st) == -1) { command_errmsg = "stat failed"; close(fd); return (CMD_ERROR); } size = st.st_size; } else if (strncmp(argv[1], "disk", 4) != 0) { command_errmsg = "can only use disk device"; close(fd); return (CMD_ERROR); } i386_getdev((void **)(&rootdev), argv[1], NULL); if (rootdev == NULL) { command_errmsg = "can't determine root device"; close(fd); return (CMD_ERROR); } - if (archsw.arch_readin(fd, mem, size) != size) { + if (archsw.arch_readin(VECTX_HANDLE(fd), mem, size) != size) { command_errmsg = "failed to read disk"; close(fd); return (CMD_ERROR); } close(fd); - +#ifdef LOADER_VERIEXEC_VECTX + verror = vectx_close(vctx, VE_MUST, __func__); + if (verror) { + free(vctx); + return (CMD_ERROR); + } +#endif if (argv[1][len-1] == ':' && *((uint16_t *)PTOV(mem + DOSMAGICOFFSET)) != DOSMAGIC) { command_errmsg = "wrong magic"; return (CMD_ERROR); } relocater_data[0].src = mem; relocater_data[0].dest = 0x7C00; relocater_data[0].size = size; relocator_edx = bd_unit2bios(rootdev); relocator_esi = relocater_size; relocator_ds = 0; relocator_es = 0; relocator_fs = 0; relocator_gs = 0; relocator_ss = 0; relocator_cs = 0; relocator_sp = 0x7C00; relocator_ip = 0x7C00; relocator_a20_enabled = 0; i386_copyin(relocater, 0x600, relocater_size); dev_cleanup(); __exec((void *)0x600); panic("exec returned"); return (CMD_ERROR); /* not reached */ } Index: stable/12/stand/libsa/pkgfs.c =================================================================== --- stable/12/stand/libsa/pkgfs.c (revision 359734) +++ stable/12/stand/libsa/pkgfs.c (revision 359735) @@ -1,792 +1,809 @@ /*- * Copyright (c) 2007-2014, Juniper Networks, Inc. * 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. */ #include __FBSDID("$FreeBSD$"); #include "stand.h" #include #include #include #include #ifdef PKGFS_DEBUG #define DBG(x) printf x #else #define DBG(x) #endif static int pkg_open(const char *, struct open_file *); static int pkg_close(struct open_file *); static int pkg_read(struct open_file *, void *, size_t, size_t *); static off_t pkg_seek(struct open_file *, off_t, int); static int pkg_stat(struct open_file *, struct stat *); static int pkg_readdir(struct open_file *, struct dirent *); +static off_t pkg_atol(const char *, unsigned); struct fs_ops pkgfs_fsops = { "pkg", pkg_open, pkg_close, pkg_read, null_write, pkg_seek, pkg_stat, pkg_readdir }; #define PKG_BUFSIZE 512 -#define PKG_MAXCACHESZ 4096 +#define PKG_MAXCACHESZ 16384 #define PKG_FILEEXT ".tgz" /* * Layout of POSIX 'ustar' header. */ struct ustar_hdr { char ut_name[100]; char ut_mode[8]; char ut_uid[8]; char ut_gid[8]; char ut_size[12]; char ut_mtime[12]; char ut_checksum[8]; char ut_typeflag[1]; char ut_linkname[100]; char ut_magic[6]; /* For POSIX: "ustar\0" */ char ut_version[2]; /* For POSIX: "00" */ char ut_uname[32]; char ut_gname[32]; char ut_rdevmajor[8]; char ut_rdevminor[8]; union { struct { char prefix[155]; } posix; struct { char atime[12]; char ctime[12]; char offset[12]; char longnames[4]; char unused[1]; struct gnu_sparse { char offset[12]; char numbytes[12]; } sparse[4]; char isextended[1]; char realsize[12]; } gnu; } u; u_char __padding[12]; }; struct package; struct tarfile { struct package *tf_pkg; struct tarfile *tf_next; struct ustar_hdr tf_hdr; off_t tf_ofs; off_t tf_size; off_t tf_fp; size_t tf_cachesz; void *tf_cache; }; struct package { struct package *pkg_chain; int pkg_fd; off_t pkg_ofs; z_stream pkg_zs; struct tarfile *pkg_first; struct tarfile *pkg_last; u_char pkg_buf[PKG_BUFSIZE]; }; static struct package *package = NULL; static int new_package(int, struct package **); void pkgfs_cleanup(void) { struct package *chain; struct tarfile *tf, *tfn; while (package != NULL) { inflateEnd(&package->pkg_zs); close(package->pkg_fd); tf = package->pkg_first; while (tf != NULL) { tfn = tf->tf_next; if (tf->tf_cachesz > 0) free(tf->tf_cache); free(tf); tf = tfn; } chain = package->pkg_chain; free(package); package = chain; } } int pkgfs_init(const char *pkgname, struct fs_ops *proto) { struct package *pkg; int error, fd; pkg = NULL; if (proto != &pkgfs_fsops) pkgfs_cleanup(); exclusive_file_system = proto; fd = open(pkgname, O_RDONLY); exclusive_file_system = NULL; if (fd == -1) return (errno); error = new_package(fd, &pkg); if (error) { close(fd); return (error); } if (pkg == NULL) return (EDOOFUS); pkg->pkg_chain = package; package = pkg; exclusive_file_system = &pkgfs_fsops; return (0); } static int get_mode(struct tarfile *); static int get_zipped(struct package *, void *, size_t); static int new_package(int, struct package **); static struct tarfile *scan_tarfile(struct package *, struct tarfile *); static int pkg_open(const char *fn, struct open_file *f) { struct tarfile *tf; if (fn == NULL || f == NULL) return (EINVAL); if (package == NULL) return (ENXIO); /* * We can only read from a package, so reject request to open * for write-only or read-write. */ if (f->f_flags != F_READ) return (EPERM); /* * Scan the file headers for the named file. We stop scanning * at the first filename that has the .pkg extension. This is * a package within a package. We assume we have all the files * we need up-front and without having to dig within nested * packages. * * Note that we preserve streaming properties as much as possible. */ while (*fn == '/') fn++; /* * Allow opening of the root directory for use by readdir() * to support listing files in the package. */ if (*fn == '\0') { f->f_fsdata = NULL; return (0); } tf = scan_tarfile(package, NULL); while (tf != NULL) { if (strcmp(fn, tf->tf_hdr.ut_name) == 0) { f->f_fsdata = tf; tf->tf_fp = 0; /* Reset the file pointer. */ return (0); } tf = scan_tarfile(package, tf); } return (errno); } static int pkg_close(struct open_file *f) { struct tarfile *tf; tf = (struct tarfile *)f->f_fsdata; if (tf == NULL) return (0); /* * Free up the cache if we read all of the file. */ if (tf->tf_fp == tf->tf_size && tf->tf_cachesz > 0) { free(tf->tf_cache); tf->tf_cachesz = 0; } return (0); } static int pkg_read(struct open_file *f, void *buf, size_t size, size_t *res) { struct tarfile *tf; char *p; off_t fp; size_t sz; tf = (struct tarfile *)f->f_fsdata; if (tf == NULL) { if (res != NULL) *res = size; return (EBADF); } fp = tf->tf_fp; p = buf; sz = 0; while (size > 0) { sz = tf->tf_size - fp; if (fp < tf->tf_cachesz && tf->tf_cachesz < tf->tf_size) sz = tf->tf_cachesz - fp; if (size < sz) sz = size; if (sz == 0) break; if (fp < tf->tf_cachesz) { /* Satisfy the request from cache. */ memcpy(p, tf->tf_cache + fp, sz); fp += sz; p += sz; size -= sz; continue; } if (get_zipped(tf->tf_pkg, p, sz) == -1) { sz = -1; break; } fp += sz; p += sz; size -= sz; if (tf->tf_cachesz != 0) continue; tf->tf_cachesz = (sz <= PKG_MAXCACHESZ) ? sz : PKG_MAXCACHESZ; tf->tf_cache = malloc(tf->tf_cachesz); if (tf->tf_cache != NULL) memcpy(tf->tf_cache, buf, tf->tf_cachesz); else tf->tf_cachesz = 0; } tf->tf_fp = fp; if (res != NULL) *res = size; return ((sz == -1) ? errno : 0); } static off_t pkg_seek(struct open_file *f, off_t ofs, int whence) { char buf[512]; struct tarfile *tf; off_t delta; + off_t nofs; size_t sz, res; int error; tf = (struct tarfile *)f->f_fsdata; if (tf == NULL) { errno = EBADF; return (-1); } switch (whence) { case SEEK_SET: delta = ofs - tf->tf_fp; break; case SEEK_CUR: delta = ofs; break; case SEEK_END: delta = tf->tf_size - tf->tf_fp + ofs; break; default: errno = EINVAL; return (-1); } if (delta < 0) { + /* seeking backwards - ok if within cache */ + if (tf->tf_cachesz > 0 && tf->tf_fp <= tf->tf_cachesz) { + nofs = tf->tf_fp + delta; + if (nofs >= 0) { + tf->tf_fp = nofs; + return (tf->tf_fp); + } + } DBG(("%s: negative file seek (%jd)\n", __func__, (intmax_t)delta)); errno = ESPIPE; return (-1); } while (delta > 0 && tf->tf_fp < tf->tf_size) { sz = (delta > sizeof(buf)) ? sizeof(buf) : delta; error = pkg_read(f, buf, sz, &res); if (error != 0) { errno = error; return (-1); } delta -= sz - res; } return (tf->tf_fp); } static int pkg_stat(struct open_file *f, struct stat *sb) { struct tarfile *tf; tf = (struct tarfile *)f->f_fsdata; if (tf == NULL) return (EBADF); memset(sb, 0, sizeof(*sb)); sb->st_mode = get_mode(tf); + if ((sb->st_mode & S_IFMT) == 0) { + /* tar file bug - assume regular file */ + sb->st_mode |= S_IFREG; + } sb->st_size = tf->tf_size; sb->st_blocks = (tf->tf_size + 511) / 512; + sb->st_mtime = pkg_atol(tf->tf_hdr.ut_mtime, 12); + sb->st_dev = (off_t)tf->tf_pkg; + sb->st_ino = tf->tf_ofs; /* unique per tf_pkg */ return (0); } static int pkg_readdir(struct open_file *f, struct dirent *d) { struct tarfile *tf; tf = (struct tarfile *)f->f_fsdata; if (tf != NULL) return (EBADF); tf = scan_tarfile(package, NULL); if (tf == NULL) return (ENOENT); d->d_fileno = 0; d->d_reclen = sizeof(*d); d->d_type = DT_REG; memcpy(d->d_name, tf->tf_hdr.ut_name, sizeof(d->d_name)); return (0); } /* * Low-level support functions. */ static int get_byte(struct package *pkg, off_t *op) { int c; if (pkg->pkg_zs.avail_in == 0) { c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE); if (c <= 0) return (-1); pkg->pkg_zs.avail_in = c; pkg->pkg_zs.next_in = pkg->pkg_buf; } c = *pkg->pkg_zs.next_in; pkg->pkg_zs.next_in++; pkg->pkg_zs.avail_in--; (*op)++; return (c); } static int get_zipped(struct package *pkg, void *buf, size_t bufsz) { int c; pkg->pkg_zs.next_out = buf; pkg->pkg_zs.avail_out = bufsz; while (pkg->pkg_zs.avail_out) { if (pkg->pkg_zs.avail_in == 0) { c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE); if (c <= 0) { errno = EIO; return (-1); } pkg->pkg_zs.avail_in = c; pkg->pkg_zs.next_in = pkg->pkg_buf; } c = inflate(&pkg->pkg_zs, Z_SYNC_FLUSH); if (c != Z_OK && c != Z_STREAM_END) { errno = EIO; return (-1); } } pkg->pkg_ofs += bufsz; return (0); } static int cache_data(struct tarfile *tf) { struct package *pkg; size_t sz; if (tf == NULL) { DBG(("%s: no file to cache data for?\n", __func__)); errno = EINVAL; return (-1); } pkg = tf->tf_pkg; if (pkg == NULL) { DBG(("%s: no package associated with file?\n", __func__)); errno = EINVAL; return (-1); } if (tf->tf_ofs != pkg->pkg_ofs) { DBG(("%s: caching after partial read of file %s?\n", __func__, tf->tf_hdr.ut_name)); errno = EINVAL; return (-1); } /* We don't cache everything... */ if (tf->tf_size > PKG_MAXCACHESZ) { errno = ENOMEM; return (-1); } /* All files are padded to a multiple of 512 bytes. */ sz = (tf->tf_size + 0x1ff) & ~0x1ff; tf->tf_cache = malloc(sz); if (tf->tf_cache == NULL) { DBG(("%s: could not allocate %d bytes\n", __func__, (int)sz)); errno = ENOMEM; return (-1); } tf->tf_cachesz = sz; return (get_zipped(pkg, tf->tf_cache, sz)); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static off_t pkg_atol8(const char *p, unsigned char_cnt) { int64_t l, limit, last_digit_limit; int digit, sign, base; base = 8; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; while (*p == ' ' || *p == '\t') p++; if (*p == '-') { sign = -1; p++; } else sign = 1; l = 0; digit = *p - '0'; while (digit >= 0 && digit < base && char_cnt-- > 0) { if (l>limit || (l == limit && digit > last_digit_limit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++p - '0'; } return (sign < 0) ? -l : l; } /* * Parse a base-256 integer. This is just a straight signed binary * value in big-endian order, except that the high-order bit is * ignored. Remember that "int64_t" may or may not be exactly 64 * bits; the implementation here tries to avoid making any assumptions * about the actual size of an int64_t. It does assume we're using * twos-complement arithmetic, though. */ static int64_t pkg_atol256(const char *_p, unsigned char_cnt) { int64_t l, upper_limit, lower_limit; const unsigned char *p = (const unsigned char *)_p; upper_limit = INT64_MAX / 256; lower_limit = INT64_MIN / 256; /* Pad with 1 or 0 bits, depending on sign. */ if ((0x40 & *p) == 0x40) l = (int64_t)-1; else l = 0; l = (l << 6) | (0x3f & *p++); while (--char_cnt > 0) { if (l > upper_limit) { l = INT64_MAX; /* Truncate on overflow */ break; } else if (l < lower_limit) { l = INT64_MIN; break; } l = (l << 8) | (0xff & (int64_t)*p++); } return (l); } static off_t pkg_atol(const char *p, unsigned char_cnt) { /* * Technically, GNU pkg considers a field to be in base-256 * only if the first byte is 0xff or 0x80. */ if (*p & 0x80) return (pkg_atol256(p, char_cnt)); return (pkg_atol8(p, char_cnt)); } static int get_mode(struct tarfile *tf) { return (pkg_atol(tf->tf_hdr.ut_mode, sizeof(tf->tf_hdr.ut_mode))); } /* GZip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ #define COMMENT 0x10 /* bit 4 set: file comment present */ #define RESERVED 0xE0 /* bits 5..7: reserved */ static int new_package(int fd, struct package **pp) { struct package *pkg; off_t ofs; int flags, i, error; pkg = malloc(sizeof(*pkg)); if (pkg == NULL) return (ENOMEM); bzero(pkg, sizeof(*pkg)); pkg->pkg_fd = fd; /* * Parse the header. */ error = EFTYPE; ofs = 0; /* Check megic. */ if (get_byte(pkg, &ofs) != 0x1f || get_byte(pkg, &ofs) != 0x8b) goto fail; /* Check method. */ if (get_byte(pkg, &ofs) != Z_DEFLATED) goto fail; /* Check flags. */ flags = get_byte(pkg, &ofs); if (flags & RESERVED) goto fail; /* Skip time, xflags and OS code. */ for (i = 0; i < 6; i++) { if (get_byte(pkg, &ofs) == -1) goto fail; } /* Skip extra field. */ if (flags & EXTRA_FIELD) { i = (get_byte(pkg, &ofs) & 0xff) | ((get_byte(pkg, &ofs) << 8) & 0xff); while (i-- > 0) { if (get_byte(pkg, &ofs) == -1) goto fail; } } /* Skip original file name. */ if (flags & ORIG_NAME) { do { i = get_byte(pkg, &ofs); } while (i != 0 && i != -1); if (i == -1) goto fail; } /* Print the comment if it's there. */ if (flags & COMMENT) { while (1) { i = get_byte(pkg, &ofs); if (i == -1) goto fail; if (i == 0) break; putchar(i); } } /* Skip the CRC. */ if (flags & HEAD_CRC) { if (get_byte(pkg, &ofs) == -1) goto fail; if (get_byte(pkg, &ofs) == -1) goto fail; } /* * Done parsing the ZIP header. Spkgt the inflation engine. */ error = inflateInit2(&pkg->pkg_zs, -15); if (error != Z_OK) goto fail; *pp = pkg; return (0); fail: free(pkg); return (error); } static struct tarfile * scan_tarfile(struct package *pkg, struct tarfile *last) { char buf[512]; struct tarfile *cur; off_t ofs; size_t sz; cur = (last != NULL) ? last->tf_next : pkg->pkg_first; if (cur == NULL) { ofs = (last != NULL) ? last->tf_ofs + last->tf_size : pkg->pkg_ofs; ofs = (ofs + 0x1ff) & ~0x1ff; /* Check if we've reached EOF. */ if (ofs < pkg->pkg_ofs) { errno = ENOSPC; return (NULL); } if (ofs != pkg->pkg_ofs) { if (last != NULL && pkg->pkg_ofs == last->tf_ofs) { if (cache_data(last) == -1) return (NULL); } else { sz = ofs - pkg->pkg_ofs; while (sz != 0) { if (sz > sizeof(buf)) sz = sizeof(buf); if (get_zipped(pkg, buf, sz) == -1) return (NULL); sz = ofs - pkg->pkg_ofs; } } } cur = malloc(sizeof(*cur)); if (cur == NULL) return (NULL); memset(cur, 0, sizeof(*cur)); cur->tf_pkg = pkg; while (1) { if (get_zipped(pkg, &cur->tf_hdr, sizeof(cur->tf_hdr)) == -1) { free(cur); return (NULL); } /* * There are always 2 empty blocks appended to * a PKG. It marks the end of the archive. */ if (strncmp(cur->tf_hdr.ut_magic, "ustar", 5) != 0) { free(cur); errno = ENOSPC; return (NULL); } cur->tf_ofs = pkg->pkg_ofs; cur->tf_size = pkg_atol(cur->tf_hdr.ut_size, sizeof(cur->tf_hdr.ut_size)); if (cur->tf_hdr.ut_name[0] != '+') break; /* * Skip package meta-files. */ ofs = cur->tf_ofs + cur->tf_size; ofs = (ofs + 0x1ff) & ~0x1ff; while (pkg->pkg_ofs < ofs) { if (get_zipped(pkg, buf, sizeof(buf)) == -1) { free(cur); return (NULL); } } } if (last != NULL) last->tf_next = cur; else pkg->pkg_first = cur; pkg->pkg_last = cur; } return (cur); } Index: stable/12/stand/loader.mk =================================================================== --- stable/12/stand/loader.mk (revision 359734) +++ stable/12/stand/loader.mk (revision 359735) @@ -1,175 +1,178 @@ # $FreeBSD$ .PATH: ${LDRSRC} ${BOOTSRC}/libsa CFLAGS+=-I${LDRSRC} SRCS+= boot.c commands.c console.c devopen.c interp.c SRCS+= interp_backslash.c interp_parse.c ls.c misc.c SRCS+= module.c .if ${MACHINE} == "i386" || ${MACHINE_CPUARCH} == "amd64" SRCS+= load_elf32.c load_elf32_obj.c reloc_elf32.c SRCS+= load_elf64.c load_elf64_obj.c reloc_elf64.c .elif ${MACHINE_CPUARCH} == "aarch64" SRCS+= load_elf64.c reloc_elf64.c .elif ${MACHINE_CPUARCH} == "arm" SRCS+= load_elf32.c reloc_elf32.c .elif ${MACHINE_CPUARCH} == "powerpc" SRCS+= load_elf32.c reloc_elf32.c SRCS+= load_elf64.c reloc_elf64.c SRCS+= metadata.c .elif ${MACHINE_CPUARCH} == "sparc64" SRCS+= load_elf64.c reloc_elf64.c SRCS+= metadata.c .elif ${MACHINE_ARCH:Mmips64*} != "" SRCS+= load_elf64.c reloc_elf64.c SRCS+= metadata.c .elif ${MACHINE} == "mips" SRCS+= load_elf32.c reloc_elf32.c SRCS+= metadata.c .endif .if ${LOADER_DISK_SUPPORT:Uyes} == "yes" SRCS+= disk.c part.c vdisk.c .endif .if ${LOADER_NET_SUPPORT:Uno} == "yes" SRCS+= dev_net.c .endif .if defined(HAVE_BCACHE) SRCS+= bcache.c .endif .if defined(MD_IMAGE_SIZE) CFLAGS+= -DMD_IMAGE_SIZE=${MD_IMAGE_SIZE} SRCS+= md.c .else CLEANFILES+= md.o .endif # Machine-independant ISA PnP .if defined(HAVE_ISABUS) SRCS+= isapnp.c .endif .if defined(HAVE_PNP) SRCS+= pnp.c .endif .if ${LOADER_INTERP} == "lua" SRCS+= interp_lua.c .include "${BOOTSRC}/lua.mk" LDR_INTERP= ${LIBLUA} LDR_INTERP32= ${LIBLUA32} CFLAGS.interp_lua.c= -DLUA_PATH=\"${LUAPATH}\" -I${FLUASRC}/modules .elif ${LOADER_INTERP} == "4th" SRCS+= interp_forth.c .include "${BOOTSRC}/ficl.mk" LDR_INTERP= ${LIBFICL} LDR_INTERP32= ${LIBFICL32} .elif ${LOADER_INTERP} == "simp" SRCS+= interp_simple.c .else .error Unknown interpreter ${LOADER_INTERP} .endif .if ${MK_LOADER_VERIEXEC} != "no" CFLAGS+= -DLOADER_VERIEXEC -I${SRCTOP}/lib/libsecureboot/h +.if ${MK_LOADER_VERIEXEC_VECTX} != "no" +CFLAGS+= -DLOADER_VERIEXEC_VECTX +.endif .endif .if ${MK_LOADER_VERIEXEC_PASS_MANIFEST} != "no" CFLAGS+= -DLOADER_VERIEXEC_PASS_MANIFEST -I${SRCTOP}/lib/libsecureboot/h .endif .if defined(BOOT_PROMPT_123) CFLAGS+= -DBOOT_PROMPT_123 .endif .if defined(LOADER_INSTALL_SUPPORT) SRCS+= install.c .endif # Filesystem support .if ${LOADER_CD9660_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_CD9660_SUPPORT .endif .if ${LOADER_EXT2FS_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_EXT2FS_SUPPORT .endif .if ${LOADER_MSDOS_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_MSDOS_SUPPORT .endif .if ${LOADER_NANDFS_SUPPORT:U${MK_NAND}} == "yes" CFLAGS+= -DLOADER_NANDFS_SUPPORT .endif .if ${LOADER_UFS_SUPPORT:Uyes} == "yes" CFLAGS+= -DLOADER_UFS_SUPPORT .endif # Compression .if ${LOADER_GZIP_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_GZIP_SUPPORT .endif .if ${LOADER_BZIP2_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_BZIP2_SUPPORT .endif # Network related things .if ${LOADER_NET_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_NET_SUPPORT .endif .if ${LOADER_NFS_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_NFS_SUPPORT .endif .if ${LOADER_TFTP_SUPPORT:Uno} == "yes" CFLAGS+= -DLOADER_TFTP_SUPPORT .endif # Partition support .if ${LOADER_GPT_SUPPORT:Uyes} == "yes" CFLAGS+= -DLOADER_GPT_SUPPORT .endif .if ${LOADER_MBR_SUPPORT:Uyes} == "yes" CFLAGS+= -DLOADER_MBR_SUPPORT .endif .if ${HAVE_ZFS:Uno} == "yes" CFLAGS+= -DLOADER_ZFS_SUPPORT CFLAGS+= -I${ZFSSRC} CFLAGS+= -I${SYSDIR}/cddl/boot/zfs SRCS+= zfs_cmd.c .endif LIBFICL= ${BOOTOBJ}/ficl/libficl.a .if ${MACHINE} == "i386" LIBFICL32= ${LIBFICL} .else LIBFICL32= ${BOOTOBJ}/ficl32/libficl.a .endif LIBLUA= ${BOOTOBJ}/liblua/liblua.a .if ${MACHINE} == "i386" LIBLUA32= ${LIBLUA} .else LIBLUA32= ${BOOTOBJ}/liblua32/liblua.a .endif CLEANFILES+= vers.c VERSION_FILE?= ${.CURDIR}/version .if ${MK_REPRODUCIBLE_BUILD} != no REPRO_FLAG= -r .endif vers.c: ${LDRSRC}/newvers.sh ${VERSION_FILE} sh ${LDRSRC}/newvers.sh ${REPRO_FLAG} ${VERSION_FILE} \ ${NEWVERSWHAT} .if !empty(HELP_FILES) HELP_FILES+= ${LDRSRC}/help.common CLEANFILES+= loader.help FILES+= loader.help loader.help: ${HELP_FILES} cat ${HELP_FILES} | awk -f ${LDRSRC}/merge_help.awk > ${.TARGET} .endif Index: stable/12/stand/mips/beri/loader/arch.c =================================================================== --- stable/12/stand/mips/beri/loader/arch.c (revision 359734) +++ stable/12/stand/mips/beri/loader/arch.c (revision 359735) @@ -1,97 +1,97 @@ /*- * Copyright (c) 2013 Robert N. M. Watson * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include static int beri_arch_autoload(void); static ssize_t beri_arch_copyin(const void *src, vm_offset_t va, size_t len); static ssize_t beri_arch_copyout(vm_offset_t va, void *dst, size_t len); static uint64_t beri_arch_loadaddr(u_int type, void *data, uint64_t addr); -static ssize_t beri_arch_readin(int fd, vm_offset_t va, size_t len); +static ssize_t beri_arch_readin(readin_handle_t fd, vm_offset_t va, size_t len); struct arch_switch archsw = { .arch_autoload = beri_arch_autoload, .arch_getdev = beri_arch_getdev, .arch_copyin = beri_arch_copyin, .arch_copyout = beri_arch_copyout, .arch_loadaddr = beri_arch_loadaddr, .arch_readin = beri_arch_readin, }; static int beri_arch_autoload(void) { return (0); } static ssize_t beri_arch_copyin(const void *src, vm_offset_t va, size_t len) { memcpy((void *)va, src, len); return (len); } static ssize_t beri_arch_copyout(vm_offset_t va, void *dst, size_t len) { memcpy(dst, (void *)va, len); return (len); } static uint64_t beri_arch_loadaddr(u_int type, void *data, uint64_t addr) { uint64_t align; /* Align ELF objects at page boundaries; others at cache lines. */ align = (type == LOAD_ELF) ? PAGE_SIZE : CACHE_LINE_SIZE; return (roundup2(addr, align)); } static ssize_t -beri_arch_readin(int fd, vm_offset_t va, size_t len) +beri_arch_readin(readin_handle_t fd, vm_offset_t va, size_t len) { - return (read(fd, (void *)va, len)); + return (VECTX_READ(fd, (void *)va, len)); } Index: stable/12/stand/powerpc/kboot/main.c =================================================================== --- stable/12/stand/powerpc/kboot/main.c (revision 359734) +++ stable/12/stand/powerpc/kboot/main.c (revision 359735) @@ -1,515 +1,515 @@ /*- * Copyright (C) 2010-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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #define _KERNEL #include -#include "bootstrap.h" +#include #include "host_syscall.h" struct arch_switch archsw; extern void *_end; int kboot_getdev(void **vdev, const char *devspec, const char **path); ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len); -ssize_t kboot_readin(const int fd, vm_offset_t dest, const size_t len); +ssize_t kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); int kboot_autoload(void); uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr); int kboot_setcurrdev(struct env_var *ev, int flags, const void *value); static void kboot_kseg_get(int *nseg, void **ptr); extern int command_fdt_internal(int argc, char *argv[]); struct region_desc { uint64_t start; uint64_t end; }; static uint64_t kboot_get_phys_load_segment(void) { int fd; uint64_t entry[2]; static uint64_t load_segment = ~(0UL); uint64_t val_64; uint32_t val_32; struct region_desc rsvd_reg[32]; int rsvd_reg_cnt = 0; int ret, a, b; uint64_t start, end; if (load_segment == ~(0UL)) { /* Default load address is 0x00000000 */ load_segment = 0UL; /* Read reserved regions */ fd = host_open("/proc/device-tree/reserved-ranges", O_RDONLY, 0); if (fd >= 0) { while (host_read(fd, &entry[0], sizeof(entry)) == sizeof(entry)) { rsvd_reg[rsvd_reg_cnt].start = be64toh(entry[0]); rsvd_reg[rsvd_reg_cnt].end = be64toh(entry[1]) + rsvd_reg[rsvd_reg_cnt].start - 1; rsvd_reg_cnt++; } host_close(fd); } /* Read where the kernel ends */ fd = host_open("/proc/device-tree/chosen/linux,kernel-end", O_RDONLY, 0); if (fd >= 0) { ret = host_read(fd, &val_64, sizeof(val_64)); if (ret == sizeof(uint64_t)) { rsvd_reg[rsvd_reg_cnt].start = 0; rsvd_reg[rsvd_reg_cnt].end = be64toh(val_64) - 1; } else { memcpy(&val_32, &val_64, sizeof(val_32)); rsvd_reg[rsvd_reg_cnt].start = 0; rsvd_reg[rsvd_reg_cnt].end = be32toh(val_32) - 1; } rsvd_reg_cnt++; host_close(fd); } /* Read memory size (SOCKET0 only) */ fd = host_open("/proc/device-tree/memory@0/reg", O_RDONLY, 0); if (fd < 0) fd = host_open("/proc/device-tree/memory/reg", O_RDONLY, 0); if (fd >= 0) { ret = host_read(fd, &entry, sizeof(entry)); /* Memory range in start:length format */ entry[0] = be64toh(entry[0]); entry[1] = be64toh(entry[1]); /* Reserve everything what is before start */ if (entry[0] != 0) { rsvd_reg[rsvd_reg_cnt].start = 0; rsvd_reg[rsvd_reg_cnt].end = entry[0] - 1; rsvd_reg_cnt++; } /* Reserve everything what is after end */ if (entry[1] != 0xffffffffffffffffUL) { rsvd_reg[rsvd_reg_cnt].start = entry[0] + entry[1]; rsvd_reg[rsvd_reg_cnt].end = 0xffffffffffffffffUL; rsvd_reg_cnt++; } host_close(fd); } /* Sort entries in ascending order (bubble) */ for (a = rsvd_reg_cnt - 1; a > 0; a--) { for (b = 0; b < a; b++) { if (rsvd_reg[b].start > rsvd_reg[b + 1].start) { struct region_desc tmp; tmp = rsvd_reg[b]; rsvd_reg[b] = rsvd_reg[b + 1]; rsvd_reg[b + 1] = tmp; } } } /* Join overlapping/adjacent regions */ for (a = 0; a < rsvd_reg_cnt - 1; ) { if ((rsvd_reg[a + 1].start >= rsvd_reg[a].start) && ((rsvd_reg[a + 1].start - 1) <= rsvd_reg[a].end)) { /* We have overlapping/adjacent regions! */ rsvd_reg[a].end = MAX(rsvd_reg[a].end, rsvd_reg[a + a].end); for (b = a + 1; b < rsvd_reg_cnt - 1; b++) rsvd_reg[b] = rsvd_reg[b + 1]; rsvd_reg_cnt--; } else a++; } /* Find the first free region */ if (rsvd_reg_cnt > 0) { start = 0; end = rsvd_reg[0].start; for (a = 0; a < rsvd_reg_cnt - 1; a++) { if ((start >= rsvd_reg[a].start) && (start <= rsvd_reg[a].end)) { start = rsvd_reg[a].end + 1; end = rsvd_reg[a + 1].start; } else break; } if (start != end) { uint64_t align = 64UL*1024UL*1024UL; /* Align both to 64MB boundary */ start = (start + align - 1UL) & ~(align - 1UL); end = ((end + 1UL) & ~(align - 1UL)) - 1UL; if (start < end) load_segment = start; } } } return (load_segment); } uint8_t kboot_get_kernel_machine_bits(void) { static uint8_t bits = 0; struct old_utsname utsname; int ret; if (bits == 0) { /* Default is 32-bit kernel */ bits = 32; /* Try to get system type */ memset(&utsname, 0, sizeof(utsname)); ret = host_uname(&utsname); if (ret == 0) { if (strcmp(utsname.machine, "ppc64") == 0) bits = 64; else if (strcmp(utsname.machine, "ppc64le") == 0) bits = 64; } } return (bits); } int kboot_getdev(void **vdev, const char *devspec, const char **path) { int i; const char *devpath, *filepath; struct devsw *dv; struct devdesc *desc; if (strchr(devspec, ':') != NULL) { devpath = devspec; filepath = strchr(devspec, ':') + 1; } else { devpath = getenv("currdev"); filepath = devspec; } for (i = 0; (dv = devsw[i]) != NULL; i++) { if (strncmp(dv->dv_name, devpath, strlen(dv->dv_name)) == 0) goto found; } return (ENOENT); found: if (path != NULL && filepath != NULL) *path = filepath; else if (path != NULL) *path = strchr(devspec, ':') + 1; if (vdev != NULL) { desc = malloc(sizeof(*desc)); desc->d_dev = dv; desc->d_unit = 0; desc->d_opendata = strdup(devpath); *vdev = desc; } return (0); } int main(int argc, const char **argv) { void *heapbase; const size_t heapsize = 15*1024*1024; const char *bootdev; /* * Set the heap to one page after the end of the loader. */ heapbase = host_getmem(heapsize); setheap(heapbase, heapbase + heapsize); /* * Set up console. */ cons_probe(); /* Choose bootdev if provided */ if (argc > 1) bootdev = argv[1]; else bootdev = ""; printf("Boot device: %s\n", bootdev); archsw.arch_getdev = kboot_getdev; archsw.arch_copyin = kboot_copyin; archsw.arch_copyout = kboot_copyout; archsw.arch_readin = kboot_readin; archsw.arch_autoload = kboot_autoload; archsw.arch_loadaddr = kboot_loadaddr; archsw.arch_kexec_kseg_get = kboot_kseg_get; printf("\n%s", bootprog_info); setenv("currdev", bootdev, 1); setenv("loaddev", bootdev, 1); setenv("LINES", "24", 1); setenv("usefdt", "1", 1); interact(); /* doesn't return */ return (0); } void exit(int code) { while (1); /* XXX: host_exit */ __unreachable(); } void delay(int usecs) { struct host_timeval tvi, tv; uint64_t ti, t; host_gettimeofday(&tvi, NULL); ti = tvi.tv_sec*1000000 + tvi.tv_usec; do { host_gettimeofday(&tv, NULL); t = tv.tv_sec*1000000 + tv.tv_usec; } while (t < ti + usecs); } time_t getsecs(void) { struct host_timeval tv; host_gettimeofday(&tv, NULL); return (tv.tv_sec); } time_t time(time_t *tloc) { time_t rv; rv = getsecs(); if (tloc != NULL) *tloc = rv; return (rv); } struct kexec_segment { void *buf; int bufsz; void *mem; int memsz; }; struct kexec_segment loaded_segments[128]; int nkexec_segments = 0; static ssize_t get_phys_buffer(vm_offset_t dest, const size_t len, void **buf) { int i = 0; const size_t segsize = 4*1024*1024; for (i = 0; i < nkexec_segments; i++) { if (dest >= (vm_offset_t)loaded_segments[i].mem && dest < (vm_offset_t)loaded_segments[i].mem + loaded_segments[i].memsz) goto out; } loaded_segments[nkexec_segments].buf = host_getmem(segsize); loaded_segments[nkexec_segments].bufsz = segsize; loaded_segments[nkexec_segments].mem = (void *)rounddown2(dest,segsize); loaded_segments[nkexec_segments].memsz = segsize; i = nkexec_segments; nkexec_segments++; out: *buf = loaded_segments[i].buf + (dest - (vm_offset_t)loaded_segments[i].mem); return (min(len,loaded_segments[i].bufsz - (dest - (vm_offset_t)loaded_segments[i].mem))); } ssize_t kboot_copyin(const void *src, vm_offset_t dest, const size_t len) { ssize_t segsize, remainder; void *destbuf; remainder = len; do { segsize = get_phys_buffer(dest, remainder, &destbuf); bcopy(src, destbuf, segsize); remainder -= segsize; src += segsize; dest += segsize; } while (remainder > 0); return (len); } ssize_t kboot_copyout(vm_offset_t src, void *dest, const size_t len) { ssize_t segsize, remainder; void *srcbuf; remainder = len; do { segsize = get_phys_buffer(src, remainder, &srcbuf); bcopy(srcbuf, dest, segsize); remainder -= segsize; src += segsize; dest += segsize; } while (remainder > 0); return (len); } ssize_t -kboot_readin(const int fd, vm_offset_t dest, const size_t len) +kboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { void *buf; size_t resid, chunk, get; ssize_t got; vm_offset_t p; p = dest; chunk = min(PAGE_SIZE, len); buf = malloc(chunk); if (buf == NULL) { printf("kboot_readin: buf malloc failed\n"); return (0); } for (resid = len; resid > 0; resid -= got, p += got) { get = min(chunk, resid); - got = read(fd, buf, get); + got = VECTX_READ(fd, buf, get); if (got <= 0) { if (got < 0) printf("kboot_readin: read failed\n"); break; } kboot_copyin(buf, p, got); } free (buf); return (len - resid); } int kboot_autoload(void) { return (0); } uint64_t kboot_loadaddr(u_int type, void *data, uint64_t addr) { if (type == LOAD_ELF) addr = roundup(addr, PAGE_SIZE); else addr += kboot_get_phys_load_segment(); return (addr); } static void kboot_kseg_get(int *nseg, void **ptr) { #if 0 int a; for (a = 0; a < nkexec_segments; a++) { printf("kseg_get: %jx %jx %jx %jx\n", (uintmax_t)loaded_segments[a].buf, (uintmax_t)loaded_segments[a].bufsz, (uintmax_t)loaded_segments[a].mem, (uintmax_t)loaded_segments[a].memsz); } #endif *nseg = nkexec_segments; *ptr = &loaded_segments[0]; } void _start(int argc, const char **argv, char **env) { // This makes error "variable 'sp' is uninitialized" be just a warning on clang. // Initializing 'sp' is not desired here as it would overwrite "r1" original value #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic warning "-Wuninitialized" #endif register volatile void **sp asm("r1"); main((int)sp[0], (const char **)&sp[1]); #if defined(__clang__) #pragma clang diagnostic pop #endif } /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); Index: stable/12/stand/uboot/lib/copy.c =================================================================== --- stable/12/stand/uboot/lib/copy.c (revision 359734) +++ stable/12/stand/uboot/lib/copy.c (revision 359735) @@ -1,166 +1,166 @@ /*- * Copyright (c) 1998 Michael Smith * Copyright (c) 2007 Semihalf, Rafal Jaworowski * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include "api_public.h" #include "glue.h" #include "libuboot.h" /* * MD primitives supporting placement of module data */ #ifdef __arm__ #define KERN_ALIGN (2 * 1024 * 1024) #else #define KERN_ALIGN PAGE_SIZE #endif /* * Avoid low memory, u-boot puts things like args and dtb blobs there. */ #define KERN_MINADDR max(KERN_ALIGN, (1024 * 1024)) extern void _start(void); /* ubldr entry point address. */ /* * This is called for every object loaded (kernel, module, dtb file, etc). The * expected return value is the next address at or after the given addr which is * appropriate for loading the given object described by type and data. On each * call the addr is the next address following the previously loaded object. * * The first call is for loading the kernel, and the addr argument will be zero, * and we search for a big block of ram to load the kernel and modules. * * On subsequent calls the addr will be non-zero, and we just round it up so * that each object begins on a page boundary. */ uint64_t uboot_loadaddr(u_int type, void *data, uint64_t addr) { struct sys_info *si; uint64_t sblock, eblock, subldr, eubldr; uint64_t biggest_block, this_block; uint64_t biggest_size, this_size; int i; char *envstr; if (addr == 0) { /* * If the loader_kernaddr environment variable is set, blindly * honor it. It had better be right. We force interpretation * of the value in base-16 regardless of any leading 0x prefix, * because that's the U-Boot convention. */ envstr = ub_env_get("loader_kernaddr"); if (envstr != NULL) return (strtoul(envstr, NULL, 16)); /* * Find addr/size of largest DRAM block. Carve our own address * range out of the block, because loading the kernel over the * top ourself is a poor memory-conservation strategy. Avoid * memory at beginning of the first block of physical ram, * since u-boot likes to pass args and data there. Assume that * u-boot has moved itself to the very top of ram and * optimistically assume that we won't run into it up there. */ if ((si = ub_get_sys_info()) == NULL) panic("could not retrieve system info"); biggest_block = 0; biggest_size = 0; subldr = rounddown2((uintptr_t)_start, KERN_ALIGN); eubldr = roundup2((uint64_t)uboot_heap_end, KERN_ALIGN); for (i = 0; i < si->mr_no; i++) { if (si->mr[i].flags != MR_ATTR_DRAM) continue; sblock = roundup2((uint64_t)si->mr[i].start, KERN_ALIGN); eblock = rounddown2((uint64_t)si->mr[i].start + si->mr[i].size, KERN_ALIGN); if (biggest_size == 0) sblock += KERN_MINADDR; if (subldr >= sblock && subldr < eblock) { if (subldr - sblock > eblock - eubldr) { this_block = sblock; this_size = subldr - sblock; } else { this_block = eubldr; this_size = eblock - eubldr; } } else if (subldr < sblock && eubldr < eblock) { /* Loader is below or engulfs the sblock */ this_block = (eubldr < sblock) ? sblock : eubldr; this_size = eblock - this_block; } else { this_block = 0; this_size = 0; } if (biggest_size < this_size) { biggest_block = this_block; biggest_size = this_size; } } if (biggest_size == 0) panic("Not enough DRAM to load kernel"); #if 0 printf("Loading kernel into region 0x%08jx-0x%08jx (%ju MiB)\n", (uintmax_t)biggest_block, (uintmax_t)biggest_block + biggest_size - 1, (uintmax_t)biggest_size / 1024 / 1024); #endif return (biggest_block); } return roundup2(addr, PAGE_SIZE); } ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len) { bcopy(src, (void *)dest, len); return (len); } ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len) { bcopy((void *)src, dest, len); return (len); } ssize_t -uboot_readin(const int fd, vm_offset_t dest, const size_t len) +uboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len) { - return (read(fd, (void *)dest, len)); + return (VECTX_READ(fd, (void *)dest, len)); } Index: stable/12/stand/uboot/lib/libuboot.h =================================================================== --- stable/12/stand/uboot/lib/libuboot.h (revision 359734) +++ stable/12/stand/uboot/lib/libuboot.h (revision 359735) @@ -1,76 +1,77 @@ /*- * Copyright (C) 2000 Benno Rice. * Copyright (C) 2007 Semihalf, Rafal Jaworowski * 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 +#include struct uboot_devdesc { union { struct devdesc dd; struct disk_devdesc d_disk; }; }; /* * Default network packet alignment in memory. On arm arches packets must be * aligned to cacheline boundaries. */ #if defined(__aarch64__) #define PKTALIGN 128 #elif defined(__arm__) #define PKTALIGN 64 #else #define PKTALIGN 32 #endif int uboot_getdev(void **vdev, const char *devspec, const char **path); char *uboot_fmtdev(void *vdev); int uboot_setcurrdev(struct env_var *ev, int flags, const void *value); extern int devs_no; extern struct netif_driver uboot_net; extern struct devsw uboot_storage; extern uintptr_t uboot_heap_start; extern uintptr_t uboot_heap_end; uint64_t uboot_loadaddr(u_int type, void *data, uint64_t addr); ssize_t uboot_copyin(const void *src, vm_offset_t dest, const size_t len); ssize_t uboot_copyout(const vm_offset_t src, void *dest, const size_t len); -ssize_t uboot_readin(const int fd, vm_offset_t dest, const size_t len); +ssize_t uboot_readin(readin_handle_t fd, vm_offset_t dest, const size_t len); extern int uboot_autoload(void); struct preloaded_file; struct file_format; extern struct file_format uboot_elf; void reboot(void); int uboot_diskgetunit(int type, int type_unit); Index: stable/12/stand/userboot/test/test.c =================================================================== --- stable/12/stand/userboot/test/test.c (revision 359734) +++ stable/12/stand/userboot/test/test.c (revision 359735) @@ -1,475 +1,480 @@ /*- * Copyright (c) 2011 Google, Inc. * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char *host_base = NULL; struct termios term, oldterm; char *image; size_t image_size; int disk_fd = -1; uint64_t regs[16]; uint64_t pc; void test_exit(void *arg, int v); /* * Console i/o */ void test_putc(void *arg, int ch) { char c = ch; write(1, &c, 1); } int test_getc(void *arg) { char c; if (read(0, &c, 1) == 1) return c; return -1; } int test_poll(void *arg) { int n; if (ioctl(0, FIONREAD, &n) >= 0) return (n > 0); return (0); } /* * Host filesystem i/o */ struct test_file { int tf_isdir; size_t tf_size; struct stat tf_stat; union { int fd; DIR *dir; } tf_u; }; int test_open(void *arg, const char *filename, void **h_return) { struct stat st; struct test_file *tf; char path[PATH_MAX]; if (!host_base) return (ENOENT); strlcpy(path, host_base, PATH_MAX); if (path[strlen(path) - 1] == '/') path[strlen(path) - 1] = 0; strlcat(path, filename, PATH_MAX); tf = malloc(sizeof(struct test_file)); if (stat(path, &tf->tf_stat) < 0) { free(tf); return (errno); } tf->tf_size = st.st_size; if (S_ISDIR(tf->tf_stat.st_mode)) { tf->tf_isdir = 1; tf->tf_u.dir = opendir(path); if (!tf->tf_u.dir) goto out; *h_return = tf; return (0); } if (S_ISREG(tf->tf_stat.st_mode)) { tf->tf_isdir = 0; tf->tf_u.fd = open(path, O_RDONLY); if (tf->tf_u.fd < 0) goto out; *h_return = tf; return (0); } out: free(tf); return (EINVAL); } int test_close(void *arg, void *h) { struct test_file *tf = h; if (tf->tf_isdir) closedir(tf->tf_u.dir); else close(tf->tf_u.fd); free(tf); return (0); } int test_isdir(void *arg, void *h) { struct test_file *tf = h; return (tf->tf_isdir); } int test_read(void *arg, void *h, void *dst, size_t size, size_t *resid_return) { struct test_file *tf = h; ssize_t sz; if (tf->tf_isdir) return (EINVAL); sz = read(tf->tf_u.fd, dst, size); if (sz < 0) return (EINVAL); *resid_return = size - sz; return (0); } int test_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return, size_t *namelen_return, char *name) { struct test_file *tf = h; struct dirent *dp; if (!tf->tf_isdir) return (EINVAL); dp = readdir(tf->tf_u.dir); if (!dp) return (ENOENT); /* * Note: d_namlen is in the range 0..255 and therefore less * than PATH_MAX so we don't need to test before copying. */ *fileno_return = dp->d_fileno; *type_return = dp->d_type; *namelen_return = dp->d_namlen; memcpy(name, dp->d_name, dp->d_namlen); name[dp->d_namlen] = 0; return (0); } int test_seek(void *arg, void *h, uint64_t offset, int whence) { struct test_file *tf = h; if (tf->tf_isdir) return (EINVAL); if (lseek(tf->tf_u.fd, offset, whence) < 0) return (errno); return (0); } int -test_stat(void *arg, void *h, int *mode_return, int *uid_return, int *gid_return, - uint64_t *size_return) +test_stat(void *arg, void *h, struct stat *stp) { struct test_file *tf = h; - *mode_return = tf->tf_stat.st_mode; - *uid_return = tf->tf_stat.st_uid; - *gid_return = tf->tf_stat.st_gid; - *size_return = tf->tf_stat.st_size; + if (!stp) + return (-1); + memset(stp, 0, sizeof(struct stat)); + stp->st_mode = tf->tf_stat.st_mode; + stp->st_uid = tf->tf_stat.st_uid; + stp->st_gid = tf->tf_stat.st_gid; + stp->st_size = tf->tf_stat.st_size; + stp->st_ino = tf->tf_stat.st_ino; + stp->st_dev = tf->tf_stat.st_dev; + stp->st_mtime = tf->tf_stat.st_mtime; return (0); } /* * Disk image i/o */ int test_diskread(void *arg, int unit, uint64_t offset, void *dst, size_t size, size_t *resid_return) { ssize_t n; if (unit != 0 || disk_fd == -1) return (EIO); n = pread(disk_fd, dst, size, offset); if (n < 0) return (errno); *resid_return = size - n; return (0); } int test_diskioctl(void *arg, int unit, u_long cmd, void *data) { struct stat sb; if (unit != 0 || disk_fd == -1) return (EBADF); switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = 512; break; case DIOCGMEDIASIZE: if (fstat(disk_fd, &sb) == 0) *(off_t *)data = sb.st_size; else return (ENOTTY); break; default: return (ENOTTY); } return (0); } /* * Guest virtual machine i/o * * Note: guest addresses are kernel virtual */ int test_copyin(void *arg, const void *from, uint64_t to, size_t size) { to &= 0x7fffffff; if (to > image_size) return (EFAULT); if (to + size > image_size) size = image_size - to; memcpy(&image[to], from, size); return(0); } int test_copyout(void *arg, uint64_t from, void *to, size_t size) { from &= 0x7fffffff; if (from > image_size) return (EFAULT); if (from + size > image_size) size = image_size - from; memcpy(to, &image[from], size); return(0); } void test_setreg(void *arg, int r, uint64_t v) { if (r < 0 || r >= 16) return; regs[r] = v; } void test_setmsr(void *arg, int r, uint64_t v) { } void test_setcr(void *arg, int r, uint64_t v) { } void test_setgdt(void *arg, uint64_t v, size_t sz) { } void test_exec(void *arg, uint64_t pc) { printf("Execute at 0x%"PRIu64"\n", pc); test_exit(arg, 0); } /* * Misc */ void test_delay(void *arg, int usec) { usleep(usec); } void test_exit(void *arg, int v) { tcsetattr(0, TCSAFLUSH, &oldterm); exit(v); } void test_getmem(void *arg, uint64_t *lowmem, uint64_t *highmem) { *lowmem = 128*1024*1024; *highmem = 0; } char * test_getenv(void *arg, int idx) { static char *vars[] = { "foo=bar", "bar=barbar", NULL }; return (vars[idx]); } struct loader_callbacks cb = { .putc = test_putc, .getc = test_getc, .poll = test_poll, .open = test_open, .close = test_close, .isdir = test_isdir, .read = test_read, .readdir = test_readdir, .seek = test_seek, .stat = test_stat, .diskread = test_diskread, .diskioctl = test_diskioctl, .copyin = test_copyin, .copyout = test_copyout, .setreg = test_setreg, .setmsr = test_setmsr, .setcr = test_setcr, .setgdt = test_setgdt, .exec = test_exec, .delay = test_delay, .exit = test_exit, .getmem = test_getmem, .getenv = test_getenv, }; void usage() { printf("usage: [-b ] [-d ] [-h \n"); exit(1); } int main(int argc, char** argv) { void *h; void (*func)(struct loader_callbacks *, void *, int, int) __dead2; int opt; char *disk_image = NULL; const char *userboot_obj = "/boot/userboot.so"; while ((opt = getopt(argc, argv, "b:d:h:")) != -1) { switch (opt) { case 'b': userboot_obj = optarg; break; case 'd': disk_image = optarg; break; case 'h': host_base = optarg; break; case '?': usage(); } } h = dlopen(userboot_obj, RTLD_LOCAL); if (!h) { printf("%s\n", dlerror()); return (1); } func = dlsym(h, "loader_main"); if (!func) { printf("%s\n", dlerror()); return (1); } image_size = 128*1024*1024; image = malloc(image_size); if (disk_image) { disk_fd = open(disk_image, O_RDONLY); if (disk_fd < 0) err(1, "Can't open disk image '%s'", disk_image); } tcgetattr(0, &term); oldterm = term; term.c_iflag &= ~(ICRNL); term.c_lflag &= ~(ICANON|ECHO); tcsetattr(0, TCSAFLUSH, &term); func(&cb, NULL, USERBOOT_VERSION_3, disk_fd >= 0); } Index: stable/12/stand/userboot/userboot/conf.c =================================================================== --- stable/12/stand/userboot/userboot/conf.c (revision 359734) +++ stable/12/stand/userboot/userboot/conf.c (revision 359735) @@ -1,107 +1,112 @@ /*- * Copyright (c) 1997 * Matthias Drochner. 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project * by Matthias Drochner. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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 THE AUTHOR 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. * * $NetBSD: conf.c,v 1.2 1997/03/22 09:03:29 thorpej Exp $ */ #include __FBSDID("$FreeBSD$"); #include #include "libuserboot.h" #if defined(USERBOOT_ZFS_SUPPORT) #include "libzfs.h" #endif /* * We could use linker sets for some or all of these, but * then we would have to control what ended up linked into * the bootstrap. So it's easier to conditionalise things * here. * * XXX rename these arrays to be consistent and less namespace-hostile */ /* Exported for libstand */ struct devsw *devsw[] = { &host_dev, &userboot_disk, #if defined(USERBOOT_ZFS_SUPPORT) &zfs_dev, #endif NULL }; struct fs_ops *file_system[] = { &host_fsops, &ufs_fsops, &cd9660_fsops, #if defined(USERBOOT_ZFS_SUPPORT) &zfs_fsops, #endif &gzipfs_fsops, &bzipfs_fsops, NULL }; +/* to keep libsa happy */ +struct netif_driver *netif_drivers[] = { + NULL +}; + /* Exported for i386 only */ /* * Sort formats so that those that can detect based on arguments * rather than reading the file go first. */ extern struct file_format i386_elf; extern struct file_format i386_elf_obj; extern struct file_format amd64_elf; extern struct file_format amd64_elf_obj; struct file_format *file_formats[] = { &i386_elf, &i386_elf_obj, &amd64_elf, &amd64_elf_obj, NULL }; /* * Consoles * * We don't prototype these in libuserboot.h because they require * data structures from bootstrap.h as well. */ extern struct console userboot_console; extern struct console userboot_comconsole; struct console *consoles[] = { &userboot_console, &userboot_comconsole, NULL }; Index: stable/12/stand/userboot/userboot/copy.c =================================================================== --- stable/12/stand/userboot/userboot/copy.c (revision 359734) +++ stable/12/stand/userboot/userboot/copy.c (revision 359735) @@ -1,73 +1,73 @@ /*- * Copyright (c) 2011 Google, Inc. * 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 THE AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include "libuserboot.h" ssize_t userboot_copyin(const void *src, vm_offset_t va, size_t len) { CALLBACK(copyin, src, va, len); return (len); } ssize_t userboot_copyout(vm_offset_t va, void *dst, size_t len) { CALLBACK(copyout, va, dst, len); return (len); } ssize_t -userboot_readin(int fd, vm_offset_t va, size_t len) +userboot_readin(readin_handle_t fd, vm_offset_t va, size_t len) { ssize_t res, s; size_t sz; char buf[4096]; res = 0; while (len > 0) { sz = len; if (sz > sizeof(buf)) sz = sizeof(buf); - s = read(fd, buf, sz); + s = VECTX_READ(fd, buf, sz); if (s == 0) break; if (s < 0) return (s); CALLBACK(copyin, buf, va, s); len -= s; res += s; va += s; } return (res); } Index: stable/12/stand/userboot/userboot/host.c =================================================================== --- stable/12/stand/userboot/userboot/host.c (revision 359734) +++ stable/12/stand/userboot/userboot/host.c (revision 359735) @@ -1,192 +1,184 @@ /*- * Copyright (c) 2011 Google, Inc. * 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. */ #include __FBSDID("$FreeBSD$"); /* * Read from the host filesystem */ #include #include #include #include #include #include #include #include "libuserboot.h" /* * Open a file. */ static int host_open(const char *upath, struct open_file *f) { if (f->f_dev != &host_dev) return (EINVAL); return (CALLBACK(open, upath, &f->f_fsdata)); } static int host_close(struct open_file *f) { CALLBACK(close, f->f_fsdata); f->f_fsdata = (void *)0; return (0); } /* * Copy a portion of a file into memory. */ static int host_read(struct open_file *f, void *start, size_t size, size_t *resid) { return (CALLBACK(read, f->f_fsdata, start, size, resid)); } static off_t host_seek(struct open_file *f, off_t offset, int where) { return (CALLBACK(seek, f->f_fsdata, offset, where)); } static int host_stat(struct open_file *f, struct stat *sb) { - int mode; - int uid; - int gid; - uint64_t size; - - CALLBACK(stat, f->f_fsdata, &mode, &uid, &gid, &size); - sb->st_mode = mode; - sb->st_uid = uid; - sb->st_gid = gid; - sb->st_size = size; + + CALLBACK(stat, f->f_fsdata, sb); return (0); } static int host_readdir(struct open_file *f, struct dirent *d) { uint32_t fileno; uint8_t type; size_t namelen; int rc; rc = CALLBACK(readdir, f->f_fsdata, &fileno, &type, &namelen, d->d_name); if (rc) return (rc); d->d_fileno = fileno; d->d_type = type; d->d_namlen = namelen; return (0); } 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, ...) { va_list args; struct devdesc *dev; va_start(args, f); dev = va_arg(args, struct devdesc*); va_end(args); 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 fs_ops host_fsops = { "host", host_open, host_close, host_read, null_write, host_seek, host_stat, host_readdir }; 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 }; Index: stable/12/stand/userboot/userboot/libuserboot.h =================================================================== --- stable/12/stand/userboot/userboot/libuserboot.h (revision 359734) +++ stable/12/stand/userboot/userboot/libuserboot.h (revision 359735) @@ -1,68 +1,69 @@ /*- * Copyright (c) 2011 Google, Inc. * 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 "userboot.h" +#include extern struct loader_callbacks *callbacks; extern void *callbacks_arg; #define CALLBACK(fn, args...) (callbacks->fn(callbacks_arg , ##args)) #define MAXDEV 31 /* maximum number of distinct devices */ typedef unsigned long physaddr_t; /* exported devices */ extern struct devsw userboot_disk; extern int userboot_disk_maxunit; extern struct devsw host_dev; /* access to host filesystem */ struct fs_ops host_fsops; struct bootinfo; struct preloaded_file; extern int bi_load(struct bootinfo *, struct preloaded_file *); extern void delay(int); extern int userboot_autoload(void); extern ssize_t userboot_copyin(const void *, vm_offset_t, size_t); extern ssize_t userboot_copyout(vm_offset_t, void *, size_t); -extern ssize_t userboot_readin(int, vm_offset_t, size_t); +extern ssize_t userboot_readin(readin_handle_t, vm_offset_t, size_t); extern int userboot_getdev(void **, const char *, const char **); char *userboot_fmtdev(void *vdev); int userboot_setcurrdev(struct env_var *ev, int flags, const void *value); int bi_getboothowto(char *kargs); void bi_setboothowto(int howto); vm_offset_t bi_copyenv(vm_offset_t addr); int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernend); int bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernend); void bios_addsmapdata(struct preloaded_file *kfp); Index: stable/12/stand/userboot/userboot.h =================================================================== --- stable/12/stand/userboot/userboot.h (revision 359734) +++ stable/12/stand/userboot/userboot.h (revision 359735) @@ -1,225 +1,224 @@ /*- * Copyright (c) 2011 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$ */ /* * USERBOOT interface versions */ #define USERBOOT_VERSION_1 1 #define USERBOOT_VERSION_2 2 #define USERBOOT_VERSION_3 3 /* * Version 4 added more generic callbacks for setting up * registers and descriptors. The callback structure is * backward compatible (new callbacks have been added at * the tail end). */ #define USERBOOT_VERSION_4 4 /* * Version 5 added a callback for indicating that the guest * should be restarted with a different interpreter. The callback * structure is still backward compatible. */ #define USERBOOT_VERSION_5 5 /* * Exit codes from the loader */ #define USERBOOT_EXIT_QUIT 1 #define USERBOOT_EXIT_REBOOT 2 struct loader_callbacks { /* * Console i/o */ /* * Wait until a key is pressed on the console and then return it */ int (*getc)(void *arg); /* * Write the character ch to the console */ void (*putc)(void *arg, int ch); /* * Return non-zero if a key can be read from the console */ int (*poll)(void *arg); /* * Host filesystem i/o */ /* * Open a file in the host filesystem */ int (*open)(void *arg, const char *filename, void **h_return); /* * Close a file */ int (*close)(void *arg, void *h); /* * Return non-zero if the file is a directory */ int (*isdir)(void *arg, void *h); /* * Read size bytes from a file. The number of bytes remaining * in dst after reading is returned in *resid_return */ int (*read)(void *arg, void *h, void *dst, size_t size, size_t *resid_return); /* * Read an entry from a directory. The entry's inode number is * returned in *fileno_return, its type in *type_return and * the name length in *namelen_return. The name itself is * copied to the buffer name which must be at least PATH_MAX * in size. */ int (*readdir)(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return, size_t *namelen_return, char *name); /* * Seek to a location within an open file */ int (*seek)(void *arg, void *h, uint64_t offset, int whence); /* * Return some stat(2) related information about the file */ - int (*stat)(void *arg, void *h, int *mode_return, - int *uid_return, int *gid_return, uint64_t *size_return); + int (*stat)(void *arg, void *h, struct stat *stp); /* * Disk image i/o */ /* * Read from a disk image at the given offset */ int (*diskread)(void *arg, int unit, uint64_t offset, void *dst, size_t size, size_t *resid_return); /* * Guest virtual machine i/o */ /* * Copy to the guest address space */ int (*copyin)(void *arg, const void *from, uint64_t to, size_t size); /* * Copy from the guest address space */ int (*copyout)(void *arg, uint64_t from, void *to, size_t size); /* * Set a guest register value */ void (*setreg)(void *arg, int, uint64_t); /* * Set a guest MSR value */ void (*setmsr)(void *arg, int, uint64_t); /* * Set a guest CR value */ void (*setcr)(void *arg, int, uint64_t); /* * Set the guest GDT address */ void (*setgdt)(void *arg, uint64_t, size_t); /* * Transfer control to the guest at the given address */ void (*exec)(void *arg, uint64_t pc); /* * Misc */ /* * Sleep for usec microseconds */ void (*delay)(void *arg, int usec); /* * Exit with the given exit code */ void (*exit)(void *arg, int v); /* * Return guest physical memory map details */ void (*getmem)(void *arg, uint64_t *lowmem, uint64_t *highmem); /* * ioctl interface to the disk device */ int (*diskioctl)(void *arg, int unit, u_long cmd, void *data); /* * Returns an environment variable in the form "name=value". * * If there are no more variables that need to be set in the * loader environment then return NULL. * * 'num' is used as a handle for the callback to identify which * environment variable to return next. It will begin at 0 and * each invocation will add 1 to the previous value of 'num'. */ char * (*getenv)(void *arg, int num); /* * Version 4 additions. */ int (*vm_set_register)(void *arg, int vcpu, int reg, uint64_t val); int (*vm_set_desc)(void *arg, int vcpu, int reg, uint64_t base, u_int limit, u_int access); /* * Version 5 addition. */ void (*swap_interpreter)(void *arg, const char *interp); }; Index: stable/12/usr.sbin/bhyveload/bhyveload.c =================================================================== --- stable/12/usr.sbin/bhyveload/bhyveload.c (revision 359734) +++ stable/12/usr.sbin/bhyveload/bhyveload.c (revision 359735) @@ -1,838 +1,843 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause * * Copyright (c) 2011 NetApp, Inc. * 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 NETAPP, INC ``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 NETAPP, INC 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$ */ /*- * Copyright (c) 2011 Google, Inc. * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "userboot.h" #define MB (1024 * 1024UL) #define GB (1024 * 1024 * 1024UL) #define BSP 0 #define NDISKS 32 static char *host_base; static struct termios term, oldterm; static int disk_fd[NDISKS]; static int ndisks; static int consin_fd, consout_fd; static int need_reinit; static void *loader_hdl; static char *loader; static int explicit_loader; static jmp_buf jb; static char *vmname, *progname; static struct vmctx *ctx; static uint64_t gdtbase, cr3, rsp; static void cb_exit(void *arg, int v); /* * Console i/o callbacks */ static void cb_putc(void *arg, int ch) { char c = ch; (void) write(consout_fd, &c, 1); } static int cb_getc(void *arg) { char c; if (read(consin_fd, &c, 1) == 1) return (c); return (-1); } static int cb_poll(void *arg) { int n; if (ioctl(consin_fd, FIONREAD, &n) >= 0) return (n > 0); return (0); } /* * Host filesystem i/o callbacks */ struct cb_file { int cf_isdir; size_t cf_size; struct stat cf_stat; union { int fd; DIR *dir; } cf_u; }; static int cb_open(void *arg, const char *filename, void **hp) { struct cb_file *cf; char path[PATH_MAX]; if (!host_base) return (ENOENT); strlcpy(path, host_base, PATH_MAX); if (path[strlen(path) - 1] == '/') path[strlen(path) - 1] = 0; strlcat(path, filename, PATH_MAX); cf = malloc(sizeof(struct cb_file)); if (stat(path, &cf->cf_stat) < 0) { free(cf); return (errno); } cf->cf_size = cf->cf_stat.st_size; if (S_ISDIR(cf->cf_stat.st_mode)) { cf->cf_isdir = 1; cf->cf_u.dir = opendir(path); if (!cf->cf_u.dir) goto out; *hp = cf; return (0); } if (S_ISREG(cf->cf_stat.st_mode)) { cf->cf_isdir = 0; cf->cf_u.fd = open(path, O_RDONLY); if (cf->cf_u.fd < 0) goto out; *hp = cf; return (0); } out: free(cf); return (EINVAL); } static int cb_close(void *arg, void *h) { struct cb_file *cf = h; if (cf->cf_isdir) closedir(cf->cf_u.dir); else close(cf->cf_u.fd); free(cf); return (0); } static int cb_isdir(void *arg, void *h) { struct cb_file *cf = h; return (cf->cf_isdir); } static int cb_read(void *arg, void *h, void *buf, size_t size, size_t *resid) { struct cb_file *cf = h; ssize_t sz; if (cf->cf_isdir) return (EINVAL); sz = read(cf->cf_u.fd, buf, size); if (sz < 0) return (EINVAL); *resid = size - sz; return (0); } static int cb_readdir(void *arg, void *h, uint32_t *fileno_return, uint8_t *type_return, size_t *namelen_return, char *name) { struct cb_file *cf = h; struct dirent *dp; if (!cf->cf_isdir) return (EINVAL); dp = readdir(cf->cf_u.dir); if (!dp) return (ENOENT); /* * Note: d_namlen is in the range 0..255 and therefore less * than PATH_MAX so we don't need to test before copying. */ *fileno_return = dp->d_fileno; *type_return = dp->d_type; *namelen_return = dp->d_namlen; memcpy(name, dp->d_name, dp->d_namlen); name[dp->d_namlen] = 0; return (0); } static int cb_seek(void *arg, void *h, uint64_t offset, int whence) { struct cb_file *cf = h; if (cf->cf_isdir) return (EINVAL); if (lseek(cf->cf_u.fd, offset, whence) < 0) return (errno); return (0); } static int -cb_stat(void *arg, void *h, int *mode, int *uid, int *gid, uint64_t *size) +cb_stat(void *arg, void *h, struct stat *sbp) { struct cb_file *cf = h; - *mode = cf->cf_stat.st_mode; - *uid = cf->cf_stat.st_uid; - *gid = cf->cf_stat.st_gid; - *size = cf->cf_stat.st_size; + memset(sbp, 0, sizeof(struct stat)); + sbp->st_mode = cf->cf_stat.st_mode; + sbp->st_uid = cf->cf_stat.st_uid; + sbp->st_gid = cf->cf_stat.st_gid; + sbp->st_size = cf->cf_stat.st_size; + sbp->st_mtime = cf->cf_stat.st_mtime; + sbp->st_dev = cf->cf_stat.st_dev; + sbp->st_ino = cf->cf_stat.st_ino; + return (0); } /* * Disk image i/o callbacks */ static int cb_diskread(void *arg, int unit, uint64_t from, void *to, size_t size, size_t *resid) { ssize_t n; if (unit < 0 || unit >= ndisks ) return (EIO); n = pread(disk_fd[unit], to, size, from); if (n < 0) return (errno); *resid = size - n; return (0); } static int cb_diskioctl(void *arg, int unit, u_long cmd, void *data) { struct stat sb; if (unit < 0 || unit >= ndisks) return (EBADF); switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = 512; break; case DIOCGMEDIASIZE: if (fstat(disk_fd[unit], &sb) != 0) return (ENOTTY); if (S_ISCHR(sb.st_mode) && ioctl(disk_fd[unit], DIOCGMEDIASIZE, &sb.st_size) != 0) return (ENOTTY); *(off_t *)data = sb.st_size; break; default: return (ENOTTY); } return (0); } /* * Guest virtual machine i/o callbacks */ static int cb_copyin(void *arg, const void *from, uint64_t to, size_t size) { char *ptr; to &= 0x7fffffff; ptr = vm_map_gpa(ctx, to, size); if (ptr == NULL) return (EFAULT); memcpy(ptr, from, size); return (0); } static int cb_copyout(void *arg, uint64_t from, void *to, size_t size) { char *ptr; from &= 0x7fffffff; ptr = vm_map_gpa(ctx, from, size); if (ptr == NULL) return (EFAULT); memcpy(to, ptr, size); return (0); } static void cb_setreg(void *arg, int r, uint64_t v) { int error; enum vm_reg_name vmreg; vmreg = VM_REG_LAST; switch (r) { case 4: vmreg = VM_REG_GUEST_RSP; rsp = v; break; default: break; } if (vmreg == VM_REG_LAST) { printf("test_setreg(%d): not implemented\n", r); cb_exit(NULL, USERBOOT_EXIT_QUIT); } error = vm_set_register(ctx, BSP, vmreg, v); if (error) { perror("vm_set_register"); cb_exit(NULL, USERBOOT_EXIT_QUIT); } } static void cb_setmsr(void *arg, int r, uint64_t v) { int error; enum vm_reg_name vmreg; vmreg = VM_REG_LAST; switch (r) { case MSR_EFER: vmreg = VM_REG_GUEST_EFER; break; default: break; } if (vmreg == VM_REG_LAST) { printf("test_setmsr(%d): not implemented\n", r); cb_exit(NULL, USERBOOT_EXIT_QUIT); } error = vm_set_register(ctx, BSP, vmreg, v); if (error) { perror("vm_set_msr"); cb_exit(NULL, USERBOOT_EXIT_QUIT); } } static void cb_setcr(void *arg, int r, uint64_t v) { int error; enum vm_reg_name vmreg; vmreg = VM_REG_LAST; switch (r) { case 0: vmreg = VM_REG_GUEST_CR0; break; case 3: vmreg = VM_REG_GUEST_CR3; cr3 = v; break; case 4: vmreg = VM_REG_GUEST_CR4; break; default: break; } if (vmreg == VM_REG_LAST) { printf("test_setcr(%d): not implemented\n", r); cb_exit(NULL, USERBOOT_EXIT_QUIT); } error = vm_set_register(ctx, BSP, vmreg, v); if (error) { perror("vm_set_cr"); cb_exit(NULL, USERBOOT_EXIT_QUIT); } } static void cb_setgdt(void *arg, uint64_t base, size_t size) { int error; error = vm_set_desc(ctx, BSP, VM_REG_GUEST_GDTR, base, size - 1, 0); if (error != 0) { perror("vm_set_desc(gdt)"); cb_exit(NULL, USERBOOT_EXIT_QUIT); } gdtbase = base; } static void cb_exec(void *arg, uint64_t rip) { int error; if (cr3 == 0) error = vm_setup_freebsd_registers_i386(ctx, BSP, rip, gdtbase, rsp); else error = vm_setup_freebsd_registers(ctx, BSP, rip, cr3, gdtbase, rsp); if (error) { perror("vm_setup_freebsd_registers"); cb_exit(NULL, USERBOOT_EXIT_QUIT); } cb_exit(NULL, 0); } /* * Misc */ static void cb_delay(void *arg, int usec) { usleep(usec); } static void cb_exit(void *arg, int v) { tcsetattr(consout_fd, TCSAFLUSH, &oldterm); exit(v); } static void cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem) { *ret_lowmem = vm_get_lowmem_size(ctx); *ret_highmem = vm_get_highmem_size(ctx); } struct env { char *str; /* name=value */ SLIST_ENTRY(env) next; }; static SLIST_HEAD(envhead, env) envhead; static void addenv(char *str) { struct env *env; env = malloc(sizeof(struct env)); env->str = str; SLIST_INSERT_HEAD(&envhead, env, next); } static char * cb_getenv(void *arg, int num) { int i; struct env *env; i = 0; SLIST_FOREACH(env, &envhead, next) { if (i == num) return (env->str); i++; } return (NULL); } static int cb_vm_set_register(void *arg, int vcpu, int reg, uint64_t val) { return (vm_set_register(ctx, vcpu, reg, val)); } static int cb_vm_set_desc(void *arg, int vcpu, int reg, uint64_t base, u_int limit, u_int access) { return (vm_set_desc(ctx, vcpu, reg, base, limit, access)); } static void cb_swap_interpreter(void *arg, const char *interp_req) { /* * If the user specified a loader but we detected a mismatch, we should * not try to pivot to a different loader on them. */ free(loader); if (explicit_loader == 1) { perror("requested loader interpreter does not match guest userboot"); cb_exit(NULL, 1); } if (interp_req == NULL || *interp_req == '\0') { perror("guest failed to request an interpreter"); cb_exit(NULL, 1); } if (asprintf(&loader, "/boot/userboot_%s.so", interp_req) == -1) err(EX_OSERR, "malloc"); need_reinit = 1; longjmp(jb, 1); } static struct loader_callbacks cb = { .getc = cb_getc, .putc = cb_putc, .poll = cb_poll, .open = cb_open, .close = cb_close, .isdir = cb_isdir, .read = cb_read, .readdir = cb_readdir, .seek = cb_seek, .stat = cb_stat, .diskread = cb_diskread, .diskioctl = cb_diskioctl, .copyin = cb_copyin, .copyout = cb_copyout, .setreg = cb_setreg, .setmsr = cb_setmsr, .setcr = cb_setcr, .setgdt = cb_setgdt, .exec = cb_exec, .delay = cb_delay, .exit = cb_exit, .getmem = cb_getmem, .getenv = cb_getenv, /* Version 4 additions */ .vm_set_register = cb_vm_set_register, .vm_set_desc = cb_vm_set_desc, /* Version 5 additions */ .swap_interpreter = cb_swap_interpreter, }; static int altcons_open(char *path) { struct stat sb; int err; int fd; /* * Allow stdio to be passed in so that the same string * can be used for the bhyveload console and bhyve com-port * parameters */ if (!strcmp(path, "stdio")) return (0); err = stat(path, &sb); if (err == 0) { if (!S_ISCHR(sb.st_mode)) err = ENOTSUP; else { fd = open(path, O_RDWR | O_NONBLOCK); if (fd < 0) err = errno; else consin_fd = consout_fd = fd; } } return (err); } static int disk_open(char *path) { int err, fd; if (ndisks >= NDISKS) return (ERANGE); err = 0; fd = open(path, O_RDONLY); if (fd > 0) { disk_fd[ndisks] = fd; ndisks++; } else err = errno; return (err); } static void usage(void) { fprintf(stderr, "usage: %s [-S][-c ] [-d ] [-e ]\n" " %*s [-h ] [-m memsize[K|k|M|m|G|g|T|t]] \n", progname, (int)strlen(progname), ""); exit(1); } int main(int argc, char** argv) { void (*func)(struct loader_callbacks *, void *, int, int); uint64_t mem_size; int opt, error, memflags; progname = basename(argv[0]); memflags = 0; mem_size = 256 * MB; consin_fd = STDIN_FILENO; consout_fd = STDOUT_FILENO; while ((opt = getopt(argc, argv, "CSc:d:e:h:l:m:")) != -1) { switch (opt) { case 'c': error = altcons_open(optarg); if (error != 0) errx(EX_USAGE, "Could not open '%s'", optarg); break; case 'd': error = disk_open(optarg); if (error != 0) errx(EX_USAGE, "Could not open '%s'", optarg); break; case 'e': addenv(optarg); break; case 'h': host_base = optarg; break; case 'l': if (loader != NULL) errx(EX_USAGE, "-l can only be given once"); loader = strdup(optarg); if (loader == NULL) err(EX_OSERR, "malloc"); explicit_loader = 1; break; case 'm': error = vm_parse_memsize(optarg, &mem_size); if (error != 0) errx(EX_USAGE, "Invalid memsize '%s'", optarg); break; case 'C': memflags |= VM_MEM_F_INCORE; break; case 'S': memflags |= VM_MEM_F_WIRED; break; case '?': usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); vmname = argv[0]; need_reinit = 0; error = vm_create(vmname); if (error) { if (errno != EEXIST) { perror("vm_create"); exit(1); } need_reinit = 1; } ctx = vm_open(vmname); if (ctx == NULL) { perror("vm_open"); exit(1); } /* * setjmp in the case the guest wants to swap out interpreter, * cb_swap_interpreter will swap out loader as appropriate and set * need_reinit so that we end up in a clean state once again. */ setjmp(jb); if (need_reinit) { error = vm_reinit(ctx); if (error) { perror("vm_reinit"); exit(1); } } vm_set_memflags(ctx, memflags); error = vm_setup_memory(ctx, mem_size, VM_MMAP_ALL); if (error) { perror("vm_setup_memory"); exit(1); } if (loader == NULL) { loader = strdup("/boot/userboot.so"); if (loader == NULL) err(EX_OSERR, "malloc"); } if (loader_hdl != NULL) dlclose(loader_hdl); loader_hdl = dlopen(loader, RTLD_LOCAL); if (!loader_hdl) { printf("%s\n", dlerror()); free(loader); return (1); } func = dlsym(loader_hdl, "loader_main"); if (!func) { printf("%s\n", dlerror()); free(loader); return (1); } tcgetattr(consout_fd, &term); oldterm = term; cfmakeraw(&term); term.c_cflag |= CLOCAL; tcsetattr(consout_fd, TCSAFLUSH, &term); addenv("smbios.bios.vendor=BHYVE"); addenv("boot_serial=1"); func(&cb, NULL, USERBOOT_VERSION_5, ndisks); free(loader); return (0); }