Index: user/ngie/bug-237403/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c =================================================================== --- user/ngie/bug-237403/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c (revision 348022) +++ user/ngie/bug-237403/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c (revision 348023) @@ -1,834 +1,834 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2015, 2017 by Delphix. All rights reserved. * Copyright 2016 Joyent, Inc. * Copyright 2016 Igor Kozhukhov */ /* * zfs diff support */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libzfs_impl.h" #define ZDIFF_SNAPDIR "/.zfs/snapshot/" #define ZDIFF_SHARESDIR "/.zfs/shares/" #define ZDIFF_PREFIX "zfs-diff-%d" #define ZDIFF_ADDED '+' #define ZDIFF_MODIFIED 'M' #define ZDIFF_REMOVED '-' #define ZDIFF_RENAMED 'R' typedef struct differ_info { zfs_handle_t *zhp; char *fromsnap; char *frommnt; char *tosnap; char *tomnt; char *ds; char *dsmnt; char *tmpsnap; char errbuf[1024]; boolean_t isclone; boolean_t scripted; boolean_t classify; boolean_t timestamped; uint64_t shares; int zerr; int cleanupfd; int outputfd; int datafd; } differ_info_t; /* * Given a {dsname, object id}, get the object path */ static int get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj, char *pn, int maxlen, zfs_stat_t *sb) { zfs_cmd_t zc = { 0 }; int error; (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name)); zc.zc_obj = obj; errno = 0; error = ioctl(di->zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_STATS, &zc); di->zerr = errno; /* we can get stats even if we failed to get a path */ (void) memcpy(sb, &zc.zc_stat, sizeof (zfs_stat_t)); if (error == 0) { ASSERT(di->zerr == 0); (void) strlcpy(pn, zc.zc_value, maxlen); return (0); } if (di->zerr == ESTALE) { (void) snprintf(pn, maxlen, "(on_delete_queue)"); return (0); } else if (di->zerr == EPERM) { (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "The sys_config privilege or diff delegated permission " "is needed\nto discover path names")); return (-1); } else { (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "Unable to determine path or stats for " - "object %lld in %s"), obj, dsname); + "object %jd in %s"), (uintmax_t)obj, dsname); return (-1); } } /* * stream_bytes * * Prints a file name out a character at a time. If the character is * not in the range of what we consider "printable" ASCII, display it * as an escaped 3-digit octal value. ASCII values less than a space * are all control characters and we declare the upper end as the * DELete character. This also is the last 7-bit ASCII character. * We choose to treat all 8-bit ASCII as not printable for this * application. */ static void stream_bytes(FILE *fp, const char *string) { char c; while ((c = *string++) != '\0') { if (c > ' ' && c != '\\' && c < '\177') { (void) fprintf(fp, "%c", c); } else { (void) fprintf(fp, "\\%03o", (uint8_t)c); } } } static void print_what(FILE *fp, mode_t what) { char symbol; switch (what & S_IFMT) { case S_IFBLK: symbol = 'B'; break; case S_IFCHR: symbol = 'C'; break; case S_IFDIR: symbol = '/'; break; #ifdef S_IFDOOR case S_IFDOOR: symbol = '>'; break; #endif case S_IFIFO: symbol = '|'; break; case S_IFLNK: symbol = '@'; break; #ifdef S_IFPORT case S_IFPORT: symbol = 'P'; break; #endif case S_IFSOCK: symbol = '='; break; case S_IFREG: symbol = 'F'; break; default: symbol = '?'; break; } (void) fprintf(fp, "%c", symbol); } static void print_cmn(FILE *fp, differ_info_t *di, const char *file) { stream_bytes(fp, di->dsmnt); stream_bytes(fp, file); } static void print_rename(FILE *fp, differ_info_t *di, const char *old, const char *new, zfs_stat_t *isb) { if (di->timestamped) (void) fprintf(fp, "%10lld.%09lld\t", (longlong_t)isb->zs_ctime[0], (longlong_t)isb->zs_ctime[1]); (void) fprintf(fp, "%c\t", ZDIFF_RENAMED); if (di->classify) { print_what(fp, isb->zs_mode); (void) fprintf(fp, "\t"); } print_cmn(fp, di, old); if (di->scripted) (void) fprintf(fp, "\t"); else (void) fprintf(fp, " -> "); print_cmn(fp, di, new); (void) fprintf(fp, "\n"); } static void print_link_change(FILE *fp, differ_info_t *di, int delta, const char *file, zfs_stat_t *isb) { if (di->timestamped) (void) fprintf(fp, "%10lld.%09lld\t", (longlong_t)isb->zs_ctime[0], (longlong_t)isb->zs_ctime[1]); (void) fprintf(fp, "%c\t", ZDIFF_MODIFIED); if (di->classify) { print_what(fp, isb->zs_mode); (void) fprintf(fp, "\t"); } print_cmn(fp, di, file); (void) fprintf(fp, "\t(%+d)", delta); (void) fprintf(fp, "\n"); } static void print_file(FILE *fp, differ_info_t *di, char type, const char *file, zfs_stat_t *isb) { if (di->timestamped) (void) fprintf(fp, "%10lld.%09lld\t", (longlong_t)isb->zs_ctime[0], (longlong_t)isb->zs_ctime[1]); (void) fprintf(fp, "%c\t", type); if (di->classify) { print_what(fp, isb->zs_mode); (void) fprintf(fp, "\t"); } print_cmn(fp, di, file); (void) fprintf(fp, "\n"); } static int write_inuse_diffs_one(FILE *fp, differ_info_t *di, uint64_t dobj) { struct zfs_stat fsb, tsb; mode_t fmode, tmode; char fobjname[MAXPATHLEN], tobjname[MAXPATHLEN]; int fobjerr, tobjerr; int change; if (dobj == di->shares) return (0); /* * Check the from and to snapshots for info on the object. If * we get ENOENT, then the object just didn't exist in that * snapshot. If we get ENOTSUP, then we tried to get * info on a non-ZPL object, which we don't care about anyway. */ fobjerr = get_stats_for_obj(di, di->fromsnap, dobj, fobjname, MAXPATHLEN, &fsb); if (fobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP) return (-1); tobjerr = get_stats_for_obj(di, di->tosnap, dobj, tobjname, MAXPATHLEN, &tsb); if (tobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP) return (-1); /* * Unallocated object sharing the same meta dnode block */ if (fobjerr && tobjerr) { ASSERT(di->zerr == ENOENT || di->zerr == ENOTSUP); di->zerr = 0; return (0); } di->zerr = 0; /* negate get_stats_for_obj() from side that failed */ fmode = fsb.zs_mode & S_IFMT; tmode = tsb.zs_mode & S_IFMT; if (fmode == S_IFDIR || tmode == S_IFDIR || fsb.zs_links == 0 || tsb.zs_links == 0) change = 0; else change = tsb.zs_links - fsb.zs_links; if (fobjerr) { if (change) { print_link_change(fp, di, change, tobjname, &tsb); return (0); } print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb); return (0); } else if (tobjerr) { if (change) { print_link_change(fp, di, change, fobjname, &fsb); return (0); } print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb); return (0); } if (fmode != tmode && fsb.zs_gen == tsb.zs_gen) tsb.zs_gen++; /* Force a generational difference */ /* Simple modification or no change */ if (fsb.zs_gen == tsb.zs_gen) { /* No apparent changes. Could we assert !this? */ if (fsb.zs_ctime[0] == tsb.zs_ctime[0] && fsb.zs_ctime[1] == tsb.zs_ctime[1]) return (0); if (change) { print_link_change(fp, di, change, change > 0 ? fobjname : tobjname, &tsb); } else if (strcmp(fobjname, tobjname) == 0) { print_file(fp, di, ZDIFF_MODIFIED, fobjname, &tsb); } else { print_rename(fp, di, fobjname, tobjname, &tsb); } return (0); } else { /* file re-created or object re-used */ print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb); print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb); return (0); } } static int write_inuse_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr) { uint64_t o; int err; for (o = dr->ddr_first; o <= dr->ddr_last; o++) { if ((err = write_inuse_diffs_one(fp, di, o)) != 0) return (err); } return (0); } static int describe_free(FILE *fp, differ_info_t *di, uint64_t object, char *namebuf, int maxlen) { struct zfs_stat sb; if (get_stats_for_obj(di, di->fromsnap, object, namebuf, maxlen, &sb) != 0) { /* Let it slide, if in the delete queue on from side */ if (di->zerr == ENOENT && sb.zs_links == 0) { di->zerr = 0; return (0); } return (-1); } print_file(fp, di, ZDIFF_REMOVED, namebuf, &sb); return (0); } static int write_free_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr) { zfs_cmd_t zc = { 0 }; libzfs_handle_t *lhdl = di->zhp->zfs_hdl; char fobjname[MAXPATHLEN]; (void) strlcpy(zc.zc_name, di->fromsnap, sizeof (zc.zc_name)); zc.zc_obj = dr->ddr_first - 1; ASSERT(di->zerr == 0); while (zc.zc_obj < dr->ddr_last) { int err; err = ioctl(lhdl->libzfs_fd, ZFS_IOC_NEXT_OBJ, &zc); if (err == 0) { if (zc.zc_obj == di->shares) { zc.zc_obj++; continue; } if (zc.zc_obj > dr->ddr_last) { break; } err = describe_free(fp, di, zc.zc_obj, fobjname, MAXPATHLEN); if (err) break; } else if (errno == ESRCH) { break; } else { (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, - "next allocated object (> %lld) find failure"), - zc.zc_obj); + "next allocated object (> %jd) find failure"), + (uintmax_t)zc.zc_obj); di->zerr = errno; break; } } if (di->zerr) return (-1); return (0); } static void * differ(void *arg) { differ_info_t *di = arg; dmu_diff_record_t dr; FILE *ofp; int err = 0; if ((ofp = fdopen(di->outputfd, "w")) == NULL) { di->zerr = errno; (void) strerror_r(errno, di->errbuf, sizeof (di->errbuf)); (void) close(di->datafd); return ((void *)-1); } for (;;) { char *cp = (char *)&dr; int len = sizeof (dr); int rv; do { rv = read(di->datafd, cp, len); cp += rv; len -= rv; } while (len > 0 && rv > 0); if (rv < 0 || (rv == 0 && len != sizeof (dr))) { di->zerr = EPIPE; break; } else if (rv == 0) { /* end of file at a natural breaking point */ break; } switch (dr.ddr_type) { case DDR_FREE: err = write_free_diffs(ofp, di, &dr); break; case DDR_INUSE: err = write_inuse_diffs(ofp, di, &dr); break; default: di->zerr = EPIPE; break; } if (err || di->zerr) break; } (void) fclose(ofp); (void) close(di->datafd); if (err) return ((void *)-1); if (di->zerr) { ASSERT(di->zerr == EINVAL); (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "Internal error: bad data from diff IOCTL")); return ((void *)-1); } return ((void *)0); } static int find_shares_object(differ_info_t *di) { char fullpath[MAXPATHLEN]; struct stat64 sb = { 0 }; (void) strlcpy(fullpath, di->dsmnt, MAXPATHLEN); (void) strlcat(fullpath, ZDIFF_SHARESDIR, MAXPATHLEN); if (stat64(fullpath, &sb) != 0) { #ifdef illumos (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "Cannot stat %s"), fullpath); return (zfs_error(di->zhp->zfs_hdl, EZFS_DIFF, di->errbuf)); #else return (0); #endif } di->shares = (uint64_t)sb.st_ino; return (0); } static int make_temp_snapshot(differ_info_t *di) { libzfs_handle_t *hdl = di->zhp->zfs_hdl; zfs_cmd_t zc = { 0 }; (void) snprintf(zc.zc_value, sizeof (zc.zc_value), ZDIFF_PREFIX, getpid()); (void) strlcpy(zc.zc_name, di->ds, sizeof (zc.zc_name)); zc.zc_cleanup_fd = di->cleanupfd; if (ioctl(hdl->libzfs_fd, ZFS_IOC_TMP_SNAPSHOT, &zc) != 0) { int err = errno; if (err == EPERM) { (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "The diff delegated " "permission is needed in order\nto create a " "just-in-time snapshot for diffing\n")); return (zfs_error(hdl, EZFS_DIFF, di->errbuf)); } else { (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "Cannot create just-in-time " "snapshot of '%s'"), zc.zc_name); return (zfs_standard_error(hdl, err, di->errbuf)); } } di->tmpsnap = zfs_strdup(hdl, zc.zc_value); di->tosnap = zfs_asprintf(hdl, "%s@%s", di->ds, di->tmpsnap); return (0); } static void teardown_differ_info(differ_info_t *di) { free(di->ds); free(di->dsmnt); free(di->fromsnap); free(di->frommnt); free(di->tosnap); free(di->tmpsnap); free(di->tomnt); (void) close(di->cleanupfd); } static int get_snapshot_names(differ_info_t *di, const char *fromsnap, const char *tosnap) { libzfs_handle_t *hdl = di->zhp->zfs_hdl; char *atptrf = NULL; char *atptrt = NULL; int fdslen, fsnlen; int tdslen, tsnlen; /* * Can accept * dataset@snap1 * dataset@snap1 dataset@snap2 * dataset@snap1 @snap2 * dataset@snap1 dataset * @snap1 dataset@snap2 */ if (tosnap == NULL) { /* only a from snapshot given, must be valid */ (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "Badly formed snapshot name %s"), fromsnap); if (!zfs_validate_name(hdl, fromsnap, ZFS_TYPE_SNAPSHOT, B_FALSE)) { return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf)); } atptrf = strchr(fromsnap, '@'); ASSERT(atptrf != NULL); fdslen = atptrf - fromsnap; di->fromsnap = zfs_strdup(hdl, fromsnap); di->ds = zfs_strdup(hdl, fromsnap); di->ds[fdslen] = '\0'; /* the to snap will be a just-in-time snap of the head */ return (make_temp_snapshot(di)); } (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "Unable to determine which snapshots to compare")); atptrf = strchr(fromsnap, '@'); atptrt = strchr(tosnap, '@'); fdslen = atptrf ? atptrf - fromsnap : strlen(fromsnap); tdslen = atptrt ? atptrt - tosnap : strlen(tosnap); fsnlen = strlen(fromsnap) - fdslen; /* includes @ sign */ tsnlen = strlen(tosnap) - tdslen; /* includes @ sign */ if (fsnlen <= 1 || tsnlen == 1 || (fdslen == 0 && tdslen == 0) || (fsnlen == 0 && tsnlen == 0)) { return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf)); } else if ((fdslen > 0 && tdslen > 0) && ((tdslen != fdslen || strncmp(fromsnap, tosnap, fdslen) != 0))) { /* * not the same dataset name, might be okay if * tosnap is a clone of a fromsnap descendant. */ char origin[ZFS_MAX_DATASET_NAME_LEN]; zprop_source_t src; zfs_handle_t *zhp; di->ds = zfs_alloc(di->zhp->zfs_hdl, tdslen + 1); (void) strncpy(di->ds, tosnap, tdslen); di->ds[tdslen] = '\0'; zhp = zfs_open(hdl, di->ds, ZFS_TYPE_FILESYSTEM); while (zhp != NULL) { if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), &src, NULL, 0, B_FALSE) != 0) { (void) zfs_close(zhp); zhp = NULL; break; } if (strncmp(origin, fromsnap, fsnlen) == 0) break; (void) zfs_close(zhp); zhp = zfs_open(hdl, origin, ZFS_TYPE_FILESYSTEM); } if (zhp == NULL) { (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "Not an earlier snapshot from the same fs")); return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf)); } else { (void) zfs_close(zhp); } di->isclone = B_TRUE; di->fromsnap = zfs_strdup(hdl, fromsnap); if (tsnlen) { di->tosnap = zfs_strdup(hdl, tosnap); } else { return (make_temp_snapshot(di)); } } else { int dslen = fdslen ? fdslen : tdslen; di->ds = zfs_alloc(hdl, dslen + 1); (void) strncpy(di->ds, fdslen ? fromsnap : tosnap, dslen); di->ds[dslen] = '\0'; di->fromsnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrf); if (tsnlen) { di->tosnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrt); } else { return (make_temp_snapshot(di)); } } return (0); } static int get_mountpoint(differ_info_t *di, char *dsnm, char **mntpt) { boolean_t mounted; mounted = is_mounted(di->zhp->zfs_hdl, dsnm, mntpt); if (mounted == B_FALSE) { (void) snprintf(di->errbuf, sizeof (di->errbuf), dgettext(TEXT_DOMAIN, "Cannot diff an unmounted snapshot")); return (zfs_error(di->zhp->zfs_hdl, EZFS_BADTYPE, di->errbuf)); } /* Avoid a double slash at the beginning of root-mounted datasets */ if (**mntpt == '/' && *(*mntpt + 1) == '\0') **mntpt = '\0'; return (0); } static int get_mountpoints(differ_info_t *di) { char *strptr; char *frommntpt; /* * first get the mountpoint for the parent dataset */ if (get_mountpoint(di, di->ds, &di->dsmnt) != 0) return (-1); strptr = strchr(di->tosnap, '@'); ASSERT3P(strptr, !=, NULL); di->tomnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", di->dsmnt, ZDIFF_SNAPDIR, ++strptr); strptr = strchr(di->fromsnap, '@'); ASSERT3P(strptr, !=, NULL); frommntpt = di->dsmnt; if (di->isclone) { char *mntpt; int err; *strptr = '\0'; err = get_mountpoint(di, di->fromsnap, &mntpt); *strptr = '@'; if (err != 0) return (-1); frommntpt = mntpt; } di->frommnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", frommntpt, ZDIFF_SNAPDIR, ++strptr); if (di->isclone) free(frommntpt); return (0); } static int setup_differ_info(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap, differ_info_t *di) { di->zhp = zhp; di->cleanupfd = open(ZFS_DEV, O_RDWR|O_EXCL); VERIFY(di->cleanupfd >= 0); if (get_snapshot_names(di, fromsnap, tosnap) != 0) return (-1); if (get_mountpoints(di) != 0) return (-1); if (find_shares_object(di) != 0) return (-1); return (0); } int zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap, const char *tosnap, int flags) { zfs_cmd_t zc = { 0 }; char errbuf[1024]; differ_info_t di = { 0 }; pthread_t tid; int pipefd[2]; int iocerr; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "zfs diff failed")); if (setup_differ_info(zhp, fromsnap, tosnap, &di)) { teardown_differ_info(&di); return (-1); } if (pipe(pipefd)) { zfs_error_aux(zhp->zfs_hdl, strerror(errno)); teardown_differ_info(&di); return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, errbuf)); } di.scripted = (flags & ZFS_DIFF_PARSEABLE); di.classify = (flags & ZFS_DIFF_CLASSIFY); di.timestamped = (flags & ZFS_DIFF_TIMESTAMP); di.outputfd = outfd; di.datafd = pipefd[0]; if (pthread_create(&tid, NULL, differ, &di)) { zfs_error_aux(zhp->zfs_hdl, strerror(errno)); (void) close(pipefd[0]); (void) close(pipefd[1]); teardown_differ_info(&di); return (zfs_error(zhp->zfs_hdl, EZFS_THREADCREATEFAILED, errbuf)); } /* do the ioctl() */ (void) strlcpy(zc.zc_value, di.fromsnap, strlen(di.fromsnap) + 1); (void) strlcpy(zc.zc_name, di.tosnap, strlen(di.tosnap) + 1); zc.zc_cookie = pipefd[1]; iocerr = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DIFF, &zc); if (iocerr != 0) { (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "Unable to obtain diffs")); if (errno == EPERM) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "\n The sys_mount privilege or diff delegated " "permission is needed\n to execute the " "diff ioctl")); } else if (errno == EXDEV) { zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, "\n Not an earlier snapshot from the same fs")); } else if (errno != EPIPE || di.zerr == 0) { zfs_error_aux(zhp->zfs_hdl, strerror(errno)); } (void) close(pipefd[1]); (void) pthread_cancel(tid); (void) pthread_join(tid, NULL); teardown_differ_info(&di); if (di.zerr != 0 && di.zerr != EPIPE) { zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr)); return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf)); } else { return (zfs_error(zhp->zfs_hdl, EZFS_DIFFDATA, errbuf)); } } (void) close(pipefd[1]); (void) pthread_join(tid, NULL); if (di.zerr != 0) { zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr)); return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf)); } teardown_differ_info(&di); return (0); } Index: user/ngie/bug-237403/cddl/contrib/opensolaris/lib/libzfs =================================================================== --- user/ngie/bug-237403/cddl/contrib/opensolaris/lib/libzfs (revision 348022) +++ user/ngie/bug-237403/cddl/contrib/opensolaris/lib/libzfs (revision 348023) Property changes on: user/ngie/bug-237403/cddl/contrib/opensolaris/lib/libzfs ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/cddl/contrib/opensolaris/lib/libzfs:r346444-348022 Index: user/ngie/bug-237403/cddl/contrib/opensolaris =================================================================== --- user/ngie/bug-237403/cddl/contrib/opensolaris (revision 348022) +++ user/ngie/bug-237403/cddl/contrib/opensolaris (revision 348023) Property changes on: user/ngie/bug-237403/cddl/contrib/opensolaris ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/cddl/contrib/opensolaris:r347987-348022 Index: user/ngie/bug-237403/cddl =================================================================== --- user/ngie/bug-237403/cddl (revision 348022) +++ user/ngie/bug-237403/cddl (revision 348023) Property changes on: user/ngie/bug-237403/cddl ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/cddl:r347987-348022 Index: user/ngie/bug-237403/lib/csu/mips/crtn.S =================================================================== --- user/ngie/bug-237403/lib/csu/mips/crtn.S (revision 348022) +++ user/ngie/bug-237403/lib/csu/mips/crtn.S (revision 348023) @@ -1,27 +1,25 @@ #include __FBSDID("$FreeBSD$"); #ifndef __clang__ .gnu_attribute 4, 0 #endif .section .init,"ax",%progbits - .align 4 .set noreorder #if defined(__ABICALLS__) && (defined(__mips_n32) || defined(__mips_n64)) REG_L gp, CALLFRAME_GP(sp) #endif REG_L ra, CALLFRAME_RA(sp) jr ra PTR_ADDU sp, sp, CALLFRAME_SIZ .set reorder .section .fini,"ax",%progbits - .align 4 .set noreorder #if defined(__ABICALLS__) && (defined(__mips_n32) || defined(__mips_n64)) REG_L gp, CALLFRAME_GP(sp) #endif REG_L ra, CALLFRAME_RA(sp) jr ra PTR_ADDU sp, sp, CALLFRAME_SIZ .set reorder Index: user/ngie/bug-237403/lib/libarchive/tests/Makefile =================================================================== --- user/ngie/bug-237403/lib/libarchive/tests/Makefile (revision 348022) +++ user/ngie/bug-237403/lib/libarchive/tests/Makefile (revision 348023) @@ -1,625 +1,626 @@ # $FreeBSD$ PACKAGE= tests _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive ATF_TESTS_SH+= functional_test TEST_METADATA.functional_test+= timeout="600" BINDIR= ${TESTSDIR} PROGS+= libarchive_test CFLAGS+= -I${.CURDIR} -I${.CURDIR:H} -I${.OBJDIR} CFLAGS+= -I${_LIBARCHIVEDIR}/libarchive -I${_LIBARCHIVEDIR}/libarchive/test CFLAGS+= -I${_LIBARCHIVEDIR}/test_utils CFLAGS+= -DHAVE_LIBLZMA=1 -DHAVE_LZMA_H=1 # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC .PATH: ${_LIBARCHIVEDIR}/libarchive/test TESTS_SRCS= \ test_acl_nfs4.c \ test_acl_pax.c \ test_acl_platform_nfs4.c \ test_acl_platform_posix1e.c \ test_acl_posix1e.c \ test_acl_text.c \ test_archive_api_feature.c \ test_archive_clear_error.c \ test_archive_cmdline.c \ test_archive_digest.c \ test_archive_getdate.c \ test_archive_match_time.c \ test_archive_match_owner.c \ test_archive_match_path.c \ test_archive_pathmatch.c \ test_archive_read_add_passphrase.c \ test_archive_read_close_twice.c \ test_archive_read_close_twice_open_fd.c \ test_archive_read_close_twice_open_filename.c \ test_archive_read_multiple_data_objects.c \ test_archive_read_next_header_empty.c \ test_archive_read_next_header_raw.c \ test_archive_read_open2.c \ test_archive_read_set_filter_option.c \ test_archive_read_set_format_option.c \ test_archive_read_set_option.c \ test_archive_read_set_options.c \ test_archive_read_support.c \ test_archive_set_error.c \ test_archive_string.c \ test_archive_string_conversion.c \ test_archive_write_add_filter_by_name.c \ test_archive_write_set_filter_option.c \ test_archive_write_set_format_by_name.c \ test_archive_write_set_format_filter_by_ext.c \ test_archive_write_set_format_option.c \ test_archive_write_set_option.c \ test_archive_write_set_options.c \ test_archive_write_set_passphrase.c \ test_bad_fd.c \ test_compat_bzip2.c \ test_compat_cpio.c \ test_compat_gtar.c \ test_compat_gzip.c \ test_compat_lz4.c \ test_compat_lzip.c \ test_compat_lzma.c \ test_compat_lzop.c \ test_compat_mac.c \ test_compat_perl_archive_tar.c \ test_compat_plexus_archiver_tar.c \ test_compat_solaris_tar_acl.c \ test_compat_solaris_pax_sparse.c \ test_compat_star_acl.c \ test_compat_tar_hardlink.c \ test_compat_uudecode.c \ test_compat_uudecode_large.c \ test_compat_xz.c \ test_compat_zip.c \ test_compat_zstd.c \ test_empty_write.c \ test_entry.c \ test_entry_strmode.c \ test_extattr_freebsd.c \ test_filter_count.c \ test_fuzz.c \ test_gnutar_filename_encoding.c \ test_link_resolver.c \ test_open_fd.c \ test_open_failure.c \ test_open_file.c \ test_open_filename.c \ test_pax_filename_encoding.c \ test_read_data_large.c \ test_read_disk.c \ test_read_disk_directory_traversals.c \ test_read_disk_entry_from_file.c \ test_read_extract.c \ test_read_file_nonexistent.c \ test_read_filter_compress.c \ test_read_filter_grzip.c \ test_read_filter_lrzip.c \ test_read_filter_lzop.c \ test_read_filter_lzop_multiple_parts.c \ test_read_filter_program.c \ test_read_filter_program_signature.c \ test_read_filter_uudecode.c \ test_read_format_7zip.c \ test_read_format_7zip_encryption_data.c \ test_read_format_7zip_encryption_header.c \ test_read_format_7zip_encryption_partially.c \ test_read_format_7zip_malformed.c \ test_read_format_ar.c \ test_read_format_cab.c \ test_read_format_cab_filename.c \ test_read_format_cpio_afio.c \ test_read_format_cpio_bin.c \ test_read_format_cpio_bin_Z.c \ test_read_format_cpio_bin_be.c \ test_read_format_cpio_bin_bz2.c \ test_read_format_cpio_bin_gz.c \ test_read_format_cpio_bin_le.c \ test_read_format_cpio_bin_lzip.c \ test_read_format_cpio_bin_lzma.c \ test_read_format_cpio_bin_xz.c \ test_read_format_cpio_filename.c \ test_read_format_cpio_odc.c \ test_read_format_cpio_svr4_gzip.c \ test_read_format_cpio_svr4c_Z.c \ test_read_format_cpio_svr4_bzip2_rpm.c \ test_read_format_cpio_svr4_gzip_rpm.c \ test_read_format_empty.c \ test_read_format_gtar_filename.c \ test_read_format_gtar_gz.c \ test_read_format_gtar_lzma.c \ test_read_format_gtar_sparse.c \ test_read_format_gtar_sparse_skip_entry.c \ test_read_format_iso_Z.c \ test_read_format_iso_multi_extent.c \ test_read_format_iso_xorriso.c \ test_read_format_isorr_rr_moved.c \ test_read_format_isojoliet_bz2.c \ test_read_format_isojoliet_long.c \ test_read_format_isojoliet_rr.c \ test_read_format_isojoliet_versioned.c \ test_read_format_isorr_bz2.c \ test_read_format_isorr_ce.c \ test_read_format_isorr_new_bz2.c \ test_read_format_isozisofs_bz2.c \ test_read_format_lha.c \ test_read_format_lha_bugfix_0.c \ test_read_format_lha_filename.c \ test_read_format_mtree.c \ test_read_format_mtree_crash747.c \ test_read_format_pax_bz2.c \ test_read_format_rar.c \ test_read_format_rar5.c \ test_read_format_rar_encryption_data.c \ test_read_format_rar_encryption_header.c \ test_read_format_rar_encryption_partially.c \ test_read_format_rar_invalid1.c \ test_read_format_raw.c \ test_read_format_tar.c \ test_read_format_tar_concatenated.c \ test_read_format_tar_empty_filename.c \ test_read_format_tar_empty_pax.c \ test_read_format_tar_empty_with_gnulabel.c \ test_read_format_tar_filename.c \ test_read_format_tbz.c \ test_read_format_tgz.c \ test_read_format_tlz.c \ test_read_format_txz.c \ test_read_format_tz.c \ test_read_format_ustar_filename.c \ test_read_format_warc.c \ test_read_format_xar.c \ test_read_format_zip.c \ test_read_format_zip_7075_utf8_paths.c \ test_read_format_zip_comment_stored.c \ test_read_format_zip_encryption_data.c \ test_read_format_zip_encryption_header.c \ test_read_format_zip_encryption_partially.c \ test_read_format_zip_extra_padding.c \ test_read_format_zip_filename.c \ test_read_format_zip_high_compression.c \ test_read_format_zip_jar.c \ test_read_format_zip_mac_metadata.c \ test_read_format_zip_malformed.c \ test_read_format_zip_msdos.c \ test_read_format_zip_nested.c \ test_read_format_zip_nofiletype.c \ test_read_format_zip_padded.c \ test_read_format_zip_sfx.c \ test_read_format_zip_traditional_encryption_data.c \ test_read_format_zip_winzip_aes.c \ test_read_format_zip_winzip_aes_large.c \ test_read_format_zip_with_invalid_traditional_eocd.c \ test_read_format_zip_zip64.c \ test_read_large.c \ test_read_pax_schily_xattr.c \ test_read_pax_truncated.c \ test_read_position.c \ test_read_set_format.c \ test_read_too_many_filters.c \ test_read_truncated.c \ test_read_truncated_filter.c \ test_sparse_basic.c \ test_tar_filenames.c \ test_tar_large.c \ test_warn_missing_hardlink_target.c \ test_ustar_filenames.c \ test_ustar_filename_encoding.c \ test_write_disk.c \ test_write_disk_appledouble.c \ test_write_disk_failures.c \ test_write_disk_hardlink.c \ test_write_disk_hfs_compression.c \ test_write_disk_lookup.c \ test_write_disk_mac_metadata.c \ test_write_disk_no_hfs_compression.c \ test_write_disk_perms.c \ test_write_disk_secure.c \ test_write_disk_secure744.c \ test_write_disk_secure745.c \ test_write_disk_secure746.c \ test_write_disk_sparse.c \ test_write_disk_symlink.c \ test_write_disk_times.c \ test_write_filter_b64encode.c \ test_write_filter_bzip2.c \ test_write_filter_compress.c \ test_write_filter_gzip.c \ test_write_filter_gzip_timestamp.c \ test_write_filter_lrzip.c \ test_write_filter_lz4.c \ test_write_filter_lzip.c \ test_write_filter_lzma.c \ test_write_filter_lzop.c \ test_write_filter_program.c \ test_write_filter_uuencode.c \ test_write_filter_xz.c \ test_write_filter_zstd.c \ test_write_format_7zip.c \ test_write_format_7zip_empty.c \ test_write_format_7zip_large.c \ test_write_format_ar.c \ test_write_format_cpio.c \ test_write_format_cpio_empty.c \ test_write_format_cpio_newc.c \ test_write_format_cpio_odc.c \ test_write_format_gnutar.c \ test_write_format_gnutar_filenames.c \ test_write_format_iso9660.c \ test_write_format_iso9660_boot.c \ test_write_format_iso9660_empty.c \ test_write_format_iso9660_filename.c \ test_write_format_iso9660_zisofs.c \ test_write_format_mtree.c \ test_write_format_mtree_absolute_path.c \ test_write_format_mtree_classic.c \ test_write_format_mtree_classic_indent.c \ test_write_format_mtree_fflags.c \ test_write_format_mtree_no_separator.c \ test_write_format_mtree_quoted_filename.c \ test_write_format_pax.c \ test_write_format_raw.c \ test_write_format_raw_b64.c \ test_write_format_shar_empty.c \ test_write_format_tar.c \ test_write_format_tar_empty.c \ test_write_format_tar_sparse.c \ test_write_format_tar_ustar.c \ test_write_format_tar_v7tar.c \ test_write_format_warc.c \ test_write_format_warc_empty.c \ test_write_format_xar.c \ test_write_format_xar_empty.c \ test_write_format_zip.c \ test_write_format_zip_compression_store.c \ test_write_format_zip_empty.c \ test_write_format_zip_empty_zip64.c \ test_write_format_zip_file.c \ test_write_format_zip_file_zip64.c \ test_write_format_zip_large.c \ test_write_format_zip_zip64.c \ test_write_open_memory.c \ test_write_read_format_zip.c \ test_xattr_platform.c \ test_zip_filename_encoding.c # Deterministic failures: # Crashes with SIGBUS BROKEN_TESTS+= test_archive_rmd160 # Fails with `libarchive/test/test_archive_crypto.c:121: md != actualmd` BROKEN_TESTS+= test_archive_sha384 # Fails with `test_read_disk_directory_traversals.c:1094: File at has atime 886622, 1443306049 seconds ago` BROKEN_TESTS+= test_read_disk_directory_traversals # Non-deterministic failures: # (Times out?) [and] crashes BROKEN_TESTS+= test_fuzz_rar # Build the test program. SRCS.libarchive_test= \ ${TESTS_SRCS} \ read_open_memory.c \ list.h LIBADD.libarchive_test= archive .PATH: ${_LIBARCHIVEDIR}/test_utils SRCS.libarchive_test+= test_main.c \ test_utils.c # list.h is just a list of all tests, as indicated by DEFINE_TEST macro lines list.h: ${TESTS_SRCS} Makefile @(cd ${_LIBARCHIVEDIR}/libarchive/test && \ grep -E -h ^DEFINE_TEST ${.ALLSRC:N*Makefile} | \ egrep -v '${BROKEN_TESTS:tW:C/ /|/g}') > ${.TARGET}.tmp @mv ${.TARGET}.tmp ${.TARGET} CLEANTESTS+= list.h list.h.tmp ${PACKAGE}FILES+= README ${PACKAGE}FILES+= test_acl_pax_posix1e.tar.uu ${PACKAGE}FILES+= test_acl_pax_nfs4.tar.uu ${PACKAGE}FILES+= test_archive_string_conversion.txt.Z.uu ${PACKAGE}FILES+= test_compat_bzip2_1.tbz.uu ${PACKAGE}FILES+= test_compat_bzip2_2.tbz.uu ${PACKAGE}FILES+= test_compat_cpio_1.cpio.uu ${PACKAGE}FILES+= test_compat_gtar_1.tar.uu ${PACKAGE}FILES+= test_compat_gtar_2.tar.uu ${PACKAGE}FILES+= test_compat_gzip_1.tgz.uu ${PACKAGE}FILES+= test_compat_gzip_2.tgz.uu ${PACKAGE}FILES+= test_compat_lz4_1.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_2.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_3.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B4.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B4BD.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B4BDBX.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B5.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B5BD.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B6.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B6BD.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B7.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lz4_B7BD.tar.lz4.uu ${PACKAGE}FILES+= test_compat_lzip_1.tlz.uu ${PACKAGE}FILES+= test_compat_lzip_2.tlz.uu ${PACKAGE}FILES+= test_compat_lzma_1.tlz.uu ${PACKAGE}FILES+= test_compat_lzma_2.tlz.uu ${PACKAGE}FILES+= test_compat_lzma_3.tlz.uu ${PACKAGE}FILES+= test_compat_lzop_1.tar.lzo.uu ${PACKAGE}FILES+= test_compat_lzop_2.tar.lzo.uu ${PACKAGE}FILES+= test_compat_lzop_3.tar.lzo.uu ${PACKAGE}FILES+= test_compat_mac-1.tar.Z.uu ${PACKAGE}FILES+= test_compat_mac-2.tar.Z.uu ${PACKAGE}FILES+= test_compat_perl_archive_tar.tar.uu ${PACKAGE}FILES+= test_compat_plexus_archiver_tar.tar.uu ${PACKAGE}FILES+= test_compat_solaris_pax_sparse_1.pax.Z.uu ${PACKAGE}FILES+= test_compat_solaris_pax_sparse_2.pax.Z.uu ${PACKAGE}FILES+= test_compat_solaris_tar_acl.tar.uu ${PACKAGE}FILES+= test_compat_star_acl_nfs4.tar.uu ${PACKAGE}FILES+= test_compat_star_acl_posix1e.tar.uu ${PACKAGE}FILES+= test_compat_tar_hardlink_1.tar.uu ${PACKAGE}FILES+= test_compat_uudecode_large.tar.Z.uu ${PACKAGE}FILES+= test_compat_xz_1.txz.uu ${PACKAGE}FILES+= test_compat_zip_1.zip.uu ${PACKAGE}FILES+= test_compat_zip_2.zip.uu ${PACKAGE}FILES+= test_compat_zip_3.zip.uu ${PACKAGE}FILES+= test_compat_zip_4.zip.uu ${PACKAGE}FILES+= test_compat_zip_5.zip.uu ${PACKAGE}FILES+= test_compat_zip_6.zip.uu ${PACKAGE}FILES+= test_compat_zip_7.xps.uu ${PACKAGE}FILES+= test_compat_zip_8.zip.uu ${PACKAGE}FILES+= test_compat_zstd_1.tar.zst.uu ${PACKAGE}FILES+= test_fuzz.cab.uu ${PACKAGE}FILES+= test_fuzz.lzh.uu ${PACKAGE}FILES+= test_fuzz_1.iso.Z.uu ${PACKAGE}FILES+= test_pax_filename_encoding.tar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part1.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part2.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part3.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part4.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part5.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_multiple_files.part6.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_single_file.part1.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_single_file.part2.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_single_file.part3.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part01.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part02.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part03.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part04.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part05.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part06.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part07.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part08.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part09.rar.uu ${PACKAGE}FILES+= test_rar_multivolume_uncompressed_files.part10.rar.uu ${PACKAGE}FILES+= test_read_filter_grzip.tar.grz.uu ${PACKAGE}FILES+= test_read_filter_lrzip.tar.lrz.uu ${PACKAGE}FILES+= test_read_filter_lzop.tar.lzo.uu ${PACKAGE}FILES+= test_read_filter_lzop_multiple_parts.tar.lzo.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_bzip2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_copy_1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_copy_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_copy_lzma.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_deflate.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_lzma1_1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_lzma1_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_lzma2_1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj2_lzma2_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_bzip2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_copy.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_deflate.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_lzma1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bcj_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_bzip2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_copy.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_copy_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_deflate.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_delta_lzma1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_delta_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_empty_archive.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_empty_file.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_encryption.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_encryption_header.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_encryption_partially.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_lzma1.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_lzma1_2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_lzma1_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_lzma2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_malformed.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_malformed2.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_ppmd.7z.uu ${PACKAGE}FILES+= test_read_format_7zip_symbolic_name.7z.uu ${PACKAGE}FILES+= test_read_format_ar.ar.uu ${PACKAGE}FILES+= test_read_format_cab_1.cab.uu ${PACKAGE}FILES+= test_read_format_cab_2.cab.uu ${PACKAGE}FILES+= test_read_format_cab_3.cab.uu ${PACKAGE}FILES+= test_read_format_cab_filename_cp932.cab.uu ${PACKAGE}FILES+= test_read_format_cpio_bin_be.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_bin_le.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_cp866.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_eucjp.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_koi8r.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_utf8_jp.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_filename_utf8_ru.cpio.uu ${PACKAGE}FILES+= test_read_format_cpio_svr4_bzip2_rpm.rpm.uu ${PACKAGE}FILES+= test_read_format_cpio_svr4_gzip_rpm.rpm.uu ${PACKAGE}FILES+= test_read_format_gtar_filename_cp866.tar.Z.uu ${PACKAGE}FILES+= test_read_format_gtar_filename_eucjp.tar.Z.uu ${PACKAGE}FILES+= test_read_format_gtar_filename_koi8r.tar.Z.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_13.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17_posix00.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17_posix01.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17_posix10.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu ${PACKAGE}FILES+= test_read_format_gtar_sparse_skip_entry.tar.Z.uu ${PACKAGE}FILES+= test_read_format_iso.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_2.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_joliet.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_joliet_by_nero.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_joliet_long.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_joliet_rockridge.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_multi_extent.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_rockridge.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_rockridge_ce.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_rockridge_new.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_rockridge_rr_moved.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_xorriso.iso.Z.uu ${PACKAGE}FILES+= test_read_format_iso_zisofs.iso.Z.uu ${PACKAGE}FILES+= test_read_format_lha_bugfix_0.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_filename_cp932.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_header0.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_header1.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_header2.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_header3.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_lh0.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_lh6.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_lh7.lzh.uu ${PACKAGE}FILES+= test_read_format_lha_withjunk.lzh.uu ${PACKAGE}FILES+= test_read_format_mtree.mtree.uu ${PACKAGE}FILES+= test_read_format_mtree_crash747.mtree.bz2.uu ${PACKAGE}FILES+= test_read_format_mtree_nomagic.mtree.uu ${PACKAGE}FILES+= test_read_format_mtree_nomagic2.mtree.uu ${PACKAGE}FILES+= test_read_format_mtree_nomagic3.mtree.uu ${PACKAGE}FILES+= test_read_format_mtree_noprint.mtree.uu ${PACKAGE}FILES+= test_read_format_rar.rar.uu ${PACKAGE}FILES+= test_read_format_rar_binary_data.rar.uu ${PACKAGE}FILES+= test_read_format_rar_compress_best.rar.uu ${PACKAGE}FILES+= test_read_format_rar_compress_normal.rar.uu ${PACKAGE}FILES+= test_read_format_rar_encryption_data.rar.uu ${PACKAGE}FILES+= test_read_format_rar_encryption_header.rar.uu ${PACKAGE}FILES+= test_read_format_rar_encryption_partially.rar.uu ${PACKAGE}FILES+= test_read_format_rar_invalid1.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multi_lzss_blocks.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multivolume.part0001.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multivolume.part0002.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multivolume.part0003.rar.uu ${PACKAGE}FILES+= test_read_format_rar_multivolume.part0004.rar.uu ${PACKAGE}FILES+= test_read_format_rar_noeof.rar.uu ${PACKAGE}FILES+= test_read_format_rar_ppmd_lzss_conversion.rar.uu ${PACKAGE}FILES+= test_read_format_rar_ppmd_use_after_free.rar.uu ${PACKAGE}FILES+= test_read_format_rar_sfx.exe.uu ${PACKAGE}FILES+= test_read_format_rar_subblock.rar.uu ${PACKAGE}FILES+= test_read_format_rar_unicode.rar.uu ${PACKAGE}FILES+= test_read_format_rar_windows.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_arm.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_blake2.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_compressed.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_distance_overflow.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_extra_field_version.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_fileattr.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_hardlink.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_invalid_dict_reference.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_leftshift1.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_leftshift2.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part01.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part02.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part03.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part04.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part05.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part06.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part07.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive.part08.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive_solid.part01.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive_solid.part02.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive_solid.part03.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiarchive_solid.part04.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiple_files.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_multiple_files_solid.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_nonempty_dir_stream.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_owner.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_readtables_overflow.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_solid.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_stored.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_stored_manyfiles.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_symlink.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_truncated_huff.rar.uu ${PACKAGE}FILES+= test_read_format_rar5_win32.rar.uu ${PACKAGE}FILES+= test_read_format_raw.bufr.uu ${PACKAGE}FILES+= test_read_format_raw.data.Z.uu ${PACKAGE}FILES+= test_read_format_raw.data.gz.uu ${PACKAGE}FILES+= test_read_format_raw.data.uu ${PACKAGE}FILES+= test_read_format_tar_concatenated.tar.uu ${PACKAGE}FILES+= test_read_format_tar_empty_filename.tar.uu ${PACKAGE}FILES+= test_read_format_tar_empty_with_gnulabel.tar.uu ${PACKAGE}FILES+= test_read_format_tar_empty_pax.tar.Z.uu ${PACKAGE}FILES+= test_read_format_tar_filename_koi8r.tar.Z.uu ${PACKAGE}FILES+= test_read_format_ustar_filename_cp866.tar.Z.uu ${PACKAGE}FILES+= test_read_format_ustar_filename_eucjp.tar.Z.uu ${PACKAGE}FILES+= test_read_format_ustar_filename_koi8r.tar.Z.uu ${PACKAGE}FILES+= test_read_format_warc.warc.uu ${PACKAGE}FILES+= test_read_format_zip.zip.uu +${PACKAGE}FILES+= test_read_format_zip_7075_utf8_paths.zip.uu ${PACKAGE}FILES+= test_read_format_zip_bz2_hang.zip.uu ${PACKAGE}FILES+= test_read_format_zip_bzip2.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_bzip2_multi.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_comment_stored_1.zip.uu ${PACKAGE}FILES+= test_read_format_zip_comment_stored_2.zip.uu ${PACKAGE}FILES+= test_read_format_zip_encryption_data.zip.uu ${PACKAGE}FILES+= test_read_format_zip_encryption_header.zip.uu ${PACKAGE}FILES+= test_read_format_zip_encryption_partially.zip.uu ${PACKAGE}FILES+= test_read_format_zip_extra_padding.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_cp866.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_cp932.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_koi8r.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_utf8_jp.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_utf8_ru.zip.uu ${PACKAGE}FILES+= test_read_format_zip_filename_utf8_ru2.zip.uu ${PACKAGE}FILES+= test_read_format_zip_high_compression.zip.uu ${PACKAGE}FILES+= test_read_format_zip_jar.jar.uu ${PACKAGE}FILES+= test_read_format_zip_length_at_end.zip.uu ${PACKAGE}FILES+= test_read_format_zip_lzma_alone_leak.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_lzma.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_lzma.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_lzma_multi.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_mac_metadata.zip.uu ${PACKAGE}FILES+= test_read_format_zip_malformed1.zip.uu ${PACKAGE}FILES+= test_read_format_zip_msdos.zip.uu ${PACKAGE}FILES+= test_read_format_zip_nested.zip.uu ${PACKAGE}FILES+= test_read_format_zip_nofiletype.zip.uu ${PACKAGE}FILES+= test_read_format_zip_padded1.zip.uu ${PACKAGE}FILES+= test_read_format_zip_padded2.zip.uu ${PACKAGE}FILES+= test_read_format_zip_padded3.zip.uu ${PACKAGE}FILES+= test_read_format_zip_ppmd8.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_ppmd8_crash_1.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_ppmd8_crash_2.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_ppmd8_multi.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_sfx.uu ${PACKAGE}FILES+= test_read_format_zip_symlink.zip.uu ${PACKAGE}FILES+= test_read_format_zip_traditional_encryption_data.zip.uu ${PACKAGE}FILES+= test_read_format_zip_ux.zip.uu ${PACKAGE}FILES+= test_read_format_zip_with_invalid_traditional_eocd.zip.uu ${PACKAGE}FILES+= test_read_format_zip_winzip_aes128.zip.uu ${PACKAGE}FILES+= test_read_format_zip_winzip_aes256.zip.uu ${PACKAGE}FILES+= test_read_format_zip_winzip_aes256_large.zip.uu ${PACKAGE}FILES+= test_read_format_zip_winzip_aes256_stored.zip.uu ${PACKAGE}FILES+= test_read_format_zip_xz_multi.zipx.uu ${PACKAGE}FILES+= test_read_format_zip_zip64a.zip.uu ${PACKAGE}FILES+= test_read_format_zip_zip64b.zip.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_aa.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_ab.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_ac.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_ad.uu ${PACKAGE}FILES+= test_read_large_splitted_rar_ae.uu ${PACKAGE}FILES+= test_read_pax_schily_xattr.tar.uu ${PACKAGE}FILES+= test_read_splitted_rar_aa.uu ${PACKAGE}FILES+= test_read_splitted_rar_ab.uu ${PACKAGE}FILES+= test_read_splitted_rar_ac.uu ${PACKAGE}FILES+= test_read_splitted_rar_ad.uu ${PACKAGE}FILES+= test_read_too_many_filters.gz.uu ${PACKAGE}FILES+= test_splitted_rar_seek_support_aa.uu ${PACKAGE}FILES+= test_splitted_rar_seek_support_ab.uu ${PACKAGE}FILES+= test_splitted_rar_seek_support_ac.uu ${PACKAGE}FILES+= test_write_disk_appledouble.cpio.gz.uu ${PACKAGE}FILES+= test_write_disk_hfs_compression.tgz.uu ${PACKAGE}FILES+= test_write_disk_mac_metadata.tar.gz.uu ${PACKAGE}FILES+= test_write_disk_no_hfs_compression.tgz.uu .include Index: user/ngie/bug-237403/share/man/man9/DEFINE_IFUNC.9 =================================================================== --- user/ngie/bug-237403/share/man/man9/DEFINE_IFUNC.9 (nonexistent) +++ user/ngie/bug-237403/share/man/man9/DEFINE_IFUNC.9 (revision 348023) @@ -0,0 +1,143 @@ +.\" Copyright (c) 2019 The FreeBSD Foundation +.\" +.\" This documentation was written by Mark Johnston +.\" 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$ +.\" +.Dd May 18, 2019 +.Dt DEFINE_IFUNC 9 +.Os +.Sh NAME +.Nm DEFINE_IFUNC +.Nd define a kernel function with an implementation selected at run-time +.Sh SYNOPSIS +.In machine/ifunc.h +.Fn DEFINE_IFUNC qual ret_type name args +.Sh DESCRIPTION +ifuncs are a linker feature which allows the programmer to define functions +whose implementation is selected at boot-time or module load-time. +The +.Nm +macro can be used to define an ifunc. +The selection is performed by a resolver function, which returns a pointer +to the selected function. +ifunc resolvers are invoked very early during the machine-dependent +initialization routine, or at load time for dynamically loaded modules. +Resolution must occur before the first call to an ifunc. +ifunc resolution is performed after CPU features are enumerated and after the +kernel's environment is initialized. +The typical use-case for an ifunc is a routine whose behavior depends on +optional CPU features. +For example, newer generations of a given CPU architecture may provide an +instruction to optimize a common operation. +To avoid the overhead of testing for the CPU feature each time the operation +is performed, an ifunc can be used to provide two implementations for the +operation: one targeting platforms with the extra instruction, and one +for older platforms. +.Pp +Because +.Nm +is a macro that defines a dynamically typed function, its usage looks somewhat +unusual. +The +.Ar qual +parameter is a list of zero or more C function qualifiers to be applied to the +ifunc. +This parameter is typically empty or the +.Dv static +qualifier. +.Ar ret_type +is the return type of the ifunc. +.Ar name +is the name of the ifunc. +.Ar args +is a parenthesized, comma-separated list of the parameter types of the function, +as they would appear in a C function declaration. +.Pp +The +.Nm +usage must be followed by the resolver function body. +The resolver must return a function with return type +.Ar ret_type +and parameter types +.Ar args . +The resolver function is defined with the +.Ql resolver +gcc-style function attribute, causing the corresponding +.Xr elf 5 +function symbol to be of type +.Dv STT_GNU_IFUNC +instead of +.Dv STT_FUNC . +The kernel linker invokes the resolver to process relocations targeting ifunc +calls and PLT entries referencing such symbols. +.Sh EXAMPLES +ifunc resolvers are executed early during boot, before most kernel facilities +are available. +They are effectively limited to checking CPU feature flags and tunables. +.Bd -literal +static size_t +fast_strlen(const char *s __unused) +{ + size_t len; + + /* Fast, but may not be correct in all cases. */ + __asm("movq $42,%0\\n" : "=r" (len)); + return (len); +} + +static size_t +slow_strlen(const char *s) +{ + const char *t; + + for (t = s; *t != '\\0'; t++); + return (t - s); +} + +DEFINE_IFUNC(, size_t, strlen, (const char *)) +{ + int enabled; + + enabled = 1; + TUNABLE_INT_FETCH("debug.use_fast_strlen", &enabled); + if (enabled && (cpu_features & CPUID_FAST_STRLEN) != 0) + return (fast_strlen); + else + return (slow_strlen); +} +.Ed +.Pp +This defines a +.Fn strlen +function with an optimized implementation for CPUs that advertise support. +.Sh SEE ALSO +.Xr elf 5 +.Sh NOTES +ifuncs are not supported on all architectures. +They require both toolchain support, to emit function symbols of type +.Dv STT_GNU_IFUNC , +and kernel linker support to invoke ifunc resolvers during boot or +during module load. Property changes on: user/ngie/bug-237403/share/man/man9/DEFINE_IFUNC.9 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: user/ngie/bug-237403/share/man/man9/DRIVER_MODULE.9 =================================================================== --- user/ngie/bug-237403/share/man/man9/DRIVER_MODULE.9 (revision 348022) +++ user/ngie/bug-237403/share/man/man9/DRIVER_MODULE.9 (revision 348023) @@ -1,151 +1,151 @@ .\" -*- nroff -*- .\" .\" Copyright (c) 2000 Alexander Langer .\" .\" All rights reserved. .\" .\" This program is free software. .\" .\" 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 DEVELOPERS ``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 DEVELOPERS 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$ .\" .Dd February 12, 2018 .Dt DRIVER_MODULE 9 .Os .Sh NAME .Nm DRIVER_MODULE , .Nm DRIVER_MODULE_ORDERED , .Nm EARLY_DRIVER_MODULE , .Nm EARLY_DRIVER_MODULE_ORDERED .Nd kernel driver declaration macro .Sh SYNOPSIS .In sys/param.h .In sys/kernel.h .In sys/bus.h .In sys/module.h .Fn DRIVER_MODULE name busname "driver_t driver" "devclass_t devclass" "modeventhand_t evh" "void *arg" .Fn DRIVER_MODULE_ORDERED name busname "driver_t driver" "devclass_t devclass" "modeventhand_t evh" "void *arg" "int order" .Fn EARLY_DRIVER_MODULE name busname "driver_t driver" "devclass_t devclass" "modeventhand_t evh" "void *arg" "enum sysinit_elem_order order" "int pass" .Fn EARLY_DRIVER_MODULE_ORDERED name busname "driver_t driver" "devclass_t devclass" "modeventhand_t evh" "void *arg" "enum sysinit_elem_order order" "int pass" .Sh DESCRIPTION The .Fn DRIVER_MODULE macro declares a kernel driver. .Fn DRIVER_MODULE expands to the real driver declaration, where the phrase .Fa name is used as the naming prefix for the driver and its functions. Note that it is supplied as plain text, and not a .Li char or .Li char * . .Pp .Fa busname is the parent bus of the driver (PCI, ISA, PPBUS and others), e.g.\& .Ql pci , .Ql isa , or .Ql ppbus . .Pp The identifier used in .Fn DRIVER_MODULE can be different from the driver name. Also, the same driver identifier can exist on different buses, which is a pretty clean way of making front ends for different cards using the same driver on the same or different buses. For example, the following is allowed: .Pp .Fn DRIVER_MODULE foo isa foo_driver foo_devclass NULL NULL ; .Pp .Fn DRIVER_MODULE foo pci foo_driver foo_devclass NULL NULL ; .Pp .Fa driver is the driver of type .Li driver_t , which contains the information about the driver and is therefore one of the two most important parts of the call to .Fn DRIVER_MODULE . .Pp The .Fa devclass argument contains the kernel-internal information about the device, which will be used within the kernel driver module. .Pp The .Fa evh argument is the event handler which is called when the driver (or module) is loaded or unloaded (see .Xr module 9 ) . .Pp The .Fa arg is unused at this time and should be a .Dv NULL pointer. .Pp The .Fn DRIVER_MODULE_ORDERED macro allows a driver to be registered in a specific order. This can be useful if a single kernel module contains multiple drivers that are inter-dependent. The .Fa order argument should be one of the .Xr SYSINIT 9 initialization ordering constants .Pq Dv SI_ORDER_* . The default order for a driver module is .Dv SI_ORDER_MIDDLE . Typically a module will specify an order of .Dv SI_ORDER_ANY for a single driver to ensure it is registered last. .Pp The .Fn EARLY_DRIVER_MODULE macro allows a driver to be registered for a specific pass level. The boot time probe and attach process makes multiple passes over the device tree. Certain critical drivers that provide basic services needed by other -devices are attach during earlier passes. +devices are attached during earlier passes. Most drivers are attached in a final general pass. A driver that attaches during an early pass must register for a specific pass level .Pq BUS_PASS_* via the .Fa pass argument. Once a driver is registered it is available to attach to devices for all subsequent passes. .Pp The .Fn EARLY_DRIVER_MODULE_ORDERED macro allows a driver to be registered both in a specific order and for a specific pass level. .Sh SEE ALSO .Xr device 9 , .Xr driver 9 , .Xr module 9 , .Xr MODULE_PNP_INFO 9 , .Xr SYSINIT 9 .Sh AUTHORS This manual page was written by .An Alexander Langer Aq Mt alex@FreeBSD.org . Index: user/ngie/bug-237403/share/man/man9/Makefile =================================================================== --- user/ngie/bug-237403/share/man/man9/Makefile (revision 348022) +++ user/ngie/bug-237403/share/man/man9/Makefile (revision 348023) @@ -1,2273 +1,2274 @@ # $FreeBSD$ .include PACKAGE=runtime-manuals MAN= accept_filter.9 \ accf_data.9 \ accf_dns.9 \ accf_http.9 \ acl.9 \ alq.9 \ altq.9 \ atomic.9 \ bhnd.9 \ bhnd_erom.9 \ bios.9 \ bitset.9 \ boot.9 \ bpf.9 \ buf.9 \ buf_ring.9 \ BUF_ISLOCKED.9 \ BUF_LOCK.9 \ BUF_LOCKFREE.9 \ BUF_LOCKINIT.9 \ BUF_RECURSED.9 \ BUF_TIMELOCK.9 \ BUF_UNLOCK.9 \ bus_activate_resource.9 \ BUS_ADD_CHILD.9 \ bus_adjust_resource.9 \ bus_alloc_resource.9 \ BUS_BIND_INTR.9 \ bus_child_present.9 \ BUS_CHILD_DELETED.9 \ BUS_CHILD_DETACHED.9 \ BUS_CONFIG_INTR.9 \ BUS_DESCRIBE_INTR.9 \ bus_dma.9 \ bus_generic_attach.9 \ bus_generic_detach.9 \ bus_generic_new_pass.9 \ bus_generic_print_child.9 \ bus_generic_read_ivar.9 \ bus_generic_shutdown.9 \ BUS_GET_CPUS.9 \ bus_get_resource.9 \ bus_map_resource.9 \ BUS_NEW_PASS.9 \ BUS_PRINT_CHILD.9 \ BUS_READ_IVAR.9 \ BUS_RESCAN.9 \ bus_release_resource.9 \ bus_set_pass.9 \ bus_set_resource.9 \ BUS_SETUP_INTR.9 \ bus_space.9 \ byteorder.9 \ casuword.9 \ cd.9 \ cnv.9 \ condvar.9 \ config_intrhook.9 \ contigmalloc.9 \ copy.9 \ counter.9 \ cpuset.9 \ cr_cansee.9 \ critical_enter.9 \ cr_seeothergids.9 \ cr_seeotheruids.9 \ crypto.9 \ CTASSERT.9 \ DB_COMMAND.9 \ DECLARE_GEOM_CLASS.9 \ DECLARE_MODULE.9 \ + DEFINE_IFUNC.9 \ DELAY.9 \ devclass.9 \ devclass_find.9 \ devclass_get_device.9 \ devclass_get_devices.9 \ devclass_get_drivers.9 \ devclass_get_maxunit.9 \ devclass_get_name.9 \ devclass_get_softc.9 \ dev_clone.9 \ devfs_set_cdevpriv.9 \ device.9 \ device_add_child.9 \ DEVICE_ATTACH.9 \ device_delete_child.9 \ device_delete_children.9 \ DEVICE_DETACH.9 \ device_enable.9 \ device_find_child.9 \ device_get_children.9 \ device_get_devclass.9 \ device_get_driver.9 \ device_get_ivars.9 \ device_get_name.9 \ device_get_parent.9 \ device_get_softc.9 \ device_get_state.9 \ device_get_sysctl.9 \ device_get_unit.9 \ DEVICE_IDENTIFY.9 \ device_printf.9 \ DEVICE_PROBE.9 \ device_probe_and_attach.9 \ device_quiet.9 \ device_set_desc.9 \ device_set_driver.9 \ device_set_flags.9 \ DEVICE_SHUTDOWN.9 \ DEV_MODULE.9 \ dev_refthread.9 \ devstat.9 \ devtoname.9 \ disk.9 \ dnv.9 \ domain.9 \ domainset.9 \ dpcpu.9 \ drbr.9 \ driver.9 \ DRIVER_MODULE.9 \ efirt.9 \ epoch.9 \ EVENTHANDLER.9 \ eventtimers.9 \ extattr.9 \ fail.9 \ fdt_pinctrl.9 \ fetch.9 \ firmware.9 \ fpu_kern.9 \ g_access.9 \ g_attach.9 \ g_bio.9 \ g_consumer.9 \ g_data.9 \ get_cyclecount.9 \ getenv.9 \ getnewvnode.9 \ g_event.9 \ g_geom.9 \ g_provider.9 \ g_provider_by_name.9 \ groupmember.9 \ g_wither_geom.9 \ hash.9 \ hashinit.9 \ hexdump.9 \ hhook.9 \ ieee80211.9 \ ieee80211_amrr.9 \ ieee80211_beacon.9 \ ieee80211_bmiss.9 \ ieee80211_crypto.9 \ ieee80211_ddb.9 \ ieee80211_input.9 \ ieee80211_node.9 \ ieee80211_output.9 \ ieee80211_proto.9 \ ieee80211_radiotap.9 \ ieee80211_regdomain.9 \ ieee80211_scan.9 \ ieee80211_vap.9 \ iflib.9 \ iflibdd.9 \ iflibdi.9 \ iflibtxrx.9 \ ifnet.9 \ inittodr.9 \ insmntque.9 \ intro.9 \ ithread.9 \ KASSERT.9 \ kern_testfrwk.9 \ kernacc.9 \ kernel_mount.9 \ khelp.9 \ kobj.9 \ kproc.9 \ kqueue.9 \ kthread.9 \ ktr.9 \ lock.9 \ locking.9 \ LOCK_PROFILING.9 \ mac.9 \ make_dev.9 \ malloc.9 \ mbchain.9 \ mbuf.9 \ mbuf_tags.9 \ MD5.9 \ mdchain.9 \ memcchr.9 \ memguard.9 \ microseq.9 \ microtime.9 \ microuptime.9 \ mi_switch.9 \ mod_cc.9 \ module.9 \ MODULE_DEPEND.9 \ MODULE_PNP_INFO.9 \ MODULE_VERSION.9 \ mtx_pool.9 \ mutex.9 \ namei.9 \ netisr.9 \ nv.9 \ OF_child.9 \ OF_device_from_xref.9 \ OF_finddevice.9 \ OF_getprop.9 \ OF_node_from_xref.9 \ OF_package_to_path.9 \ ofw_bus_is_compatible.9 \ ofw_bus_status_okay.9 \ osd.9 \ owll.9 \ own.9 \ panic.9 \ PCBGROUP.9 \ p_candebug.9 \ p_cansee.9 \ pci.9 \ PCI_IOV_ADD_VF.9 \ PCI_IOV_INIT.9 \ pci_iov_schema.9 \ PCI_IOV_UNINIT.9 \ pfil.9 \ pfind.9 \ pget.9 \ pgfind.9 \ PHOLD.9 \ physio.9 \ pmap.9 \ pmap_activate.9 \ pmap_clear_modify.9 \ pmap_copy.9 \ pmap_enter.9 \ pmap_extract.9 \ pmap_growkernel.9 \ pmap_init.9 \ pmap_is_modified.9 \ pmap_is_prefaultable.9 \ pmap_map.9 \ pmap_mincore.9 \ pmap_object_init_pt.9 \ pmap_page_exists_quick.9 \ pmap_page_init.9 \ pmap_pinit.9 \ pmap_protect.9 \ pmap_qenter.9 \ pmap_quick_enter_page.9 \ pmap_release.9 \ pmap_remove.9 \ pmap_resident_count.9 \ pmap_unwire.9 \ pmap_zero_page.9 \ printf.9 \ prison_check.9 \ priv.9 \ proc_rwmem.9 \ pseudofs.9 \ psignal.9 \ pwm.9 \ pwmbus.9 \ random.9 \ random_harvest.9 \ ratecheck.9 \ redzone.9 \ refcount.9 \ resettodr.9 \ resource_int_value.9 \ rijndael.9 \ rman.9 \ rmlock.9 \ rtalloc.9 \ rtentry.9 \ runqueue.9 \ rwlock.9 \ sbuf.9 \ scheduler.9 \ SDT.9 \ securelevel_gt.9 \ selrecord.9 \ sema.9 \ sf_buf.9 \ sglist.9 \ shm_map.9 \ signal.9 \ sleep.9 \ sleepqueue.9 \ socket.9 \ stack.9 \ store.9 \ style.9 \ style.lua.9 \ swi.9 \ sx.9 \ syscall_helper_register.9 \ SYSCALL_MODULE.9 \ sysctl.9 \ sysctl_add_oid.9 \ sysctl_ctx_init.9 \ SYSINIT.9 \ taskqueue.9 \ tcp_functions.9 \ thread_exit.9 \ time.9 \ timeout.9 \ tvtohz.9 \ ucred.9 \ uidinfo.9 \ uio.9 \ unr.9 \ vaccess.9 \ vaccess_acl_nfs4.9 \ vaccess_acl_posix1e.9 \ vcount.9 \ vflush.9 \ VFS.9 \ vfs_busy.9 \ VFS_CHECKEXP.9 \ vfsconf.9 \ VFS_FHTOVP.9 \ vfs_getnewfsid.9 \ vfs_getopt.9 \ vfs_getvfs.9 \ VFS_MOUNT.9 \ vfs_mountedfrom.9 \ VFS_QUOTACTL.9 \ VFS_ROOT.9 \ vfs_rootmountalloc.9 \ VFS_SET.9 \ VFS_STATFS.9 \ vfs_suser.9 \ VFS_SYNC.9 \ vfs_timestamp.9 \ vfs_unbusy.9 \ VFS_UNMOUNT.9 \ vfs_unmountall.9 \ VFS_VGET.9 \ vget.9 \ vgone.9 \ vhold.9 \ vinvalbuf.9 \ vm_fault_prefault.9 \ vm_map.9 \ vm_map_check_protection.9 \ vm_map_create.9 \ vm_map_delete.9 \ vm_map_entry_resize_free.9 \ vm_map_find.9 \ vm_map_findspace.9 \ vm_map_inherit.9 \ vm_map_init.9 \ vm_map_insert.9 \ vm_map_lock.9 \ vm_map_lookup.9 \ vm_map_madvise.9 \ vm_map_max.9 \ vm_map_protect.9 \ vm_map_remove.9 \ vm_map_simplify_entry.9 \ vm_map_stack.9 \ vm_map_submap.9 \ vm_map_sync.9 \ vm_map_wire.9 \ vm_page_alloc.9 \ vm_page_bits.9 \ vm_page_busy.9 \ vm_page_deactivate.9 \ vm_page_dontneed.9 \ vm_page_aflag.9 \ vm_page_free.9 \ vm_page_grab.9 \ vm_page_hold.9 \ vm_page_insert.9 \ vm_page_lookup.9 \ vm_page_rename.9 \ vm_page_wire.9 \ vm_set_page_size.9 \ vmem.9 \ vn_fullpath.9 \ vn_isdisk.9 \ vnet.9 \ vnode.9 \ VOP_ACCESS.9 \ VOP_ACLCHECK.9 \ VOP_ADVISE.9 \ VOP_ADVLOCK.9 \ VOP_ALLOCATE.9 \ VOP_ATTRIB.9 \ VOP_BWRITE.9 \ VOP_CREATE.9 \ VOP_FSYNC.9 \ VOP_GETACL.9 \ VOP_GETEXTATTR.9 \ VOP_GETPAGES.9 \ VOP_INACTIVE.9 \ VOP_IOCTL.9 \ VOP_LINK.9 \ VOP_LISTEXTATTR.9 \ VOP_LOCK.9 \ VOP_LOOKUP.9 \ VOP_OPENCLOSE.9 \ VOP_PATHCONF.9 \ VOP_PRINT.9 \ VOP_RDWR.9 \ VOP_READDIR.9 \ VOP_READLINK.9 \ VOP_REALLOCBLKS.9 \ VOP_REMOVE.9 \ VOP_RENAME.9 \ VOP_REVOKE.9 \ VOP_SETACL.9 \ VOP_SETEXTATTR.9 \ VOP_STRATEGY.9 \ VOP_VPTOCNP.9 \ VOP_VPTOFH.9 \ vref.9 \ vrefcnt.9 \ vrele.9 \ vslock.9 \ watchdog.9 \ zone.9 MLINKS= unr.9 alloc_unr.9 \ unr.9 alloc_unrl.9 \ unr.9 alloc_unr_specific.9 \ unr.9 clear_unrhdr.9 \ unr.9 delete_unrhdr.9 \ unr.9 free_unr.9 \ unr.9 new_unrhdr.9 MLINKS+=accept_filter.9 accept_filt_add.9 \ accept_filter.9 accept_filt_del.9 \ accept_filter.9 accept_filt_generic_mod_event.9 \ accept_filter.9 accept_filt_get.9 MLINKS+=alq.9 ALQ.9 \ alq.9 alq_close.9 \ alq.9 alq_flush.9 \ alq.9 alq_get.9 \ alq.9 alq_getn.9 \ alq.9 alq_open.9 \ alq.9 alq_open_flags.9 \ alq.9 alq_post.9 \ alq.9 alq_post_flags.9 \ alq.9 alq_write.9 \ alq.9 alq_writen.9 MLINKS+=altq.9 ALTQ.9 MLINKS+=atomic.9 atomic_add.9 \ atomic.9 atomic_clear.9 \ atomic.9 atomic_cmpset.9 \ atomic.9 atomic_fcmpset.9 \ atomic.9 atomic_fetchadd.9 \ atomic.9 atomic_load.9 \ atomic.9 atomic_readandclear.9 \ atomic.9 atomic_set.9 \ atomic.9 atomic_store.9 \ atomic.9 atomic_subtract.9 \ atomic.9 atomic_swap.9 \ atomic.9 atomic_testandclear.9 \ atomic.9 atomic_testandset.9 \ atomic.9 atomic_thread_fence.9 MLINKS+=bhnd.9 BHND_MATCH_BOARD_TYPE.9 \ bhnd.9 BHND_MATCH_BOARD_VENDOR.9 \ bhnd.9 BHND_MATCH_CHIP_ID.9 \ bhnd.9 BHND_MATCH_CHIP_PKG.9 \ bhnd.9 BHND_MATCH_CHIP_REV.9 \ bhnd.9 BHND_MATCH_CORE_ID.9 \ bhnd.9 BHND_MATCH_CORE_VENDOR.9 \ bhnd.9 bhnd_activate_resource.9 \ bhnd.9 bhnd_alloc_pmu.9 \ bhnd.9 bhnd_alloc_resource.9 \ bhnd.9 bhnd_alloc_resource_any.9 \ bhnd.9 bhnd_alloc_resources.9 \ bhnd.9 bhnd_board_matches.9 \ bhnd.9 bhnd_bus_match_child.9 \ bhnd.9 bhnd_bus_read_1.9 \ bhnd.9 bhnd_bus_read_2.9 \ bhnd.9 bhnd_bus_read_4.9 \ bhnd.9 bhnd_bus_read_stream_1.9 \ bhnd.9 bhnd_bus_read_stream_2.9 \ bhnd.9 bhnd_bus_read_stream_4.9 \ bhnd.9 bhnd_bus_write_1.9 \ bhnd.9 bhnd_bus_write_2.9 \ bhnd.9 bhnd_bus_write_4.9 \ bhnd.9 bhnd_bus_write_stream_1.9 \ bhnd.9 bhnd_bus_write_stream_2.9 \ bhnd.9 bhnd_bus_write_stream_4.9 \ bhnd.9 bhnd_chip_matches.9 \ bhnd.9 bhnd_core_class.9 \ bhnd.9 bhnd_core_get_match_desc.9 \ bhnd.9 bhnd_core_matches.9 \ bhnd.9 bhnd_core_name.9 \ bhnd.9 bhnd_cores_equal.9 \ bhnd.9 bhnd_deactivate_resource.9 \ bhnd.9 bhnd_decode_port_rid.9 \ bhnd.9 bhnd_deregister_provider.9 \ bhnd.9 bhnd_device_lookup.9 \ bhnd.9 bhnd_device_matches.9 \ bhnd.9 bhnd_device_quirks.9 \ bhnd.9 bhnd_driver_get_erom_class.9 \ bhnd.9 bhnd_enable_clocks.9 \ bhnd.9 bhnd_find_core_class.9 \ bhnd.9 bhnd_find_core_name.9 \ bhnd.9 bhnd_format_chip_id.9 \ bhnd.9 bhnd_get_attach_type.9 \ bhnd.9 bhnd_get_chipid.9 \ bhnd.9 bhnd_get_class.9 \ bhnd.9 bhnd_get_clock_freq.9 \ bhnd.9 bhnd_get_clock_latency.9 \ bhnd.9 bhnd_get_core_index.9 \ bhnd.9 bhnd_get_core_info.9 \ bhnd.9 bhnd_get_core_unit.9 \ bhnd.9 bhnd_get_device.9 \ bhnd.9 bhnd_get_device_name.9 \ bhnd.9 bhnd_get_dma_translation.9 \ bhnd.9 bhnd_get_hwrev.9 \ bhnd.9 bhnd_get_intr_count.9 \ bhnd.9 bhnd_get_intr_ivec.9 \ bhnd.9 bhnd_get_port_count.9 \ bhnd.9 bhnd_get_port_rid.9 \ bhnd.9 bhnd_get_region_addr.9 \ bhnd.9 bhnd_get_region_count.9 \ bhnd.9 bhnd_get_vendor.9 \ bhnd.9 bhnd_get_vendor_name.9 \ bhnd.9 bhnd_hwrev_matches.9 \ bhnd.9 bhnd_is_hw_suspended.9 \ bhnd.9 bhnd_is_region_valid.9 \ bhnd.9 bhnd_map_intr.9 \ bhnd.9 bhnd_match_core.9 \ bhnd.9 bhnd_nvram_getvar.9 \ bhnd.9 bhnd_nvram_getvar_array.9 \ bhnd.9 bhnd_nvram_getvar_int.9 \ bhnd.9 bhnd_nvram_getvar_int16.9 \ bhnd.9 bhnd_nvram_getvar_int32.9 \ bhnd.9 bhnd_nvram_getvar_int8.9 \ bhnd.9 bhnd_nvram_getvar_str.9 \ bhnd.9 bhnd_nvram_getvar_uint.9 \ bhnd.9 bhnd_nvram_getvar_uint16.9 \ bhnd.9 bhnd_nvram_getvar_uint32.9 \ bhnd.9 bhnd_nvram_getvar_uint8.9 \ bhnd.9 bhnd_nvram_string_array_next.9 \ bhnd.9 bhnd_read_board_info.9 \ bhnd.9 bhnd_read_config.9 \ bhnd.9 bhnd_read_ioctl.9 \ bhnd.9 bhnd_read_iost.9 \ bhnd.9 bhnd_register_provider.9 \ bhnd.9 bhnd_release_ext_rsrc.9 \ bhnd.9 bhnd_release_pmu.9 \ bhnd.9 bhnd_release_provider.9 \ bhnd.9 bhnd_release_resource.9 \ bhnd.9 bhnd_release_resources.9 \ bhnd.9 bhnd_request_clock.9 \ bhnd.9 bhnd_request_ext_rsrc.9 \ bhnd.9 bhnd_reset_hw.9 \ bhnd.9 bhnd_retain_provider.9 \ bhnd.9 bhnd_set_custom_core_desc.9 \ bhnd.9 bhnd_set_default_core_desc.9 \ bhnd.9 bhnd_suspend_hw.9 \ bhnd.9 bhnd_unmap_intr.9 \ bhnd.9 bhnd_vendor_name.9 \ bhnd.9 bhnd_write_config.9 \ bhnd.9 bhnd_write_ioctl.9 MLINKS+=bhnd_erom.9 bhnd_erom_alloc.9 \ bhnd_erom.9 bhnd_erom_dump.9 \ bhnd_erom.9 bhnd_erom_fini_static.9 \ bhnd_erom.9 bhnd_erom_free.9 \ bhnd_erom.9 bhnd_erom_free_core_table.9 \ bhnd_erom.9 bhnd_erom_get_core_table.9 \ bhnd_erom.9 bhnd_erom_init_static.9 \ bhnd_erom.9 bhnd_erom_io.9 \ bhnd_erom.9 bhnd_erom_io_fini.9 \ bhnd_erom.9 bhnd_erom_io_map.9 \ bhnd_erom.9 bhnd_erom_io_read.9 \ bhnd_erom.9 bhnd_erom_iobus_init.9 \ bhnd_erom.9 bhnd_erom_iores_new.9 \ bhnd_erom.9 bhnd_erom_lookup_core.9 \ bhnd_erom.9 bhnd_erom_lookup_core_addr.9 \ bhnd_erom.9 bhnd_erom_probe.9 \ bhnd_erom.9 bhnd_erom_probe_driver_classes.9 MLINKS+=bitset.9 BITSET_DEFINE.9 \ bitset.9 BITSET_T_INITIALIZER.9 \ bitset.9 BITSET_FSET.9 \ bitset.9 BIT_CLR.9 \ bitset.9 BIT_COPY.9 \ bitset.9 BIT_ISSET.9 \ bitset.9 BIT_SET.9 \ bitset.9 BIT_ZERO.9 \ bitset.9 BIT_FILL.9 \ bitset.9 BIT_SETOF.9 \ bitset.9 BIT_EMPTY.9 \ bitset.9 BIT_ISFULLSET.9 \ bitset.9 BIT_FFS.9 \ bitset.9 BIT_COUNT.9 \ bitset.9 BIT_SUBSET.9 \ bitset.9 BIT_OVERLAP.9 \ bitset.9 BIT_CMP.9 \ bitset.9 BIT_OR.9 \ bitset.9 BIT_AND.9 \ bitset.9 BIT_NAND.9 \ bitset.9 BIT_CLR_ATOMIC.9 \ bitset.9 BIT_SET_ATOMIC.9 \ bitset.9 BIT_SET_ATOMIC_ACQ.9 \ bitset.9 BIT_AND_ATOMIC.9 \ bitset.9 BIT_OR_ATOMIC.9 \ bitset.9 BIT_COPY_STORE_REL.9 MLINKS+=bpf.9 bpfattach.9 \ bpf.9 bpfattach2.9 \ bpf.9 bpfdetach.9 \ bpf.9 bpf_filter.9 \ bpf.9 bpf_mtap.9 \ bpf.9 bpf_mtap2.9 \ bpf.9 bpf_tap.9 \ bpf.9 bpf_validate.9 MLINKS+=buf.9 bp.9 MLINKS+=buf_ring.9 buf_ring_alloc.9 \ buf_ring.9 buf_ring_free.9 \ buf_ring.9 buf_ring_enqueue.9 \ buf_ring.9 buf_ring_enqueue_bytes.9 \ buf_ring.9 buf_ring_dequeue_mc.9 \ buf_ring.9 buf_ring_dequeue_sc.9 \ buf_ring.9 buf_ring_count.9 \ buf_ring.9 buf_ring_empty.9 \ buf_ring.9 buf_ring_full.9 \ buf_ring.9 buf_ring_peek.9 MLINKS+=bus_activate_resource.9 bus_deactivate_resource.9 MLINKS+=bus_alloc_resource.9 bus_alloc_resource_any.9 MLINKS+=BUS_BIND_INTR.9 bus_bind_intr.9 MLINKS+=BUS_DESCRIBE_INTR.9 bus_describe_intr.9 MLINKS+=bus_dma.9 busdma.9 \ bus_dma.9 bus_dmamap_create.9 \ bus_dma.9 bus_dmamap_destroy.9 \ bus_dma.9 bus_dmamap_load.9 \ bus_dma.9 bus_dmamap_load_bio.9 \ bus_dma.9 bus_dmamap_load_ccb.9 \ bus_dma.9 bus_dmamap_load_mbuf.9 \ bus_dma.9 bus_dmamap_load_mbuf_sg.9 \ bus_dma.9 bus_dmamap_load_uio.9 \ bus_dma.9 bus_dmamap_sync.9 \ bus_dma.9 bus_dmamap_unload.9 \ bus_dma.9 bus_dmamem_alloc.9 \ bus_dma.9 bus_dmamem_free.9 \ bus_dma.9 bus_dma_tag_create.9 \ bus_dma.9 bus_dma_tag_destroy.9 MLINKS+=bus_generic_read_ivar.9 bus_generic_write_ivar.9 MLINKS+=BUS_GET_CPUS.9 bus_get_cpus.9 MLINKS+=bus_map_resource.9 bus_unmap_resource.9 \ bus_map_resource.9 resource_init_map_request.9 MLINKS+=BUS_READ_IVAR.9 BUS_WRITE_IVAR.9 MLINKS+=BUS_SETUP_INTR.9 bus_setup_intr.9 \ BUS_SETUP_INTR.9 BUS_TEARDOWN_INTR.9 \ BUS_SETUP_INTR.9 bus_teardown_intr.9 MLINKS+=bus_space.9 bus_space_alloc.9 \ bus_space.9 bus_space_barrier.9 \ bus_space.9 bus_space_copy_region_1.9 \ bus_space.9 bus_space_copy_region_2.9 \ bus_space.9 bus_space_copy_region_4.9 \ bus_space.9 bus_space_copy_region_8.9 \ bus_space.9 bus_space_copy_region_stream_1.9 \ bus_space.9 bus_space_copy_region_stream_2.9 \ bus_space.9 bus_space_copy_region_stream_4.9 \ bus_space.9 bus_space_copy_region_stream_8.9 \ bus_space.9 bus_space_free.9 \ bus_space.9 bus_space_map.9 \ bus_space.9 bus_space_read_1.9 \ bus_space.9 bus_space_read_2.9 \ bus_space.9 bus_space_read_4.9 \ bus_space.9 bus_space_read_8.9 \ bus_space.9 bus_space_read_multi_1.9 \ bus_space.9 bus_space_read_multi_2.9 \ bus_space.9 bus_space_read_multi_4.9 \ bus_space.9 bus_space_read_multi_8.9 \ bus_space.9 bus_space_read_multi_stream_1.9 \ bus_space.9 bus_space_read_multi_stream_2.9 \ bus_space.9 bus_space_read_multi_stream_4.9 \ bus_space.9 bus_space_read_multi_stream_8.9 \ bus_space.9 bus_space_read_region_1.9 \ bus_space.9 bus_space_read_region_2.9 \ bus_space.9 bus_space_read_region_4.9 \ bus_space.9 bus_space_read_region_8.9 \ bus_space.9 bus_space_read_region_stream_1.9 \ bus_space.9 bus_space_read_region_stream_2.9 \ bus_space.9 bus_space_read_region_stream_4.9 \ bus_space.9 bus_space_read_region_stream_8.9 \ bus_space.9 bus_space_read_stream_1.9 \ bus_space.9 bus_space_read_stream_2.9 \ bus_space.9 bus_space_read_stream_4.9 \ bus_space.9 bus_space_read_stream_8.9 \ bus_space.9 bus_space_set_multi_1.9 \ bus_space.9 bus_space_set_multi_2.9 \ bus_space.9 bus_space_set_multi_4.9 \ bus_space.9 bus_space_set_multi_8.9 \ bus_space.9 bus_space_set_multi_stream_1.9 \ bus_space.9 bus_space_set_multi_stream_2.9 \ bus_space.9 bus_space_set_multi_stream_4.9 \ bus_space.9 bus_space_set_multi_stream_8.9 \ bus_space.9 bus_space_set_region_1.9 \ bus_space.9 bus_space_set_region_2.9 \ bus_space.9 bus_space_set_region_4.9 \ bus_space.9 bus_space_set_region_8.9 \ bus_space.9 bus_space_set_region_stream_1.9 \ bus_space.9 bus_space_set_region_stream_2.9 \ bus_space.9 bus_space_set_region_stream_4.9 \ bus_space.9 bus_space_set_region_stream_8.9 \ bus_space.9 bus_space_subregion.9 \ bus_space.9 bus_space_unmap.9 \ bus_space.9 bus_space_write_1.9 \ bus_space.9 bus_space_write_2.9 \ bus_space.9 bus_space_write_4.9 \ bus_space.9 bus_space_write_8.9 \ bus_space.9 bus_space_write_multi_1.9 \ bus_space.9 bus_space_write_multi_2.9 \ bus_space.9 bus_space_write_multi_4.9 \ bus_space.9 bus_space_write_multi_8.9 \ bus_space.9 bus_space_write_multi_stream_1.9 \ bus_space.9 bus_space_write_multi_stream_2.9 \ bus_space.9 bus_space_write_multi_stream_4.9 \ bus_space.9 bus_space_write_multi_stream_8.9 \ bus_space.9 bus_space_write_region_1.9 \ bus_space.9 bus_space_write_region_2.9 \ bus_space.9 bus_space_write_region_4.9 \ bus_space.9 bus_space_write_region_8.9 \ bus_space.9 bus_space_write_region_stream_1.9 \ bus_space.9 bus_space_write_region_stream_2.9 \ bus_space.9 bus_space_write_region_stream_4.9 \ bus_space.9 bus_space_write_region_stream_8.9 \ bus_space.9 bus_space_write_stream_1.9 \ bus_space.9 bus_space_write_stream_2.9 \ bus_space.9 bus_space_write_stream_4.9 \ bus_space.9 bus_space_write_stream_8.9 MLINKS+=byteorder.9 be16dec.9 \ byteorder.9 be16enc.9 \ byteorder.9 be16toh.9 \ byteorder.9 be32dec.9 \ byteorder.9 be32enc.9 \ byteorder.9 be32toh.9 \ byteorder.9 be64dec.9 \ byteorder.9 be64enc.9 \ byteorder.9 be64toh.9 \ byteorder.9 bswap16.9 \ byteorder.9 bswap32.9 \ byteorder.9 bswap64.9 \ byteorder.9 htobe16.9 \ byteorder.9 htobe32.9 \ byteorder.9 htobe64.9 \ byteorder.9 htole16.9 \ byteorder.9 htole32.9 \ byteorder.9 htole64.9 \ byteorder.9 le16dec.9 \ byteorder.9 le16enc.9 \ byteorder.9 le16toh.9 \ byteorder.9 le32dec.9 \ byteorder.9 le32enc.9 \ byteorder.9 le32toh.9 \ byteorder.9 le64dec.9 \ byteorder.9 le64enc.9 \ byteorder.9 le64toh.9 MLINKS+=cnv.9 cnvlist.9 \ cnv.9 cnvlist_free_binary.9 \ cnv.9 cnvlist_free_bool.9 \ cnv.9 cnvlist_free_bool_array.9 \ cnv.9 cnvlist_free_descriptor.9 \ cnv.9 cnvlist_free_descriptor_array.9 \ cnv.9 cnvlist_free_null.9 \ cnv.9 cnvlist_free_number.9 \ cnv.9 cnvlist_free_number_array.9 \ cnv.9 cnvlist_free_nvlist.9 \ cnv.9 cnvlist_free_nvlist_array.9 \ cnv.9 cnvlist_free_string.9 \ cnv.9 cnvlist_free_string_array.9 \ cnv.9 cnvlist_get_binary.9 \ cnv.9 cnvlist_get_bool.9 \ cnv.9 cnvlist_get_bool_array.9 \ cnv.9 cnvlist_get_descriptor.9 \ cnv.9 cnvlist_get_descriptor_array.9 \ cnv.9 cnvlist_get_number.9 \ cnv.9 cnvlist_get_number_array.9 \ cnv.9 cnvlist_get_nvlist.9 \ cnv.9 cnvlist_get_nvlist_array.9 \ cnv.9 cnvlist_get_string.9 \ cnv.9 cnvlist_get_string_array.9 \ cnv.9 cnvlist_take_binary.9 \ cnv.9 cnvlist_take_bool.9 \ cnv.9 cnvlist_take_bool_array.9 \ cnv.9 cnvlist_take_descriptor.9 \ cnv.9 cnvlist_take_descriptor_array.9 \ cnv.9 cnvlist_take_number.9 \ cnv.9 cnvlist_take_number_array.9 \ cnv.9 cnvlist_take_nvlist.9 \ cnv.9 cnvlist_take_nvlist_array.9 \ cnv.9 cnvlist_take_string.9 \ cnv.9 cnvlist_take_string_array.9 MLINKS+=condvar.9 cv_broadcast.9 \ condvar.9 cv_broadcastpri.9 \ condvar.9 cv_destroy.9 \ condvar.9 cv_init.9 \ condvar.9 cv_signal.9 \ condvar.9 cv_timedwait.9 \ condvar.9 cv_timedwait_sig.9 \ condvar.9 cv_timedwait_sig_sbt.9 \ condvar.9 cv_wait.9 \ condvar.9 cv_wait_sig.9 \ condvar.9 cv_wait_unlock.9 \ condvar.9 cv_wmesg.9 MLINKS+=config_intrhook.9 config_intrhook_disestablish.9 \ config_intrhook.9 config_intrhook_establish.9 \ config_intrhook.9 config_intrhook_oneshot.9 MLINKS+=contigmalloc.9 contigmalloc_domainset.9 \ contigmalloc.9 contigfree.9 MLINKS+=casuword.9 casueword.9 \ casuword.9 casueword32.9 \ casuword.9 casuword32.9 MLINKS+=copy.9 copyin.9 \ copy.9 copyin_nofault.9 \ copy.9 copyinstr.9 \ copy.9 copyout.9 \ copy.9 copyout_nofault.9 \ copy.9 copystr.9 MLINKS+=counter.9 counter_u64_alloc.9 \ counter.9 counter_u64_free.9 \ counter.9 counter_u64_add.9 \ counter.9 counter_enter.9 \ counter.9 counter_exit.9 \ counter.9 counter_u64_add_protected.9 \ counter.9 counter_u64_fetch.9 \ counter.9 counter_u64_zero.9 \ counter.9 SYSCTL_COUNTER_U64.9 \ counter.9 SYSCTL_ADD_COUNTER_U64.9 \ counter.9 SYSCTL_COUNTER_U64_ARRAY.9 \ counter.9 SYSCTL_ADD_COUNTER_U64_ARRAY.9 MLINKS+=cpuset.9 CPUSET_T_INITIALIZER.9 \ cpuset.9 CPUSET_FSET.9 \ cpuset.9 CPU_CLR.9 \ cpuset.9 CPU_COPY.9 \ cpuset.9 CPU_ISSET.9 \ cpuset.9 CPU_SET.9 \ cpuset.9 CPU_ZERO.9 \ cpuset.9 CPU_FILL.9 \ cpuset.9 CPU_SETOF.9 \ cpuset.9 CPU_EMPTY.9 \ cpuset.9 CPU_ISFULLSET.9 \ cpuset.9 CPU_FFS.9 \ cpuset.9 CPU_COUNT.9 \ cpuset.9 CPU_SUBSET.9 \ cpuset.9 CPU_OVERLAP.9 \ cpuset.9 CPU_CMP.9 \ cpuset.9 CPU_OR.9 \ cpuset.9 CPU_AND.9 \ cpuset.9 CPU_NAND.9 \ cpuset.9 CPU_CLR_ATOMIC.9 \ cpuset.9 CPU_SET_ATOMIC.9 \ cpuset.9 CPU_SET_ATOMIC_ACQ.9 \ cpuset.9 CPU_AND_ATOMIC.9 \ cpuset.9 CPU_OR_ATOMIC.9 \ cpuset.9 CPU_COPY_STORE_REL.9 MLINKS+=critical_enter.9 critical.9 \ critical_enter.9 critical_exit.9 MLINKS+=crypto.9 crypto_dispatch.9 \ crypto.9 crypto_done.9 \ crypto.9 crypto_freereq.9 \ crypto.9 crypto_freesession.9 \ crypto.9 crypto_get_driverid.9 \ crypto.9 crypto_getreq.9 \ crypto.9 crypto_kdispatch.9 \ crypto.9 crypto_kdone.9 \ crypto.9 crypto_kregister.9 \ crypto.9 crypto_newsession.9 \ crypto.9 crypto_register.9 \ crypto.9 crypto_unblock.9 \ crypto.9 crypto_unregister.9 \ crypto.9 crypto_unregister_all.9 MLINKS+=DB_COMMAND.9 DB_SHOW_ALL_COMMAND.9 \ DB_COMMAND.9 DB_SHOW_COMMAND.9 MLINKS+=DECLARE_MODULE.9 DECLARE_MODULE_TIED.9 MLINKS+=dev_clone.9 drain_dev_clone_events.9 MLINKS+=dev_refthread.9 devvn_refthread.9 \ dev_refthread.9 dev_relthread.9 MLINKS+=devfs_set_cdevpriv.9 devfs_clear_cdevpriv.9 \ devfs_set_cdevpriv.9 devfs_get_cdevpriv.9 MLINKS+=device_add_child.9 device_add_child_ordered.9 MLINKS+=device_enable.9 device_disable.9 \ device_enable.9 device_is_enabled.9 MLINKS+=device_get_ivars.9 device_set_ivars.9 MLINKS+=device_get_name.9 device_get_nameunit.9 MLINKS+=device_get_state.9 device_busy.9 \ device_get_state.9 device_is_alive.9 \ device_get_state.9 device_is_attached.9 \ device_get_state.9 device_unbusy.9 MLINKS+=device_get_sysctl.9 device_get_sysctl_ctx.9 \ device_get_sysctl.9 device_get_sysctl_tree.9 MLINKS+=device_quiet.9 device_is_quiet.9 \ device_quiet.9 device_verbose.9 MLINKS+=device_set_desc.9 device_get_desc.9 \ device_set_desc.9 device_set_desc_copy.9 MLINKS+=device_set_flags.9 device_get_flags.9 MLINKS+=devstat.9 devicestat.9 \ devstat.9 devstat_add_entry.9 \ devstat.9 devstat_end_transaction.9 \ devstat.9 devstat_remove_entry.9 \ devstat.9 devstat_start_transaction.9 MLINKS+=disk.9 disk_add_alias.9 \ disk.9 disk_alloc.9 \ disk.9 disk_create.9 \ disk.9 disk_destroy.9 \ disk.9 disk_gone.9 \ disk.9 disk_resize.9 MLINKS+=dnv.9 dnvlist.9 \ dnv.9 dnvlist_get_binary.9 \ dnv.9 dnvlist_get_bool.9 \ dnv.9 dnvlist_get_descriptor.9 \ dnv.9 dnvlist_get_number.9 \ dnv.9 dnvlist_get_nvlist.9 \ dnv.9 dnvlist_get_string.9 \ dnv.9 dnvlist_take_binary.9 \ dnv.9 dnvlist_take_bool.9 \ dnv.9 dnvlist_take_descriptor.9 \ dnv.9 dnvlist_take_number.9 \ dnv.9 dnvlist_take_nvlist.9 \ dnv.9 dnvlist_take_string.9 MLINKS+=domain.9 DOMAIN_SET.9 \ domain.9 domain_add.9 \ domain.9 pfctlinput.9 \ domain.9 pfctlinput2.9 \ domain.9 pffinddomain.9 \ domain.9 pffindproto.9 \ domain.9 pffindtype.9 MLINKS+=drbr.9 drbr_free.9 \ drbr.9 drbr_enqueue.9 \ drbr.9 drbr_dequeue.9 \ drbr.9 drbr_dequeue_cond.9 \ drbr.9 drbr_flush.9 \ drbr.9 drbr_empty.9 \ drbr.9 drbr_inuse.9 \ drbr.9 drbr_stats_update.9 MLINKS+=DRIVER_MODULE.9 DRIVER_MODULE_ORDERED.9 \ DRIVER_MODULE.9 EARLY_DRIVER_MODULE.9 \ DRIVER_MODULE.9 EARLY_DRIVER_MODULE_ORDERED.9 MLINKS+=EVENTHANDLER.9 EVENTHANDLER_DECLARE.9 \ EVENTHANDLER.9 EVENTHANDLER_DEFINE.9 \ EVENTHANDLER.9 EVENTHANDLER_DEREGISTER.9 \ EVENTHANDLER.9 eventhandler_deregister.9 \ EVENTHANDLER.9 eventhandler_find_list.9 \ EVENTHANDLER.9 EVENTHANDLER_INVOKE.9 \ EVENTHANDLER.9 eventhandler_prune_list.9 \ EVENTHANDLER.9 EVENTHANDLER_REGISTER.9 \ EVENTHANDLER.9 eventhandler_register.9 MLINKS+=eventtimers.9 et_register.9 \ eventtimers.9 et_deregister.9 \ eventtimers.9 et_ban.9 \ eventtimers.9 et_find.9 \ eventtimers.9 et_free.9 \ eventtimers.9 et_init.9 \ eventtimers.9 ET_LOCK.9 \ eventtimers.9 ET_UNLOCK.9 \ eventtimers.9 et_start.9 \ eventtimers.9 et_stop.9 MLINKS+=fail.9 KFAIL_POINT_CODE.9 \ fail.9 KFAIL_POINT_ERROR.9 \ fail.9 KFAIL_POINT_GOTO.9 \ fail.9 KFAIL_POINT_RETURN.9 \ fail.9 KFAIL_POINT_RETURN_VOID.9 MLINKS+=fdt_pinctrl.9 fdt_pinctrl_configure.9 \ fdt_pinctrl.9 fdt_pinctrl_configure_by_name.9 \ fdt_pinctrl.9 fdt_pinctrl_configure_tree.9 \ fdt_pinctrl.9 fdt_pinctrl_register.9 MLINKS+=fetch.9 fubyte.9 \ fetch.9 fuword.9 \ fetch.9 fuword16.9 \ fetch.9 fuword32.9 \ fetch.9 fuword64.9 \ fetch.9 fueword.9 \ fetch.9 fueword32.9 \ fetch.9 fueword64.9 MLINKS+=firmware.9 firmware_get.9 \ firmware.9 firmware_put.9 \ firmware.9 firmware_register.9 \ firmware.9 firmware_unregister.9 MLINKS+=fpu_kern.9 fpu_kern_alloc_ctx.9 \ fpu_kern.9 fpu_kern_free_ctx.9 \ fpu_kern.9 fpu_kern_enter.9 \ fpu_kern.9 fpu_kern_leave.9 \ fpu_kern.9 fpu_kern_thread.9 \ fpu_kern.9 is_fpu_kern_thread.9 MLINKS+=g_attach.9 g_detach.9 MLINKS+=g_bio.9 g_alloc_bio.9 \ g_bio.9 g_clone_bio.9 \ g_bio.9 g_destroy_bio.9 \ g_bio.9 g_duplicate_bio.9 \ g_bio.9 g_new_bio.9 \ g_bio.9 g_print_bio.9 \ g_bio.9 g_reset_bio.9 MLINKS+=g_consumer.9 g_destroy_consumer.9 \ g_consumer.9 g_new_consumer.9 MLINKS+=g_data.9 g_read_data.9 \ g_data.9 g_write_data.9 MLINKS+=getenv.9 freeenv.9 \ getenv.9 getenv_int.9 \ getenv.9 getenv_long.9 \ getenv.9 getenv_string.9 \ getenv.9 getenv_quad.9 \ getenv.9 getenv_uint.9 \ getenv.9 getenv_ulong.9 \ getenv.9 kern_getenv.9 \ getenv.9 kern_setenv.9 \ getenv.9 kern_unsetenv.9 \ getenv.9 setenv.9 \ getenv.9 testenv.9 \ getenv.9 unsetenv.9 MLINKS+=g_event.9 g_cancel_event.9 \ g_event.9 g_post_event.9 \ g_event.9 g_waitfor_event.9 MLINKS+=g_geom.9 g_destroy_geom.9 \ g_geom.9 g_new_geomf.9 MLINKS+=g_provider.9 g_destroy_provider.9 \ g_provider.9 g_error_provider.9 \ g_provider.9 g_new_providerf.9 MLINKS+=hash.9 hash32.9 \ hash.9 hash32_buf.9 \ hash.9 hash32_str.9 \ hash.9 hash32_stre.9 \ hash.9 hash32_strn.9 \ hash.9 hash32_strne.9 \ hash.9 jenkins_hash.9 \ hash.9 jenkins_hash32.9 MLINKS+=hashinit.9 hashdestroy.9 \ hashinit.9 hashinit_flags.9 \ hashinit.9 phashinit.9 MLINKS+=hhook.9 hhook_head_register.9 \ hhook.9 hhook_head_deregister.9 \ hhook.9 hhook_head_deregister_lookup.9 \ hhook.9 hhook_run_hooks.9 \ hhook.9 HHOOKS_RUN_IF.9 \ hhook.9 HHOOKS_RUN_LOOKUP_IF.9 MLINKS+=ieee80211.9 ieee80211_ifattach.9 \ ieee80211.9 ieee80211_ifdetach.9 MLINKS+=ieee80211_amrr.9 ieee80211_amrr_choose.9 \ ieee80211_amrr.9 ieee80211_amrr_cleanup.9 \ ieee80211_amrr.9 ieee80211_amrr_init.9 \ ieee80211_amrr.9 ieee80211_amrr_node_init.9 \ ieee80211_amrr.9 ieee80211_amrr_setinterval.9 \ ieee80211_amrr.9 ieee80211_amrr_tx_complete.9 \ ieee80211_amrr.9 ieee80211_amrr_tx_update.9 MLINKS+=ieee80211_beacon.9 ieee80211_beacon_alloc.9 \ ieee80211_beacon.9 ieee80211_beacon_notify.9 \ ieee80211_beacon.9 ieee80211_beacon_update.9 MLINKS+=ieee80211_bmiss.9 ieee80211_beacon_miss.9 MLINKS+=ieee80211_crypto.9 ieee80211_crypto_available.9 \ ieee80211_crypto.9 ieee80211_crypto_decap.9 \ ieee80211_crypto.9 ieee80211_crypto_delglobalkeys.9 \ ieee80211_crypto.9 ieee80211_crypto_delkey.9 \ ieee80211_crypto.9 ieee80211_crypto_demic.9 \ ieee80211_crypto.9 ieee80211_crypto_encap.9 \ ieee80211_crypto.9 ieee80211_crypto_enmic.9 \ ieee80211_crypto.9 ieee80211_crypto_newkey.9 \ ieee80211_crypto.9 ieee80211_crypto_register.9 \ ieee80211_crypto.9 ieee80211_crypto_reload_keys.9 \ ieee80211_crypto.9 ieee80211_crypto_setkey.9 \ ieee80211_crypto.9 ieee80211_crypto_unregister.9 \ ieee80211_crypto.9 ieee80211_key_update_begin.9 \ ieee80211_crypto.9 ieee80211_key_update_end.9 \ ieee80211_crypto.9 ieee80211_notify_michael_failure.9 \ ieee80211_crypto.9 ieee80211_notify_replay_failure.9 MLINKS+=ieee80211_input.9 ieee80211_input_all.9 MLINKS+=ieee80211_node.9 ieee80211_dump_node.9 \ ieee80211_node.9 ieee80211_dump_nodes.9 \ ieee80211_node.9 ieee80211_find_rxnode.9 \ ieee80211_node.9 ieee80211_find_rxnode_withkey.9 \ ieee80211_node.9 ieee80211_free_node.9 \ ieee80211_node.9 ieee80211_iterate_nodes.9 \ ieee80211_node.9 ieee80211_ref_node.9 \ ieee80211_node.9 ieee80211_unref_node.9 MLINKS+=ieee80211_output.9 ieee80211_process_callback.9 \ ieee80211_output.9 M_SEQNO_GET.9 \ ieee80211_output.9 M_WME_GETAC.9 MLINKS+=ieee80211_proto.9 ieee80211_new_state.9 \ ieee80211_proto.9 ieee80211_resume_all.9 \ ieee80211_proto.9 ieee80211_start_all.9 \ ieee80211_proto.9 ieee80211_stop_all.9 \ ieee80211_proto.9 ieee80211_suspend_all.9 \ ieee80211_proto.9 ieee80211_waitfor_parent.9 MLINKS+=ieee80211_radiotap.9 ieee80211_radiotap_active.9 \ ieee80211_radiotap.9 ieee80211_radiotap_active_vap.9 \ ieee80211_radiotap.9 ieee80211_radiotap_attach.9 \ ieee80211_radiotap.9 ieee80211_radiotap_tx.9 \ ieee80211_radiotap.9 radiotap.9 MLINKS+=ieee80211_regdomain.9 ieee80211_alloc_countryie.9 \ ieee80211_regdomain.9 ieee80211_init_channels.9 \ ieee80211_regdomain.9 ieee80211_sort_channels.9 MLINKS+=ieee80211_scan.9 ieee80211_add_scan.9 \ ieee80211_scan.9 ieee80211_bg_scan.9 \ ieee80211_scan.9 ieee80211_cancel_scan.9 \ ieee80211_scan.9 ieee80211_cancel_scan_any.9 \ ieee80211_scan.9 ieee80211_check_scan.9 \ ieee80211_scan.9 ieee80211_check_scan_current.9 \ ieee80211_scan.9 ieee80211_flush.9 \ ieee80211_scan.9 ieee80211_probe_curchan.9 \ ieee80211_scan.9 ieee80211_scan_assoc_fail.9 \ ieee80211_scan.9 ieee80211_scan_done.9 \ ieee80211_scan.9 ieee80211_scan_dump_channels.9 \ ieee80211_scan.9 ieee80211_scan_flush.9 \ ieee80211_scan.9 ieee80211_scan_iterate.9 \ ieee80211_scan.9 ieee80211_scan_next.9 \ ieee80211_scan.9 ieee80211_scan_timeout.9 \ ieee80211_scan.9 ieee80211_scanner_get.9 \ ieee80211_scan.9 ieee80211_scanner_register.9 \ ieee80211_scan.9 ieee80211_scanner_unregister.9 \ ieee80211_scan.9 ieee80211_scanner_unregister_all.9 \ ieee80211_scan.9 ieee80211_start_scan.9 MLINKS+=ieee80211_vap.9 ieee80211_vap_attach.9 \ ieee80211_vap.9 ieee80211_vap_detach.9 \ ieee80211_vap.9 ieee80211_vap_setup.9 MLINKS+=iflibdd.9 ifdi_attach_pre.9 \ iflibdd.9 ifdi_attach_post.9 \ iflibdd.9 ifdi_detach.9 \ iflibdd.9 ifdi_get_counter.9 \ iflibdd.9 ifdi_i2c_req.9 \ iflibdd.9 ifdi_init.9 \ iflibdd.9 ifdi_intr_enable.9 \ iflibdd.9 ifdi_intr_disable.9 \ iflibdd.9 ifdi_led_func.9 \ iflibdd.9 ifdi_link_intr_enable.9 \ iflibdd.9 ifdi_media_set.9 \ iflibdd.9 ifdi_media_status.9 \ iflibdd.9 ifdi_media_change.9 \ iflibdd.9 ifdi_mtu_set.9 \ iflibdd.9 ifdi_multi_set.9 \ iflibdd.9 ifdi_promisc_set.9 \ iflibdd.9 ifdi_queues_alloc.9 \ iflibdd.9 ifdi_queues_free.9 \ iflibdd.9 ifdi_queue_intr_enable.9 \ iflibdd.9 ifdi_resume.9 \ iflibdd.9 ifdi_rxq_setup.9 \ iflibdd.9 ifdi_stop.9 \ iflibdd.9 ifdi_suspend.9 \ iflibdd.9 ifdi_sysctl_int_delay.9 \ iflibdd.9 ifdi_timer.9 \ iflibdd.9 ifdi_txq_setup.9 \ iflibdd.9 ifdi_update_admin_status.9 \ iflibdd.9 ifdi_vf_add.9 \ iflibdd.9 ifdi_vflr_handle.9 \ iflibdd.9 ifdi_vlan_register.9 \ iflibdd.9 ifdi_vlan_unregister.9 \ iflibdd.9 ifdi_watchdog_reset.9 \ iflibdd.9 iov_init.9 \ iflibdd.9 iov_uinit.9 MLINKS+=iflibdi.9 iflib_add_int_delay_sysctl.9 \ iflibdi.9 iflib_device_attach.9 \ iflibdi.9 iflib_device_deregister.9 \ iflibdi.9 iflib_device_detach.9 \ iflibdi.9 iflib_device_suspend.9 \ iflibdi.9 iflib_device_register.9 \ iflibdi.9 iflib_device_resume.9 \ iflibdi.9 iflib_led_create.9 \ iflibdi.9 iflib_irq_alloc.9 \ iflibdi.9 iflib_irq_alloc_generic.9 \ iflibdi.9 iflib_link_intr_deferred.9 \ iflibdi.9 iflib_link_state_change.9 \ iflibdi.9 iflib_rx_intr_deferred.9 \ iflibdi.9 iflib_tx_intr_deferred.9 MLINKS+=iflibtxrx.9 isc_rxd_available.9 \ iflibtxrx.9 isc_rxd_refill.9 \ iflibtxrx.9 isc_rxd_flush.9 \ iflibtxrx.9 isc_rxd_pkt_get.9 \ iflibtxrx.9 isc_txd_credits_update.9 \ iflibtxrx.9 isc_txd_encap.9 \ iflibtxrx.9 isc_txd_flush.9 MLINKS+=ifnet.9 if_addmulti.9 \ ifnet.9 if_alloc.9 \ ifnet.9 if_alloc_dev.9 \ ifnet.9 if_alloc_domain.9 \ ifnet.9 if_allmulti.9 \ ifnet.9 if_attach.9 \ ifnet.9 if_data.9 \ ifnet.9 IF_DEQUEUE.9 \ ifnet.9 if_delmulti.9 \ ifnet.9 if_detach.9 \ ifnet.9 if_down.9 \ ifnet.9 if_findmulti.9 \ ifnet.9 if_free.9 \ ifnet.9 if_free_type.9 \ ifnet.9 if_up.9 \ ifnet.9 ifa_free.9 \ ifnet.9 ifa_ifwithaddr.9 \ ifnet.9 ifa_ifwithdstaddr.9 \ ifnet.9 ifa_ifwithnet.9 \ ifnet.9 ifa_ref.9 \ ifnet.9 ifaddr.9 \ ifnet.9 ifaddr_byindex.9 \ ifnet.9 ifaof_ifpforaddr.9 \ ifnet.9 ifioctl.9 \ ifnet.9 ifpromisc.9 \ ifnet.9 ifqueue.9 \ ifnet.9 ifunit.9 \ ifnet.9 ifunit_ref.9 MLINKS+=insmntque.9 insmntque1.9 MLINKS+=ithread.9 ithread_add_handler.9 \ ithread.9 ithread_create.9 \ ithread.9 ithread_destroy.9 \ ithread.9 ithread_priority.9 \ ithread.9 ithread_remove_handler.9 \ ithread.9 ithread_schedule.9 MLINKS+=kernacc.9 useracc.9 MLINKS+=kernel_mount.9 free_mntarg.9 \ kernel_mount.9 kernel_vmount.9 \ kernel_mount.9 mount_arg.9 \ kernel_mount.9 mount_argb.9 \ kernel_mount.9 mount_argf.9 \ kernel_mount.9 mount_argsu.9 MLINKS+=khelp.9 khelp_add_hhook.9 \ khelp.9 KHELP_DECLARE_MOD.9 \ khelp.9 KHELP_DECLARE_MOD_UMA.9 \ khelp.9 khelp_destroy_osd.9 \ khelp.9 khelp_get_id.9 \ khelp.9 khelp_get_osd.9 \ khelp.9 khelp_init_osd.9 \ khelp.9 khelp_remove_hhook.9 MLINKS+=kobj.9 DEFINE_CLASS.9 \ kobj.9 kobj_class_compile.9 \ kobj.9 kobj_class_compile_static.9 \ kobj.9 kobj_class_free.9 \ kobj.9 kobj_create.9 \ kobj.9 kobj_delete.9 \ kobj.9 kobj_init.9 \ kobj.9 kobj_init_static.9 MLINKS+=kproc.9 kproc_create.9 \ kproc.9 kproc_exit.9 \ kproc.9 kproc_kthread_add.9 \ kproc.9 kproc_resume.9 \ kproc.9 kproc_shutdown.9 \ kproc.9 kproc_start.9 \ kproc.9 kproc_suspend.9 \ kproc.9 kproc_suspend_check.9 \ kproc.9 kthread_create.9 MLINKS+=kqueue.9 knlist_add.9 \ kqueue.9 knlist_clear.9 \ kqueue.9 knlist_delete.9 \ kqueue.9 knlist_destroy.9 \ kqueue.9 knlist_empty.9 \ kqueue.9 knlist_init.9 \ kqueue.9 knlist_init_mtx.9 \ kqueue.9 knlist_init_rw_reader.9 \ kqueue.9 knlist_remove.9 \ kqueue.9 knlist_remove_inevent.9 \ kqueue.9 knote_fdclose.9 \ kqueue.9 KNOTE_LOCKED.9 \ kqueue.9 KNOTE_UNLOCKED.9 \ kqueue.9 kqfd_register.9 \ kqueue.9 kqueue_add_filteropts.9 \ kqueue.9 kqueue_del_filteropts.9 MLINKS+=kthread.9 kthread_add.9 \ kthread.9 kthread_exit.9 \ kthread.9 kthread_resume.9 \ kthread.9 kthread_shutdown.9 \ kthread.9 kthread_start.9 \ kthread.9 kthread_suspend.9 \ kthread.9 kthread_suspend_check.9 MLINKS+=ktr.9 CTR0.9 \ ktr.9 CTR1.9 \ ktr.9 CTR2.9 \ ktr.9 CTR3.9 \ ktr.9 CTR4.9 \ ktr.9 CTR5.9 \ ktr.9 CTR6.9 MLINKS+=lock.9 lockdestroy.9 \ lock.9 lockinit.9 \ lock.9 lockmgr.9 \ lock.9 lockmgr_args.9 \ lock.9 lockmgr_args_rw.9 \ lock.9 lockmgr_assert.9 \ lock.9 lockmgr_disown.9 \ lock.9 lockmgr_printinfo.9 \ lock.9 lockmgr_recursed.9 \ lock.9 lockmgr_rw.9 \ lock.9 lockstatus.9 MLINKS+=LOCK_PROFILING.9 MUTEX_PROFILING.9 MLINKS+=make_dev.9 destroy_dev.9 \ make_dev.9 destroy_dev_drain.9 \ make_dev.9 destroy_dev_sched.9 \ make_dev.9 destroy_dev_sched_cb.9 \ make_dev.9 dev_depends.9 \ make_dev.9 make_dev_alias.9 \ make_dev.9 make_dev_alias_p.9 \ make_dev.9 make_dev_cred.9 \ make_dev.9 make_dev_credf.9 \ make_dev.9 make_dev_p.9 \ make_dev.9 make_dev_s.9 MLINKS+=malloc.9 free.9 \ malloc.9 malloc_domainset.9 \ malloc.9 free_domain.9 \ malloc.9 mallocarray.9 \ malloc.9 MALLOC_DECLARE.9 \ malloc.9 MALLOC_DEFINE.9 \ malloc.9 realloc.9 \ malloc.9 reallocf.9 MLINKS+=mbchain.9 mb_detach.9 \ mbchain.9 mb_done.9 \ mbchain.9 mb_fixhdr.9 \ mbchain.9 mb_init.9 \ mbchain.9 mb_initm.9 \ mbchain.9 mb_put_int64be.9 \ mbchain.9 mb_put_int64le.9 \ mbchain.9 mb_put_mbuf.9 \ mbchain.9 mb_put_mem.9 \ mbchain.9 mb_put_uint16be.9 \ mbchain.9 mb_put_uint16le.9 \ mbchain.9 mb_put_uint32be.9 \ mbchain.9 mb_put_uint32le.9 \ mbchain.9 mb_put_uint8.9 \ mbchain.9 mb_put_uio.9 \ mbchain.9 mb_reserve.9 MLINKS+=\ mbuf.9 m_adj.9 \ mbuf.9 m_align.9 \ mbuf.9 M_ALIGN.9 \ mbuf.9 m_append.9 \ mbuf.9 m_apply.9 \ mbuf.9 m_cat.9 \ mbuf.9 m_catpkt.9 \ mbuf.9 MCHTYPE.9 \ mbuf.9 MCLGET.9 \ mbuf.9 m_collapse.9 \ mbuf.9 m_copyback.9 \ mbuf.9 m_copydata.9 \ mbuf.9 m_copym.9 \ mbuf.9 m_copypacket.9 \ mbuf.9 m_copyup.9 \ mbuf.9 m_defrag.9 \ mbuf.9 m_devget.9 \ mbuf.9 m_dup.9 \ mbuf.9 m_dup_pkthdr.9 \ mbuf.9 MEXTADD.9 \ mbuf.9 m_fixhdr.9 \ mbuf.9 m_free.9 \ mbuf.9 m_freem.9 \ mbuf.9 MGET.9 \ mbuf.9 m_get.9 \ mbuf.9 m_get2.9 \ mbuf.9 m_getjcl.9 \ mbuf.9 m_getcl.9 \ mbuf.9 MGETHDR.9 \ mbuf.9 m_gethdr.9 \ mbuf.9 m_getm.9 \ mbuf.9 m_getptr.9 \ mbuf.9 MH_ALIGN.9 \ mbuf.9 M_LEADINGSPACE.9 \ mbuf.9 m_length.9 \ mbuf.9 M_MOVE_PKTHDR.9 \ mbuf.9 m_move_pkthdr.9 \ mbuf.9 M_PREPEND.9 \ mbuf.9 m_prepend.9 \ mbuf.9 m_pulldown.9 \ mbuf.9 m_pullup.9 \ mbuf.9 m_split.9 \ mbuf.9 mtod.9 \ mbuf.9 M_TRAILINGSPACE.9 \ mbuf.9 m_unshare.9 \ mbuf.9 M_WRITABLE.9 MLINKS+=\ mbuf_tags.9 m_tag_alloc.9 \ mbuf_tags.9 m_tag_copy.9 \ mbuf_tags.9 m_tag_copy_chain.9 \ mbuf_tags.9 m_tag_delete.9 \ mbuf_tags.9 m_tag_delete_chain.9 \ mbuf_tags.9 m_tag_delete_nonpersistent.9 \ mbuf_tags.9 m_tag_find.9 \ mbuf_tags.9 m_tag_first.9 \ mbuf_tags.9 m_tag_free.9 \ mbuf_tags.9 m_tag_get.9 \ mbuf_tags.9 m_tag_init.9 \ mbuf_tags.9 m_tag_locate.9 \ mbuf_tags.9 m_tag_next.9 \ mbuf_tags.9 m_tag_prepend.9 \ mbuf_tags.9 m_tag_unlink.9 MLINKS+=MD5.9 MD5Init.9 \ MD5.9 MD5Transform.9 MLINKS+=mdchain.9 md_append_record.9 \ mdchain.9 md_done.9 \ mdchain.9 md_get_int64.9 \ mdchain.9 md_get_int64be.9 \ mdchain.9 md_get_int64le.9 \ mdchain.9 md_get_mbuf.9 \ mdchain.9 md_get_mem.9 \ mdchain.9 md_get_uint16.9 \ mdchain.9 md_get_uint16be.9 \ mdchain.9 md_get_uint16le.9 \ mdchain.9 md_get_uint32.9 \ mdchain.9 md_get_uint32be.9 \ mdchain.9 md_get_uint32le.9 \ mdchain.9 md_get_uint8.9 \ mdchain.9 md_get_uio.9 \ mdchain.9 md_initm.9 \ mdchain.9 md_next_record.9 MLINKS+=microtime.9 bintime.9 \ microtime.9 getbintime.9 \ microtime.9 getmicrotime.9 \ microtime.9 getnanotime.9 \ microtime.9 nanotime.9 MLINKS+=microuptime.9 binuptime.9 \ microuptime.9 getbinuptime.9 \ microuptime.9 getmicrouptime.9 \ microuptime.9 getnanouptime.9 \ microuptime.9 getsbinuptime.9 \ microuptime.9 nanouptime.9 \ microuptime.9 sbinuptime.9 MLINKS+=mi_switch.9 cpu_switch.9 \ mi_switch.9 cpu_throw.9 MLINKS+=mod_cc.9 CCV.9 \ mod_cc.9 DECLARE_CC_MODULE.9 MLINKS+=mtx_pool.9 mtx_pool_alloc.9 \ mtx_pool.9 mtx_pool_create.9 \ mtx_pool.9 mtx_pool_destroy.9 \ mtx_pool.9 mtx_pool_find.9 \ mtx_pool.9 mtx_pool_lock.9 \ mtx_pool.9 mtx_pool_lock_spin.9 \ mtx_pool.9 mtx_pool_unlock.9 \ mtx_pool.9 mtx_pool_unlock_spin.9 MLINKS+=mutex.9 mtx_assert.9 \ mutex.9 mtx_destroy.9 \ mutex.9 mtx_init.9 \ mutex.9 mtx_initialized.9 \ mutex.9 mtx_lock.9 \ mutex.9 mtx_lock_flags.9 \ mutex.9 mtx_lock_spin.9 \ mutex.9 mtx_lock_spin_flags.9 \ mutex.9 mtx_owned.9 \ mutex.9 mtx_recursed.9 \ mutex.9 mtx_sleep.9 \ mutex.9 MTX_SYSINIT.9 \ mutex.9 mtx_trylock.9 \ mutex.9 mtx_trylock_flags.9 \ mutex.9 mtx_trylock_spin.9 \ mutex.9 mtx_trylock_spin_flags.9 \ mutex.9 mtx_unlock.9 \ mutex.9 mtx_unlock_flags.9 \ mutex.9 mtx_unlock_spin.9 \ mutex.9 mtx_unlock_spin_flags.9 MLINKS+=namei.9 NDFREE.9 \ namei.9 NDINIT.9 MLINKS+=netisr.9 netisr_clearqdrops.9 \ netisr.9 netisr_default_flow2cpu.9 \ netisr.9 netisr_dispatch.9 \ netisr.9 netisr_dispatch_src.9 \ netisr.9 netisr_get_cpucount.9 \ netisr.9 netisr_get_cpuid.9 \ netisr.9 netisr_getqdrops.9 \ netisr.9 netisr_getqlimit.9 \ netisr.9 netisr_queue.9 \ netisr.9 netisr_queue_src.9 \ netisr.9 netisr_register.9 \ netisr.9 netisr_setqlimit.9 \ netisr.9 netisr_unregister.9 MLINKS+=nv.9 libnv.9 \ nv.9 nvlist.9 \ nv.9 nvlist_add_binary.9 \ nv.9 nvlist_add_bool.9 \ nv.9 nvlist_add_bool_array.9 \ nv.9 nvlist_add_descriptor.9 \ nv.9 nvlist_add_descriptor_array.9 \ nv.9 nvlist_add_null.9 \ nv.9 nvlist_add_number.9 \ nv.9 nvlist_add_number_array.9 \ nv.9 nvlist_add_nvlist.9 \ nv.9 nvlist_add_nvlist_array.9 \ nv.9 nvlist_add_string.9 \ nv.9 nvlist_add_stringf.9 \ nv.9 nvlist_add_stringv.9 \ nv.9 nvlist_add_string_array.9 \ nv.9 nvlist_clone.9 \ nv.9 nvlist_create.9 \ nv.9 nvlist_destroy.9 \ nv.9 nvlist_dump.9 \ nv.9 nvlist_empty.9 \ nv.9 nvlist_error.9 \ nv.9 nvlist_exists.9 \ nv.9 nvlist_exists_binary.9 \ nv.9 nvlist_exists_bool.9 \ nv.9 nvlist_exists_bool_array.9 \ nv.9 nvlist_exists_descriptor.9 \ nv.9 nvlist_exists_descriptor_array.9 \ nv.9 nvlist_exists_null.9 \ nv.9 nvlist_exists_number.9 \ nv.9 nvlist_exists_number_array.9 \ nv.9 nvlist_exists_nvlist.9 \ nv.9 nvlist_exists_nvlist_array.9 \ nv.9 nvlist_exists_string.9 \ nv.9 nvlist_exists_type.9 \ nv.9 nvlist_fdump.9 \ nv.9 nvlist_flags.9 \ nv.9 nvlist_free.9 \ nv.9 nvlist_free_binary.9 \ nv.9 nvlist_free_bool.9 \ nv.9 nvlist_free_bool_array.9 \ nv.9 nvlist_free_descriptor.9 \ nv.9 nvlist_free_descriptor_array.9 \ nv.9 nvlist_free_null.9 \ nv.9 nvlist_free_number.9 \ nv.9 nvlist_free_number_array.9 \ nv.9 nvlist_free_nvlist.9 \ nv.9 nvlist_free_nvlist_array.9 \ nv.9 nvlist_free_string.9 \ nv.9 nvlist_free_string_array.9 \ nv.9 nvlist_free_type.9 \ nv.9 nvlist_get_binary.9 \ nv.9 nvlist_get_bool.9 \ nv.9 nvlist_get_bool_array.9 \ nv.9 nvlist_get_descriptor.9 \ nv.9 nvlist_get_descriptor_array.9 \ nv.9 nvlist_get_number.9 \ nv.9 nvlist_get_number_array.9 \ nv.9 nvlist_get_nvlist.9 \ nv.9 nvlist_get_nvlist_array.9 \ nv.9 nvlist_get_parent.9 \ nv.9 nvlist_get_string.9 \ nv.9 nvlist_get_string_array.9 \ nv.9 nvlist_move_binary.9 \ nv.9 nvlist_move_descriptor.9 \ nv.9 nvlist_move_descriptor_array.9 \ nv.9 nvlist_move_nvlist.9 \ nv.9 nvlist_move_nvlist_array.9 \ nv.9 nvlist_move_string.9 \ nv.9 nvlist_move_string_array.9 \ nv.9 nvlist_next.9 \ nv.9 nvlist_pack.9 \ nv.9 nvlist_recv.9 \ nv.9 nvlist_send.9 \ nv.9 nvlist_set_error.9 \ nv.9 nvlist_size.9 \ nv.9 nvlist_take_binary.9 \ nv.9 nvlist_take_bool.9 \ nv.9 nvlist_take_bool_array.9 \ nv.9 nvlist_take_descriptor.9 \ nv.9 nvlist_take_descriptor_array.9 \ nv.9 nvlist_take_number.9 \ nv.9 nvlist_take_number_array.9 \ nv.9 nvlist_take_nvlist.9 \ nv.9 nvlist_take_nvlist_array.9 \ nv.9 nvlist_take_string.9 \ nv.9 nvlist_take_string_array.9 \ nv.9 nvlist_unpack.9 \ nv.9 nvlist_xfer.9 MLINKS+=OF_child.9 OF_parent.9 \ OF_child.9 OF_peer.9 MLINKS+=OF_device_from_xref.9 OF_device_register_xref.9 \ OF_device_from_xref.9 OF_xref_from_device.9 MLINKS+=OF_getprop.9 OF_getencprop.9 \ OF_getprop.9 OF_getencprop_alloc.9 \ OF_getprop.9 OF_getencprop_alloc_multi.9 \ OF_getprop.9 OF_getprop_alloc.9 \ OF_getprop.9 OF_getprop_alloc_multi.9 \ OF_getprop.9 OF_getproplen.9 \ OF_getprop.9 OF_hasprop.9 \ OF_getprop.9 OF_nextprop.9 \ OF_getprop.9 OF_prop_free.9 \ OF_getprop.9 OF_searchencprop.9 \ OF_getprop.9 OF_searchprop.9 \ OF_getprop.9 OF_setprop.9 MLINKS+=OF_node_from_xref.9 OF_xref_from_node.9 MLINKS+=ofw_bus_is_compatible.9 ofw_bus_is_compatible_strict.9 \ ofw_bus_is_compatible.9 ofw_bus_node_is_compatible.9 \ ofw_bus_is_compatible.9 ofw_bus_search_compatible.9 MLINKS+= ofw_bus_status_okay.9 ofw_bus_get_status.9 \ ofw_bus_status_okay.9 ofw_bus_node_status_okay.9 MLINKS+=osd.9 osd_call.9 \ osd.9 osd_del.9 \ osd.9 osd_deregister.9 \ osd.9 osd_exit.9 \ osd.9 osd_get.9 \ osd.9 osd_register.9 \ osd.9 osd_set.9 MLINKS+=panic.9 vpanic.9 MLINKS+=PCBGROUP.9 in_pcbgroup_byhash.9 \ PCBGROUP.9 in_pcbgroup_byinpcb.9 \ PCBGROUP.9 in_pcbgroup_destroy.9 \ PCBGROUP.9 in_pcbgroup_enabled.9 \ PCBGROUP.9 in_pcbgroup_init.9 \ PCBGROUP.9 in_pcbgroup_remove.9 \ PCBGROUP.9 in_pcbgroup_update.9 \ PCBGROUP.9 in_pcbgroup_update_mbuf.9 \ PCBGROUP.9 in6_pcbgroup_byhash.9 MLINKS+=pci.9 pci_alloc_msi.9 \ pci.9 pci_alloc_msix.9 \ pci.9 pci_disable_busmaster.9 \ pci.9 pci_disable_io.9 \ pci.9 pci_enable_busmaster.9 \ pci.9 pci_enable_io.9 \ pci.9 pci_find_bsf.9 \ pci.9 pci_find_cap.9 \ pci.9 pci_find_dbsf.9 \ pci.9 pci_find_device.9 \ pci.9 pci_find_extcap.9 \ pci.9 pci_find_htcap.9 \ pci.9 pci_find_pcie_root_port.9 \ pci.9 pci_get_id.9 \ pci.9 pci_get_max_read_req.9 \ pci.9 pci_get_powerstate.9 \ pci.9 pci_get_vpd_ident.9 \ pci.9 pci_get_vpd_readonly.9 \ pci.9 pci_iov_attach.9 \ pci.9 pci_iov_attach_name.9 \ pci.9 pci_iov_detach.9 \ pci.9 pci_msi_count.9 \ pci.9 pci_msix_count.9 \ pci.9 pci_msix_pba_bar.9 \ pci.9 pci_msix_table_bar.9 \ pci.9 pci_pending_msix.9 \ pci.9 pci_read_config.9 \ pci.9 pci_release_msi.9 \ pci.9 pci_remap_msix.9 \ pci.9 pci_restore_state.9 \ pci.9 pci_save_state.9 \ pci.9 pci_set_powerstate.9 \ pci.9 pci_set_max_read_req.9 \ pci.9 pci_write_config.9 \ pci.9 pcie_adjust_config.9 \ pci.9 pcie_flr.9 \ pci.9 pcie_max_completion_timeout.9 \ pci.9 pcie_read_config.9 \ pci.9 pcie_wait_for_pending_transactions.9 \ pci.9 pcie_write_config.9 MLINKS+=pci_iov_schema.9 pci_iov_schema_alloc_node.9 \ pci_iov_schema.9 pci_iov_schema_add_bool.9 \ pci_iov_schema.9 pci_iov_schema_add_string.9 \ pci_iov_schema.9 pci_iov_schema_add_uint8.9 \ pci_iov_schema.9 pci_iov_schema_add_uint16.9 \ pci_iov_schema.9 pci_iov_schema_add_uint32.9 \ pci_iov_schema.9 pci_iov_schema_add_uint64.9 \ pci_iov_schema.9 pci_iov_schema_add_unicast_mac.9 MLINKS+=pfil.9 pfil_add_hook.9 \ pfil.9 pfil_head_register.9 \ pfil.9 pfil_head_unregister.9 \ pfil.9 pfil_remove_hook.9 \ pfil.9 pfil_run_hooks.9 \ pfil.9 pfil_link.9 MLINKS+=pfind.9 zpfind.9 MLINKS+=PHOLD.9 PRELE.9 \ PHOLD.9 _PHOLD.9 \ PHOLD.9 _PRELE.9 \ PHOLD.9 PROC_ASSERT_HELD.9 \ PHOLD.9 PROC_ASSERT_NOT_HELD.9 MLINKS+=pmap_copy.9 pmap_copy_page.9 MLINKS+=pmap_extract.9 pmap_extract_and_hold.9 MLINKS+=pmap_init.9 pmap_init2.9 MLINKS+=pmap_is_modified.9 pmap_ts_referenced.9 MLINKS+=pmap_pinit.9 pmap_pinit0.9 \ pmap_pinit.9 pmap_pinit2.9 MLINKS+=pmap_qenter.9 pmap_qremove.9 MLINKS+=pmap_quick_enter_page.9 pmap_quick_remove_page.9 MLINKS+=pmap_remove.9 pmap_remove_all.9 \ pmap_remove.9 pmap_remove_pages.9 MLINKS+=pmap_resident_count.9 pmap_wired_count.9 MLINKS+=pmap_zero_page.9 pmap_zero_area.9 MLINKS+=printf.9 log.9 \ printf.9 tprintf.9 \ printf.9 uprintf.9 MLINKS+=priv.9 priv_check.9 \ priv.9 priv_check_cred.9 MLINKS+=proc_rwmem.9 proc_readmem.9 \ proc_rwmem.9 proc_writemem.9 MLINKS+=psignal.9 gsignal.9 \ psignal.9 pgsignal.9 \ psignal.9 tdsignal.9 MLINKS+=random.9 arc4rand.9 \ random.9 arc4random.9 \ random.9 is_random_seeded.9 \ random.9 read_random.9 \ random.9 read_random_uio.9 \ random.9 srandom.9 MLINKS+=random_harvest.9 random_harvest_direct.9 \ random_harvest.9 random_harvest_fast.9 \ random_harvest.9 random_harvest_queue.9 MLINKS+=ratecheck.9 ppsratecheck.9 MLINKS+=refcount.9 refcount_acquire.9 \ refcount.9 refcount_init.9 \ refcount.9 refcount_release.9 MLINKS+=resource_int_value.9 resource_long_value.9 \ resource_int_value.9 resource_string_value.9 MLINKS+=rman.9 rman_activate_resource.9 \ rman.9 rman_adjust_resource.9 \ rman.9 rman_deactivate_resource.9 \ rman.9 rman_fini.9 \ rman.9 rman_first_free_region.9 \ rman.9 rman_get_bushandle.9 \ rman.9 rman_get_bustag.9 \ rman.9 rman_get_device.9 \ rman.9 rman_get_end.9 \ rman.9 rman_get_flags.9 \ rman.9 rman_get_mapping.9 \ rman.9 rman_get_rid.9 \ rman.9 rman_get_size.9 \ rman.9 rman_get_start.9 \ rman.9 rman_get_virtual.9 \ rman.9 rman_init.9 \ rman.9 rman_init_from_resource.9 \ rman.9 rman_is_region_manager.9 \ rman.9 rman_last_free_region.9 \ rman.9 rman_make_alignment_flags.9 \ rman.9 rman_manage_region.9 \ rman.9 rman_release_resource.9 \ rman.9 rman_reserve_resource.9 \ rman.9 rman_reserve_resource_bound.9 \ rman.9 rman_set_bushandle.9 \ rman.9 rman_set_bustag.9 \ rman.9 rman_set_mapping.9 \ rman.9 rman_set_rid.9 \ rman.9 rman_set_virtual.9 MLINKS+=rmlock.9 rm_assert.9 \ rmlock.9 rm_destroy.9 \ rmlock.9 rm_init.9 \ rmlock.9 rm_init_flags.9 \ rmlock.9 rm_rlock.9 \ rmlock.9 rm_runlock.9 \ rmlock.9 rm_sleep.9 \ rmlock.9 RM_SYSINIT.9 \ rmlock.9 RM_SYSINIT_FLAGS.9 \ rmlock.9 rm_try_rlock.9 \ rmlock.9 rm_wlock.9 \ rmlock.9 rm_wowned.9 \ rmlock.9 rm_wunlock.9 MLINKS+=rtalloc.9 rtalloc1.9 \ rtalloc.9 rtalloc_ign.9 \ rtalloc.9 RT_ADDREF.9 \ rtalloc.9 RT_LOCK.9 \ rtalloc.9 RT_REMREF.9 \ rtalloc.9 RT_RTFREE.9 \ rtalloc.9 RT_UNLOCK.9 \ rtalloc.9 RTFREE_LOCKED.9 \ rtalloc.9 RTFREE.9 \ rtalloc.9 rtfree.9 \ rtalloc.9 rtalloc1_fib.9 \ rtalloc.9 rtalloc_ign_fib.9 \ rtalloc.9 rtalloc_fib.9 MLINKS+=runqueue.9 choosethread.9 \ runqueue.9 procrunnable.9 \ runqueue.9 remrunqueue.9 \ runqueue.9 setrunqueue.9 MLINKS+=rwlock.9 rw_assert.9 \ rwlock.9 rw_destroy.9 \ rwlock.9 rw_downgrade.9 \ rwlock.9 rw_init.9 \ rwlock.9 rw_init_flags.9 \ rwlock.9 rw_initialized.9 \ rwlock.9 rw_rlock.9 \ rwlock.9 rw_runlock.9 \ rwlock.9 rw_unlock.9 \ rwlock.9 rw_sleep.9 \ rwlock.9 RW_SYSINIT.9 \ rwlock.9 RW_SYSINIT_FLAGS.9 \ rwlock.9 rw_try_rlock.9 \ rwlock.9 rw_try_upgrade.9 \ rwlock.9 rw_try_wlock.9 \ rwlock.9 rw_wlock.9 \ rwlock.9 rw_wowned.9 \ rwlock.9 rw_wunlock.9 MLINKS+=sbuf.9 sbuf_bcat.9 \ sbuf.9 sbuf_bcopyin.9 \ sbuf.9 sbuf_bcpy.9 \ sbuf.9 sbuf_cat.9 \ sbuf.9 sbuf_clear.9 \ sbuf.9 sbuf_clear_flags.9 \ sbuf.9 sbuf_copyin.9 \ sbuf.9 sbuf_cpy.9 \ sbuf.9 sbuf_data.9 \ sbuf.9 sbuf_delete.9 \ sbuf.9 sbuf_done.9 \ sbuf.9 sbuf_error.9 \ sbuf.9 sbuf_finish.9 \ sbuf.9 sbuf_get_flags.9 \ sbuf.9 sbuf_hexdump.9 \ sbuf.9 sbuf_len.9 \ sbuf.9 sbuf_new.9 \ sbuf.9 sbuf_new_auto.9 \ sbuf.9 sbuf_new_for_sysctl.9 \ sbuf.9 sbuf_printf.9 \ sbuf.9 sbuf_printf_drain.9 \ sbuf.9 sbuf_putbuf.9 \ sbuf.9 sbuf_putc.9 \ sbuf.9 sbuf_set_drain.9 \ sbuf.9 sbuf_set_flags.9 \ sbuf.9 sbuf_setpos.9 \ sbuf.9 sbuf_start_section.9 \ sbuf.9 sbuf_end_section.9 \ sbuf.9 sbuf_trim.9 \ sbuf.9 sbuf_vprintf.9 MLINKS+=scheduler.9 curpriority_cmp.9 \ scheduler.9 maybe_resched.9 \ scheduler.9 propagate_priority.9 \ scheduler.9 resetpriority.9 \ scheduler.9 roundrobin.9 \ scheduler.9 roundrobin_interval.9 \ scheduler.9 schedclock.9 \ scheduler.9 schedcpu.9 \ scheduler.9 sched_setup.9 \ scheduler.9 setrunnable.9 \ scheduler.9 updatepri.9 MLINKS+=SDT.9 SDT_PROVIDER_DECLARE.9 \ SDT.9 SDT_PROVIDER_DEFINE.9 \ SDT.9 SDT_PROBE_DECLARE.9 \ SDT.9 SDT_PROBE_DEFINE.9 \ SDT.9 SDT_PROBE.9 MLINKS+=securelevel_gt.9 securelevel_ge.9 MLINKS+=selrecord.9 seldrain.9 \ selrecord.9 selwakeup.9 MLINKS+=sema.9 sema_destroy.9 \ sema.9 sema_init.9 \ sema.9 sema_post.9 \ sema.9 sema_timedwait.9 \ sema.9 sema_trywait.9 \ sema.9 sema_value.9 \ sema.9 sema_wait.9 MLINKS+=sf_buf.9 sf_buf_alloc.9 \ sf_buf.9 sf_buf_free.9 \ sf_buf.9 sf_buf_kva.9 \ sf_buf.9 sf_buf_page.9 MLINKS+=sglist.9 sglist_alloc.9 \ sglist.9 sglist_append.9 \ sglist.9 sglist_append_bio.9 \ sglist.9 sglist_append_mbuf.9 \ sglist.9 sglist_append_phys.9 \ sglist.9 sglist_append_sglist.9 \ sglist.9 sglist_append_uio.9 \ sglist.9 sglist_append_user.9 \ sglist.9 sglist_append_vmpages.9 \ sglist.9 sglist_build.9 \ sglist.9 sglist_clone.9 \ sglist.9 sglist_consume_uio.9 \ sglist.9 sglist_count.9 \ sglist.9 sglist_count_vmpages.9 \ sglist.9 sglist_free.9 \ sglist.9 sglist_hold.9 \ sglist.9 sglist_init.9 \ sglist.9 sglist_join.9 \ sglist.9 sglist_length.9 \ sglist.9 sglist_reset.9 \ sglist.9 sglist_slice.9 \ sglist.9 sglist_split.9 MLINKS+=shm_map.9 shm_unmap.9 MLINKS+=signal.9 cursig.9 \ signal.9 execsigs.9 \ signal.9 issignal.9 \ signal.9 killproc.9 \ signal.9 pgsigio.9 \ signal.9 postsig.9 \ signal.9 SETSETNEQ.9 \ signal.9 SETSETOR.9 \ signal.9 SIGADDSET.9 \ signal.9 SIG_CONTSIGMASK.9 \ signal.9 SIGDELSET.9 \ signal.9 SIGEMPTYSET.9 \ signal.9 sigexit.9 \ signal.9 SIGFILLSET.9 \ signal.9 siginit.9 \ signal.9 SIGISEMPTY.9 \ signal.9 SIGISMEMBER.9 \ signal.9 SIGNOTEMPTY.9 \ signal.9 signotify.9 \ signal.9 SIGPENDING.9 \ signal.9 SIGSETAND.9 \ signal.9 SIGSETCANTMASK.9 \ signal.9 SIGSETEQ.9 \ signal.9 SIGSETNAND.9 \ signal.9 SIG_STOPSIGMASK.9 \ signal.9 trapsignal.9 MLINKS+=sleep.9 msleep.9 \ sleep.9 msleep_sbt.9 \ sleep.9 msleep_spin.9 \ sleep.9 msleep_spin_sbt.9 \ sleep.9 pause.9 \ sleep.9 pause_sig.9 \ sleep.9 pause_sbt.9 \ sleep.9 tsleep.9 \ sleep.9 tsleep_sbt.9 \ sleep.9 wakeup.9 \ sleep.9 wakeup_one.9 MLINKS+=sleepqueue.9 init_sleepqueues.9 \ sleepqueue.9 sleepq_abort.9 \ sleepqueue.9 sleepq_add.9 \ sleepqueue.9 sleepq_alloc.9 \ sleepqueue.9 sleepq_broadcast.9 \ sleepqueue.9 sleepq_free.9 \ sleepqueue.9 sleepq_lookup.9 \ sleepqueue.9 sleepq_lock.9 \ sleepqueue.9 sleepq_release.9 \ sleepqueue.9 sleepq_remove.9 \ sleepqueue.9 sleepq_set_timeout.9 \ sleepqueue.9 sleepq_set_timeout_sbt.9 \ sleepqueue.9 sleepq_signal.9 \ sleepqueue.9 sleepq_sleepcnt.9 \ sleepqueue.9 sleepq_timedwait.9 \ sleepqueue.9 sleepq_timedwait_sig.9 \ sleepqueue.9 sleepq_type.9 \ sleepqueue.9 sleepq_wait.9 \ sleepqueue.9 sleepq_wait_sig.9 MLINKS+=socket.9 soabort.9 \ socket.9 soaccept.9 \ socket.9 sobind.9 \ socket.9 socheckuid.9 \ socket.9 soclose.9 \ socket.9 soconnect.9 \ socket.9 socreate.9 \ socket.9 sodisconnect.9 \ socket.9 sodtor_set.9 \ socket.9 sodupsockaddr.9 \ socket.9 sofree.9 \ socket.9 sogetopt.9 \ socket.9 sohasoutofband.9 \ socket.9 solisten.9 \ socket.9 solisten_proto.9 \ socket.9 solisten_proto_check.9 \ socket.9 sonewconn.9 \ socket.9 sooptcopyin.9 \ socket.9 sooptcopyout.9 \ socket.9 sopoll.9 \ socket.9 sopoll_generic.9 \ socket.9 soreceive.9 \ socket.9 soreceive_dgram.9 \ socket.9 soreceive_generic.9 \ socket.9 soreceive_stream.9 \ socket.9 soreserve.9 \ socket.9 sorflush.9 \ socket.9 sosend.9 \ socket.9 sosend_dgram.9 \ socket.9 sosend_generic.9 \ socket.9 sosetopt.9 \ socket.9 soshutdown.9 \ socket.9 sotoxsocket.9 \ socket.9 soupcall_clear.9 \ socket.9 soupcall_set.9 \ socket.9 sowakeup.9 MLINKS+=stack.9 stack_copy.9 \ stack.9 stack_create.9 \ stack.9 stack_destroy.9 \ stack.9 stack_print.9 \ stack.9 stack_print_ddb.9 \ stack.9 stack_print_short.9 \ stack.9 stack_print_short_ddb.9 \ stack.9 stack_put.9 \ stack.9 stack_save.9 \ stack.9 stack_sbuf_print.9 \ stack.9 stack_sbuf_print_ddb.9 \ stack.9 stack_zero.9 MLINKS+=store.9 subyte.9 \ store.9 suword.9 \ store.9 suword16.9 \ store.9 suword32.9 \ store.9 suword64.9 MLINKS+=swi.9 swi_add.9 \ swi.9 swi_remove.9 \ swi.9 swi_sched.9 MLINKS+=sx.9 sx_assert.9 \ sx.9 sx_destroy.9 \ sx.9 sx_downgrade.9 \ sx.9 sx_init.9 \ sx.9 sx_init_flags.9 \ sx.9 sx_sleep.9 \ sx.9 sx_slock.9 \ sx.9 sx_slock_sig.9 \ sx.9 sx_sunlock.9 \ sx.9 SX_SYSINIT.9 \ sx.9 SX_SYSINIT_FLAGS.9 \ sx.9 sx_try_slock.9 \ sx.9 sx_try_upgrade.9 \ sx.9 sx_try_xlock.9 \ sx.9 sx_unlock.9 \ sx.9 sx_xholder.9 \ sx.9 sx_xlock.9 \ sx.9 sx_xlock_sig.9 \ sx.9 sx_xlocked.9 \ sx.9 sx_xunlock.9 MLINKS+=syscall_helper_register.9 syscall_helper_unregister.9 \ syscall_helper_register.9 SYSCALL_INIT_HELPER.9 \ syscall_helper_register.9 SYSCALL_INIT_HELPER_COMPAT.9 \ syscall_helper_register.9 SYSCALL_INIT_HELPER_COMPAT_F.9 \ syscall_helper_register.9 SYSCALL_INIT_HELPER_F.9 MLINKS+=sysctl.9 SYSCTL_DECL.9 \ sysctl.9 SYSCTL_ADD_INT.9 \ sysctl.9 SYSCTL_ADD_LONG.9 \ sysctl.9 SYSCTL_ADD_NODE.9 \ sysctl.9 SYSCTL_ADD_NODE_WITH_LABEL.9 \ sysctl.9 SYSCTL_ADD_OPAQUE.9 \ sysctl.9 SYSCTL_ADD_PROC.9 \ sysctl.9 SYSCTL_ADD_QUAD.9 \ sysctl.9 SYSCTL_ADD_ROOT_NODE.9 \ sysctl.9 SYSCTL_ADD_S8.9 \ sysctl.9 SYSCTL_ADD_S16.9 \ sysctl.9 SYSCTL_ADD_S32.9 \ sysctl.9 SYSCTL_ADD_S64.9 \ sysctl.9 SYSCTL_ADD_STRING.9 \ sysctl.9 SYSCTL_ADD_STRUCT.9 \ sysctl.9 SYSCTL_ADD_U8.9 \ sysctl.9 SYSCTL_ADD_U16.9 \ sysctl.9 SYSCTL_ADD_U32.9 \ sysctl.9 SYSCTL_ADD_U64.9 \ sysctl.9 SYSCTL_ADD_UAUTO.9 \ sysctl.9 SYSCTL_ADD_UINT.9 \ sysctl.9 SYSCTL_ADD_ULONG.9 \ sysctl.9 SYSCTL_ADD_UQUAD.9 \ sysctl.9 SYSCTL_CHILDREN.9 \ sysctl.9 SYSCTL_STATIC_CHILDREN.9 \ sysctl.9 SYSCTL_NODE_CHILDREN.9 \ sysctl.9 SYSCTL_PARENT.9 \ sysctl.9 SYSCTL_INT.9 \ sysctl.9 SYSCTL_INT_WITH_LABEL.9 \ sysctl.9 SYSCTL_LONG.9 \ sysctl.9 SYSCTL_NODE.9 \ sysctl.9 SYSCTL_NODE_WITH_LABEL.9 \ sysctl.9 SYSCTL_OPAQUE.9 \ sysctl.9 SYSCTL_PROC.9 \ sysctl.9 SYSCTL_QUAD.9 \ sysctl.9 SYSCTL_ROOT_NODE.9 \ sysctl.9 SYSCTL_S8.9 \ sysctl.9 SYSCTL_S16.9 \ sysctl.9 SYSCTL_S32.9 \ sysctl.9 SYSCTL_S64.9 \ sysctl.9 SYSCTL_STRING.9 \ sysctl.9 SYSCTL_STRUCT.9 \ sysctl.9 SYSCTL_U8.9 \ sysctl.9 SYSCTL_U16.9 \ sysctl.9 SYSCTL_U32.9 \ sysctl.9 SYSCTL_U64.9 \ sysctl.9 SYSCTL_UINT.9 \ sysctl.9 SYSCTL_ULONG.9 \ sysctl.9 SYSCTL_UQUAD.9 MLINKS+=sysctl_add_oid.9 sysctl_move_oid.9 \ sysctl_add_oid.9 sysctl_remove_oid.9 \ sysctl_add_oid.9 sysctl_remove_name.9 MLINKS+=sysctl_ctx_init.9 sysctl_ctx_entry_add.9 \ sysctl_ctx_init.9 sysctl_ctx_entry_del.9 \ sysctl_ctx_init.9 sysctl_ctx_entry_find.9 \ sysctl_ctx_init.9 sysctl_ctx_free.9 MLINKS+=SYSINIT.9 SYSUNINIT.9 MLINKS+=taskqueue.9 TASK_INIT.9 \ taskqueue.9 TASK_INITIALIZER.9 \ taskqueue.9 taskqueue_block.9 \ taskqueue.9 taskqueue_cancel.9 \ taskqueue.9 taskqueue_cancel_timeout.9 \ taskqueue.9 taskqueue_create.9 \ taskqueue.9 taskqueue_create_fast.9 \ taskqueue.9 TASKQUEUE_DECLARE.9 \ taskqueue.9 TASKQUEUE_DEFINE.9 \ taskqueue.9 TASKQUEUE_DEFINE_THREAD.9 \ taskqueue.9 taskqueue_drain.9 \ taskqueue.9 taskqueue_drain_all.9 \ taskqueue.9 taskqueue_drain_timeout.9 \ taskqueue.9 taskqueue_enqueue.9 \ taskqueue.9 taskqueue_enqueue_timeout.9 \ taskqueue.9 TASKQUEUE_FAST_DEFINE.9 \ taskqueue.9 TASKQUEUE_FAST_DEFINE_THREAD.9 \ taskqueue.9 taskqueue_free.9 \ taskqueue.9 taskqueue_member.9 \ taskqueue.9 taskqueue_quiesce.9 \ taskqueue.9 taskqueue_run.9 \ taskqueue.9 taskqueue_set_callback.9 \ taskqueue.9 taskqueue_start_threads.9 \ taskqueue.9 taskqueue_start_threads_pinned.9 \ taskqueue.9 taskqueue_unblock.9 \ taskqueue.9 TIMEOUT_TASK_INIT.9 MLINKS+=tcp_functions.9 register_tcp_functions.9 \ tcp_functions.9 register_tcp_functions_as_name.9 \ tcp_functions.9 register_tcp_functions_as_names.9 \ tcp_functions.9 deregister_tcp_functions.9 MLINKS+=time.9 boottime.9 \ time.9 time_second.9 \ time.9 time_uptime.9 MLINKS+=timeout.9 callout.9 \ timeout.9 callout_active.9 \ timeout.9 callout_async_drain.9 \ timeout.9 callout_deactivate.9 \ timeout.9 callout_drain.9 \ timeout.9 callout_handle_init.9 \ timeout.9 callout_init.9 \ timeout.9 callout_init_mtx.9 \ timeout.9 callout_init_rm.9 \ timeout.9 callout_init_rw.9 \ timeout.9 callout_pending.9 \ timeout.9 callout_reset.9 \ timeout.9 callout_reset_curcpu.9 \ timeout.9 callout_reset_on.9 \ timeout.9 callout_reset_sbt.9 \ timeout.9 callout_reset_sbt_curcpu.9 \ timeout.9 callout_reset_sbt_on.9 \ timeout.9 callout_schedule.9 \ timeout.9 callout_schedule_curcpu.9 \ timeout.9 callout_schedule_on.9 \ timeout.9 callout_schedule_sbt.9 \ timeout.9 callout_schedule_sbt_curcpu.9 \ timeout.9 callout_schedule_sbt_on.9 \ timeout.9 callout_stop.9 \ timeout.9 callout_when.9 \ timeout.9 untimeout.9 MLINKS+=ucred.9 crcopy.9 \ ucred.9 crcopysafe.9 \ ucred.9 crdup.9 \ ucred.9 crfree.9 \ ucred.9 crget.9 \ ucred.9 crhold.9 \ ucred.9 crsetgroups.9 \ ucred.9 cru2x.9 MLINKS+=uidinfo.9 uifind.9 \ uidinfo.9 uifree.9 \ uidinfo.9 uihashinit.9 \ uidinfo.9 uihold.9 MLINKS+=uio.9 uiomove.9 \ uio.9 uiomove_frombuf.9 \ uio.9 uiomove_nofault.9 .if ${MK_USB} != "no" MAN+= usbdi.9 MLINKS+=usbdi.9 usbd_do_request.9 \ usbdi.9 usbd_do_request_flags.9 \ usbdi.9 usbd_errstr.9 \ usbdi.9 usbd_lookup_id_by_info.9 \ usbdi.9 usbd_lookup_id_by_uaa.9 \ usbdi.9 usbd_transfer_clear_stall.9 \ usbdi.9 usbd_transfer_drain.9 \ usbdi.9 usbd_transfer_pending.9 \ usbdi.9 usbd_transfer_poll.9 \ usbdi.9 usbd_transfer_setup.9 \ usbdi.9 usbd_transfer_start.9 \ usbdi.9 usbd_transfer_stop.9 \ usbdi.9 usbd_transfer_submit.9 \ usbdi.9 usbd_transfer_unsetup.9 \ usbdi.9 usbd_xfer_clr_flag.9 \ usbdi.9 usbd_xfer_frame_data.9 \ usbdi.9 usbd_xfer_frame_len.9 \ usbdi.9 usbd_xfer_get_frame.9 \ usbdi.9 usbd_xfer_get_priv.9 \ usbdi.9 usbd_xfer_is_stalled.9 \ usbdi.9 usbd_xfer_max_framelen.9 \ usbdi.9 usbd_xfer_max_frames.9 \ usbdi.9 usbd_xfer_max_len.9 \ usbdi.9 usbd_xfer_set_flag.9 \ usbdi.9 usbd_xfer_set_frame_data.9 \ usbdi.9 usbd_xfer_set_frame_len.9 \ usbdi.9 usbd_xfer_set_frame_offset.9 \ usbdi.9 usbd_xfer_set_frames.9 \ usbdi.9 usbd_xfer_set_interval.9 \ usbdi.9 usbd_xfer_set_priv.9 \ usbdi.9 usbd_xfer_set_stall.9 \ usbdi.9 usbd_xfer_set_timeout.9 \ usbdi.9 usbd_xfer_softc.9 \ usbdi.9 usbd_xfer_state.9 \ usbdi.9 usbd_xfer_status.9 \ usbdi.9 usb_fifo_alloc_buffer.9 \ usbdi.9 usb_fifo_attach.9 \ usbdi.9 usb_fifo_detach.9 \ usbdi.9 usb_fifo_free_buffer.9 \ usbdi.9 usb_fifo_get_data.9 \ usbdi.9 usb_fifo_get_data_buffer.9 \ usbdi.9 usb_fifo_get_data_error.9 \ usbdi.9 usb_fifo_get_data_linear.9 \ usbdi.9 usb_fifo_put_bytes_max.9 \ usbdi.9 usb_fifo_put_data.9 \ usbdi.9 usb_fifo_put_data_buffer.9 \ usbdi.9 usb_fifo_put_data_error.9 \ usbdi.9 usb_fifo_put_data_linear.9 \ usbdi.9 usb_fifo_reset.9 \ usbdi.9 usb_fifo_softc.9 \ usbdi.9 usb_fifo_wakeup.9 .endif MLINKS+=vcount.9 count_dev.9 MLINKS+=vfsconf.9 vfs_modevent.9 \ vfsconf.9 vfs_register.9 \ vfsconf.9 vfs_unregister.9 MLINKS+=vfs_getopt.9 vfs_copyopt.9 \ vfs_getopt.9 vfs_filteropt.9 \ vfs_getopt.9 vfs_flagopt.9 \ vfs_getopt.9 vfs_getopts.9 \ vfs_getopt.9 vfs_scanopt.9 \ vfs_getopt.9 vfs_setopt.9 \ vfs_getopt.9 vfs_setopt_part.9 \ vfs_getopt.9 vfs_setopts.9 MLINKS+=vhold.9 vdrop.9 \ vhold.9 vdropl.9 \ vhold.9 vholdl.9 MLINKS+=vmem.9 vmem_add.9 \ vmem.9 vmem_alloc.9 \ vmem.9 vmem_create.9 \ vmem.9 vmem_destroy.9 \ vmem.9 vmem_free.9 \ vmem.9 vmem_xalloc.9 \ vmem.9 vmem_xfree.9 MLINKS+=vm_map_lock.9 vm_map_lock_downgrade.9 \ vm_map_lock.9 vm_map_lock_read.9 \ vm_map_lock.9 vm_map_lock_upgrade.9 \ vm_map_lock.9 vm_map_trylock.9 \ vm_map_lock.9 vm_map_trylock_read.9 \ vm_map_lock.9 vm_map_unlock.9 \ vm_map_lock.9 vm_map_unlock_read.9 MLINKS+=vm_map_lookup.9 vm_map_lookup_done.9 MLINKS+=vm_map_max.9 vm_map_min.9 \ vm_map_max.9 vm_map_pmap.9 MLINKS+=vm_map_stack.9 vm_map_growstack.9 MLINKS+=vm_map_wire.9 vm_map_unwire.9 MLINKS+=vm_page_bits.9 vm_page_clear_dirty.9 \ vm_page_bits.9 vm_page_dirty.9 \ vm_page_bits.9 vm_page_is_valid.9 \ vm_page_bits.9 vm_page_set_invalid.9 \ vm_page_bits.9 vm_page_set_validclean.9 \ vm_page_bits.9 vm_page_test_dirty.9 \ vm_page_bits.9 vm_page_undirty.9 \ vm_page_bits.9 vm_page_zero_invalid.9 MLINKS+=vm_page_busy.9 vm_page_busied.9 \ vm_page_busy.9 vm_page_busy_downgrade.9 \ vm_page_busy.9 vm_page_busy_sleep.9 \ vm_page_busy.9 vm_page_sbusied.9 \ vm_page_busy.9 vm_page_sbusy.9 \ vm_page_busy.9 vm_page_sleep_if_busy.9 \ vm_page_busy.9 vm_page_sunbusy.9 \ vm_page_busy.9 vm_page_trysbusy.9 \ vm_page_busy.9 vm_page_tryxbusy.9 \ vm_page_busy.9 vm_page_xbusied.9 \ vm_page_busy.9 vm_page_xbusy.9 \ vm_page_busy.9 vm_page_xunbusy.9 \ vm_page_busy.9 vm_page_assert_sbusied.9 \ vm_page_busy.9 vm_page_assert_unbusied.9 \ vm_page_busy.9 vm_page_assert_xbusied.9 MLINKS+=vm_page_aflag.9 vm_page_aflag_clear.9 \ vm_page_aflag.9 vm_page_aflag_set.9 \ vm_page_aflag.9 vm_page_reference.9 MLINKS+=vm_page_free.9 vm_page_free_toq.9 \ vm_page_free.9 vm_page_free_zero.9 \ vm_page_free.9 vm_page_try_to_free.9 MLINKS+=vm_page_hold.9 vm_page_unhold.9 MLINKS+=vm_page_insert.9 vm_page_remove.9 MLINKS+=vm_page_wire.9 vm_page_unwire.9 MLINKS+=VOP_ACCESS.9 VOP_ACCESSX.9 MLINKS+=VOP_ATTRIB.9 VOP_GETATTR.9 \ VOP_ATTRIB.9 VOP_SETATTR.9 MLINKS+=VOP_CREATE.9 VOP_MKDIR.9 \ VOP_CREATE.9 VOP_MKNOD.9 \ VOP_CREATE.9 VOP_SYMLINK.9 MLINKS+=VOP_FSYNC.9 VOP_FDATASYNC.9 MLINKS+=VOP_GETPAGES.9 VOP_PUTPAGES.9 MLINKS+=VOP_INACTIVE.9 VOP_RECLAIM.9 MLINKS+=VOP_LOCK.9 vn_lock.9 \ VOP_LOCK.9 VOP_ISLOCKED.9 \ VOP_LOCK.9 VOP_UNLOCK.9 MLINKS+=VOP_OPENCLOSE.9 VOP_CLOSE.9 \ VOP_OPENCLOSE.9 VOP_OPEN.9 MLINKS+=VOP_RDWR.9 VOP_READ.9 \ VOP_RDWR.9 VOP_WRITE.9 MLINKS+=VOP_REMOVE.9 VOP_RMDIR.9 MLINKS+=vnet.9 vimage.9 MLINKS+=vref.9 VREF.9 \ vref.9 vrefl.9 MLINKS+=vrele.9 vput.9 \ vrele.9 vunref.9 MLINKS+=vslock.9 vsunlock.9 MLINKS+=zone.9 uma.9 \ zone.9 uma_zalloc.9 \ zone.9 uma_zalloc_arg.9 \ zone.9 uma_zalloc_domain.9 \ zone.9 uma_zcreate.9 \ zone.9 uma_zdestroy.9 \ zone.9 uma_zfree.9 \ zone.9 uma_zfree_arg.9 \ zone.9 uma_zfree_domain.9 \ zone.9 uma_zone_get_cur.9 \ zone.9 uma_zone_get_max.9 \ zone.9 uma_zone_set_max.9 \ zone.9 uma_zone_set_warning.9 \ zone.9 uma_zone_set_maxaction.9 .include Index: user/ngie/bug-237403/stand/common/load_elf.c =================================================================== --- user/ngie/bug-237403/stand/common/load_elf.c (revision 348022) +++ user/ngie/bug-237403/stand/common/load_elf.c (revision 348023) @@ -1,1224 +1,1224 @@ /*- * 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; } *elf_file_t; 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); 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; } #endif return (0); error: if (ef->firstpage != NULL) { free(ef->firstpage); ef->firstpage = NULL; } if (ef->fd != -1) { 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) 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%08x\n", ehdr->e_entry); + 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%08x, va<->pa off %llx\n", - ehdr->e_entry, off); + 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, 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); 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); 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) { 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); 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); if (shdr == NULL) { err = ENOMEM; goto out; } /* Load shstrtab. */ shstrtab = alloc_pread(ef.fd, 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); 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, 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, 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) 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: user/ngie/bug-237403/stand/ofw/libofw/ofw_net.c =================================================================== --- user/ngie/bug-237403/stand/ofw/libofw/ofw_net.c (revision 348022) +++ user/ngie/bug-237403/stand/ofw/libofw/ofw_net.c (revision 348023) @@ -1,275 +1,275 @@ /*- * Copyright (c) 2000-2001 Benno Rice * 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 Benno Rice ``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 REGENTS 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 #include #include #include "openfirm.h" static int ofwn_probe(struct netif *, void *); static int ofwn_match(struct netif *, void *); static void ofwn_init(struct iodesc *, void *); static ssize_t ofwn_get(struct iodesc *, void **, time_t); static ssize_t ofwn_put(struct iodesc *, void *, size_t); static void ofwn_end(struct netif *); extern struct netif_stats ofwn_stats[]; struct netif_dif ofwn_ifs[] = { /* dif_unit dif_nsel dif_stats dif_private */ { 0, 1, &ofwn_stats[0], 0, }, }; struct netif_stats ofwn_stats[nitems(ofwn_ifs)]; struct netif_driver ofwnet = { "net", /* netif_bname */ ofwn_match, /* netif_match */ ofwn_probe, /* netif_probe */ ofwn_init, /* netif_init */ ofwn_get, /* netif_get */ ofwn_put, /* netif_put */ ofwn_end, /* netif_end */ ofwn_ifs, /* netif_ifs */ nitems(ofwn_ifs) /* netif_nifs */ }; static ihandle_t netinstance; static void *dmabuf; static int ofwn_match(struct netif *nif, void *machdep_hint) { return 1; } static int ofwn_probe(struct netif *nif, void *machdep_hint) { return 0; } static ssize_t ofwn_put(struct iodesc *desc, void *pkt, size_t len) { size_t sendlen; ssize_t rv; #if defined(NETIF_DEBUG) struct ether_header *eh; printf("netif_put: desc=0x%x pkt=0x%x len=%d\n", desc, pkt, len); eh = pkt; printf("dst: %s ", ether_sprintf(eh->ether_dhost)); printf("src: %s ", ether_sprintf(eh->ether_shost)); printf("type: 0x%x\n", eh->ether_type & 0xffff); #endif sendlen = len; if (sendlen < 60) { sendlen = 60; #if defined(NETIF_DEBUG) printf("netif_put: length padded to %d\n", sendlen); #endif } if (dmabuf) { bcopy(pkt, dmabuf, sendlen); pkt = dmabuf; } rv = OF_write(netinstance, pkt, len); #if defined(NETIF_DEBUG) printf("netif_put: OF_write returned %d\n", rv); #endif return rv; } static ssize_t ofwn_get(struct iodesc *desc, void **pkt, time_t timeout) { time_t t; ssize_t length; size_t len; char *buf, *ptr; #if defined(NETIF_DEBUG) printf("netif_get: pkt=%p, timeout=%d\n", pkt, timeout); #endif /* * We should read the "max-frame-size" int property instead, * but at this time the iodesc does not have mtu, so we will take * a small shortcut here. */ len = ETHER_MAX_LEN; buf = malloc(len + ETHER_ALIGN); if (buf == NULL) return (-1); ptr = buf + ETHER_ALIGN; t = getsecs(); do { length = OF_read(netinstance, ptr, len); } while ((length == -2 || length == 0) && (getsecs() - t < timeout)); #if defined(NETIF_DEBUG) printf("netif_get: received length=%d (%x)\n", length, length); #endif if (length < 12) { free(buf); return (-1); } #if defined(NETIF_VERBOSE_DEBUG) { char *ch = ptr; int i; for(i = 0; i < 96; i += 4) { printf("%02x%02x%02x%02x ", ch[i], ch[i+1], ch[i+2], ch[i+3]); } printf("\n"); } #endif #if defined(NETIF_DEBUG) { struct ether_header *eh = ptr; printf("dst: %s ", ether_sprintf(eh->ether_dhost)); printf("src: %s ", ether_sprintf(eh->ether_shost)); printf("type: 0x%x\n", eh->ether_type & 0xffff); } #endif *pkt = buf; return (length); } extern char *strchr(); static void ofwn_init(struct iodesc *desc, void *machdep_hint) { phandle_t netdev; char path[64]; char *ch; int pathlen; pathlen = OF_getprop(chosen, "bootpath", path, 64); if ((ch = strchr(path, ':')) != NULL) *ch = '\0'; netdev = OF_finddevice(path); #ifdef __sparc64__ if (OF_getprop(netdev, "mac-address", desc->myea, 6) == -1) #else if (OF_getprop(netdev, "local-mac-address", desc->myea, 6) == -1) #endif goto punt; printf("boot: ethernet address: %s\n", ether_sprintf(desc->myea)); if ((netinstance = OF_open(path)) == -1) { printf("Could not open network device.\n"); goto punt; } #if defined(NETIF_DEBUG) printf("ofwn_init: Open Firmware instance handle: %08x\n", netinstance); #endif #ifndef __sparc64__ dmabuf = NULL; if (OF_call_method("dma-alloc", netinstance, 1, 1, (64 * 1024), &dmabuf) < 0) { - printf("Failed to allocate DMA buffer (got %08x).\n", dmabuf); + printf("Failed to allocate DMA buffer (got %p).\n", dmabuf); goto punt; } #if defined(NETIF_DEBUG) - printf("ofwn_init: allocated DMA buffer: %08x\n", dmabuf); + printf("ofwn_init: allocated DMA buffer: %p\n", dmabuf); #endif #endif return; punt: printf("\n"); printf("Could not boot from %s.\n", path); OF_enter(); } static void ofwn_end(struct netif *nif) { #ifdef BROKEN /* dma-free freezes at least some Apple ethernet controllers */ OF_call_method("dma-free", netinstance, 2, 0, dmabuf, MAXPHYS); #endif OF_close(netinstance); } #if 0 int ofwn_getunit(const char *path) { int i; char newpath[255]; OF_canon(path, newpath, 254); for (i = 0; i < nofwninfo; i++) { printf(">>> test =\t%s\n", ofwninfo[i].ofwn_path); if (strcmp(path, ofwninfo[i].ofwn_path) == 0) return i; if (strcmp(newpath, ofwninfo[i].ofwn_path) == 0) return i; } return -1; } #endif Index: user/ngie/bug-237403/stand/ofw/libofw/openfirm.c =================================================================== --- user/ngie/bug-237403/stand/ofw/libofw/openfirm.c (revision 348022) +++ user/ngie/bug-237403/stand/ofw/libofw/openfirm.c (revision 348023) @@ -1,832 +1,831 @@ /* $NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $ */ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * 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 by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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. */ /*- * Copyright (C) 2000 Benno Rice. * 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 Benno Rice ``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 "openfirm.h" int (*openfirmware)(void *); phandle_t chosen; ihandle_t mmu; ihandle_t memory; int real_mode = 0; /* Initialiser */ void OF_init(int (*openfirm)(void *)) { phandle_t options; char mode[sizeof("true")]; openfirmware = openfirm; if ((chosen = OF_finddevice("/chosen")) == -1) OF_exit(); if (OF_getprop(chosen, "memory", &memory, sizeof(memory)) == -1) { memory = OF_open("/memory"); if (memory == -1) memory = OF_open("/memory@0"); if (memory == -1) OF_exit(); } if (OF_getprop(chosen, "mmu", &mmu, sizeof(mmu)) == -1) OF_exit(); /* * Check if we run in real mode. If so, we do not need to map * memory later on. */ options = OF_finddevice("/options"); if (OF_getprop(options, "real-mode?", mode, sizeof(mode)) > 0 && strcmp(mode, "true") == 0) real_mode = 1; } /* * Generic functions */ /* Test to see if a service exists. */ int OF_test(char *name) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t service; cell_t missing; } args = { (cell_t)"test", 1, 1, }; args.service = (cell_t)name; if (openfirmware(&args) == -1) return (-1); return (args.missing); } /* Return firmware millisecond count. */ int OF_milliseconds() { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t ms; } args = { (cell_t)"milliseconds", 0, 1, }; openfirmware(&args); return (args.ms); } /* * Device tree functions */ /* Return the next sibling of this node or 0. */ phandle_t OF_peer(phandle_t node) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t next; } args = { (cell_t)"peer", 1, 1, }; args.node = node; if (openfirmware(&args) == -1) return (-1); return (args.next); } /* Return the first child of this node or 0. */ phandle_t OF_child(phandle_t node) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t child; } args = { (cell_t)"child", 1, 1, }; args.node = node; if (openfirmware(&args) == -1) return (-1); return (args.child); } /* Return the parent of this node or 0. */ phandle_t OF_parent(phandle_t node) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t node; cell_t parent; } args = { (cell_t)"parent", 1, 1, }; args.node = node; if (openfirmware(&args) == -1) return (-1); return (args.parent); } /* Return the package handle that corresponds to an instance handle. */ phandle_t OF_instance_to_package(ihandle_t instance) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t package; } args = { (cell_t)"instance-to-package", 1, 1, }; args.instance = instance; if (openfirmware(&args) == -1) return (-1); return (args.package); } /* Get the length of a property of a package. */ int OF_getproplen(phandle_t package, const char *propname) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; cell_t proplen; } args = { (cell_t)"getproplen", 2, 1, }; args.package = package; args.propname = (cell_t)propname; if (openfirmware(&args) == -1) return (-1); return (args.proplen); } /* Get the value of a property of a package. */ int OF_getprop(phandle_t package, const char *propname, void *buf, int buflen) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; cell_t buf; cell_t buflen; cell_t size; } args = { (cell_t)"getprop", 4, 1, }; args.package = package; args.propname = (cell_t)propname; args.buf = (cell_t)buf; args.buflen = buflen; if (openfirmware(&args) == -1) return (-1); return (args.size); } /* Get the next property of a package. */ int OF_nextprop(phandle_t package, const char *previous, char *buf) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t previous; cell_t buf; cell_t flag; } args = { (cell_t)"nextprop", 3, 1, }; args.package = package; args.previous = (cell_t)previous; args.buf = (cell_t)buf; if (openfirmware(&args) == -1) return (-1); return (args.flag); } /* Set the value of a property of a package. */ /* XXX Has a bug on FirePower */ int OF_setprop(phandle_t package, const char *propname, void *buf, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t propname; cell_t buf; cell_t len; cell_t size; } args = { (cell_t)"setprop", 4, 1, }; args.package = package; args.propname = (cell_t)propname; args.buf = (cell_t)buf; args.len = len; if (openfirmware(&args) == -1) return (-1); return (args.size); } /* Convert a device specifier to a fully qualified pathname. */ int OF_canon(const char *device, char *buf, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t buf; cell_t len; cell_t size; } args = { (cell_t)"canon", 3, 1, }; args.device = (cell_t)device; args.buf = (cell_t)buf; args.len = len; if (openfirmware(&args) == -1) return (-1); return (args.size); } /* Return a package handle for the specified device. */ phandle_t OF_finddevice(const char *device) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t package; } args = { (cell_t)"finddevice", 1, 1, }; args.device = (cell_t)device; if (openfirmware(&args) == -1) return (-1); return (args.package); } /* Return the fully qualified pathname corresponding to an instance. */ int OF_instance_to_path(ihandle_t instance, char *buf, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t buf; cell_t len; cell_t size; } args = { (cell_t)"instance-to-path", 3, 1, }; args.instance = instance; args.buf = (cell_t)buf; args.len = len; if (openfirmware(&args) == -1) return (-1); return (args.size); } /* Return the fully qualified pathname corresponding to a package. */ int OF_package_to_path(phandle_t package, char *buf, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t package; cell_t buf; cell_t len; cell_t size; } args = { (cell_t)"package-to-path", 3, 1, }; args.package = package; args.buf = (cell_t)buf; args.len = len; if (openfirmware(&args) == -1) return (-1); return (args.size); } /* Call the method in the scope of a given instance. */ int OF_call_method(char *method, ihandle_t instance, int nargs, int nreturns, ...) { va_list ap; static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t method; cell_t instance; cell_t args_n_results[12]; } args = { (cell_t)"call-method", 2, 1, }; cell_t *cp; int n; if (nargs > 6) return (-1); args.nargs = nargs + 2; args.nreturns = nreturns + 1; args.method = (cell_t)method; args.instance = instance; va_start(ap, nreturns); for (cp = (cell_t *)(args.args_n_results + (n = nargs)); --n >= 0;) *--cp = va_arg(ap, cell_t); if (openfirmware(&args) == -1) return (-1); if (args.args_n_results[nargs]) return (args.args_n_results[nargs]); for (cp = (cell_t *)(args.args_n_results + nargs + (n = args.nreturns)); --n > 0;) *va_arg(ap, cell_t *) = *--cp; va_end(ap); return (0); } /* * Device I/O functions */ /* Open an instance for a device. */ ihandle_t OF_open(char *device) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t device; cell_t instance; } args = { (cell_t)"open", 1, 1, }; args.device = (cell_t)device; if (openfirmware(&args) == -1 || args.instance == 0) { return (-1); } return (args.instance); } /* Close an instance. */ void OF_close(ihandle_t instance) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; } args = { (cell_t)"close", 1, }; args.instance = instance; openfirmware(&args); } /* Read from an instance. */ int OF_read(ihandle_t instance, void *addr, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t addr; cell_t len; cell_t actual; } args = { (cell_t)"read", 3, 1, }; args.instance = instance; args.addr = (cell_t)addr; args.len = len; #if defined(OPENFIRM_DEBUG) printf("OF_read: called with instance=%08x, addr=%p, len=%d\n", args.instance, args.addr, args.len); #endif if (openfirmware(&args) == -1) return (-1); #if defined(OPENFIRM_DEBUG) printf("OF_read: returning instance=%d, addr=%p, len=%d, actual=%d\n", args.instance, args.addr, args.len, args.actual); #endif return (args.actual); } /* Write to an instance. */ int OF_write(ihandle_t instance, void *addr, int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t addr; cell_t len; cell_t actual; } args = { (cell_t)"write", 3, 1, }; args.instance = instance; args.addr = (cell_t)addr; args.len = len; if (openfirmware(&args) == -1) return (-1); return (args.actual); } /* Seek to a position. */ int OF_seek(ihandle_t instance, uint64_t pos) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t poshi; cell_t poslo; cell_t status; } args = { (cell_t)"seek", 3, 1, }; args.instance = instance; args.poshi = pos >> 32; args.poslo = pos; if (openfirmware(&args) == -1) return (-1); return (args.status); } /* Blocks. */ unsigned int OF_blocks(ihandle_t instance) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t result; cell_t blocks; } args = { (cell_t)"#blocks", 2, 1, }; args.instance = instance; if (openfirmware(&args) == -1) return ((unsigned int)-1); return (args.blocks); } /* Block size. */ int OF_block_size(ihandle_t instance) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t instance; cell_t result; cell_t size; } args = { (cell_t)"block-size", 2, 1, }; args.instance = instance; if (openfirmware(&args) == -1) return (512); return (args.size); } /* -/* * Memory functions */ /* Claim an area of memory. */ void * OF_claim(void *virt, u_int size, u_int align) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t virt; cell_t size; cell_t align; cell_t baseaddr; } args = { (cell_t)"claim", 3, 1, }; args.virt = (cell_t)virt; args.size = size; args.align = align; if (openfirmware(&args) == -1) return ((void *)-1); return ((void *)args.baseaddr); } /* Release an area of memory. */ void OF_release(void *virt, u_int size) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t virt; cell_t size; } args = { (cell_t)"release", 2, }; args.virt = (cell_t)virt; args.size = size; openfirmware(&args); } /* * Control transfer functions */ /* Reset the system and call "boot ". */ void OF_boot(char *bootspec) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t bootspec; } args = { (cell_t)"boot", 1, }; args.bootspec = (cell_t)bootspec; openfirmware(&args); for (;;) /* just in case */ ; } /* Suspend and drop back to the Open Firmware interface. */ void OF_enter() { static struct { cell_t name; cell_t nargs; cell_t nreturns; } args = { (cell_t)"enter", }; openfirmware(&args); /* We may come back. */ } /* Shut down and drop back to the Open Firmware interface. */ void OF_exit() { static struct { cell_t name; cell_t nargs; cell_t nreturns; } args = { (cell_t)"exit", }; openfirmware(&args); for (;;) /* just in case */ ; } void OF_quiesce() { static struct { cell_t name; cell_t nargs; cell_t nreturns; } args = { (cell_t)"quiesce", }; openfirmware(&args); } /* Free bytes starting at , then call with . */ #if 0 void OF_chain(void *virt, u_int size, void (*entry)(), void *arg, u_int len) { static struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t virt; cell_t size; cell_t entry; cell_t arg; cell_t len; } args = { (cell_t)"chain", 5, }; args.virt = (cell_t)virt; args.size = size; args.entry = (cell_t)entry; args.arg = (cell_t)arg; args.len = len; openfirmware(&args); } #else void OF_chain(void *virt, u_int size, void (*entry)(), void *arg, u_int len) { /* * This is a REALLY dirty hack till the firmware gets this going */ #if 0 if (size > 0) OF_release(virt, size); #endif entry(0, 0, openfirmware, arg, len); } #endif Index: user/ngie/bug-237403/stand/powerpc/boot1.chrp/boot1.c =================================================================== --- user/ngie/bug-237403/stand/powerpc/boot1.chrp/boot1.c (revision 348022) +++ user/ngie/bug-237403/stand/powerpc/boot1.chrp/boot1.c (revision 348023) @@ -1,776 +1,777 @@ /*- * Copyright (c) 1998 Robert Nordier * All rights reserved. * Copyright (c) 2001 Robert Drehmel * All rights reserved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this * paragraph and the following disclaimer are duplicated in all * such forms. * * This software is provided "AS IS" and without any express or * implied warranties, including, without limitation, the implied * warranties of merchantability and fitness for a particular * purpose. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include +#include #include "paths.h" #define BSIZEMAX 16384 typedef int putc_func_t(char c, void *arg); typedef int32_t ofwh_t; struct sp_data { char *sp_buf; u_int sp_len; u_int sp_size; }; static const char digits[] = "0123456789abcdef"; static char bootpath[128]; static char bootargs[128]; static ofwh_t bootdev; static struct fs fs; static char blkbuf[BSIZEMAX]; static unsigned int fsblks; static uint32_t fs_off; int main(int ac, char **av); static void exit(int) __dead2; static void load(const char *); static int dskread(void *, uint64_t, int); static void usage(void); static void bcopy(const void *src, void *dst, size_t len); static void bzero(void *b, size_t len); static int domount(const char *device, int quiet); static void panic(const char *fmt, ...) __dead2; static int printf(const char *fmt, ...); static int putchar(char c, void *arg); static int vprintf(const char *fmt, va_list ap); static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap); static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap); static int __putc(char c, void *arg); static int __puts(const char *s, putc_func_t *putc, void *arg); static int __sputc(char c, void *arg); static char *__uitoa(char *buf, u_int val, int base); static char *__ultoa(char *buf, u_long val, int base); /* * Open Firmware interface functions */ typedef uint32_t ofwcell_t; typedef uint32_t u_ofwh_t; typedef int (*ofwfp_t)(void *); ofwfp_t ofw; /* the prom Open Firmware entry */ ofwh_t chosenh; void ofw_init(void *, int, int (*)(void *), char *, int); static ofwh_t ofw_finddevice(const char *); static ofwh_t ofw_open(const char *); static int ofw_close(ofwh_t); static int ofw_getprop(ofwh_t, const char *, void *, size_t); static int ofw_setprop(ofwh_t, const char *, void *, size_t); static int ofw_read(ofwh_t, void *, size_t); static int ofw_write(ofwh_t, const void *, size_t); static int ofw_claim(void *virt, size_t len, u_int align); static int ofw_seek(ofwh_t, uint64_t); static void ofw_exit(void) __dead2; ofwh_t bootdevh; ofwh_t stdinh, stdouth; __asm(" \n\ .data \n\ .align 4 \n\ stack: \n\ .space 16384 \n\ \n\ .text \n\ .globl _start \n\ _start: \n\ lis %r1,stack@ha \n\ addi %r1,%r1,stack@l \n\ addi %r1,%r1,8192 \n\ \n\ b ofw_init \n\ "); void ofw_init(void *vpd, int res, int (*openfirm)(void *), char *arg, int argl) { char *av[16]; char *p; int ac; ofw = openfirm; chosenh = ofw_finddevice("/chosen"); ofw_getprop(chosenh, "stdin", &stdinh, sizeof(stdinh)); ofw_getprop(chosenh, "stdout", &stdouth, sizeof(stdouth)); ofw_getprop(chosenh, "bootargs", bootargs, sizeof(bootargs)); ofw_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath)); bootargs[sizeof(bootargs) - 1] = '\0'; bootpath[sizeof(bootpath) - 1] = '\0'; p = bootpath; while (*p != '\0') { /* Truncate partition ID */ if (*p == ':') { ofw_close(bootdev); *(++p) = '\0'; break; } p++; } ac = 0; p = bootargs; for (;;) { while (*p == ' ' && *p != '\0') p++; if (*p == '\0' || ac >= 16) break; av[ac++] = p; while (*p != ' ' && *p != '\0') p++; if (*p != '\0') *p++ = '\0'; } exit(main(ac, av)); } static ofwh_t ofw_finddevice(const char *name) { ofwcell_t args[] = { (ofwcell_t)"finddevice", 1, 1, (ofwcell_t)name, 0 }; if ((*ofw)(args)) { printf("ofw_finddevice: name=\"%s\"\n", name); return (1); } return (args[4]); } static int ofw_getprop(ofwh_t ofwh, const char *name, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"getprop", 4, 1, (u_ofwh_t)ofwh, (ofwcell_t)name, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_getprop: ofwh=0x%x buf=%p len=%u\n", ofwh, buf, len); return (1); } return (0); } static int ofw_setprop(ofwh_t ofwh, const char *name, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"setprop", 4, 1, (u_ofwh_t)ofwh, (ofwcell_t)name, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_setprop: ofwh=0x%x buf=%p len=%u\n", ofwh, buf, len); return (1); } return (0); } static ofwh_t ofw_open(const char *path) { ofwcell_t args[] = { (ofwcell_t)"open", 1, 1, (ofwcell_t)path, 0 }; if ((*ofw)(args)) { printf("ofw_open: path=\"%s\"\n", path); return (-1); } return (args[4]); } static int ofw_close(ofwh_t devh) { ofwcell_t args[] = { (ofwcell_t)"close", 1, 0, (u_ofwh_t)devh }; if ((*ofw)(args)) { printf("ofw_close: devh=0x%x\n", devh); return (1); } return (0); } static int ofw_claim(void *virt, size_t len, u_int align) { ofwcell_t args[] = { (ofwcell_t)"claim", 3, 1, (ofwcell_t)virt, len, align, 0, 0 }; if ((*ofw)(args)) { printf("ofw_claim: virt=%p len=%u\n", virt, len); return (1); } return (0); } static int ofw_read(ofwh_t devh, void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"read", 3, 1, (u_ofwh_t)devh, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_read: devh=0x%x buf=%p len=%u\n", devh, buf, len); return (1); } return (0); } static int ofw_write(ofwh_t devh, const void *buf, size_t len) { ofwcell_t args[] = { (ofwcell_t)"write", 3, 1, (u_ofwh_t)devh, (ofwcell_t)buf, len, 0 }; if ((*ofw)(args)) { printf("ofw_write: devh=0x%x buf=%p len=%u\n", devh, buf, len); return (1); } return (0); } static int ofw_seek(ofwh_t devh, uint64_t off) { ofwcell_t args[] = { (ofwcell_t)"seek", 3, 1, (u_ofwh_t)devh, off >> 32, off, 0 }; if ((*ofw)(args)) { printf("ofw_seek: devh=0x%x off=0x%lx\n", devh, off); return (1); } return (0); } static void ofw_exit(void) { ofwcell_t args[3]; args[0] = (ofwcell_t)"exit"; args[1] = 0; args[2] = 0; for (;;) (*ofw)(args); } static void bcopy(const void *src, void *dst, size_t len) { const char *s = src; char *d = dst; while (len-- != 0) *d++ = *s++; } static void memcpy(void *dst, const void *src, size_t len) { bcopy(src, dst, len); } static void bzero(void *b, size_t len) { char *p = b; while (len-- != 0) *p++ = 0; } static int strcmp(const char *s1, const char *s2) { for (; *s1 == *s2 && *s1; s1++, s2++) ; return ((u_char)*s1 - (u_char)*s2); } #include "ufsread.c" int main(int ac, char **av) { const char *path; char bootpath_full[255]; int i, len; path = PATH_LOADER; for (i = 0; i < ac; i++) { switch (av[i][0]) { case '-': switch (av[i][1]) { default: usage(); } break; default: path = av[i]; break; } } printf(" \n>> FreeBSD/powerpc Open Firmware boot block\n" " Boot path: %s\n" " Boot loader: %s\n", bootpath, path); len = 0; while (bootpath[len] != '\0') len++; memcpy(bootpath_full,bootpath,len+1); if (bootpath_full[len-1] != ':') { /* First try full volume */ if (domount(bootpath_full,1) == 0) goto out; /* Add a : so that we try partitions if that fails */ if (bootdev > 0) ofw_close(bootdev); bootpath_full[len] = ':'; len += 1; } /* Loop through first 16 partitions to find a UFS one */ for (i = 0; i < 16; i++) { if (i < 10) { bootpath_full[len] = i + '0'; bootpath_full[len+1] = '\0'; } else { bootpath_full[len] = '1'; bootpath_full[len+1] = i - 10 + '0'; bootpath_full[len+2] = '\0'; } if (domount(bootpath_full,1) >= 0) break; if (bootdev > 0) ofw_close(bootdev); } if (i >= 16) panic("domount"); out: printf(" Boot volume: %s\n",bootpath_full); ofw_setprop(chosenh, "bootargs", bootpath_full, len+2); load(path); return (1); } static void usage(void) { printf("usage: boot device [/path/to/loader]\n"); exit(1); } static void exit(int code) { ofw_exit(); } static struct dmadat __dmadat; static int domount(const char *device, int quiet) { dmadat = &__dmadat; if ((bootdev = ofw_open(device)) == -1) { printf("domount: can't open device\n"); return (-1); } if (fsread(0, NULL, 0)) { if (!quiet) printf("domount: can't read superblock\n"); return (-1); } return (0); } static void load(const char *fname) { Elf32_Ehdr eh; Elf32_Phdr ph; caddr_t p; ufs_ino_t ino; int i; if ((ino = lookup(fname)) == 0) { printf("File %s not found\n", fname); return; } if (fsread(ino, &eh, sizeof(eh)) != sizeof(eh)) { printf("Can't read elf header\n"); return; } if (!IS_ELF(eh)) { printf("Not an ELF file\n"); return; } for (i = 0; i < eh.e_phnum; i++) { fs_off = eh.e_phoff + i * eh.e_phentsize; if (fsread(ino, &ph, sizeof(ph)) != sizeof(ph)) { printf("Can't read program header %d\n", i); return; } if (ph.p_type != PT_LOAD) continue; fs_off = ph.p_offset; p = (caddr_t)ph.p_vaddr; ofw_claim(p,(ph.p_filesz > ph.p_memsz) ? ph.p_filesz : ph.p_memsz,0); if (fsread(ino, p, ph.p_filesz) != ph.p_filesz) { printf("Can't read content of section %d\n", i); return; } if (ph.p_filesz != ph.p_memsz) bzero(p + ph.p_filesz, ph.p_memsz - ph.p_filesz); __syncicache(p, ph.p_memsz); } ofw_close(bootdev); (*(void (*)(void *, int, ofwfp_t, char *, int))eh.e_entry)(NULL, 0, ofw,NULL,0); } static int dskread(void *buf, uint64_t lba, int nblk) { /* * The Open Firmware should open the correct partition for us. * That means, if we read from offset zero on an open instance handle, * we should read from offset zero of that partition. */ ofw_seek(bootdev, lba * DEV_BSIZE); ofw_read(bootdev, buf, nblk * DEV_BSIZE); return (0); } static void panic(const char *fmt, ...) { char buf[128]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof buf, fmt, ap); printf("panic: %s\n", buf); va_end(ap); exit(1); } static int printf(const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = vprintf(fmt, ap); va_end(ap); return (ret); } static int putchar(char c, void *arg) { char buf; if (c == '\n') { buf = '\r'; ofw_write(stdouth, &buf, 1); } buf = c; ofw_write(stdouth, &buf, 1); return (1); } static int vprintf(const char *fmt, va_list ap) { int ret; ret = __printf(fmt, putchar, 0, ap); return (ret); } static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap) { struct sp_data sp; int ret; sp.sp_buf = str; sp.sp_len = 0; sp.sp_size = sz; ret = __printf(fmt, __sputc, &sp, ap); return (ret); } static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap) { char buf[(sizeof(long) * 8) + 1]; char *nbuf; u_long ul; u_int ui; int lflag; int sflag; char *s; int pad; int ret; int c; nbuf = &buf[sizeof buf - 1]; ret = 0; while ((c = *fmt++) != 0) { if (c != '%') { ret += putc(c, arg); continue; } lflag = 0; sflag = 0; pad = 0; reswitch: c = *fmt++; switch (c) { case '#': sflag = 1; goto reswitch; case '%': ret += putc('%', arg); break; case 'c': c = va_arg(ap, int); ret += putc(c, arg); break; case 'd': if (lflag == 0) { ui = (u_int)va_arg(ap, int); if (ui < (int)ui) { ui = -ui; ret += putc('-', arg); } s = __uitoa(nbuf, ui, 10); } else { ul = (u_long)va_arg(ap, long); if (ul < (long)ul) { ul = -ul; ret += putc('-', arg); } s = __ultoa(nbuf, ul, 10); } ret += __puts(s, putc, arg); break; case 'l': lflag = 1; goto reswitch; case 'o': if (lflag == 0) { ui = (u_int)va_arg(ap, u_int); s = __uitoa(nbuf, ui, 8); } else { ul = (u_long)va_arg(ap, u_long); s = __ultoa(nbuf, ul, 8); } ret += __puts(s, putc, arg); break; case 'p': ul = (u_long)va_arg(ap, void *); s = __ultoa(nbuf, ul, 16); ret += __puts("0x", putc, arg); ret += __puts(s, putc, arg); break; case 's': s = va_arg(ap, char *); ret += __puts(s, putc, arg); break; case 'u': if (lflag == 0) { ui = va_arg(ap, u_int); s = __uitoa(nbuf, ui, 10); } else { ul = va_arg(ap, u_long); s = __ultoa(nbuf, ul, 10); } ret += __puts(s, putc, arg); break; case 'x': if (lflag == 0) { ui = va_arg(ap, u_int); s = __uitoa(nbuf, ui, 16); } else { ul = va_arg(ap, u_long); s = __ultoa(nbuf, ul, 16); } if (sflag) ret += __puts("0x", putc, arg); ret += __puts(s, putc, arg); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': pad = pad * 10 + c - '0'; goto reswitch; default: break; } } return (ret); } static int __sputc(char c, void *arg) { struct sp_data *sp; sp = arg; if (sp->sp_len < sp->sp_size) sp->sp_buf[sp->sp_len++] = c; sp->sp_buf[sp->sp_len] = '\0'; return (1); } static int __puts(const char *s, putc_func_t *putc, void *arg) { const char *p; int ret; ret = 0; for (p = s; *p != '\0'; p++) ret += putc(*p, arg); return (ret); } static char * __uitoa(char *buf, u_int ui, int base) { char *p; p = buf; *p = '\0'; do *--p = digits[ui % base]; while ((ui /= base) != 0); return (p); } static char * __ultoa(char *buf, u_long ul, int base) { char *p; p = buf; *p = '\0'; do *--p = digits[ul % base]; while ((ul /= base) != 0); return (p); } Index: user/ngie/bug-237403/stand/powerpc/kboot/Makefile =================================================================== --- user/ngie/bug-237403/stand/powerpc/kboot/Makefile (revision 348022) +++ user/ngie/bug-237403/stand/powerpc/kboot/Makefile (revision 348023) @@ -1,45 +1,42 @@ # $FreeBSD$ LOADER_CD9660_SUPPORT?= yes LOADER_MSDOS_SUPPORT?= no LOADER_EXT2FS_SUPPORT?= yes LOADER_UFS_SUPPORT?= yes LOADER_NET_SUPPORT?= yes LOADER_NFS_SUPPORT?= yes LOADER_TFTP_SUPPORT?= no LOADER_GZIP_SUPPORT?= yes LOADER_BZIP2_SUPPORT?= no .include PROG= loader.kboot NEWVERSWHAT= "kboot loader" ${MACHINE_ARCH} INSTALLFLAGS= -b # Architecture-specific loader code SRCS= conf.c vers.c main.c ppc64_elf_freebsd.c SRCS+= host_syscall.S hostcons.c hostdisk.c kerneltramp.S kbootfdt.c SRCS+= ucmpdi2.c .include "${BOOTSRC}/fdt.mk" CFLAGS+= -mcpu=powerpc64 # Always add MI sources .include "${BOOTSRC}/loader.mk" .PATH: ${SYSDIR}/libkern CFLAGS+= -Wall -DAIM # load address. set in linker script RELOC?= 0x0 CFLAGS+= -DRELOC=${RELOC} LDFLAGS= -nostdlib -static -T ${.CURDIR}/ldscript.powerpc -# 64-bit bridge extensions -CFLAGS+= -Wa,-mppc64bridge - DPADD= ${LDR_INTERP} ${LIBOFW} ${LIBFDT} ${LIBSA} LDADD= ${LDR_INTERP} ${LIBOFW} ${LIBFDT} ${LIBSA} .include Index: user/ngie/bug-237403/stand/powerpc/kboot/main.c =================================================================== --- user/ngie/bug-237403/stand/powerpc/kboot/main.c (revision 348022) +++ user/ngie/bug-237403/stand/powerpc/kboot/main.c (revision 348023) @@ -1,505 +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 "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); 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) { 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); 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: user/ngie/bug-237403/stand/powerpc/ofw/elf_freebsd.c =================================================================== --- user/ngie/bug-237403/stand/powerpc/ofw/elf_freebsd.c (revision 348022) +++ user/ngie/bug-237403/stand/powerpc/ofw/elf_freebsd.c (revision 348023) @@ -1,106 +1,106 @@ /*- * Copyright (c) 2001 Benno Rice * 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 #if defined(__powerpc__) #include #endif #include #include "bootstrap.h" #include "libofw.h" #include "openfirm.h" extern char end[]; extern vm_offset_t reloc; /* From /conf.c */ int __elfN(ofw_loadfile)(char *filename, uint64_t dest, struct preloaded_file **result) { int r; r = __elfN(loadfile)(filename, dest, result); if (r != 0) return (r); #if defined(__powerpc__) /* * No need to sync the icache for modules: this will * be done by the kernel after relocation. */ if (!strcmp((*result)->f_type, "elf kernel")) __syncicache((void *) (*result)->f_addr, (*result)->f_size); #endif return (0); } int __elfN(ofw_exec)(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp, dtbp; Elf_Ehdr *e; int error; intptr_t entry; if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { return(EFTYPE); } e = (Elf_Ehdr *)&fmp->md_data; entry = e->e_entry; if ((error = md_load(fp->f_args, &mdp, &dtbp)) != 0) return (error); - printf("Kernel entry at 0x%lx ...\n", e->e_entry); + printf("Kernel entry at 0x%x ...\n", entry); dev_cleanup(); if (dtbp != 0) { OF_quiesce(); ((int (*)(u_long, u_long, u_long, void *, u_long))entry)(dtbp, 0, 0, (void *)mdp, 0xfb5d104d); } else { OF_chain((void *)reloc, end - (char *)reloc, (void *)entry, (void *)mdp, 0xfb5d104d); } panic("exec returned"); } struct file_format ofw_elf = { __elfN(ofw_loadfile), __elfN(ofw_exec) }; Index: user/ngie/bug-237403/stand/powerpc/ofw/ppc64_elf_freebsd.c =================================================================== --- user/ngie/bug-237403/stand/powerpc/ofw/ppc64_elf_freebsd.c (revision 348022) +++ user/ngie/bug-237403/stand/powerpc/ofw/ppc64_elf_freebsd.c (revision 348023) @@ -1,110 +1,110 @@ /*- * Copyright (c) 2001 Benno Rice * 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$"); #define __ELF_WORD_SIZE 64 #include #include #include #include #include #include #include "bootstrap.h" #include "libofw.h" #include "openfirm.h" extern char end[]; extern vm_offset_t reloc; /* From /conf.c */ int ppc64_ofw_elf_loadfile(char *filename, uint64_t dest, struct preloaded_file **result) { int r; r = __elfN(loadfile)(filename, dest, result); if (r != 0) return (r); /* * No need to sync the icache for modules: this will * be done by the kernel after relocation. */ if (!strcmp((*result)->f_type, "elf kernel")) __syncicache((void *) (*result)->f_addr, (*result)->f_size); return (0); } int ppc64_ofw_elf_exec(struct preloaded_file *fp) { struct file_metadata *fmp; vm_offset_t mdp, dtbp; Elf_Ehdr *e; int error; intptr_t entry; if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == NULL) { return(EFTYPE); } e = (Elf_Ehdr *)&fmp->md_data; /* Handle function descriptor for ELFv1 kernels */ if ((e->e_flags & 3) == 2) entry = e->e_entry; else entry = *(uint64_t *)(intptr_t)e->e_entry; if ((error = md_load64(fp->f_args, &mdp, &dtbp)) != 0) return (error); - printf("Kernel entry at 0x%lx ...\n", entry); + printf("Kernel entry at 0x%x ...\n", entry); dev_cleanup(); if (dtbp != 0) { OF_quiesce(); ((int (*)(u_long, u_long, u_long, void *, u_long))entry)(dtbp, 0, 0, (void *)mdp, 0xfb5d104d); } else { OF_chain((void *)reloc, end - (char *)reloc, (void *)entry, (void *)mdp, 0xfb5d104d); } panic("exec returned"); } struct file_format ofw_elf64 = { ppc64_ofw_elf_loadfile, ppc64_ofw_elf_exec }; Index: user/ngie/bug-237403/sys/dev/netmap/netmap_freebsd.c =================================================================== --- user/ngie/bug-237403/sys/dev/netmap/netmap_freebsd.c (revision 348022) +++ user/ngie/bug-237403/sys/dev/netmap/netmap_freebsd.c (revision 348023) @@ -1,1618 +1,1619 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2013-2014 Universita` di Pisa. 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 "opt_inet.h" #include "opt_inet6.h" #include #include #include +#include #include #include /* POLLIN, POLLOUT */ #include /* types used in module initialization */ #include /* DEV_MODULE_ORDERED */ #include #include /* kern_ioctl() */ #include #include /* vtophys */ #include /* vtophys */ #include #include #include #include #include #include #include /* sockaddrs */ #include #include /* kthread_add() */ #include /* PROC_LOCK() */ #include /* RFNOWAIT */ #include /* sched_bind() */ #include /* mp_maxid */ #include /* taskqueue_enqueue(), taskqueue_create(), ... */ #include #include #include /* IFT_ETHER */ #include /* ether_ifdetach */ #include /* LLADDR */ #include /* bus_dmamap_* */ #include /* in6_cksum_pseudo() */ #include /* in_pseudo(), in_cksum_hdr() */ #include #include #include #include /* ======================== FREEBSD-SPECIFIC ROUTINES ================== */ static void nm_kqueue_notify(void *opaque, int pending) { struct nm_selinfo *si = opaque; /* We use a non-zero hint to distinguish this notification call * from the call done in kqueue_scan(), which uses hint=0. */ KNOTE_UNLOCKED(&si->si.si_note, /*hint=*/0x100); } int nm_os_selinfo_init(NM_SELINFO_T *si, const char *name) { int err; TASK_INIT(&si->ntfytask, 0, nm_kqueue_notify, si); si->ntfytq = taskqueue_create(name, M_NOWAIT, taskqueue_thread_enqueue, &si->ntfytq); if (si->ntfytq == NULL) return -ENOMEM; err = taskqueue_start_threads(&si->ntfytq, 1, PI_NET, "tq %s", name); if (err) { taskqueue_free(si->ntfytq); si->ntfytq = NULL; return err; } snprintf(si->mtxname, sizeof(si->mtxname), "nmkl%s", name); mtx_init(&si->m, si->mtxname, NULL, MTX_DEF); knlist_init_mtx(&si->si.si_note, &si->m); si->kqueue_users = 0; return (0); } void nm_os_selinfo_uninit(NM_SELINFO_T *si) { if (si->ntfytq == NULL) { return; /* si was not initialized */ } taskqueue_drain(si->ntfytq, &si->ntfytask); taskqueue_free(si->ntfytq); si->ntfytq = NULL; knlist_delete(&si->si.si_note, curthread, /*islocked=*/0); knlist_destroy(&si->si.si_note); /* now we don't need the mutex anymore */ mtx_destroy(&si->m); } void * nm_os_malloc(size_t size) { return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); } void * nm_os_realloc(void *addr, size_t new_size, size_t old_size __unused) { return realloc(addr, new_size, M_DEVBUF, M_NOWAIT | M_ZERO); } void nm_os_free(void *addr) { free(addr, M_DEVBUF); } void nm_os_ifnet_lock(void) { IFNET_RLOCK(); } void nm_os_ifnet_unlock(void) { IFNET_RUNLOCK(); } static int netmap_use_count = 0; void nm_os_get_module(void) { netmap_use_count++; } void nm_os_put_module(void) { netmap_use_count--; } static void netmap_ifnet_arrival_handler(void *arg __unused, struct ifnet *ifp) { netmap_undo_zombie(ifp); } static void netmap_ifnet_departure_handler(void *arg __unused, struct ifnet *ifp) { netmap_make_zombie(ifp); } static eventhandler_tag nm_ifnet_ah_tag; static eventhandler_tag nm_ifnet_dh_tag; int nm_os_ifnet_init(void) { nm_ifnet_ah_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event, netmap_ifnet_arrival_handler, NULL, EVENTHANDLER_PRI_ANY); nm_ifnet_dh_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, netmap_ifnet_departure_handler, NULL, EVENTHANDLER_PRI_ANY); return 0; } void nm_os_ifnet_fini(void) { EVENTHANDLER_DEREGISTER(ifnet_arrival_event, nm_ifnet_ah_tag); EVENTHANDLER_DEREGISTER(ifnet_departure_event, nm_ifnet_dh_tag); } unsigned nm_os_ifnet_mtu(struct ifnet *ifp) { #if __FreeBSD_version < 1100030 return ifp->if_data.ifi_mtu; #else /* __FreeBSD_version >= 1100030 */ return ifp->if_mtu; #endif } rawsum_t nm_os_csum_raw(uint8_t *data, size_t len, rawsum_t cur_sum) { /* TODO XXX please use the FreeBSD implementation for this. */ uint16_t *words = (uint16_t *)data; int nw = len / 2; int i; for (i = 0; i < nw; i++) cur_sum += be16toh(words[i]); if (len & 1) cur_sum += (data[len-1] << 8); return cur_sum; } /* Fold a raw checksum: 'cur_sum' is in host byte order, while the * return value is in network byte order. */ uint16_t nm_os_csum_fold(rawsum_t cur_sum) { /* TODO XXX please use the FreeBSD implementation for this. */ while (cur_sum >> 16) cur_sum = (cur_sum & 0xFFFF) + (cur_sum >> 16); return htobe16((~cur_sum) & 0xFFFF); } uint16_t nm_os_csum_ipv4(struct nm_iphdr *iph) { #if 0 return in_cksum_hdr((void *)iph); #else return nm_os_csum_fold(nm_os_csum_raw((uint8_t*)iph, sizeof(struct nm_iphdr), 0)); #endif } void nm_os_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data, size_t datalen, uint16_t *check) { #ifdef INET uint16_t pseudolen = datalen + iph->protocol; /* Compute and insert the pseudo-header cheksum. */ *check = in_pseudo(iph->saddr, iph->daddr, htobe16(pseudolen)); /* Compute the checksum on TCP/UDP header + payload * (includes the pseudo-header). */ *check = nm_os_csum_fold(nm_os_csum_raw(data, datalen, 0)); #else static int notsupported = 0; if (!notsupported) { notsupported = 1; nm_prerr("inet4 segmentation not supported"); } #endif } void nm_os_csum_tcpudp_ipv6(struct nm_ipv6hdr *ip6h, void *data, size_t datalen, uint16_t *check) { #ifdef INET6 *check = in6_cksum_pseudo((void*)ip6h, datalen, ip6h->nexthdr, 0); *check = nm_os_csum_fold(nm_os_csum_raw(data, datalen, 0)); #else static int notsupported = 0; if (!notsupported) { notsupported = 1; nm_prerr("inet6 segmentation not supported"); } #endif } /* on FreeBSD we send up one packet at a time */ void * nm_os_send_up(struct ifnet *ifp, struct mbuf *m, struct mbuf *prev) { NA(ifp)->if_input(ifp, m); return NULL; } int nm_os_mbuf_has_csum_offld(struct mbuf *m) { return m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP | CSUM_SCTP | CSUM_TCP_IPV6 | CSUM_UDP_IPV6 | CSUM_SCTP_IPV6); } int nm_os_mbuf_has_seg_offld(struct mbuf *m) { return m->m_pkthdr.csum_flags & CSUM_TSO; } static void freebsd_generic_rx_handler(struct ifnet *ifp, struct mbuf *m) { int stolen; if (unlikely(!NM_NA_VALID(ifp))) { nm_prlim(1, "Warning: RX packet intercepted, but no" " emulated adapter"); return; } stolen = generic_rx_handler(ifp, m); if (!stolen) { struct netmap_generic_adapter *gna = (struct netmap_generic_adapter *)NA(ifp); gna->save_if_input(ifp, m); } } /* * Intercept the rx routine in the standard device driver. * Second argument is non-zero to intercept, 0 to restore */ int nm_os_catch_rx(struct netmap_generic_adapter *gna, int intercept) { struct netmap_adapter *na = &gna->up.up; struct ifnet *ifp = na->ifp; int ret = 0; nm_os_ifnet_lock(); if (intercept) { if (gna->save_if_input) { nm_prerr("RX on %s already intercepted", na->name); ret = EBUSY; /* already set */ goto out; } gna->save_if_input = ifp->if_input; ifp->if_input = freebsd_generic_rx_handler; } else { if (!gna->save_if_input) { nm_prerr("Failed to undo RX intercept on %s", na->name); ret = EINVAL; /* not saved */ goto out; } ifp->if_input = gna->save_if_input; gna->save_if_input = NULL; } out: nm_os_ifnet_unlock(); return ret; } /* * Intercept the packet steering routine in the tx path, * so that we can decide which queue is used for an mbuf. * Second argument is non-zero to intercept, 0 to restore. * On freebsd we just intercept if_transmit. */ int nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept) { struct netmap_adapter *na = &gna->up.up; struct ifnet *ifp = netmap_generic_getifp(gna); nm_os_ifnet_lock(); if (intercept) { na->if_transmit = ifp->if_transmit; ifp->if_transmit = netmap_transmit; } else { ifp->if_transmit = na->if_transmit; } nm_os_ifnet_unlock(); return 0; } /* * Transmit routine used by generic_netmap_txsync(). Returns 0 on success * and non-zero on error (which may be packet drops or other errors). * addr and len identify the netmap buffer, m is the (preallocated) * mbuf to use for transmissions. * * We should add a reference to the mbuf so the m_freem() at the end * of the transmission does not consume resources. * * On FreeBSD, and on multiqueue cards, we can force the queue using * if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) * i = m->m_pkthdr.flowid % adapter->num_queues; * else * i = curcpu % adapter->num_queues; * */ int nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) { int ret; u_int len = a->len; struct ifnet *ifp = a->ifp; struct mbuf *m = a->m; #if __FreeBSD_version < 1100000 /* * Old FreeBSD versions. The mbuf has a cluster attached, * we need to copy from the cluster to the netmap buffer. */ if (MBUF_REFCNT(m) != 1) { nm_prerr("invalid refcnt %d for %p", MBUF_REFCNT(m), m); panic("in generic_xmit_frame"); } if (m->m_ext.ext_size < len) { nm_prlim(2, "size %d < len %d", m->m_ext.ext_size, len); len = m->m_ext.ext_size; } bcopy(a->addr, m->m_data, len); #else /* __FreeBSD_version >= 1100000 */ /* New FreeBSD versions. Link the external storage to * the netmap buffer, so that no copy is necessary. */ m->m_ext.ext_buf = m->m_data = a->addr; m->m_ext.ext_size = len; #endif /* __FreeBSD_version >= 1100000 */ m->m_len = m->m_pkthdr.len = len; /* mbuf refcnt is not contended, no need to use atomic * (a memory barrier is enough). */ SET_MBUF_REFCNT(m, 2); M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); m->m_pkthdr.flowid = a->ring_nr; m->m_pkthdr.rcvif = ifp; /* used for tx notification */ ret = NA(ifp)->if_transmit(ifp, m); return ret ? -1 : 0; } #if __FreeBSD_version >= 1100005 struct netmap_adapter * netmap_getna(if_t ifp) { return (NA((struct ifnet *)ifp)); } #endif /* __FreeBSD_version >= 1100005 */ /* * The following two functions are empty until we have a generic * way to extract the info from the ifp */ int nm_os_generic_find_num_desc(struct ifnet *ifp, unsigned int *tx, unsigned int *rx) { return 0; } void nm_os_generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq) { unsigned num_rings = netmap_generic_rings ? netmap_generic_rings : 1; *txq = num_rings; *rxq = num_rings; } void nm_os_generic_set_features(struct netmap_generic_adapter *gna) { gna->rxsg = 1; /* Supported through m_copydata. */ gna->txqdisc = 0; /* Not supported. */ } void nm_os_mitigation_init(struct nm_generic_mit *mit, int idx, struct netmap_adapter *na) { mit->mit_pending = 0; mit->mit_ring_idx = idx; mit->mit_na = na; } void nm_os_mitigation_start(struct nm_generic_mit *mit) { } void nm_os_mitigation_restart(struct nm_generic_mit *mit) { } int nm_os_mitigation_active(struct nm_generic_mit *mit) { return 0; } void nm_os_mitigation_cleanup(struct nm_generic_mit *mit) { } static int nm_vi_dummy(struct ifnet *ifp, u_long cmd, caddr_t addr) { return EINVAL; } static void nm_vi_start(struct ifnet *ifp) { panic("nm_vi_start() must not be called"); } /* * Index manager of persistent virtual interfaces. * It is used to decide the lowest byte of the MAC address. * We use the same algorithm with management of bridge port index. */ #define NM_VI_MAX 255 static struct { uint8_t index[NM_VI_MAX]; /* XXX just for a reasonable number */ uint8_t active; struct mtx lock; } nm_vi_indices; void nm_os_vi_init_index(void) { int i; for (i = 0; i < NM_VI_MAX; i++) nm_vi_indices.index[i] = i; nm_vi_indices.active = 0; mtx_init(&nm_vi_indices.lock, "nm_vi_indices_lock", NULL, MTX_DEF); } /* return -1 if no index available */ static int nm_vi_get_index(void) { int ret; mtx_lock(&nm_vi_indices.lock); ret = nm_vi_indices.active == NM_VI_MAX ? -1 : nm_vi_indices.index[nm_vi_indices.active++]; mtx_unlock(&nm_vi_indices.lock); return ret; } static void nm_vi_free_index(uint8_t val) { int i, lim; mtx_lock(&nm_vi_indices.lock); lim = nm_vi_indices.active; for (i = 0; i < lim; i++) { if (nm_vi_indices.index[i] == val) { /* swap index[lim-1] and j */ int tmp = nm_vi_indices.index[lim-1]; nm_vi_indices.index[lim-1] = val; nm_vi_indices.index[i] = tmp; nm_vi_indices.active--; break; } } if (lim == nm_vi_indices.active) nm_prerr("Index %u not found", val); mtx_unlock(&nm_vi_indices.lock); } #undef NM_VI_MAX /* * Implementation of a netmap-capable virtual interface that * registered to the system. * It is based on if_tap.c and ip_fw_log.c in FreeBSD 9. * * Note: Linux sets refcount to 0 on allocation of net_device, * then increments it on registration to the system. * FreeBSD sets refcount to 1 on if_alloc(), and does not * increment this refcount on if_attach(). */ int nm_os_vi_persist(const char *name, struct ifnet **ret) { struct ifnet *ifp; u_short macaddr_hi; uint32_t macaddr_mid; u_char eaddr[6]; int unit = nm_vi_get_index(); /* just to decide MAC address */ if (unit < 0) return EBUSY; /* * We use the same MAC address generation method with tap * except for the highest octet is 00:be instead of 00:bd */ macaddr_hi = htons(0x00be); /* XXX tap + 1 */ macaddr_mid = (uint32_t) ticks; bcopy(&macaddr_hi, eaddr, sizeof(short)); bcopy(&macaddr_mid, &eaddr[2], sizeof(uint32_t)); eaddr[5] = (uint8_t)unit; ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { nm_prerr("if_alloc failed"); return ENOMEM; } if_initname(ifp, name, IF_DUNIT_NONE); ifp->if_mtu = 65536; ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = (void *)nm_vi_dummy; ifp->if_ioctl = nm_vi_dummy; ifp->if_start = nm_vi_start; ifp->if_mtu = ETHERMTU; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_capabilities |= IFCAP_LINKSTATE; ifp->if_capenable |= IFCAP_LINKSTATE; ether_ifattach(ifp, eaddr); *ret = ifp; return 0; } /* unregister from the system and drop the final refcount */ void nm_os_vi_detach(struct ifnet *ifp) { nm_vi_free_index(((char *)IF_LLADDR(ifp))[5]); ether_ifdetach(ifp); if_free(ifp); } #ifdef WITH_EXTMEM #include #include struct nm_os_extmem { vm_object_t obj; vm_offset_t kva; vm_offset_t size; uintptr_t scan; }; void nm_os_extmem_delete(struct nm_os_extmem *e) { nm_prinf("freeing %zx bytes", (size_t)e->size); vm_map_remove(kernel_map, e->kva, e->kva + e->size); nm_os_free(e); } char * nm_os_extmem_nextpage(struct nm_os_extmem *e) { char *rv = NULL; if (e->scan < e->kva + e->size) { rv = (char *)e->scan; e->scan += PAGE_SIZE; } return rv; } int nm_os_extmem_isequal(struct nm_os_extmem *e1, struct nm_os_extmem *e2) { return (e1->obj == e2->obj); } int nm_os_extmem_nr_pages(struct nm_os_extmem *e) { return e->size >> PAGE_SHIFT; } struct nm_os_extmem * nm_os_extmem_create(unsigned long p, struct nmreq_pools_info *pi, int *perror) { vm_map_t map; vm_map_entry_t entry; vm_object_t obj; vm_prot_t prot; vm_pindex_t index; boolean_t wired; struct nm_os_extmem *e = NULL; int rv, error = 0; e = nm_os_malloc(sizeof(*e)); if (e == NULL) { error = ENOMEM; goto out; } map = &curthread->td_proc->p_vmspace->vm_map; rv = vm_map_lookup(&map, p, VM_PROT_RW, &entry, &obj, &index, &prot, &wired); if (rv != KERN_SUCCESS) { nm_prerr("address %lx not found", p); goto out_free; } /* check that we are given the whole vm_object ? */ vm_map_lookup_done(map, entry); // XXX can we really use obj after releasing the map lock? e->obj = obj; vm_object_reference(obj); /* wire the memory and add the vm_object to the kernel map, * to make sure that it is not fred even if the processes that * are mmap()ing it all exit */ e->kva = vm_map_min(kernel_map); e->size = obj->size << PAGE_SHIFT; rv = vm_map_find(kernel_map, obj, 0, &e->kva, e->size, 0, VMFS_OPTIMAL_SPACE, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE, 0); if (rv != KERN_SUCCESS) { nm_prerr("vm_map_find(%zx) failed", (size_t)e->size); goto out_rel; } rv = vm_map_wire(kernel_map, e->kva, e->kva + e->size, VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES); if (rv != KERN_SUCCESS) { nm_prerr("vm_map_wire failed"); goto out_rem; } e->scan = e->kva; return e; out_rem: vm_map_remove(kernel_map, e->kva, e->kva + e->size); e->obj = NULL; out_rel: vm_object_deallocate(e->obj); out_free: nm_os_free(e); out: if (perror) *perror = error; return NULL; } #endif /* WITH_EXTMEM */ /* ================== PTNETMAP GUEST SUPPORT ==================== */ #ifdef WITH_PTNETMAP #include #include #include /* bus_dmamap_* */ #include #include #include /* * ptnetmap memory device (memdev) for freebsd guest, * ssed to expose host netmap memory to the guest through a PCI BAR. */ /* * ptnetmap memdev private data structure */ struct ptnetmap_memdev { device_t dev; struct resource *pci_io; struct resource *pci_mem; struct netmap_mem_d *nm_mem; }; static int ptn_memdev_probe(device_t); static int ptn_memdev_attach(device_t); static int ptn_memdev_detach(device_t); static int ptn_memdev_shutdown(device_t); static device_method_t ptn_memdev_methods[] = { DEVMETHOD(device_probe, ptn_memdev_probe), DEVMETHOD(device_attach, ptn_memdev_attach), DEVMETHOD(device_detach, ptn_memdev_detach), DEVMETHOD(device_shutdown, ptn_memdev_shutdown), DEVMETHOD_END }; static driver_t ptn_memdev_driver = { PTNETMAP_MEMDEV_NAME, ptn_memdev_methods, sizeof(struct ptnetmap_memdev), }; /* We use (SI_ORDER_MIDDLE+1) here, see DEV_MODULE_ORDERED() invocation * below. */ static devclass_t ptnetmap_devclass; DRIVER_MODULE_ORDERED(ptn_memdev, pci, ptn_memdev_driver, ptnetmap_devclass, NULL, NULL, SI_ORDER_MIDDLE + 1); /* * Map host netmap memory through PCI-BAR in the guest OS, * returning physical (nm_paddr) and virtual (nm_addr) addresses * of the netmap memory mapped in the guest. */ int nm_os_pt_memdev_iomap(struct ptnetmap_memdev *ptn_dev, vm_paddr_t *nm_paddr, void **nm_addr, uint64_t *mem_size) { int rid; nm_prinf("ptn_memdev_driver iomap"); rid = PCIR_BAR(PTNETMAP_MEM_PCI_BAR); *mem_size = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMSIZE_HI); *mem_size = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMSIZE_LO) | (*mem_size << 32); /* map memory allocator */ ptn_dev->pci_mem = bus_alloc_resource(ptn_dev->dev, SYS_RES_MEMORY, &rid, 0, ~0, *mem_size, RF_ACTIVE); if (ptn_dev->pci_mem == NULL) { *nm_paddr = 0; *nm_addr = NULL; return ENOMEM; } *nm_paddr = rman_get_start(ptn_dev->pci_mem); *nm_addr = rman_get_virtual(ptn_dev->pci_mem); nm_prinf("=== BAR %d start %lx len %lx mem_size %lx ===", PTNETMAP_MEM_PCI_BAR, (unsigned long)(*nm_paddr), (unsigned long)rman_get_size(ptn_dev->pci_mem), (unsigned long)*mem_size); return (0); } uint32_t nm_os_pt_memdev_ioread(struct ptnetmap_memdev *ptn_dev, unsigned int reg) { return bus_read_4(ptn_dev->pci_io, reg); } /* Unmap host netmap memory. */ void nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *ptn_dev) { nm_prinf("ptn_memdev_driver iounmap"); if (ptn_dev->pci_mem) { bus_release_resource(ptn_dev->dev, SYS_RES_MEMORY, PCIR_BAR(PTNETMAP_MEM_PCI_BAR), ptn_dev->pci_mem); ptn_dev->pci_mem = NULL; } } /* Device identification routine, return BUS_PROBE_DEFAULT on success, * positive on failure */ static int ptn_memdev_probe(device_t dev) { char desc[256]; if (pci_get_vendor(dev) != PTNETMAP_PCI_VENDOR_ID) return (ENXIO); if (pci_get_device(dev) != PTNETMAP_PCI_DEVICE_ID) return (ENXIO); snprintf(desc, sizeof(desc), "%s PCI adapter", PTNETMAP_MEMDEV_NAME); device_set_desc_copy(dev, desc); return (BUS_PROBE_DEFAULT); } /* Device initialization routine. */ static int ptn_memdev_attach(device_t dev) { struct ptnetmap_memdev *ptn_dev; int rid; uint16_t mem_id; ptn_dev = device_get_softc(dev); ptn_dev->dev = dev; pci_enable_busmaster(dev); rid = PCIR_BAR(PTNETMAP_IO_PCI_BAR); ptn_dev->pci_io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (ptn_dev->pci_io == NULL) { device_printf(dev, "cannot map I/O space\n"); return (ENXIO); } mem_id = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMID); /* create guest allocator */ ptn_dev->nm_mem = netmap_mem_pt_guest_attach(ptn_dev, mem_id); if (ptn_dev->nm_mem == NULL) { ptn_memdev_detach(dev); return (ENOMEM); } netmap_mem_get(ptn_dev->nm_mem); nm_prinf("ptnetmap memdev attached, host memid: %u", mem_id); return (0); } /* Device removal routine. */ static int ptn_memdev_detach(device_t dev) { struct ptnetmap_memdev *ptn_dev; ptn_dev = device_get_softc(dev); if (ptn_dev->nm_mem) { nm_prinf("ptnetmap memdev detached, host memid %u", netmap_mem_get_id(ptn_dev->nm_mem)); netmap_mem_put(ptn_dev->nm_mem); ptn_dev->nm_mem = NULL; } if (ptn_dev->pci_mem) { bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(PTNETMAP_MEM_PCI_BAR), ptn_dev->pci_mem); ptn_dev->pci_mem = NULL; } if (ptn_dev->pci_io) { bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(PTNETMAP_IO_PCI_BAR), ptn_dev->pci_io); ptn_dev->pci_io = NULL; } return (0); } static int ptn_memdev_shutdown(device_t dev) { return bus_generic_shutdown(dev); } #endif /* WITH_PTNETMAP */ /* * In order to track whether pages are still mapped, we hook into * the standard cdev_pager and intercept the constructor and * destructor. */ struct netmap_vm_handle_t { struct cdev *dev; struct netmap_priv_d *priv; }; static int netmap_dev_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, vm_ooffset_t foff, struct ucred *cred, u_short *color) { struct netmap_vm_handle_t *vmh = handle; if (netmap_verbose) nm_prinf("handle %p size %jd prot %d foff %jd", handle, (intmax_t)size, prot, (intmax_t)foff); if (color) *color = 0; dev_ref(vmh->dev); return 0; } static void netmap_dev_pager_dtor(void *handle) { struct netmap_vm_handle_t *vmh = handle; struct cdev *dev = vmh->dev; struct netmap_priv_d *priv = vmh->priv; if (netmap_verbose) nm_prinf("handle %p", handle); netmap_dtor(priv); free(vmh, M_DEVBUF); dev_rel(dev); } static int netmap_dev_pager_fault(vm_object_t object, vm_ooffset_t offset, int prot, vm_page_t *mres) { struct netmap_vm_handle_t *vmh = object->handle; struct netmap_priv_d *priv = vmh->priv; struct netmap_adapter *na = priv->np_na; vm_paddr_t paddr; vm_page_t page; vm_memattr_t memattr; vm_pindex_t pidx; nm_prdis("object %p offset %jd prot %d mres %p", object, (intmax_t)offset, prot, mres); memattr = object->memattr; pidx = OFF_TO_IDX(offset); paddr = netmap_mem_ofstophys(na->nm_mem, offset); if (paddr == 0) return VM_PAGER_FAIL; if (((*mres)->flags & PG_FICTITIOUS) != 0) { /* * If the passed in result page is a fake page, update it with * the new physical address. */ page = *mres; vm_page_updatefake(page, paddr, memattr); } else { /* * Replace the passed in reqpage page with our own fake page and * free up the all of the original pages. */ #ifndef VM_OBJECT_WUNLOCK /* FreeBSD < 10.x */ #define VM_OBJECT_WUNLOCK VM_OBJECT_UNLOCK #define VM_OBJECT_WLOCK VM_OBJECT_LOCK #endif /* VM_OBJECT_WUNLOCK */ VM_OBJECT_WUNLOCK(object); page = vm_page_getfake(paddr, memattr); VM_OBJECT_WLOCK(object); vm_page_lock(*mres); vm_page_free(*mres); vm_page_unlock(*mres); *mres = page; vm_page_insert(page, object, pidx); } page->valid = VM_PAGE_BITS_ALL; return (VM_PAGER_OK); } static struct cdev_pager_ops netmap_cdev_pager_ops = { .cdev_pg_ctor = netmap_dev_pager_ctor, .cdev_pg_dtor = netmap_dev_pager_dtor, .cdev_pg_fault = netmap_dev_pager_fault, }; static int netmap_mmap_single(struct cdev *cdev, vm_ooffset_t *foff, vm_size_t objsize, vm_object_t *objp, int prot) { int error; struct netmap_vm_handle_t *vmh; struct netmap_priv_d *priv; vm_object_t obj; if (netmap_verbose) nm_prinf("cdev %p foff %jd size %jd objp %p prot %d", cdev, (intmax_t )*foff, (intmax_t )objsize, objp, prot); vmh = malloc(sizeof(struct netmap_vm_handle_t), M_DEVBUF, M_NOWAIT | M_ZERO); if (vmh == NULL) return ENOMEM; vmh->dev = cdev; NMG_LOCK(); error = devfs_get_cdevpriv((void**)&priv); if (error) goto err_unlock; if (priv->np_nifp == NULL) { error = EINVAL; goto err_unlock; } vmh->priv = priv; priv->np_refs++; NMG_UNLOCK(); obj = cdev_pager_allocate(vmh, OBJT_DEVICE, &netmap_cdev_pager_ops, objsize, prot, *foff, NULL); if (obj == NULL) { nm_prerr("cdev_pager_allocate failed"); error = EINVAL; goto err_deref; } *objp = obj; return 0; err_deref: NMG_LOCK(); priv->np_refs--; err_unlock: NMG_UNLOCK(); // err: free(vmh, M_DEVBUF); return error; } /* * On FreeBSD the close routine is only called on the last close on * the device (/dev/netmap) so we cannot do anything useful. * To track close() on individual file descriptors we pass netmap_dtor() to * devfs_set_cdevpriv() on open(). The FreeBSD kernel will call the destructor * when the last fd pointing to the device is closed. * * Note that FreeBSD does not even munmap() on close() so we also have * to track mmap() ourselves, and postpone the call to * netmap_dtor() is called when the process has no open fds and no active * memory maps on /dev/netmap, as in linux. */ static int netmap_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { if (netmap_verbose) nm_prinf("dev %p fflag 0x%x devtype %d td %p", dev, fflag, devtype, td); return 0; } static int netmap_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct netmap_priv_d *priv; int error; (void)dev; (void)oflags; (void)devtype; (void)td; NMG_LOCK(); priv = netmap_priv_new(); if (priv == NULL) { error = ENOMEM; goto out; } error = devfs_set_cdevpriv(priv, netmap_dtor); if (error) { netmap_priv_delete(priv); } out: NMG_UNLOCK(); return error; } /******************** kthread wrapper ****************/ #include u_int nm_os_ncpus(void) { return mp_maxid + 1; } struct nm_kctx_ctx { /* Userspace thread (kthread creator). */ struct thread *user_td; /* worker function and parameter */ nm_kctx_worker_fn_t worker_fn; void *worker_private; struct nm_kctx *nmk; /* integer to manage multiple worker contexts (e.g., RX or TX on ptnetmap) */ long type; }; struct nm_kctx { struct thread *worker; struct mtx worker_lock; struct nm_kctx_ctx worker_ctx; int run; /* used to stop kthread */ int attach_user; /* kthread attached to user_process */ int affinity; }; static void nm_kctx_worker(void *data) { struct nm_kctx *nmk = data; struct nm_kctx_ctx *ctx = &nmk->worker_ctx; if (nmk->affinity >= 0) { thread_lock(curthread); sched_bind(curthread, nmk->affinity); thread_unlock(curthread); } while (nmk->run) { /* * check if the parent process dies * (when kthread is attached to user process) */ if (ctx->user_td) { PROC_LOCK(curproc); thread_suspend_check(0); PROC_UNLOCK(curproc); } else { kthread_suspend_check(); } /* Continuously execute worker process. */ ctx->worker_fn(ctx->worker_private); /* worker body */ } kthread_exit(); } void nm_os_kctx_worker_setaff(struct nm_kctx *nmk, int affinity) { nmk->affinity = affinity; } struct nm_kctx * nm_os_kctx_create(struct nm_kctx_cfg *cfg, void *opaque) { struct nm_kctx *nmk = NULL; nmk = malloc(sizeof(*nmk), M_DEVBUF, M_NOWAIT | M_ZERO); if (!nmk) return NULL; mtx_init(&nmk->worker_lock, "nm_kthread lock", NULL, MTX_DEF); nmk->worker_ctx.worker_fn = cfg->worker_fn; nmk->worker_ctx.worker_private = cfg->worker_private; nmk->worker_ctx.type = cfg->type; nmk->affinity = -1; /* attach kthread to user process (ptnetmap) */ nmk->attach_user = cfg->attach_user; return nmk; } int nm_os_kctx_worker_start(struct nm_kctx *nmk) { struct proc *p = NULL; int error = 0; /* Temporarily disable this function as it is currently broken * and causes kernel crashes. The failure can be triggered by * the "vale_polling_enable_disable" test in ctrl-api-test.c. */ return EOPNOTSUPP; if (nmk->worker) return EBUSY; /* check if we want to attach kthread to user process */ if (nmk->attach_user) { nmk->worker_ctx.user_td = curthread; p = curthread->td_proc; } /* enable kthread main loop */ nmk->run = 1; /* create kthread */ if((error = kthread_add(nm_kctx_worker, nmk, p, &nmk->worker, RFNOWAIT /* to be checked */, 0, "nm-kthread-%ld", nmk->worker_ctx.type))) { goto err; } nm_prinf("nm_kthread started td %p", nmk->worker); return 0; err: nm_prerr("nm_kthread start failed err %d", error); nmk->worker = NULL; return error; } void nm_os_kctx_worker_stop(struct nm_kctx *nmk) { if (!nmk->worker) return; /* tell to kthread to exit from main loop */ nmk->run = 0; /* wake up kthread if it sleeps */ kthread_resume(nmk->worker); nmk->worker = NULL; } void nm_os_kctx_destroy(struct nm_kctx *nmk) { if (!nmk) return; if (nmk->worker) nm_os_kctx_worker_stop(nmk); free(nmk, M_DEVBUF); } /******************** kqueue support ****************/ /* * In addition to calling selwakeuppri(), nm_os_selwakeup() also * needs to call knote() to wake up kqueue listeners. * This operation is deferred to a taskqueue in order to avoid possible * lock order reversals; these may happen because knote() grabs a * private lock associated to the 'si' (see struct selinfo, * struct nm_selinfo, and nm_os_selinfo_init), and nm_os_selwakeup() * can be called while holding the lock associated to a different * 'si'. * When calling knote() we use a non-zero 'hint' argument to inform * the netmap_knrw() function that it is being called from * 'nm_os_selwakeup'; this is necessary because when netmap_knrw() is * called by the kevent subsystem (i.e. kevent_scan()) we also need to * call netmap_poll(). * * The netmap_kqfilter() function registers one or another f_event * depending on read or write mode. A pointer to the struct * 'netmap_priv_d' is stored into kn->kn_hook, so that it can later * be passed to netmap_poll(). We pass NULL as a third argument to * netmap_poll(), so that the latter only runs the txsync/rxsync * (if necessary), and skips the nm_os_selrecord() calls. */ void nm_os_selwakeup(struct nm_selinfo *si) { selwakeuppri(&si->si, PI_NET); if (si->kqueue_users > 0) { taskqueue_enqueue(si->ntfytq, &si->ntfytask); } } void nm_os_selrecord(struct thread *td, struct nm_selinfo *si) { selrecord(td, &si->si); } static void netmap_knrdetach(struct knote *kn) { struct netmap_priv_d *priv = (struct netmap_priv_d *)kn->kn_hook; struct nm_selinfo *si = priv->np_si[NR_RX]; knlist_remove(&si->si.si_note, kn, /*islocked=*/0); NMG_LOCK(); KASSERT(si->kqueue_users > 0, ("kqueue_user underflow on %s", si->mtxname)); si->kqueue_users--; nm_prinf("kqueue users for %s: %d", si->mtxname, si->kqueue_users); NMG_UNLOCK(); } static void netmap_knwdetach(struct knote *kn) { struct netmap_priv_d *priv = (struct netmap_priv_d *)kn->kn_hook; struct nm_selinfo *si = priv->np_si[NR_TX]; knlist_remove(&si->si.si_note, kn, /*islocked=*/0); NMG_LOCK(); si->kqueue_users--; nm_prinf("kqueue users for %s: %d", si->mtxname, si->kqueue_users); NMG_UNLOCK(); } /* * Callback triggered by netmap notifications (see netmap_notify()), * and by the application calling kevent(). In the former case we * just return 1 (events ready), since we are not able to do better. * In the latter case we use netmap_poll() to see which events are * ready. */ static int netmap_knrw(struct knote *kn, long hint, int events) { struct netmap_priv_d *priv; int revents; if (hint != 0) { /* Called from netmap_notify(), typically from a * thread different from the one issuing kevent(). * Assume we are ready. */ return 1; } /* Called from kevent(). */ priv = kn->kn_hook; revents = netmap_poll(priv, events, /*thread=*/NULL); return (events & revents) ? 1 : 0; } static int netmap_knread(struct knote *kn, long hint) { return netmap_knrw(kn, hint, POLLIN); } static int netmap_knwrite(struct knote *kn, long hint) { return netmap_knrw(kn, hint, POLLOUT); } static struct filterops netmap_rfiltops = { .f_isfd = 1, .f_detach = netmap_knrdetach, .f_event = netmap_knread, }; static struct filterops netmap_wfiltops = { .f_isfd = 1, .f_detach = netmap_knwdetach, .f_event = netmap_knwrite, }; /* * This is called when a thread invokes kevent() to record * a change in the configuration of the kqueue(). * The 'priv' is the one associated to the open netmap device. */ static int netmap_kqfilter(struct cdev *dev, struct knote *kn) { struct netmap_priv_d *priv; int error; struct netmap_adapter *na; struct nm_selinfo *si; int ev = kn->kn_filter; if (ev != EVFILT_READ && ev != EVFILT_WRITE) { nm_prerr("bad filter request %d", ev); return 1; } error = devfs_get_cdevpriv((void**)&priv); if (error) { nm_prerr("device not yet setup"); return 1; } na = priv->np_na; if (na == NULL) { nm_prerr("no netmap adapter for this file descriptor"); return 1; } /* the si is indicated in the priv */ si = priv->np_si[(ev == EVFILT_WRITE) ? NR_TX : NR_RX]; kn->kn_fop = (ev == EVFILT_WRITE) ? &netmap_wfiltops : &netmap_rfiltops; kn->kn_hook = priv; NMG_LOCK(); si->kqueue_users++; nm_prinf("kqueue users for %s: %d", si->mtxname, si->kqueue_users); NMG_UNLOCK(); knlist_add(&si->si.si_note, kn, /*islocked=*/0); return 0; } static int freebsd_netmap_poll(struct cdev *cdevi __unused, int events, struct thread *td) { struct netmap_priv_d *priv; if (devfs_get_cdevpriv((void **)&priv)) { return POLLERR; } return netmap_poll(priv, events, td); } static int freebsd_netmap_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int ffla __unused, struct thread *td) { int error; struct netmap_priv_d *priv; CURVNET_SET(TD_TO_VNET(td)); error = devfs_get_cdevpriv((void **)&priv); if (error) { /* XXX ENOENT should be impossible, since the priv * is now created in the open */ if (error == ENOENT) error = ENXIO; goto out; } error = netmap_ioctl(priv, cmd, data, td, /*nr_body_is_user=*/1); out: CURVNET_RESTORE(); return error; } void nm_os_onattach(struct ifnet *ifp) { ifp->if_capabilities |= IFCAP_NETMAP; } void nm_os_onenter(struct ifnet *ifp) { struct netmap_adapter *na = NA(ifp); na->if_transmit = ifp->if_transmit; ifp->if_transmit = netmap_transmit; ifp->if_capenable |= IFCAP_NETMAP; } void nm_os_onexit(struct ifnet *ifp) { struct netmap_adapter *na = NA(ifp); ifp->if_transmit = na->if_transmit; ifp->if_capenable &= ~IFCAP_NETMAP; } extern struct cdevsw netmap_cdevsw; /* XXX used in netmap.c, should go elsewhere */ struct cdevsw netmap_cdevsw = { .d_version = D_VERSION, .d_name = "netmap", .d_open = netmap_open, .d_mmap_single = netmap_mmap_single, .d_ioctl = freebsd_netmap_ioctl, .d_poll = freebsd_netmap_poll, .d_kqfilter = netmap_kqfilter, .d_close = netmap_close, }; /*--- end of kqueue support ----*/ /* * Kernel entry point. * * Initialize/finalize the module and return. * * Return 0 on success, errno on failure. */ static int netmap_loader(__unused struct module *module, int event, __unused void *arg) { int error = 0; switch (event) { case MOD_LOAD: error = netmap_init(); break; case MOD_UNLOAD: /* * if some one is still using netmap, * then the module can not be unloaded. */ if (netmap_use_count) { nm_prerr("netmap module can not be unloaded - netmap_use_count: %d", netmap_use_count); error = EBUSY; break; } netmap_fini(); break; default: error = EOPNOTSUPP; break; } return (error); } #ifdef DEV_MODULE_ORDERED /* * The netmap module contains three drivers: (i) the netmap character device * driver; (ii) the ptnetmap memdev PCI device driver, (iii) the ptnet PCI * device driver. The attach() routines of both (ii) and (iii) need the * lock of the global allocator, and such lock is initialized in netmap_init(), * which is part of (i). * Therefore, we make sure that (i) is loaded before (ii) and (iii), using * the 'order' parameter of driver declaration macros. For (i), we specify * SI_ORDER_MIDDLE, while higher orders are used with the DRIVER_MODULE_ORDERED * macros for (ii) and (iii). */ DEV_MODULE_ORDERED(netmap, netmap_loader, NULL, SI_ORDER_MIDDLE); #else /* !DEV_MODULE_ORDERED */ DEV_MODULE(netmap, netmap_loader, NULL); #endif /* DEV_MODULE_ORDERED */ MODULE_DEPEND(netmap, pci, 1, 1, 1); MODULE_VERSION(netmap, 1); /* reduce conditional code */ // linux API, use for the knlist in FreeBSD /* use a private mutex for the knlist */ Index: user/ngie/bug-237403/sys/dev/usb/net/if_muge.c =================================================================== --- user/ngie/bug-237403/sys/dev/usb/net/if_muge.c (revision 348022) +++ user/ngie/bug-237403/sys/dev/usb/net/if_muge.c (revision 348023) @@ -1,2320 +1,2281 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2012 Ben Gray . * Copyright (C) 2018 The FreeBSD Foundation. * * This software was developed by Arshan Khanifar * 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$ */ #include __FBSDID("$FreeBSD$"); /* * USB-To-Ethernet adapter driver for Microchip's LAN78XX and related families. * * USB 3.1 to 10/100/1000 Mbps Ethernet * LAN7800 http://www.microchip.com/wwwproducts/en/LAN7800 * * USB 2.0 to 10/100/1000 Mbps Ethernet * LAN7850 http://www.microchip.com/wwwproducts/en/LAN7850 * * USB 2 to 10/100/1000 Mbps Ethernet with built-in USB hub * LAN7515 (no datasheet available, but probes and functions as LAN7800) * * This driver is based on the if_smsc driver, with lan78xx-specific * functionality modelled on Microchip's Linux lan78xx driver. * * UNIMPLEMENTED FEATURES * ------------------ * A number of features supported by the lan78xx are not yet implemented in * this driver: * * - RX/TX checksum offloading: Nothing has been implemented yet for * TX checksumming. RX checksumming works with ICMP messages, but is broken * for TCP/UDP packets. * - Direct address translation filtering: Implemented but untested. * - VLAN tag removal. * - Support for USB interrupt endpoints. * - Latency Tolerance Messaging (LTM) support. * - TCP LSO support. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_platform.h" #ifdef FDT #include #include #include +#include #endif #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR lan78xx_debug #include #include #include #include #ifdef USB_DEBUG static int muge_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, muge, CTLFLAG_RW, 0, "Microchip LAN78xx USB-GigE"); SYSCTL_INT(_hw_usb_muge, OID_AUTO, debug, CTLFLAG_RWTUN, &muge_debug, 0, "Debug level"); #endif #define MUGE_DEFAULT_RX_CSUM_ENABLE (false) #define MUGE_DEFAULT_TX_CSUM_ENABLE (false) #define MUGE_DEFAULT_TSO_CSUM_ENABLE (false) /* Supported Vendor and Product IDs. */ static const struct usb_device_id lan78xx_devs[] = { #define MUGE_DEV(p,i) { USB_VPI(USB_VENDOR_SMC2, USB_PRODUCT_SMC2_##p, i) } MUGE_DEV(LAN7800_ETH, 0), MUGE_DEV(LAN7801_ETH, 0), MUGE_DEV(LAN7850_ETH, 0), #undef MUGE_DEV }; #ifdef USB_DEBUG #define muge_dbg_printf(sc, fmt, args...) \ do { \ if (muge_debug > 0) \ device_printf((sc)->sc_ue.ue_dev, "debug: " fmt, ##args); \ } while(0) #else #define muge_dbg_printf(sc, fmt, args...) do { } while (0) #endif #define muge_warn_printf(sc, fmt, args...) \ device_printf((sc)->sc_ue.ue_dev, "warning: " fmt, ##args) #define muge_err_printf(sc, fmt, args...) \ device_printf((sc)->sc_ue.ue_dev, "error: " fmt, ##args) #define ETHER_IS_ZERO(addr) \ (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5])) #define ETHER_IS_VALID(addr) \ (!ETHER_IS_MULTICAST(addr) && !ETHER_IS_ZERO(addr)) /* USB endpoints. */ enum { MUGE_BULK_DT_RD, MUGE_BULK_DT_WR, #if 0 /* Ignore interrupt endpoints for now as we poll on MII status. */ MUGE_INTR_DT_WR, MUGE_INTR_DT_RD, #endif MUGE_N_TRANSFER, }; struct muge_softc { struct usb_ether sc_ue; struct mtx sc_mtx; struct usb_xfer *sc_xfer[MUGE_N_TRANSFER]; int sc_phyno; + uint32_t sc_leds; + uint16_t sc_led_modes; + uint16_t sc_led_modes_mask; /* Settings for the mac control (MAC_CSR) register. */ uint32_t sc_rfe_ctl; uint32_t sc_mdix_ctl; uint16_t chipid; uint16_t chiprev; uint32_t sc_mchash_table[ETH_DP_SEL_VHF_HASH_LEN]; uint32_t sc_pfilter_table[MUGE_NUM_PFILTER_ADDRS_][2]; uint32_t sc_flags; #define MUGE_FLAG_LINK 0x0001 #define MUGE_FLAG_INIT_DONE 0x0002 }; #define MUGE_IFACE_IDX 0 #define MUGE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define MUGE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define MUGE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t) static device_probe_t muge_probe; static device_attach_t muge_attach; static device_detach_t muge_detach; static usb_callback_t muge_bulk_read_callback; static usb_callback_t muge_bulk_write_callback; static miibus_readreg_t lan78xx_miibus_readreg; static miibus_writereg_t lan78xx_miibus_writereg; static miibus_statchg_t lan78xx_miibus_statchg; static int muge_attach_post_sub(struct usb_ether *ue); static uether_fn_t muge_attach_post; static uether_fn_t muge_init; static uether_fn_t muge_stop; static uether_fn_t muge_start; static uether_fn_t muge_tick; static uether_fn_t muge_setmulti; static uether_fn_t muge_setpromisc; static int muge_ifmedia_upd(struct ifnet *); static void muge_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int lan78xx_chip_init(struct muge_softc *sc); static int muge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); static const struct usb_config muge_config[MUGE_N_TRANSFER] = { [MUGE_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .frames = 16, .bufsize = 16 * (MCLBYTES + 16), .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = muge_bulk_write_callback, .timeout = 10000, /* 10 seconds */ }, [MUGE_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = 20480, /* bytes */ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = muge_bulk_read_callback, .timeout = 0, /* no timeout */ }, /* * The chip supports interrupt endpoints, however they aren't * needed as we poll on the MII status. */ }; static const struct usb_ether_methods muge_ue_methods = { .ue_attach_post = muge_attach_post, .ue_attach_post_sub = muge_attach_post_sub, .ue_start = muge_start, .ue_ioctl = muge_ioctl, .ue_init = muge_init, .ue_stop = muge_stop, .ue_tick = muge_tick, .ue_setmulti = muge_setmulti, .ue_setpromisc = muge_setpromisc, .ue_mii_upd = muge_ifmedia_upd, .ue_mii_sts = muge_ifmedia_sts, }; /** * lan78xx_read_reg - Read a 32-bit register on the device * @sc: driver soft context * @off: offset of the register * @data: pointer a value that will be populated with the register value * * LOCKING: * The device lock must be held before calling this function. * * RETURNS: * 0 on success, a USB_ERR_?? error code on failure. */ static int lan78xx_read_reg(struct muge_softc *sc, uint32_t off, uint32_t *data) { struct usb_device_request req; uint32_t buf; usb_error_t err; MUGE_LOCK_ASSERT(sc, MA_OWNED); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UVR_READ_REG; USETW(req.wValue, 0); USETW(req.wIndex, off); USETW(req.wLength, 4); err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); if (err != 0) muge_warn_printf(sc, "Failed to read register 0x%0x\n", off); *data = le32toh(buf); return (err); } /** * lan78xx_write_reg - Write a 32-bit register on the device * @sc: driver soft context * @off: offset of the register * @data: the 32-bit value to write into the register * * LOCKING: * The device lock must be held before calling this function. * * RETURNS: * 0 on success, a USB_ERR_?? error code on failure. */ static int lan78xx_write_reg(struct muge_softc *sc, uint32_t off, uint32_t data) { struct usb_device_request req; uint32_t buf; usb_error_t err; MUGE_LOCK_ASSERT(sc, MA_OWNED); buf = htole32(data); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UVR_WRITE_REG; USETW(req.wValue, 0); USETW(req.wIndex, off); USETW(req.wLength, 4); err = uether_do_request(&sc->sc_ue, &req, &buf, 1000); if (err != 0) muge_warn_printf(sc, "Failed to write register 0x%0x\n", off); return (err); } /** * lan78xx_wait_for_bits - Poll on a register value until bits are cleared * @sc: soft context * @reg: offset of the register * @bits: if the bits are clear the function returns * * LOCKING: * The device lock must be held before calling this function. * * RETURNS: * 0 on success, or a USB_ERR_?? error code on failure. */ static int lan78xx_wait_for_bits(struct muge_softc *sc, uint32_t reg, uint32_t bits) { usb_ticks_t start_ticks; const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); uint32_t val; int err; MUGE_LOCK_ASSERT(sc, MA_OWNED); start_ticks = (usb_ticks_t)ticks; do { if ((err = lan78xx_read_reg(sc, reg, &val)) != 0) return (err); if (!(val & bits)) return (0); uether_pause(&sc->sc_ue, hz / 100); } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); return (USB_ERR_TIMEOUT); } /** * lan78xx_eeprom_read_raw - Read the attached EEPROM * @sc: soft context * @off: the eeprom address offset * @buf: stores the bytes * @buflen: the number of bytes to read * * Simply reads bytes from an attached eeprom. * * LOCKING: * The function takes and releases the device lock if not already held. * * RETURNS: * 0 on success, or a USB_ERR_?? error code on failure. */ static int lan78xx_eeprom_read_raw(struct muge_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) { usb_ticks_t start_ticks; const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); int err, locked; uint32_t val, saved; uint16_t i; locked = mtx_owned(&sc->sc_mtx); /* XXX */ if (!locked) MUGE_LOCK(sc); if (sc->chipid == ETH_ID_REV_CHIP_ID_7800_) { /* EEDO/EECLK muxed with LED0/LED1 on LAN7800. */ err = lan78xx_read_reg(sc, ETH_HW_CFG, &val); saved = val; val &= ~(ETH_HW_CFG_LEDO_EN_ | ETH_HW_CFG_LED1_EN_); err = lan78xx_write_reg(sc, ETH_HW_CFG, val); } err = lan78xx_wait_for_bits(sc, ETH_E2P_CMD, ETH_E2P_CMD_BUSY_); if (err != 0) { muge_warn_printf(sc, "eeprom busy, failed to read data\n"); goto done; } /* Start reading the bytes, one at a time. */ for (i = 0; i < buflen; i++) { val = ETH_E2P_CMD_BUSY_ | ETH_E2P_CMD_READ_; val |= (ETH_E2P_CMD_ADDR_MASK_ & (off + i)); if ((err = lan78xx_write_reg(sc, ETH_E2P_CMD, val)) != 0) goto done; start_ticks = (usb_ticks_t)ticks; do { if ((err = lan78xx_read_reg(sc, ETH_E2P_CMD, &val)) != 0) goto done; if (!(val & ETH_E2P_CMD_BUSY_) || (val & ETH_E2P_CMD_TIMEOUT_)) break; uether_pause(&sc->sc_ue, hz / 100); } while (((usb_ticks_t)(ticks - start_ticks)) < max_ticks); if (val & (ETH_E2P_CMD_BUSY_ | ETH_E2P_CMD_TIMEOUT_)) { muge_warn_printf(sc, "eeprom command failed\n"); err = USB_ERR_IOERROR; break; } if ((err = lan78xx_read_reg(sc, ETH_E2P_DATA, &val)) != 0) goto done; buf[i] = (val & 0xff); } done: if (!locked) MUGE_UNLOCK(sc); if (sc->chipid == ETH_ID_REV_CHIP_ID_7800_) { /* Restore saved LED configuration. */ lan78xx_write_reg(sc, ETH_HW_CFG, saved); } return (err); } static bool lan78xx_eeprom_present(struct muge_softc *sc) { int ret; uint8_t sig; ret = lan78xx_eeprom_read_raw(sc, ETH_E2P_INDICATOR_OFFSET, &sig, 1); return (ret == 0 && sig == ETH_E2P_INDICATOR); } /** * lan78xx_otp_read_raw * @sc: soft context * @off: the otp address offset * @buf: stores the bytes * @buflen: the number of bytes to read * * Simply reads bytes from the OTP. * * LOCKING: * The function takes and releases the device lock if not already held. * * RETURNS: * 0 on success, or a USB_ERR_?? error code on failure. * */ static int lan78xx_otp_read_raw(struct muge_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) { int locked, err; uint32_t val; uint16_t i; locked = mtx_owned(&sc->sc_mtx); if (!locked) MUGE_LOCK(sc); err = lan78xx_read_reg(sc, OTP_PWR_DN, &val); /* Checking if bit is set. */ if (val & OTP_PWR_DN_PWRDN_N) { /* Clear it, then wait for it to be cleared. */ lan78xx_write_reg(sc, OTP_PWR_DN, 0); err = lan78xx_wait_for_bits(sc, OTP_PWR_DN, OTP_PWR_DN_PWRDN_N); if (err != 0) { muge_warn_printf(sc, "OTP off? failed to read data\n"); goto done; } } /* Start reading the bytes, one at a time. */ for (i = 0; i < buflen; i++) { err = lan78xx_write_reg(sc, OTP_ADDR1, ((off + i) >> 8) & OTP_ADDR1_15_11); err = lan78xx_write_reg(sc, OTP_ADDR2, ((off + i) & OTP_ADDR2_10_3)); err = lan78xx_write_reg(sc, OTP_FUNC_CMD, OTP_FUNC_CMD_READ_); err = lan78xx_write_reg(sc, OTP_CMD_GO, OTP_CMD_GO_GO_); err = lan78xx_wait_for_bits(sc, OTP_STATUS, OTP_STATUS_BUSY_); if (err != 0) { muge_warn_printf(sc, "OTP busy failed to read data\n"); goto done; } if ((err = lan78xx_read_reg(sc, OTP_RD_DATA, &val)) != 0) goto done; buf[i] = (uint8_t)(val & 0xff); } done: if (!locked) MUGE_UNLOCK(sc); return (err); } /** * lan78xx_otp_read * @sc: soft context * @off: the otp address offset * @buf: stores the bytes * @buflen: the number of bytes to read * * Simply reads bytes from the otp. * * LOCKING: * The function takes and releases device lock if it is not already held. * * RETURNS: * 0 on success, or a USB_ERR_?? error code on failure. */ static int lan78xx_otp_read(struct muge_softc *sc, uint16_t off, uint8_t *buf, uint16_t buflen) { uint8_t sig; int err; err = lan78xx_otp_read_raw(sc, OTP_INDICATOR_OFFSET, &sig, 1); if (err == 0) { if (sig == OTP_INDICATOR_1) { } else if (sig == OTP_INDICATOR_2) { off += 0x100; /* XXX */ } else { err = -EINVAL; } if (!err) err = lan78xx_otp_read_raw(sc, off, buf, buflen); } return (err); } /** * lan78xx_setmacaddress - Set the mac address in the device * @sc: driver soft context * @addr: pointer to array contain at least 6 bytes of the mac * * LOCKING: * Should be called with the MUGE lock held. * * RETURNS: * Returns 0 on success or a negative error code. */ static int lan78xx_setmacaddress(struct muge_softc *sc, const uint8_t *addr) { int err; uint32_t val; muge_dbg_printf(sc, "setting mac address to %02x:%02x:%02x:%02x:%02x:%02x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); MUGE_LOCK_ASSERT(sc, MA_OWNED); val = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; if ((err = lan78xx_write_reg(sc, ETH_RX_ADDRL, val)) != 0) goto done; val = (addr[5] << 8) | addr[4]; err = lan78xx_write_reg(sc, ETH_RX_ADDRH, val); done: return (err); } /** * lan78xx_set_rx_max_frame_length * @sc: driver soft context * @size: pointer to array contain at least 6 bytes of the mac * * Sets the maximum frame length to be received. Frames bigger than * this size are aborted. * * RETURNS: * Returns 0 on success or a negative error code. */ static int lan78xx_set_rx_max_frame_length(struct muge_softc *sc, int size) { int err = 0; uint32_t buf; bool rxenabled; /* First we have to disable rx before changing the length. */ err = lan78xx_read_reg(sc, ETH_MAC_RX, &buf); rxenabled = ((buf & ETH_MAC_RX_EN_) != 0); if (rxenabled) { buf &= ~ETH_MAC_RX_EN_; err = lan78xx_write_reg(sc, ETH_MAC_RX, buf); } /* Setting max frame length. */ buf &= ~ETH_MAC_RX_MAX_FR_SIZE_MASK_; buf |= (((size + 4) << ETH_MAC_RX_MAX_FR_SIZE_SHIFT_) & ETH_MAC_RX_MAX_FR_SIZE_MASK_); err = lan78xx_write_reg(sc, ETH_MAC_RX, buf); /* If it were enabled before, we enable it back. */ if (rxenabled) { buf |= ETH_MAC_RX_EN_; err = lan78xx_write_reg(sc, ETH_MAC_RX, buf); } return (0); } /** * lan78xx_miibus_readreg - Read a MII/MDIO register * @dev: usb ether device * @phy: the number of phy reading from * @reg: the register address * * LOCKING: * Takes and releases the device mutex lock if not already held. * * RETURNS: * Returns the 16-bits read from the MII register, if this function fails * 0 is returned. */ static int lan78xx_miibus_readreg(device_t dev, int phy, int reg) { struct muge_softc *sc = device_get_softc(dev); int locked; uint32_t addr, val; val = 0; locked = mtx_owned(&sc->sc_mtx); if (!locked) MUGE_LOCK(sc); if (lan78xx_wait_for_bits(sc, ETH_MII_ACC, ETH_MII_ACC_MII_BUSY_) != 0) { muge_warn_printf(sc, "MII is busy\n"); goto done; } addr = (phy << 11) | (reg << 6) | ETH_MII_ACC_MII_READ_ | ETH_MII_ACC_MII_BUSY_; lan78xx_write_reg(sc, ETH_MII_ACC, addr); if (lan78xx_wait_for_bits(sc, ETH_MII_ACC, ETH_MII_ACC_MII_BUSY_) != 0) { muge_warn_printf(sc, "MII read timeout\n"); goto done; } lan78xx_read_reg(sc, ETH_MII_DATA, &val); val = le32toh(val); done: if (!locked) MUGE_UNLOCK(sc); return (val & 0xFFFF); } /** * lan78xx_miibus_writereg - Writes a MII/MDIO register * @dev: usb ether device * @phy: the number of phy writing to * @reg: the register address * @val: the value to write * * Attempts to write a PHY register through the usb controller registers. * * LOCKING: * Takes and releases the device mutex lock if not already held. * * RETURNS: * Always returns 0 regardless of success or failure. */ static int lan78xx_miibus_writereg(device_t dev, int phy, int reg, int val) { struct muge_softc *sc = device_get_softc(dev); int locked; uint32_t addr; if (sc->sc_phyno != phy) return (0); locked = mtx_owned(&sc->sc_mtx); if (!locked) MUGE_LOCK(sc); if (lan78xx_wait_for_bits(sc, ETH_MII_ACC, ETH_MII_ACC_MII_BUSY_) != 0) { muge_warn_printf(sc, "MII is busy\n"); goto done; } val = htole32(val); lan78xx_write_reg(sc, ETH_MII_DATA, val); addr = (phy << 11) | (reg << 6) | ETH_MII_ACC_MII_WRITE_ | ETH_MII_ACC_MII_BUSY_; lan78xx_write_reg(sc, ETH_MII_ACC, addr); if (lan78xx_wait_for_bits(sc, ETH_MII_ACC, ETH_MII_ACC_MII_BUSY_) != 0) muge_warn_printf(sc, "MII write timeout\n"); done: if (!locked) MUGE_UNLOCK(sc); return (0); } /* * lan78xx_miibus_statchg - Called to detect phy status change * @dev: usb ether device * * This function is called periodically by the system to poll for status * changes of the link. * * LOCKING: * Takes and releases the device mutex lock if not already held. */ static void lan78xx_miibus_statchg(device_t dev) { struct muge_softc *sc = device_get_softc(dev); struct mii_data *mii = uether_getmii(&sc->sc_ue); struct ifnet *ifp; int locked; int err; uint32_t flow = 0; uint32_t fct_flow = 0; locked = mtx_owned(&sc->sc_mtx); if (!locked) MUGE_LOCK(sc); ifp = uether_getifp(&sc->sc_ue); if (mii == NULL || ifp == NULL || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) goto done; /* Use the MII status to determine link status */ sc->sc_flags &= ~MUGE_FLAG_LINK; if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == (IFM_ACTIVE | IFM_AVALID)) { muge_dbg_printf(sc, "media is active\n"); switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_10_T: case IFM_100_TX: sc->sc_flags |= MUGE_FLAG_LINK; muge_dbg_printf(sc, "10/100 ethernet\n"); break; case IFM_1000_T: sc->sc_flags |= MUGE_FLAG_LINK; muge_dbg_printf(sc, "Gigabit ethernet\n"); break; default: break; } } /* Lost link, do nothing. */ if ((sc->sc_flags & MUGE_FLAG_LINK) == 0) { muge_dbg_printf(sc, "link flag not set\n"); goto done; } err = lan78xx_read_reg(sc, ETH_FCT_FLOW, &fct_flow); if (err) { muge_warn_printf(sc, "failed to read initial flow control thresholds, error %d\n", err); goto done; } /* Enable/disable full duplex operation and TX/RX pause. */ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { muge_dbg_printf(sc, "full duplex operation\n"); /* Enable transmit MAC flow control function. */ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) flow |= ETH_FLOW_CR_TX_FCEN_ | 0xFFFF; if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) flow |= ETH_FLOW_CR_RX_FCEN_; } /* XXX Flow control settings obtained from Microchip's driver. */ switch(usbd_get_speed(sc->sc_ue.ue_udev)) { case USB_SPEED_SUPER: fct_flow = 0x817; break; case USB_SPEED_HIGH: fct_flow = 0x211; break; default: break; } err += lan78xx_write_reg(sc, ETH_FLOW, flow); err += lan78xx_write_reg(sc, ETH_FCT_FLOW, fct_flow); if (err) muge_warn_printf(sc, "media change failed, error %d\n", err); done: if (!locked) MUGE_UNLOCK(sc); } /* * lan78xx_set_mdix_auto - Configure the device to enable automatic * crossover and polarity detection. LAN7800 provides HP Auto-MDIX * functionality for seamless crossover and polarity detection. * * @sc: driver soft context * * LOCKING: * Takes and releases the device mutex lock if not already held. */ static void lan78xx_set_mdix_auto(struct muge_softc *sc) { uint32_t buf, err; err = lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MUGE_EXT_PAGE_ACCESS, MUGE_EXT_PAGE_SPACE_1); buf = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MUGE_EXT_MODE_CTRL); buf &= ~MUGE_EXT_MODE_CTRL_MDIX_MASK_; buf |= MUGE_EXT_MODE_CTRL_AUTO_MDIX_; lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); err += lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MUGE_EXT_MODE_CTRL, buf); err += lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MUGE_EXT_PAGE_ACCESS, MUGE_EXT_PAGE_SPACE_0); if (err != 0) muge_warn_printf(sc, "error setting PHY's MDIX status\n"); sc->sc_mdix_ctl = buf; } /** * lan78xx_phy_init - Initialises the in-built MUGE phy * @sc: driver soft context * * Resets the PHY part of the chip and then initialises it to default * values. The 'link down' and 'auto-negotiation complete' interrupts * from the PHY are also enabled, however we don't monitor the interrupt * endpoints for the moment. * * RETURNS: * Returns 0 on success or EIO if failed to reset the PHY. */ static int lan78xx_phy_init(struct muge_softc *sc) { muge_dbg_printf(sc, "Initializing PHY.\n"); - uint16_t bmcr; + uint16_t bmcr, lmsr; usb_ticks_t start_ticks; + uint32_t hw_reg; const usb_ticks_t max_ticks = USB_MS_TO_TICKS(1000); MUGE_LOCK_ASSERT(sc, MA_OWNED); /* Reset phy and wait for reset to complete. */ lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, BMCR_RESET); start_ticks = ticks; do { uether_pause(&sc->sc_ue, hz / 100); bmcr = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); } while ((bmcr & BMCR_RESET) && ((ticks - start_ticks) < max_ticks)); if (((usb_ticks_t)(ticks - start_ticks)) >= max_ticks) { muge_err_printf(sc, "PHY reset timed-out\n"); return (EIO); } /* Setup phy to interrupt upon link down or autoneg completion. */ lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MUGE_PHY_INTR_STAT); lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MUGE_PHY_INTR_MASK, (MUGE_PHY_INTR_ANEG_COMP | MUGE_PHY_INTR_LINK_CHANGE)); /* Enable Auto-MDIX for crossover and polarity detection. */ lan78xx_set_mdix_auto(sc); /* Enable all modes. */ lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_ANAR, ANAR_10 | ANAR_10_FD | ANAR_TX | ANAR_TX_FD | ANAR_CSMA | ANAR_FC | ANAR_PAUSE_ASYM); /* Restart auto-negotation. */ bmcr |= BMCR_STARTNEG; bmcr |= BMCR_AUTOEN; lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR, bmcr); bmcr = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, MII_BMCR); + + /* Configure LED Modes. */ + if (sc->sc_led_modes_mask != 0) { + lmsr = lan78xx_miibus_readreg(sc->sc_ue.ue_dev, sc->sc_phyno, + MUGE_PHY_LED_MODE); + lmsr &= ~sc->sc_led_modes_mask; + lmsr |= sc->sc_led_modes; + lan78xx_miibus_writereg(sc->sc_ue.ue_dev, sc->sc_phyno, + MUGE_PHY_LED_MODE, lmsr); + } + + /* Enable appropriate LEDs. */ + if (sc->sc_leds != 0 && + lan78xx_read_reg(sc, ETH_HW_CFG, &hw_reg) == 0) { + hw_reg &= ~(ETH_HW_CFG_LEDO_EN_ | ETH_HW_CFG_LED1_EN_ | + ETH_HW_CFG_LED2_EN_ | ETH_HW_CFG_LED3_EN_ ); + hw_reg |= sc->sc_leds; + lan78xx_write_reg(sc, ETH_HW_CFG, hw_reg); + } return (0); } /** * lan78xx_chip_init - Initialises the chip after power on * @sc: driver soft context * * This initialisation sequence is modelled on the procedure in the Linux * driver. * * RETURNS: * Returns 0 on success or an error code on failure. */ static int lan78xx_chip_init(struct muge_softc *sc) { int err; uint32_t buf; uint32_t burst_cap; MUGE_LOCK_ASSERT(sc, MA_OWNED); /* Enter H/W config mode. */ lan78xx_write_reg(sc, ETH_HW_CFG, ETH_HW_CFG_LRST_); if ((err = lan78xx_wait_for_bits(sc, ETH_HW_CFG, ETH_HW_CFG_LRST_)) != 0) { muge_warn_printf(sc, "timed-out waiting for lite reset to complete\n"); goto init_failed; } /* Set the mac address. */ if ((err = lan78xx_setmacaddress(sc, sc->sc_ue.ue_eaddr)) != 0) { muge_warn_printf(sc, "failed to set the MAC address\n"); goto init_failed; } /* Read and display the revision register. */ if ((err = lan78xx_read_reg(sc, ETH_ID_REV, &buf)) < 0) { muge_warn_printf(sc, "failed to read ETH_ID_REV (err = %d)\n", err); goto init_failed; } sc->chipid = (buf & ETH_ID_REV_CHIP_ID_MASK_) >> 16; sc->chiprev = buf & ETH_ID_REV_CHIP_REV_MASK_; switch (sc->chipid) { case ETH_ID_REV_CHIP_ID_7800_: case ETH_ID_REV_CHIP_ID_7850_: break; default: muge_warn_printf(sc, "Chip ID 0x%04x not yet supported\n", sc->chipid); goto init_failed; } device_printf(sc->sc_ue.ue_dev, "Chip ID 0x%04x rev %04x\n", sc->chipid, sc->chiprev); /* Respond to BULK-IN tokens with a NAK when RX FIFO is empty. */ if ((err = lan78xx_read_reg(sc, ETH_USB_CFG0, &buf)) != 0) { muge_warn_printf(sc, "failed to read ETH_USB_CFG0 (err=%d)\n", err); goto init_failed; } buf |= ETH_USB_CFG_BIR_; lan78xx_write_reg(sc, ETH_USB_CFG0, buf); /* * XXX LTM support will go here. */ /* Configuring the burst cap. */ switch (usbd_get_speed(sc->sc_ue.ue_udev)) { case USB_SPEED_SUPER: burst_cap = MUGE_DEFAULT_BURST_CAP_SIZE/MUGE_SS_USB_PKT_SIZE; break; case USB_SPEED_HIGH: burst_cap = MUGE_DEFAULT_BURST_CAP_SIZE/MUGE_HS_USB_PKT_SIZE; break; default: burst_cap = MUGE_DEFAULT_BURST_CAP_SIZE/MUGE_FS_USB_PKT_SIZE; } lan78xx_write_reg(sc, ETH_BURST_CAP, burst_cap); /* Set the default bulk in delay (same value from Linux driver). */ lan78xx_write_reg(sc, ETH_BULK_IN_DLY, MUGE_DEFAULT_BULK_IN_DELAY); /* Multiple ethernet frames per USB packets. */ err = lan78xx_read_reg(sc, ETH_HW_CFG, &buf); buf |= ETH_HW_CFG_MEF_; err = lan78xx_write_reg(sc, ETH_HW_CFG, buf); /* Enable burst cap. */ if ((err = lan78xx_read_reg(sc, ETH_USB_CFG0, &buf)) < 0) { muge_warn_printf(sc, "failed to read ETH_USB_CFG0 (err=%d)\n", err); goto init_failed; } buf |= ETH_USB_CFG_BCE_; err = lan78xx_write_reg(sc, ETH_USB_CFG0, buf); /* * Set FCL's RX and TX FIFO sizes: according to data sheet this is * already the default value. But we initialize it to the same value * anyways, as that's what the Linux driver does. * */ buf = (MUGE_MAX_RX_FIFO_SIZE - 512) / 512; err = lan78xx_write_reg(sc, ETH_FCT_RX_FIFO_END, buf); buf = (MUGE_MAX_TX_FIFO_SIZE - 512) / 512; err = lan78xx_write_reg(sc, ETH_FCT_TX_FIFO_END, buf); /* Enabling interrupts. (Not using them for now) */ err = lan78xx_write_reg(sc, ETH_INT_STS, ETH_INT_STS_CLEAR_ALL_); /* * Initializing flow control registers to 0. These registers are * properly set is handled in link-reset function in the Linux driver. */ err = lan78xx_write_reg(sc, ETH_FLOW, 0); err = lan78xx_write_reg(sc, ETH_FCT_FLOW, 0); /* * Settings for the RFE, we enable broadcast and destination address * perfect filtering. */ err = lan78xx_read_reg(sc, ETH_RFE_CTL, &buf); buf |= ETH_RFE_CTL_BCAST_EN_ | ETH_RFE_CTL_DA_PERFECT_; err = lan78xx_write_reg(sc, ETH_RFE_CTL, buf); /* * At this point the Linux driver writes multicast tables, and enables * checksum engines. But in FreeBSD that gets done in muge_init, * which gets called when the interface is brought up. */ /* Reset the PHY. */ lan78xx_write_reg(sc, ETH_PMT_CTL, ETH_PMT_CTL_PHY_RST_); if ((err = lan78xx_wait_for_bits(sc, ETH_PMT_CTL, ETH_PMT_CTL_PHY_RST_)) != 0) { muge_warn_printf(sc, "timed-out waiting for phy reset to complete\n"); goto init_failed; } err = lan78xx_read_reg(sc, ETH_MAC_CR, &buf); if (sc->chipid == ETH_ID_REV_CHIP_ID_7800_ && !lan78xx_eeprom_present(sc)) { /* Set automatic duplex and speed on LAN7800 without EEPROM. */ buf |= ETH_MAC_CR_AUTO_DUPLEX_ | ETH_MAC_CR_AUTO_SPEED_; } err = lan78xx_write_reg(sc, ETH_MAC_CR, buf); /* * Enable PHY interrupts (Not really getting used for now) * ETH_INT_EP_CTL: interrupt endpoint control register * phy events cause interrupts to be issued */ err = lan78xx_read_reg(sc, ETH_INT_EP_CTL, &buf); buf |= ETH_INT_ENP_PHY_INT; err = lan78xx_write_reg(sc, ETH_INT_EP_CTL, buf); /* * Enables mac's transmitter. It will transmit frames from the buffer * onto the cable. */ err = lan78xx_read_reg(sc, ETH_MAC_TX, &buf); buf |= ETH_MAC_TX_TXEN_; err = lan78xx_write_reg(sc, ETH_MAC_TX, buf); /* FIFO is capable of transmitting frames to MAC. */ err = lan78xx_read_reg(sc, ETH_FCT_TX_CTL, &buf); buf |= ETH_FCT_TX_CTL_EN_; err = lan78xx_write_reg(sc, ETH_FCT_TX_CTL, buf); /* * Set max frame length. In linux this is dev->mtu (which by default * is 1500) + VLAN_ETH_HLEN = 1518. */ err = lan78xx_set_rx_max_frame_length(sc, ETHER_MAX_LEN); /* Initialise the PHY. */ if ((err = lan78xx_phy_init(sc)) != 0) goto init_failed; /* Enable MAC RX. */ err = lan78xx_read_reg(sc, ETH_MAC_RX, &buf); buf |= ETH_MAC_RX_EN_; err = lan78xx_write_reg(sc, ETH_MAC_RX, buf); /* Enable FIFO controller RX. */ err = lan78xx_read_reg(sc, ETH_FCT_RX_CTL, &buf); buf |= ETH_FCT_TX_CTL_EN_; err = lan78xx_write_reg(sc, ETH_FCT_RX_CTL, buf); sc->sc_flags |= MUGE_FLAG_INIT_DONE; return (0); init_failed: muge_err_printf(sc, "lan78xx_chip_init failed (err=%d)\n", err); return (err); } static void muge_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct muge_softc *sc = usbd_xfer_softc(xfer); struct usb_ether *ue = &sc->sc_ue; struct ifnet *ifp = uether_getifp(ue); struct mbuf *m; struct usb_page_cache *pc; uint16_t pktlen; uint32_t rx_cmd_a, rx_cmd_b; uint16_t rx_cmd_c; int off; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); muge_dbg_printf(sc, "rx : actlen %d\n", actlen); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: /* * There is always a zero length frame after bringing the * interface up. */ if (actlen < (sizeof(rx_cmd_a) + ETHER_CRC_LEN)) goto tr_setup; /* * There may be multiple packets in the USB frame. Each will * have a header and each needs to have its own mbuf allocated * and populated for it. */ pc = usbd_xfer_get_frame(xfer, 0); off = 0; while (off < actlen) { /* The frame header is aligned on a 4 byte boundary. */ off = ((off + 0x3) & ~0x3); /* Extract RX CMD A. */ if (off + sizeof(rx_cmd_a) > actlen) goto tr_setup; usbd_copy_out(pc, off, &rx_cmd_a, sizeof(rx_cmd_a)); off += (sizeof(rx_cmd_a)); rx_cmd_a = le32toh(rx_cmd_a); /* Extract RX CMD B. */ if (off + sizeof(rx_cmd_b) > actlen) goto tr_setup; usbd_copy_out(pc, off, &rx_cmd_b, sizeof(rx_cmd_b)); off += (sizeof(rx_cmd_b)); rx_cmd_b = le32toh(rx_cmd_b); /* Extract RX CMD C. */ if (off + sizeof(rx_cmd_c) > actlen) goto tr_setup; usbd_copy_out(pc, off, &rx_cmd_c, sizeof(rx_cmd_c)); off += (sizeof(rx_cmd_c)); rx_cmd_c = le16toh(rx_cmd_c); if (off > actlen) goto tr_setup; pktlen = (rx_cmd_a & RX_CMD_A_LEN_MASK_); muge_dbg_printf(sc, "rx_cmd_a 0x%08x rx_cmd_b 0x%08x rx_cmd_c 0x%04x " " pktlen %d actlen %d off %d\n", rx_cmd_a, rx_cmd_b, rx_cmd_c, pktlen, actlen, off); if (rx_cmd_a & RX_CMD_A_RED_) { muge_dbg_printf(sc, "rx error (hdr 0x%08x)\n", rx_cmd_a); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); } else { /* Ethernet frame too big or too small? */ if ((pktlen < ETHER_HDR_LEN) || (pktlen > (actlen - off))) goto tr_setup; /* Create a new mbuf to store the packet. */ m = uether_newbuf(); if (m == NULL) { muge_warn_printf(sc, "failed to create new mbuf\n"); if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); goto tr_setup; } usbd_copy_out(pc, off, mtod(m, uint8_t *), pktlen); /* * Check if RX checksums are computed, and * offload them */ if ((ifp->if_capabilities & IFCAP_RXCSUM) && !(rx_cmd_a & RX_CMD_A_ICSM_)) { struct ether_header *eh; eh = mtod(m, struct ether_header *); /* * Remove the extra 2 bytes of the csum * * The checksum appears to be * simplistically calculated over the * protocol headers up to the end of the * eth frame. Which means if the eth * frame is padded the csum calculation * is incorrectly performed over the * padding bytes as well. Therefore to * be safe we ignore the H/W csum on * frames less than or equal to * 64 bytes. * * Protocols checksummed: * TCP, UDP, ICMP, IGMP, IP */ if (pktlen > ETHER_MIN_LEN) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID; /* * Copy the checksum from the * last 2 bytes of the transfer * and put in the csum_data * field. */ usbd_copy_out(pc, (off + pktlen), &m->m_pkthdr.csum_data, 2); /* * The data is copied in network * order, but the csum algorithm * in the kernel expects it to * be in host network order. */ m->m_pkthdr.csum_data = ntohs(m->m_pkthdr.csum_data); muge_dbg_printf(sc, "RX checksum offloaded (0x%04x)\n", m->m_pkthdr.csum_data); } } /* Enqueue the mbuf on the receive queue. */ if (pktlen < (4 + ETHER_HDR_LEN)) { m_freem(m); goto tr_setup; } /* Remove 4 trailing bytes */ uether_rxmbuf(ue, m, pktlen - 4); } /* * Update the offset to move to the next potential * packet. */ off += pktlen; } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); uether_rxflush(ue); return; default: if (error != USB_ERR_CANCELLED) { muge_warn_printf(sc, "bulk read error, %s\n", usbd_errstr(error)); usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } /** * muge_bulk_write_callback - Write callback used to send ethernet frame(s) * @xfer: the USB transfer * @error: error code if the transfers is in an errored state * * The main write function that pulls ethernet frames off the queue and * sends them out. * */ static void muge_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct muge_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = uether_getifp(&sc->sc_ue); struct usb_page_cache *pc; struct mbuf *m; int nframes; uint32_t frm_len = 0, tx_cmd_a = 0, tx_cmd_b = 0; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: muge_dbg_printf(sc, "USB TRANSFER status: USB_ST_TRANSFERRED\n"); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* FALLTHROUGH */ case USB_ST_SETUP: muge_dbg_printf(sc, "USB TRANSFER status: USB_ST_SETUP\n"); tr_setup: if ((sc->sc_flags & MUGE_FLAG_LINK) == 0 || (ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) { muge_dbg_printf(sc, "sc->sc_flags & MUGE_FLAG_LINK: %d\n", (sc->sc_flags & MUGE_FLAG_LINK)); muge_dbg_printf(sc, "ifp->if_drv_flags & IFF_DRV_OACTIVE: %d\n", (ifp->if_drv_flags & IFF_DRV_OACTIVE)); muge_dbg_printf(sc, "USB TRANSFER not sending: no link or controller is busy \n"); /* * Don't send anything if there is no link or * controller is busy. */ return; } for (nframes = 0; nframes < 16 && !IFQ_DRV_IS_EMPTY(&ifp->if_snd); nframes++) { IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; usbd_xfer_set_frame_offset(xfer, nframes * MCLBYTES, nframes); frm_len = 0; pc = usbd_xfer_get_frame(xfer, nframes); /* * Each frame is prefixed with two 32-bit values * describing the length of the packet and buffer. */ tx_cmd_a = (m->m_pkthdr.len & TX_CMD_A_LEN_MASK_) | TX_CMD_A_FCS_; tx_cmd_a = htole32(tx_cmd_a); usbd_copy_in(pc, 0, &tx_cmd_a, sizeof(tx_cmd_a)); tx_cmd_b = 0; /* TCP LSO Support will probably be implemented here. */ tx_cmd_b = htole32(tx_cmd_b); usbd_copy_in(pc, 4, &tx_cmd_b, sizeof(tx_cmd_b)); frm_len += 8; /* Next copy in the actual packet */ usbd_m_copy_in(pc, frm_len, m, 0, m->m_pkthdr.len); frm_len += m->m_pkthdr.len; if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); /* * If there's a BPF listener, bounce a copy of this * frame to it. */ BPF_MTAP(ifp, m); m_freem(m); /* Set frame length. */ usbd_xfer_set_frame_len(xfer, nframes, frm_len); } muge_dbg_printf(sc, "USB TRANSFER nframes: %d\n", nframes); if (nframes != 0) { muge_dbg_printf(sc, "USB TRANSFER submit attempt\n"); usbd_xfer_set_frames(xfer, nframes); usbd_transfer_submit(xfer); ifp->if_drv_flags |= IFF_DRV_OACTIVE; } return; default: if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; if (error != USB_ERR_CANCELLED) { muge_err_printf(sc, "usb error on tx: %s\n", usbd_errstr(error)); usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } -#ifdef FDT /** - * muge_fdt_find_eth_node - find descendant node with required compatibility - * @start: start node - * @compatible: compatible string used to identify the node - * - * Loop through all descendant nodes and return first match with required - * compatibility. - * - * RETURNS: - * Returns node's phandle on success -1 otherwise - */ -static phandle_t -muge_fdt_find_eth_node(phandle_t start, const char *compatible) -{ - phandle_t child, node; - - /* Traverse through entire tree to find usb ethernet nodes. */ - for (node = OF_child(start); node != 0; node = OF_peer(node)) { - if (ofw_bus_node_is_compatible(node, compatible)) - return (node); - child = muge_fdt_find_eth_node(node, compatible); - if (child != -1) - return (child); - } - - return (-1); -} - -/** - * muge_fdt_read_mac_property - read MAC address from node - * @node: USB device node - * @mac: memory to store MAC address to - * - * Check for common properties that might contain MAC address - * passed by boot loader. - * - * RETURNS: - * Returns 0 on success, error code otherwise - */ -static int -muge_fdt_read_mac_property(phandle_t node, unsigned char *mac) -{ - int len; - - /* Check if there is property */ - if ((len = OF_getproplen(node, "local-mac-address")) > 0) { - if (len != ETHER_ADDR_LEN) - return (EINVAL); - - OF_getprop(node, "local-mac-address", mac, - ETHER_ADDR_LEN); - return (0); - } - - if ((len = OF_getproplen(node, "mac-address")) > 0) { - if (len != ETHER_ADDR_LEN) - return (EINVAL); - - OF_getprop(node, "mac-address", mac, - ETHER_ADDR_LEN); - return (0); - } - - return (ENXIO); -} - -/** - * muge_fdt_find_mac - read MAC address from node - * @compatible: compatible string for DTB node in the form "usb[N]NNN,[M]MMM" - * where NNN is vendor id and MMM is product id - * @mac: memory to store MAC address to - * - * Tries to find matching node in DTS and obtain MAC address info from it - * - * RETURNS: - * Returns 0 on success, error code otherwise - */ -static int -muge_fdt_find_mac(const char *compatible, unsigned char *mac) -{ - phandle_t node, root; - - root = OF_finddevice("/"); - node = muge_fdt_find_eth_node(root, compatible); - if (node != -1) { - if (muge_fdt_read_mac_property(node, mac) == 0) - return (0); - } - - return (ENXIO); -} -#endif - -/** * muge_set_mac_addr - Initiailizes NIC MAC address * @ue: the USB ethernet device * * Tries to obtain MAC address from number of sources: registers, * EEPROM, DTB blob. If all sources fail - generates random MAC. */ static void muge_set_mac_addr(struct usb_ether *ue) { struct muge_softc *sc = uether_getsc(ue); uint32_t mac_h, mac_l; -#ifdef FDT - char compatible[16]; - struct usb_attach_arg *uaa = device_get_ivars(ue->ue_dev); -#endif - memset(sc->sc_ue.ue_eaddr, 0xff, ETHER_ADDR_LEN); + memset(ue->ue_eaddr, 0xff, ETHER_ADDR_LEN); uint32_t val; lan78xx_read_reg(sc, 0, &val); /* Read current MAC address from RX_ADDRx registers. */ if ((lan78xx_read_reg(sc, ETH_RX_ADDRL, &mac_l) == 0) && (lan78xx_read_reg(sc, ETH_RX_ADDRH, &mac_h) == 0)) { - sc->sc_ue.ue_eaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); - sc->sc_ue.ue_eaddr[4] = (uint8_t)((mac_h) & 0xff); - sc->sc_ue.ue_eaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); - sc->sc_ue.ue_eaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); - sc->sc_ue.ue_eaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); - sc->sc_ue.ue_eaddr[0] = (uint8_t)((mac_l) & 0xff); + ue->ue_eaddr[5] = (uint8_t)((mac_h >> 8) & 0xff); + ue->ue_eaddr[4] = (uint8_t)((mac_h) & 0xff); + ue->ue_eaddr[3] = (uint8_t)((mac_l >> 24) & 0xff); + ue->ue_eaddr[2] = (uint8_t)((mac_l >> 16) & 0xff); + ue->ue_eaddr[1] = (uint8_t)((mac_l >> 8) & 0xff); + ue->ue_eaddr[0] = (uint8_t)((mac_l) & 0xff); } - /* If RX_ADDRx did not provide a valid MAC address, try EEPROM. */ - if (ETHER_IS_VALID(sc->sc_ue.ue_eaddr)) { + /* + * If RX_ADDRx did not provide a valid MAC address, try EEPROM. If that + * doesn't work, try OTP. Whether any of these methods work or not, try + * FDT data, because it is allowed to override the EEPROM/OTP values. + */ + if (ETHER_IS_VALID(ue->ue_eaddr)) { muge_dbg_printf(sc, "MAC assigned from registers\n"); - return; + } else if (lan78xx_eeprom_present(sc) && lan78xx_eeprom_read_raw(sc, + ETH_E2P_MAC_OFFSET, ue->ue_eaddr, ETHER_ADDR_LEN) == 0 && + ETHER_IS_VALID(ue->ue_eaddr)) { + muge_dbg_printf(sc, "MAC assigned from EEPROM\n"); + } else if (lan78xx_otp_read(sc, OTP_MAC_OFFSET, ue->ue_eaddr, + ETHER_ADDR_LEN) == 0 && ETHER_IS_VALID(ue->ue_eaddr)) { + muge_dbg_printf(sc, "MAC assigned from OTP\n"); } - if ((lan78xx_eeprom_present(sc) && - lan78xx_eeprom_read_raw(sc, ETH_E2P_MAC_OFFSET, - sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN) == 0) || - (lan78xx_otp_read(sc, OTP_MAC_OFFSET, - sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN) == 0)) { - if (ETHER_IS_VALID(sc->sc_ue.ue_eaddr)) { - muge_dbg_printf(sc, "MAC read from EEPROM\n"); - return; - } +#ifdef FDT + /* ue->ue_eaddr modified only if config exists for this dev instance. */ + usb_fdt_get_mac_addr(ue->ue_dev, ue); + if (ETHER_IS_VALID(ue->ue_eaddr)) { + muge_dbg_printf(sc, "MAC assigned from FDT data\n"); } +#endif + if (!ETHER_IS_VALID(ue->ue_eaddr)) { + muge_dbg_printf(sc, "MAC assigned randomly\n"); + arc4rand(ue->ue_eaddr, ETHER_ADDR_LEN, 0); + ue->ue_eaddr[0] &= ~0x01; /* unicast */ + ue->ue_eaddr[0] |= 0x02; /* locally administered */ + } +} + +/** + * muge_set_leds - Initializes NIC LEDs pattern + * @ue: the USB ethernet device + * + * Tries to store the LED modes. + * Supports only DTB blob like the Linux driver does. + */ +static void +muge_set_leds(struct usb_ether *ue) +{ #ifdef FDT - snprintf(compatible, sizeof(compatible), "usb%x,%x", - uaa->info.idVendor, uaa->info.idProduct); - if (muge_fdt_find_mac(compatible, sc->sc_ue.ue_eaddr) == 0) { - muge_dbg_printf(sc, "MAC assigned from FDT blob\n"); - return; + struct muge_softc *sc = uether_getsc(ue); + phandle_t node; + pcell_t modes[4]; /* 4 LEDs are possible */ + ssize_t proplen; + uint32_t count; + + if ((node = usb_fdt_get_node(ue->ue_dev, ue->ue_udev)) != -1 && + (proplen = OF_getencprop(node, "microchip,led-modes", modes, + sizeof(modes))) > 0) { + count = proplen / sizeof( uint32_t ); + sc->sc_leds = (count > 0) * ETH_HW_CFG_LEDO_EN_ | + (count > 1) * ETH_HW_CFG_LED1_EN_ | + (count > 2) * ETH_HW_CFG_LED2_EN_ | + (count > 3) * ETH_HW_CFG_LED3_EN_; + while (count-- > 0) { + sc->sc_led_modes |= (modes[count] & 0xf) << (4 * count); + sc->sc_led_modes_mask |= 0xf << (4 * count); + } + muge_dbg_printf(sc, "LED modes set from FDT data\n"); } #endif - - muge_dbg_printf(sc, "MAC assigned randomly\n"); - arc4rand(sc->sc_ue.ue_eaddr, ETHER_ADDR_LEN, 0); - sc->sc_ue.ue_eaddr[0] &= ~0x01; /* unicast */ - sc->sc_ue.ue_eaddr[0] |= 0x02; /* locally administered */ } /** * muge_attach_post - Called after the driver attached to the USB interface * @ue: the USB ethernet device * * This is where the chip is intialised for the first time. This is * different from the muge_init() function in that that one is designed to * setup the H/W to match the UE settings and can be called after a reset. * */ static void muge_attach_post(struct usb_ether *ue) { struct muge_softc *sc = uether_getsc(ue); muge_dbg_printf(sc, "Calling muge_attach_post.\n"); /* Setup some of the basics */ sc->sc_phyno = 1; muge_set_mac_addr(ue); + muge_set_leds(ue); /* Initialise the chip for the first time */ lan78xx_chip_init(sc); } /** * muge_attach_post_sub - Called after attach to the USB interface * @ue: the USB ethernet device * * Most of this is boilerplate code and copied from the base USB ethernet * driver. It has been overriden so that we can indicate to the system * that the chip supports H/W checksumming. * * RETURNS: * Returns 0 on success or a negative error code. */ static int muge_attach_post_sub(struct usb_ether *ue) { struct muge_softc *sc; struct ifnet *ifp; int error; sc = uether_getsc(ue); muge_dbg_printf(sc, "Calling muge_attach_post_sub.\n"); ifp = ue->ue_ifp; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_start = uether_start; ifp->if_ioctl = muge_ioctl; ifp->if_init = uether_init; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); /* * The chip supports TCP/UDP checksum offloading on TX and RX paths, * however currently only RX checksum is supported in the driver * (see top of file). */ ifp->if_hwassist = 0; if (MUGE_DEFAULT_RX_CSUM_ENABLE) ifp->if_capabilities |= IFCAP_RXCSUM; if (MUGE_DEFAULT_TX_CSUM_ENABLE) ifp->if_capabilities |= IFCAP_TXCSUM; /* * In the Linux driver they also enable scatter/gather (NETIF_F_SG) * here, that's something related to socket buffers used in Linux. * FreeBSD doesn't have that as an interface feature. */ if (MUGE_DEFAULT_TSO_CSUM_ENABLE) ifp->if_capabilities |= IFCAP_TSO4 | IFCAP_TSO6; #if 0 /* TX checksuming is disabled since not yet implemented. */ ifp->if_capabilities |= IFCAP_TXCSUM; ifp->if_capenable |= IFCAP_TXCSUM; ifp->if_hwassist = CSUM_TCP | CSUM_UDP; #endif ifp->if_capenable = ifp->if_capabilities; mtx_lock(&Giant); error = mii_attach(ue->ue_dev, &ue->ue_miibus, ifp, uether_ifmedia_upd, ue->ue_methods->ue_mii_sts, BMSR_DEFCAPMASK, sc->sc_phyno, MII_OFFSET_ANY, 0); mtx_unlock(&Giant); return (0); } /** * muge_start - Starts communication with the LAN78xx chip * @ue: USB ether interface */ static void muge_start(struct usb_ether *ue) { struct muge_softc *sc = uether_getsc(ue); /* * Start the USB transfers, if not already started. */ usbd_transfer_start(sc->sc_xfer[MUGE_BULK_DT_RD]); usbd_transfer_start(sc->sc_xfer[MUGE_BULK_DT_WR]); } /** * muge_ioctl - ioctl function for the device * @ifp: interface pointer * @cmd: the ioctl command * @data: data passed in the ioctl call, typically a pointer to struct * ifreq. * * The ioctl routine is overridden to detect change requests for the H/W * checksum capabilities. * * RETURNS: * 0 on success and an error code on failure. */ static int muge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct usb_ether *ue = ifp->if_softc; struct muge_softc *sc; struct ifreq *ifr; int rc; int mask; int reinit; if (cmd == SIOCSIFCAP) { sc = uether_getsc(ue); ifr = (struct ifreq *)data; MUGE_LOCK(sc); rc = 0; reinit = 0; mask = ifr->ifr_reqcap ^ ifp->if_capenable; /* Modify the RX CSUM enable bits. */ if ((mask & IFCAP_RXCSUM) != 0 && (ifp->if_capabilities & IFCAP_RXCSUM) != 0) { ifp->if_capenable ^= IFCAP_RXCSUM; if (ifp->if_drv_flags & IFF_DRV_RUNNING) { ifp->if_drv_flags &= ~IFF_DRV_RUNNING; reinit = 1; } } MUGE_UNLOCK(sc); if (reinit) uether_init(ue); } else { rc = uether_ioctl(ifp, cmd, data); } return (rc); } /** * muge_reset - Reset the SMSC chip * @sc: device soft context * * LOCKING: * Should be called with the SMSC lock held. */ static void muge_reset(struct muge_softc *sc) { struct usb_config_descriptor *cd; usb_error_t err; cd = usbd_get_config_descriptor(sc->sc_ue.ue_udev); err = usbd_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx, cd->bConfigurationValue); if (err) muge_warn_printf(sc, "reset failed (ignored)\n"); /* Wait a little while for the chip to get its brains in order. */ uether_pause(&sc->sc_ue, hz / 100); /* Reinitialize controller to achieve full reset. */ lan78xx_chip_init(sc); } /** * muge_set_addr_filter * * @sc: device soft context * @index: index of the entry to the perfect address table * @addr: address to be written * */ static void muge_set_addr_filter(struct muge_softc *sc, int index, uint8_t addr[ETHER_ADDR_LEN]) { uint32_t tmp; if ((sc) && (index > 0) && (index < MUGE_NUM_PFILTER_ADDRS_)) { tmp = addr[3]; tmp |= addr[2] | (tmp << 8); tmp |= addr[1] | (tmp << 8); tmp |= addr[0] | (tmp << 8); sc->sc_pfilter_table[index][1] = tmp; tmp = addr[5]; tmp |= addr[4] | (tmp << 8); tmp |= ETH_MAF_HI_VALID_ | ETH_MAF_HI_TYPE_DST_; sc->sc_pfilter_table[index][0] = tmp; } } /** * lan78xx_dataport_write - write to the selected RAM * @sc: The device soft context. * @ram_select: Select which RAM to access. * @addr: Starting address to write to. * @buf: word-sized buffer to write to RAM, starting at @addr. * @length: length of @buf * * * RETURNS: * 0 if write successful. */ static int lan78xx_dataport_write(struct muge_softc *sc, uint32_t ram_select, uint32_t addr, uint32_t length, uint32_t *buf) { uint32_t dp_sel; int i, ret; MUGE_LOCK_ASSERT(sc, MA_OWNED); ret = lan78xx_wait_for_bits(sc, ETH_DP_SEL, ETH_DP_SEL_DPRDY_); if (ret < 0) goto done; ret = lan78xx_read_reg(sc, ETH_DP_SEL, &dp_sel); dp_sel &= ~ETH_DP_SEL_RSEL_MASK_; dp_sel |= ram_select; ret = lan78xx_write_reg(sc, ETH_DP_SEL, dp_sel); for (i = 0; i < length; i++) { ret = lan78xx_write_reg(sc, ETH_DP_ADDR, addr + i); ret = lan78xx_write_reg(sc, ETH_DP_DATA, buf[i]); ret = lan78xx_write_reg(sc, ETH_DP_CMD, ETH_DP_CMD_WRITE_); ret = lan78xx_wait_for_bits(sc, ETH_DP_SEL, ETH_DP_SEL_DPRDY_); if (ret != 0) goto done; } done: return (ret); } /** * muge_multicast_write * @sc: device's soft context * * Writes perfect addres filters and hash address filters to their * corresponding registers and RAMs. * */ static void muge_multicast_write(struct muge_softc *sc) { int i, ret; lan78xx_dataport_write(sc, ETH_DP_SEL_RSEL_VLAN_DA_, ETH_DP_SEL_VHF_VLAN_LEN, ETH_DP_SEL_VHF_HASH_LEN, sc->sc_mchash_table); for (i = 1; i < MUGE_NUM_PFILTER_ADDRS_; i++) { ret = lan78xx_write_reg(sc, PFILTER_HI(i), 0); ret = lan78xx_write_reg(sc, PFILTER_LO(i), sc->sc_pfilter_table[i][1]); ret = lan78xx_write_reg(sc, PFILTER_HI(i), sc->sc_pfilter_table[i][0]); } } /** * muge_hash - Calculate the hash of a mac address * @addr: The mac address to calculate the hash on * * This function is used when configuring a range of multicast mac * addresses to filter on. The hash of the mac address is put in the * device's mac hash table. * * RETURNS: * Returns a value from 0-63 value which is the hash of the mac address. */ static inline uint32_t muge_hash(uint8_t addr[ETHER_ADDR_LEN]) { return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 23) & 0x1ff; } /** * muge_setmulti - Setup multicast * @ue: usb ethernet device context * * Tells the device to either accept frames with a multicast mac address, * a select group of m'cast mac addresses or just the devices mac address. * * LOCKING: * Should be called with the MUGE lock held. */ static void muge_setmulti(struct usb_ether *ue) { struct muge_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); uint8_t i, *addr; struct ifmultiaddr *ifma; MUGE_LOCK_ASSERT(sc, MA_OWNED); sc->sc_rfe_ctl &= ~(ETH_RFE_CTL_UCAST_EN_ | ETH_RFE_CTL_MCAST_EN_ | ETH_RFE_CTL_DA_PERFECT_ | ETH_RFE_CTL_MCAST_HASH_); /* Initialize hash filter table. */ for (i = 0; i < ETH_DP_SEL_VHF_HASH_LEN; i++) sc->sc_mchash_table[i] = 0; /* Initialize perfect filter table. */ for (i = 1; i < MUGE_NUM_PFILTER_ADDRS_; i++) { sc->sc_pfilter_table[i][0] = sc->sc_pfilter_table[i][1] = 0; } sc->sc_rfe_ctl |= ETH_RFE_CTL_BCAST_EN_; if (ifp->if_flags & IFF_PROMISC) { muge_dbg_printf(sc, "promiscuous mode enabled\n"); sc->sc_rfe_ctl |= ETH_RFE_CTL_MCAST_EN_ | ETH_RFE_CTL_UCAST_EN_; } else if (ifp->if_flags & IFF_ALLMULTI){ muge_dbg_printf(sc, "receive all multicast enabled\n"); sc->sc_rfe_ctl |= ETH_RFE_CTL_MCAST_EN_; } else { /* Lock the mac address list before hashing each of them. */ if_maddr_rlock(ifp); if (!CK_STAILQ_EMPTY(&ifp->if_multiaddrs)) { i = 1; CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { /* First fill up the perfect address table. */ addr = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); if (i < 33 /* XXX */) { muge_set_addr_filter(sc, i, addr); } else { uint32_t bitnum = muge_hash(addr); sc->sc_mchash_table[bitnum / 32] |= (1 << (bitnum % 32)); sc->sc_rfe_ctl |= ETH_RFE_CTL_MCAST_HASH_; } i++; } } if_maddr_runlock(ifp); muge_multicast_write(sc); } lan78xx_write_reg(sc, ETH_RFE_CTL, sc->sc_rfe_ctl); } /** * muge_setpromisc - Enables/disables promiscuous mode * @ue: usb ethernet device context * * LOCKING: * Should be called with the MUGE lock held. */ static void muge_setpromisc(struct usb_ether *ue) { struct muge_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); muge_dbg_printf(sc, "promiscuous mode %sabled\n", (ifp->if_flags & IFF_PROMISC) ? "en" : "dis"); MUGE_LOCK_ASSERT(sc, MA_OWNED); if (ifp->if_flags & IFF_PROMISC) sc->sc_rfe_ctl |= ETH_RFE_CTL_MCAST_EN_ | ETH_RFE_CTL_UCAST_EN_; else sc->sc_rfe_ctl &= ~(ETH_RFE_CTL_MCAST_EN_); lan78xx_write_reg(sc, ETH_RFE_CTL, sc->sc_rfe_ctl); } /** * muge_sethwcsum - Enable or disable H/W UDP and TCP checksumming * @sc: driver soft context * * LOCKING: * Should be called with the MUGE lock held. * * RETURNS: * Returns 0 on success or a negative error code. */ static int muge_sethwcsum(struct muge_softc *sc) { struct ifnet *ifp = uether_getifp(&sc->sc_ue); int err; if (!ifp) return (-EIO); MUGE_LOCK_ASSERT(sc, MA_OWNED); if (ifp->if_capabilities & IFCAP_RXCSUM) { sc->sc_rfe_ctl |= ETH_RFE_CTL_IGMP_COE_ | ETH_RFE_CTL_ICMP_COE_; sc->sc_rfe_ctl |= ETH_RFE_CTL_TCPUDP_COE_ | ETH_RFE_CTL_IP_COE_; } else { sc->sc_rfe_ctl &= ~(ETH_RFE_CTL_IGMP_COE_ | ETH_RFE_CTL_ICMP_COE_); sc->sc_rfe_ctl &= ~(ETH_RFE_CTL_TCPUDP_COE_ | ETH_RFE_CTL_IP_COE_); } sc->sc_rfe_ctl &= ~ETH_RFE_CTL_VLAN_FILTER_; err = lan78xx_write_reg(sc, ETH_RFE_CTL, sc->sc_rfe_ctl); if (err != 0) { muge_warn_printf(sc, "failed to write ETH_RFE_CTL (err=%d)\n", err); return (err); } return (0); } /** * muge_ifmedia_upd - Set media options * @ifp: interface pointer * * Basically boilerplate code that simply calls the mii functions to set * the media options. * * LOCKING: * The device lock must be held before this function is called. * * RETURNS: * Returns 0 on success or a negative error code. */ static int muge_ifmedia_upd(struct ifnet *ifp) { struct muge_softc *sc = ifp->if_softc; muge_dbg_printf(sc, "Calling muge_ifmedia_upd.\n"); struct mii_data *mii = uether_getmii(&sc->sc_ue); struct mii_softc *miisc; int err; MUGE_LOCK_ASSERT(sc, MA_OWNED); LIST_FOREACH(miisc, &mii->mii_phys, mii_list) PHY_RESET(miisc); err = mii_mediachg(mii); return (err); } /** * muge_init - Initialises the LAN95xx chip * @ue: USB ether interface * * Called when the interface is brought up (i.e. ifconfig ue0 up), this * initialise the interface and the rx/tx pipes. * * LOCKING: * Should be called with the MUGE lock held. */ static void muge_init(struct usb_ether *ue) { struct muge_softc *sc = uether_getsc(ue); muge_dbg_printf(sc, "Calling muge_init.\n"); struct ifnet *ifp = uether_getifp(ue); MUGE_LOCK_ASSERT(sc, MA_OWNED); if (lan78xx_setmacaddress(sc, IF_LLADDR(ifp))) muge_dbg_printf(sc, "setting MAC address failed\n"); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; /* Cancel pending I/O. */ muge_stop(ue); /* Reset the ethernet interface. */ muge_reset(sc); /* Load the multicast filter. */ muge_setmulti(ue); /* TCP/UDP checksum offload engines. */ muge_sethwcsum(sc); usbd_xfer_set_stall(sc->sc_xfer[MUGE_BULK_DT_WR]); /* Indicate we are up and running. */ ifp->if_drv_flags |= IFF_DRV_RUNNING; /* Switch to selected media. */ muge_ifmedia_upd(ifp); muge_start(ue); } /** * muge_stop - Stops communication with the LAN78xx chip * @ue: USB ether interface */ static void muge_stop(struct usb_ether *ue) { struct muge_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); MUGE_LOCK_ASSERT(sc, MA_OWNED); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->sc_flags &= ~MUGE_FLAG_LINK; /* * Stop all the transfers, if not already stopped. */ usbd_transfer_stop(sc->sc_xfer[MUGE_BULK_DT_WR]); usbd_transfer_stop(sc->sc_xfer[MUGE_BULK_DT_RD]); } /** * muge_tick - Called periodically to monitor the state of the LAN95xx chip * @ue: USB ether interface * * Simply calls the mii status functions to check the state of the link. * * LOCKING: * Should be called with the MUGE lock held. */ static void muge_tick(struct usb_ether *ue) { struct muge_softc *sc = uether_getsc(ue); struct mii_data *mii = uether_getmii(&sc->sc_ue); MUGE_LOCK_ASSERT(sc, MA_OWNED); mii_tick(mii); if ((sc->sc_flags & MUGE_FLAG_LINK) == 0) { lan78xx_miibus_statchg(ue->ue_dev); if ((sc->sc_flags & MUGE_FLAG_LINK) != 0) muge_start(ue); } } /** * muge_ifmedia_sts - Report current media status * @ifp: inet interface pointer * @ifmr: interface media request * * Call the mii functions to get the media status. * * LOCKING: * Internally takes and releases the device lock. */ static void muge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct muge_softc *sc = ifp->if_softc; struct mii_data *mii = uether_getmii(&sc->sc_ue); MUGE_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; MUGE_UNLOCK(sc); } /** * muge_probe - Probe the interface. * @dev: muge device handle * * Checks if the device is a match for this driver. * * RETURNS: * Returns 0 on success or an error code on failure. */ static int muge_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != MUGE_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != MUGE_IFACE_IDX) return (ENXIO); return (usbd_lookup_id_by_uaa(lan78xx_devs, sizeof(lan78xx_devs), uaa)); } /** * muge_attach - Attach the interface. * @dev: muge device handle * * Allocate softc structures, do ifmedia setup and ethernet/BPF attach. * * RETURNS: * Returns 0 on success or a negative error code. */ static int muge_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct muge_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; uint8_t iface_index; int err; sc->sc_flags = USB_GET_DRIVER_INFO(uaa); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* Setup the endpoints for the Microchip LAN78xx device. */ iface_index = MUGE_IFACE_IDX; err = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, muge_config, MUGE_N_TRANSFER, sc, &sc->sc_mtx); if (err) { device_printf(dev, "error: allocating USB transfers failed\n"); goto err; } ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &muge_ue_methods; err = uether_ifattach(ue); if (err) { device_printf(dev, "error: could not attach interface\n"); goto err_usbd; } /* Wait for lan78xx_chip_init from post-attach callback to complete. */ uether_ifattach_wait(ue); if (!(sc->sc_flags & MUGE_FLAG_INIT_DONE)) goto err_attached; return (0); err_attached: uether_ifdetach(ue); err_usbd: usbd_transfer_unsetup(sc->sc_xfer, MUGE_N_TRANSFER); err: mtx_destroy(&sc->sc_mtx); return (ENXIO); } /** * muge_detach - Detach the interface. * @dev: muge device handle * * RETURNS: * Returns 0. */ static int muge_detach(device_t dev) { struct muge_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; usbd_transfer_unsetup(sc->sc_xfer, MUGE_N_TRANSFER); uether_ifdetach(ue); mtx_destroy(&sc->sc_mtx); return (0); } static device_method_t muge_methods[] = { /* Device interface */ DEVMETHOD(device_probe, muge_probe), DEVMETHOD(device_attach, muge_attach), DEVMETHOD(device_detach, muge_detach), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, lan78xx_miibus_readreg), DEVMETHOD(miibus_writereg, lan78xx_miibus_writereg), DEVMETHOD(miibus_statchg, lan78xx_miibus_statchg), DEVMETHOD_END }; static driver_t muge_driver = { .name = "muge", .methods = muge_methods, .size = sizeof(struct muge_softc), }; static devclass_t muge_devclass; DRIVER_MODULE(muge, uhub, muge_driver, muge_devclass, NULL, NULL); DRIVER_MODULE(miibus, muge, miibus_driver, miibus_devclass, NULL, NULL); MODULE_DEPEND(muge, uether, 1, 1, 1); MODULE_DEPEND(muge, usb, 1, 1, 1); MODULE_DEPEND(muge, ether, 1, 1, 1); MODULE_DEPEND(muge, miibus, 1, 1, 1); MODULE_VERSION(muge, 1); USB_PNP_HOST_INFO(lan78xx_devs); Index: user/ngie/bug-237403/sys/dev/usb/net/if_mugereg.h =================================================================== --- user/ngie/bug-237403/sys/dev/usb/net/if_mugereg.h (revision 348022) +++ user/ngie/bug-237403/sys/dev/usb/net/if_mugereg.h (revision 348023) @@ -1,376 +1,378 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2018 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$ */ /* * Definitions for the Microchip LAN78xx USB-to-Ethernet controllers. * * This information was mostly taken from the LAN7800 manual, but some * undocumented registers are based on the Linux driver. * */ #ifndef _IF_MUGEREG_H_ #define _IF_MUGEREG_H_ /* USB Vendor Requests */ #define UVR_WRITE_REG 0xA0 #define UVR_READ_REG 0xA1 #define UVR_GET_STATS 0xA2 /* Device ID and revision register */ #define ETH_ID_REV 0x000 #define ETH_ID_REV_CHIP_ID_MASK_ 0xFFFF0000UL #define ETH_ID_REV_CHIP_REV_MASK_ 0x0000FFFFUL #define ETH_ID_REV_CHIP_ID_7800_ 0x7800 #define ETH_ID_REV_CHIP_ID_7801_ 0x7801 #define ETH_ID_REV_CHIP_ID_7850_ 0x7850 /* Device interrupt status register. */ #define ETH_INT_STS 0x00C #define ETH_INT_STS_CLEAR_ALL_ 0xFFFFFFFFUL /* Hardware Configuration Register. */ #define ETH_HW_CFG 0x010 #define ETH_HW_CFG_LED3_EN_ (0x1UL << 23) #define ETH_HW_CFG_LED2_EN_ (0x1UL << 22) #define ETH_HW_CFG_LED1_EN_ (0x1UL << 21) #define ETH_HW_CFG_LEDO_EN_ (0x1UL << 20) #define ETH_HW_CFG_MEF_ (0x1UL << 4) #define ETH_HW_CFG_ETC_ (0x1UL << 3) #define ETH_HW_CFG_LRST_ (0x1UL << 1) /* Lite reset */ #define ETH_HW_CFG_SRST_ (0x1UL << 0) /* Soft reset */ /* Power Management Control Register. */ #define ETH_PMT_CTL 0x014 #define ETH_PMT_CTL_PHY_RST_ (0x1UL << 4) /* PHY reset */ #define ETH_PMT_CTL_WOL_EN_ (0x1UL << 3) /* PHY wake-on-lan */ #define ETH_PMT_CTL_PHY_WAKE_EN_ (0x1UL << 2) /* PHY int wake */ /* GPIO Configuration 0 Register. */ #define ETH_GPIO_CFG0 0x018 /* GPIO Configuration 1 Register. */ #define ETH_GPIO_CFG1 0x01C /* GPIO wake enable and polarity register. */ #define ETH_GPIO_WAKE 0x020 /* RX Command A */ #define RX_CMD_A_RED_ (0x1UL << 22) /* Receive Error Det */ #define RX_CMD_A_ICSM_ (0x1UL << 14) #define RX_CMD_A_LEN_MASK_ 0x00003FFFUL /* TX Command A */ #define TX_CMD_A_LEN_MASK_ 0x000FFFFFUL #define TX_CMD_A_FCS_ (0x1UL << 22) /* Data Port Select Register */ #define ETH_DP_SEL 0x024 #define ETH_DP_SEL_DPRDY_ (0x1UL << 31) #define ETH_DP_SEL_RSEL_VLAN_DA_ (0x1UL << 0) /* RFE VLAN/DA Hash */ #define ETH_DP_SEL_RSEL_MASK_ 0x0000000F #define ETH_DP_SEL_VHF_HASH_LEN 16 #define ETH_DP_SEL_VHF_VLAN_LEN 128 /* Data Port Command Register */ #define ETH_DP_CMD 0x028 #define ETH_DP_CMD_WRITE_ (0x1UL << 0) /* 1 for write */ #define ETH_DP_CMD_READ_ (0x0UL << 0) /* 0 for read */ /* Data Port Address Register */ #define ETH_DP_ADDR 0x02C /* Data Port Data Register */ #define ETH_DP_DATA 0x030 /* EEPROM Command Register */ #define ETH_E2P_CMD 0x040 #define ETH_E2P_CMD_MASK_ 0x70000000UL #define ETH_E2P_CMD_ADDR_MASK_ 0x000001FFUL #define ETH_E2P_CMD_BUSY_ (0x1UL << 31) #define ETH_E2P_CMD_READ_ (0x0UL << 28) #define ETH_E2P_CMD_WRITE_ (0x3UL << 28) #define ETH_E2P_CMD_ERASE_ (0x5UL << 28) #define ETH_E2P_CMD_RELOAD_ (0x7UL << 28) #define ETH_E2P_CMD_TIMEOUT_ (0x1UL << 10) #define ETH_E2P_MAC_OFFSET 0x01 #define ETH_E2P_INDICATOR_OFFSET 0x00 /* EEPROM Data Register */ #define ETH_E2P_DATA 0x044 #define ETH_E2P_INDICATOR 0xA5 /* EEPROM is present */ /* Packet sizes. */ #define MUGE_SS_USB_PKT_SIZE 1024 #define MUGE_HS_USB_PKT_SIZE 512 #define MUGE_FS_USB_PKT_SIZE 64 /* Receive Filtering Engine Control Register */ #define ETH_RFE_CTL 0x0B0 #define ETH_RFE_CTL_IGMP_COE_ (0x1U << 14) #define ETH_RFE_CTL_ICMP_COE_ (0x1U << 13) #define ETH_RFE_CTL_TCPUDP_COE_ (0x1U << 12) #define ETH_RFE_CTL_IP_COE_ (0x1U << 11) #define ETH_RFE_CTL_BCAST_EN_ (0x1U << 10) #define ETH_RFE_CTL_MCAST_EN_ (0x1U << 9) #define ETH_RFE_CTL_UCAST_EN_ (0x1U << 8) #define ETH_RFE_CTL_VLAN_FILTER_ (0x1U << 5) #define ETH_RFE_CTL_MCAST_HASH_ (0x1U << 3) #define ETH_RFE_CTL_DA_PERFECT_ (0x1U << 1) /* End address of the RX FIFO */ #define ETH_FCT_RX_FIFO_END 0x0C8 #define ETH_FCT_RX_FIFO_END_MASK_ 0x0000007FUL #define MUGE_MAX_RX_FIFO_SIZE (12 * 1024) /* End address of the TX FIFO */ #define ETH_FCT_TX_FIFO_END 0x0CC #define ETH_FCT_TX_FIFO_END_MASK_ 0x0000003FUL #define MUGE_MAX_TX_FIFO_SIZE (12 * 1024) /* USB Configuration Register 0 */ #define ETH_USB_CFG0 0x080 #define ETH_USB_CFG_BIR_ (0x1U << 6) /* Bulk-In Empty resp */ #define ETH_USB_CFG_BCE_ (0x1U << 5) /* Burst Cap Enable */ /* USB Configuration Register 1 */ #define ETH_USB_CFG1 0x084 /* USB Configuration Register 2 */ #define ETH_USB_CFG2 0x088 /* USB bConfigIndex: it only has one configuration. */ #define MUGE_CONFIG_INDEX 0 /* Burst Cap Register */ #define ETH_BURST_CAP 0x090 #define MUGE_DEFAULT_BURST_CAP_SIZE MUGE_MAX_TX_FIFO_SIZE /* Bulk-In Delay Register */ #define ETH_BULK_IN_DLY 0x094 #define MUGE_DEFAULT_BULK_IN_DELAY 0x0800 /* Interrupt Endpoint Control Register */ #define ETH_INT_EP_CTL 0x098 #define ETH_INT_ENP_PHY_INT (0x1U << 17) /* PHY Enable */ /* Registers on the phy, accessed via MII/MDIO */ #define MUGE_PHY_INTR_STAT 25 #define MUGE_PHY_INTR_MASK 26 #define MUGE_PHY_INTR_LINK_CHANGE (0x1U << 13) #define MUGE_PHY_INTR_ANEG_COMP (0x1U << 10) #define MUGE_EXT_PAGE_ACCESS 0x1F #define MUGE_EXT_PAGE_SPACE_0 0x0000 #define MUGE_EXT_PAGE_SPACE_1 0x0001 #define MUGE_EXT_PAGE_SPACE_2 0x0002 +#define MUGE_PHY_LED_MODE 29 + /* Extended Register Page 1 Space */ #define MUGE_EXT_MODE_CTRL 0x0013 #define MUGE_EXT_MODE_CTRL_MDIX_MASK_ 0x000C #define MUGE_EXT_MODE_CTRL_AUTO_MDIX_ 0x0000 /* FCT Flow Control Threshold Register */ #define ETH_FCT_FLOW 0x0D0 /* FCT RX FIFO Control Register */ #define ETH_FCT_RX_CTL 0x0C0 /* FCT TX FIFO Control Register */ #define ETH_FCT_TX_CTL 0x0C4 #define ETH_FCT_TX_CTL_EN_ (0x1U << 31) /* MAC Control Register */ #define ETH_MAC_CR 0x100 #define ETH_MAC_CR_GMII_EN_ (0x1U << 19) /* GMII Enable */ #define ETH_MAC_CR_AUTO_DUPLEX_ (0x1U << 12) #define ETH_MAC_CR_AUTO_SPEED_ (0x1U << 11) /* MAC Receive Register */ #define ETH_MAC_RX 0x104 #define ETH_MAC_RX_MAX_FR_SIZE_MASK_ 0x3FFF0000 #define ETH_MAC_RX_MAX_FR_SIZE_SHIFT_ 16 #define ETH_MAC_RX_EN_ (0x1U << 0) /* Enable Receiver */ /* MAC Transmit Register */ #define ETH_MAC_TX 0x108 #define ETH_MAC_TX_TXEN_ (0x1U << 0) /* Enable Transmitter */ /* Flow Control Register */ #define ETH_FLOW 0x10C #define ETH_FLOW_CR_TX_FCEN_ (0x1U << 30) /* TX FC Enable */ #define ETH_FLOW_CR_RX_FCEN_ (0x1U << 29) /* RX FC Enable */ /* MAC Receive Address Registers */ #define ETH_RX_ADDRH 0x118 /* High */ #define ETH_RX_ADDRL 0x11C /* Low */ /* MII Access Register */ #define ETH_MII_ACC 0x120 #define ETH_MII_ACC_MII_BUSY_ (0x1UL << 0) #define ETH_MII_ACC_MII_READ_ (0x0UL << 1) #define ETH_MII_ACC_MII_WRITE_ (0x1UL << 1) /* MII Data Register */ #define ETH_MII_DATA 0x124 /* MAC address perfect filter registers (ADDR_FILTx) */ #define ETH_MAF_BASE 0x400 #define ETH_MAF_HIx 0x00 #define ETH_MAF_LOx 0x04 #define MUGE_NUM_PFILTER_ADDRS_ 33 #define ETH_MAF_HI_VALID_ (0x1UL << 31) #define ETH_MAF_HI_TYPE_SRC_ (0x1UL << 30) #define ETH_MAF_HI_TYPE_DST_ (0x0UL << 30) #define PFILTER_HI(index) (ETH_MAF_BASE + (8 * (index)) + (ETH_MAF_HIx)) #define PFILTER_LO(index) (ETH_MAF_BASE + (8 * (index)) + (ETH_MAF_LOx)) /* * These registers are not documented in the datasheet, and are based on * the Linux driver. */ #define OTP_BASE_ADDR 0x01000 #define OTP_PWR_DN (OTP_BASE_ADDR + 4 * 0x00) #define OTP_PWR_DN_PWRDN_N 0x01 #define OTP_ADDR1 (OTP_BASE_ADDR + 4 * 0x01) #define OTP_ADDR1_15_11 0x1F #define OTP_ADDR2 (OTP_BASE_ADDR + 4 * 0x02) #define OTP_ADDR2_10_3 0xFF #define OTP_ADDR3 (OTP_BASE_ADDR + 4 * 0x03) #define OTP_ADDR3_2_0 0x03 #define OTP_RD_DATA (OTP_BASE_ADDR + 4 * 0x06) #define OTP_FUNC_CMD (OTP_BASE_ADDR + 4 * 0x08) #define OTP_FUNC_CMD_RESET 0x04 #define OTP_FUNC_CMD_PROGRAM_ 0x02 #define OTP_FUNC_CMD_READ_ 0x01 #define OTP_MAC_OFFSET 0x01 #define OTP_INDICATOR_OFFSET 0x00 #define OTP_INDICATOR_1 0xF3 #define OTP_INDICATOR_2 0xF7 #define OTP_CMD_GO (OTP_BASE_ADDR + 4 * 0x0A) #define OTP_CMD_GO_GO_ 0x01 #define OTP_STATUS (OTP_BASE_ADDR + 4 * 0x0A) #define OTP_STATUS_OTP_LOCK_ 0x10 #define OTP_STATUS_BUSY_ 0x01 /* Some unused registers, from the data sheet. */ #if 0 #define ETH_BOS_ATTR 0x050 #define ETH_SS_ATTR 0x054 #define ETH_HS_ATTR 0x058 #define ETH_FS_ATTR 0x05C #define ETH_STRNG_ATTR0 0x060 #define ETH_STRNG_ATTR1 0x064 #define ETH_STRNGFLAG_ATTR 0x068 #define ETH_SW_GP_0 0x06C #define ETH_SW_GP_1 0x070 #define ETH_SW_GP_2 0x074 #define ETH_VLAN_TYPE 0x0B4 #define ETH_RX_DP_STOR 0x0D4 #define ETH_TX_DP_STOR 0x0D8 #define ETH_LTM_BELT_IDLE0 0x0E0 #define ETH_LTM_BELT_IDLE1 0x0E4 #define ETH_LTM_BELT_ACT0 0x0E8 #define ETH_LTM_BELT_ACT1 0x0EC #define ETH_LTM_INACTIVE0 0x0F0 #define ETH_LTM_INACTIVE1 0x0F4 #define ETH_RAND_SEED 0x110 #define ETH_ERR_STS 0x114 #define ETH_EEE_TX_LPI_REQ_DLY 0x130 #define ETH_EEE_TW_TX_SYS 0x134 #define ETH_EEE_TX_LPI_REM_DLY 0x138 #define ETH_WUCSR1 0x140 #define ETH_WK_SRC 0x144 #define ETH_WUF_CFGx 0x150 #define ETH_WUF_MASKx 0x200 #define ETH_WUCSR2 0x600 #define ETH_NS1_IPV6_ADDR_DEST0 0x610 #define ETH_NS1_IPV6_ADDR_DEST1 0x614 #define ETH_NS1_IPV6_ADDR_DEST2 0x618 #define ETH_NS1_IPV6_ADDR_DEST3 0x61C #define ETH_NS1_IPV6_ADDR_SRC0 0x620 #define ETH_NS1_IPV6_ADDR_SRC1 0x624 #define ETH_NS1_IPV6_ADDR_SRC2 0x628 #define ETH_NS1_IPV6_ADDR_SRC3 0x62C #define ETH_NS1_ICMPV6_ADDR0_0 0x630 #define ETH_NS1_ICMPV6_ADDR0_1 0x634 #define ETH_NS1_ICMPV6_ADDR0_2 0x638 #define ETH_NS1_ICMPV6_ADDR0_3 0x63C #define ETH_NS1_ICMPV6_ADDR1_0 0x640 #define ETH_NS1_ICMPV6_ADDR1_1 0x644 #define ETH_NS1_ICMPV6_ADDR1_2 0x648 #define ETH_NS1_ICMPV6_ADDR1_3 0x64C #define ETH_NS2_IPV6_ADDR_DEST0 0x650 #define ETH_NS2_IPV6_ADDR_DEST1 0x654 #define ETH_NS2_IPV6_ADDR_DEST2 0x658 #define ETH_NS2_IPV6_ADDR_DEST3 0x65C #define ETH_NS2_IPV6_ADDR_SRC0 0x660 #define ETH_NS2_IPV6_ADDR_SRC1 0x664 #define ETH_NS2_IPV6_ADDR_SRC2 0x668 #define ETH_NS2_IPV6_ADDR_SRC3 0x66C #define ETH_NS2_ICMPV6_ADDR0_0 0x670 #define ETH_NS2_ICMPV6_ADDR0_1 0x674 #define ETH_NS2_ICMPV6_ADDR0_2 0x678 #define ETH_NS2_ICMPV6_ADDR0_3 0x67C #define ETH_NS2_ICMPV6_ADDR1_0 0x680 #define ETH_NS2_ICMPV6_ADDR1_1 0x684 #define ETH_NS2_ICMPV6_ADDR1_2 0x688 #define ETH_NS2_ICMPV6_ADDR1_3 0x68C #define ETH_SYN_IPV4_ADDR_SRC 0x690 #define ETH_SYN_IPV4_ADDR_DEST 0x694 #define ETH_SYN_IPV4_TCP_PORTS 0x698 #define ETH_SYN_IPV6_ADDR_SRC0 0x69C #define ETH_SYN_IPV6_ADDR_SRC1 0x6A0 #define ETH_SYN_IPV6_ADDR_SRC2 0x6A4 #define ETH_SYN_IPV6_ADDR_SRC3 0x6A8 #define ETH_SYN_IPV6_ADDR_DEST0 0x6AC #define ETH_SYN_IPV6_ADDR_DEST1 0x6B0 #define ETH_SYN_IPV6_ADDR_DEST2 0x6B4 #define ETH_SYN_IPV6_ADDR_DEST3 0x6B8 #define ETH_SYN_IPV6_TCP_PORTS 0x6BC #define ETH_ARP_SPA 0x6C0 #define ETH_ARP_TPA 0x6C4 #define ETH_PHY_DEV_ID 0x700 #endif #endif /* _IF_MUGEREG_H_ */ Index: user/ngie/bug-237403/sys/kern/kern_kcov.c =================================================================== --- user/ngie/bug-237403/sys/kern/kern_kcov.c (revision 348022) +++ user/ngie/bug-237403/sys/kern/kern_kcov.c (revision 348023) @@ -1,580 +1,581 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2018 The FreeBSD Foundation. All rights reserved. * Copyright (C) 2018, 2019 Andrew Turner * * This software was developed by Mitchell Horne under sponsorship of * the FreeBSD Foundation. * * 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. * * $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 MALLOC_DEFINE(M_KCOV_INFO, "kcovinfo", "KCOV info type"); #define KCOV_ELEMENT_SIZE sizeof(uint64_t) /* * To know what the code can safely perform at any point in time we use a * state machine. In the normal case the state transitions are: * * OPEN -> READY -> RUNNING -> DYING * | | ^ | ^ ^ * | | +--------+ | | * | +-------------------+ | * +-----------------------------+ * * The states are: * OPEN: The kcov fd has been opened, but no buffer is available to store * coverage data. * READY: The buffer to store coverage data has been allocated. Userspace * can set this by using ioctl(fd, KIOSETBUFSIZE, entries);. When * this has been set the buffer can be written to by the kernel, * and mmaped by userspace. * RUNNING: The coverage probes are able to store coverage data in the buffer. * This is entered with ioctl(fd, KIOENABLE, mode);. The READY state * can be exited by ioctl(fd, KIODISABLE); or exiting the thread to * return to the READY state to allow tracing to be reused, or by * closing the kcov fd to enter the DYING state. * DYING: The fd has been closed. All states can enter into this state when * userspace closes the kcov fd. * * We need to be careful when moving into and out of the RUNNING state. As * an interrupt may happen while this is happening the ordering of memory * operations is important so struct kcov_info is valid for the tracing * functions. * * When moving into the RUNNING state prior stores to struct kcov_info need * to be observed before the state is set. This allows for interrupts that * may call into one of the coverage functions to fire at any point while * being enabled and see a consistent struct kcov_info. * * When moving out of the RUNNING state any later stores to struct kcov_info * need to be observed after the state is set. As with entering this is to * present a consistent struct kcov_info to interrupts. */ typedef enum { KCOV_STATE_INVALID, KCOV_STATE_OPEN, /* The device is open, but with no buffer */ KCOV_STATE_READY, /* The buffer has been allocated */ KCOV_STATE_RUNNING, /* Recording trace data */ KCOV_STATE_DYING, /* The fd was closed */ } kcov_state_t; /* * (l) Set while holding the kcov_lock mutex and not in the RUNNING state. * (o) Only set once while in the OPEN state. Cleaned up while in the DYING * state, and with no thread associated with the struct kcov_info. * (s) Set atomically to enter or exit the RUNNING state, non-atomically * otherwise. See above for a description of the other constraints while * moving into or out of the RUNNING state. */ struct kcov_info { struct thread *thread; /* (l) */ vm_object_t bufobj; /* (o) */ vm_offset_t kvaddr; /* (o) */ size_t entries; /* (o) */ size_t bufsize; /* (o) */ kcov_state_t state; /* (s) */ int mode; /* (l) */ }; /* Prototypes */ static d_open_t kcov_open; static d_close_t kcov_close; static d_mmap_single_t kcov_mmap_single; static d_ioctl_t kcov_ioctl; static int kcov_alloc(struct kcov_info *info, size_t entries); static void kcov_free(struct kcov_info *info); static void kcov_init(const void *unused); static struct cdevsw kcov_cdevsw = { .d_version = D_VERSION, .d_open = kcov_open, .d_close = kcov_close, .d_mmap_single = kcov_mmap_single, .d_ioctl = kcov_ioctl, .d_name = "kcov", }; SYSCTL_NODE(_kern, OID_AUTO, kcov, CTLFLAG_RW, 0, "Kernel coverage"); static u_int kcov_max_entries = KCOV_MAXENTRIES; SYSCTL_UINT(_kern_kcov, OID_AUTO, max_entries, CTLFLAG_RW, &kcov_max_entries, 0, "Maximum number of entries in the kcov buffer"); static struct mtx kcov_lock; static int active_count; static struct kcov_info * get_kinfo(struct thread *td) { struct kcov_info *info; /* We might have a NULL thread when releasing the secondary CPUs */ if (td == NULL) return (NULL); /* * We are in an interrupt, stop tracing as it is not explicitly * part of a syscall. */ if (td->td_intr_nesting_level > 0 || td->td_intr_frame != NULL) return (NULL); /* * If info is NULL or the state is not running we are not tracing. */ info = td->td_kcov_info; if (info == NULL || atomic_load_acq_int(&info->state) != KCOV_STATE_RUNNING) return (NULL); return (info); } static void trace_pc(uintptr_t ret) { struct thread *td; struct kcov_info *info; uint64_t *buf, index; td = curthread; info = get_kinfo(td); if (info == NULL) return; /* * Check we are in the PC-trace mode. */ if (info->mode != KCOV_MODE_TRACE_PC) return; KASSERT(info->kvaddr != 0, ("__sanitizer_cov_trace_pc: NULL buf while running")); buf = (uint64_t *)info->kvaddr; /* The first entry of the buffer holds the index */ index = buf[0]; if (index + 2 > info->entries) return; buf[index + 1] = ret; buf[0] = index + 1; } static bool trace_cmp(uint64_t type, uint64_t arg1, uint64_t arg2, uint64_t ret) { struct thread *td; struct kcov_info *info; uint64_t *buf, index; td = curthread; info = get_kinfo(td); if (info == NULL) return (false); /* * Check we are in the comparison-trace mode. */ if (info->mode != KCOV_MODE_TRACE_CMP) return (false); KASSERT(info->kvaddr != 0, ("__sanitizer_cov_trace_pc: NULL buf while running")); buf = (uint64_t *)info->kvaddr; /* The first entry of the buffer holds the index */ index = buf[0]; /* Check we have space to store all elements */ if (index * 4 + 4 + 1 > info->entries) return (false); while (1) { buf[index * 4 + 1] = type; buf[index * 4 + 2] = arg1; buf[index * 4 + 3] = arg2; buf[index * 4 + 4] = ret; if (atomic_cmpset_64(&buf[0], index, index + 1)) break; buf[0] = index; } return (true); } /* * The fd is being closed, cleanup everything we can. */ static void kcov_mmap_cleanup(void *arg) { struct kcov_info *info = arg; struct thread *thread; mtx_lock_spin(&kcov_lock); /* * Move to KCOV_STATE_DYING to stop adding new entries. * * If the thread is running we need to wait until thread exit to * clean up as it may currently be adding a new entry. If this is * the case being in KCOV_STATE_DYING will signal that the buffer * needs to be cleaned up. */ atomic_store_int(&info->state, KCOV_STATE_DYING); atomic_thread_fence_seq_cst(); thread = info->thread; mtx_unlock_spin(&kcov_lock); if (thread != NULL) return; /* * We can safely clean up the info struct as it is in the * KCOV_STATE_DYING state with no thread associated. * * The KCOV_STATE_DYING stops new threads from using it. * The lack of a thread means nothing is currently using the buffers. */ kcov_free(info); } static int kcov_open(struct cdev *dev, int oflags, int devtype, struct thread *td) { struct kcov_info *info; int error; info = malloc(sizeof(struct kcov_info), M_KCOV_INFO, M_ZERO | M_WAITOK); info->state = KCOV_STATE_OPEN; info->thread = NULL; info->mode = -1; if ((error = devfs_set_cdevpriv(info, kcov_mmap_cleanup)) != 0) kcov_mmap_cleanup(info); return (error); } static int kcov_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct kcov_info *info; int error; if ((error = devfs_get_cdevpriv((void **)&info)) != 0) return (error); KASSERT(info != NULL, ("kcov_close with no kcov_info structure")); /* Trying to close, but haven't disabled */ if (info->state == KCOV_STATE_RUNNING) return (EBUSY); return (0); } static int kcov_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **object, int nprot) { struct kcov_info *info; int error; if ((nprot & (PROT_EXEC | PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) return (EINVAL); if ((error = devfs_get_cdevpriv((void **)&info)) != 0) return (error); if (info->kvaddr == 0 || size / KCOV_ELEMENT_SIZE != info->entries) return (EINVAL); vm_object_reference(info->bufobj); *offset = 0; *object = info->bufobj; return (0); } static int kcov_alloc(struct kcov_info *info, size_t entries) { size_t n, pages; vm_page_t m; KASSERT(info->kvaddr == 0, ("kcov_alloc: Already have a buffer")); KASSERT(info->state == KCOV_STATE_OPEN, ("kcov_alloc: Not in open state (%x)", info->state)); if (entries < 2 || entries > kcov_max_entries) return (EINVAL); /* Align to page size so mmap can't access other kernel memory */ info->bufsize = roundup2(entries * KCOV_ELEMENT_SIZE, PAGE_SIZE); pages = info->bufsize / PAGE_SIZE; if ((info->kvaddr = kva_alloc(info->bufsize)) == 0) return (ENOMEM); info->bufobj = vm_pager_allocate(OBJT_PHYS, 0, info->bufsize, PROT_READ | PROT_WRITE, 0, curthread->td_ucred); VM_OBJECT_WLOCK(info->bufobj); for (n = 0; n < pages; n++) { m = vm_page_grab(info->bufobj, n, VM_ALLOC_NOBUSY | VM_ALLOC_ZERO | VM_ALLOC_WIRED); m->valid = VM_PAGE_BITS_ALL; pmap_qenter(info->kvaddr + n * PAGE_SIZE, &m, 1); } VM_OBJECT_WUNLOCK(info->bufobj); info->entries = entries; return (0); } static void kcov_free(struct kcov_info *info) { vm_page_t m; size_t i; if (info->kvaddr != 0) { pmap_qremove(info->kvaddr, info->bufsize / PAGE_SIZE); kva_free(info->kvaddr, info->bufsize); } if (info->bufobj != NULL) { VM_OBJECT_WLOCK(info->bufobj); m = vm_page_lookup(info->bufobj, 0); for (i = 0; i < info->bufsize / PAGE_SIZE; i++) { vm_page_lock(m); vm_page_unwire_noq(m); vm_page_unlock(m); m = vm_page_next(m); } VM_OBJECT_WUNLOCK(info->bufobj); vm_object_deallocate(info->bufobj); } free(info, M_KCOV_INFO); } static int kcov_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag __unused, struct thread *td) { struct kcov_info *info; int mode, error; if ((error = devfs_get_cdevpriv((void **)&info)) != 0) return (error); if (cmd == KIOSETBUFSIZE) { /* * Set the size of the coverage buffer. Should be called * before enabling coverage collection for that thread. */ if (info->state != KCOV_STATE_OPEN) { return (EBUSY); } error = kcov_alloc(info, *(u_int *)data); if (error == 0) info->state = KCOV_STATE_READY; return (error); } mtx_lock_spin(&kcov_lock); switch (cmd) { case KIOENABLE: if (info->state != KCOV_STATE_READY) { error = EBUSY; break; } if (td->td_kcov_info != NULL) { error = EINVAL; break; } mode = *(int *)data; if (mode != KCOV_MODE_TRACE_PC && mode != KCOV_MODE_TRACE_CMP) { error = EINVAL; break; } /* Lets hope nobody opens this 2 billion times */ KASSERT(active_count < INT_MAX, ("%s: Open too many times", __func__)); active_count++; if (active_count == 1) { cov_register_pc(&trace_pc); cov_register_cmp(&trace_cmp); } KASSERT(info->thread == NULL, ("Enabling kcov when already enabled")); info->thread = td; info->mode = mode; /* * Ensure the mode has been set before starting coverage * tracing. */ atomic_store_rel_int(&info->state, KCOV_STATE_RUNNING); td->td_kcov_info = info; break; case KIODISABLE: /* Only the currently enabled thread may disable itself */ if (info->state != KCOV_STATE_RUNNING || info != td->td_kcov_info) { error = EINVAL; break; } KASSERT(active_count > 0, ("%s: Open count is zero", __func__)); active_count--; if (active_count == 0) { cov_unregister_pc(); cov_unregister_cmp(); } td->td_kcov_info = NULL; atomic_store_int(&info->state, KCOV_STATE_READY); /* * Ensure we have exited the READY state before clearing the * rest of the info struct. */ atomic_thread_fence_rel(); info->mode = -1; info->thread = NULL; break; default: error = EINVAL; break; } mtx_unlock_spin(&kcov_lock); return (error); } static void kcov_thread_dtor(void *arg __unused, struct thread *td) { struct kcov_info *info; info = td->td_kcov_info; if (info == NULL) return; mtx_lock_spin(&kcov_lock); KASSERT(active_count > 0, ("%s: Open count is zero", __func__)); active_count--; if (active_count == 0) { cov_unregister_pc(); cov_unregister_cmp(); } td->td_kcov_info = NULL; if (info->state != KCOV_STATE_DYING) { /* * The kcov file is still open. Mark it as unused and * wait for it to be closed before cleaning up. */ atomic_store_int(&info->state, KCOV_STATE_READY); atomic_thread_fence_seq_cst(); /* This info struct is unused */ info->thread = NULL; mtx_unlock_spin(&kcov_lock); return; } mtx_unlock_spin(&kcov_lock); /* * We can safely clean up the info struct as it is in the * KCOV_STATE_DYING state where the info struct is associated with * the current thread that's about to exit. * * The KCOV_STATE_DYING stops new threads from using it. * It also stops the current thread from trying to use the info struct. */ kcov_free(info); } static void kcov_init(const void *unused) { struct make_dev_args args; struct cdev *dev; mtx_init(&kcov_lock, "kcov lock", NULL, MTX_SPIN); make_dev_args_init(&args); args.mda_devsw = &kcov_cdevsw; args.mda_uid = UID_ROOT; args.mda_gid = GID_WHEEL; args.mda_mode = 0600; if (make_dev_s(&args, &dev, "kcov") != 0) { printf("%s", "Failed to create kcov device"); return; } EVENTHANDLER_REGISTER(thread_dtor, kcov_thread_dtor, NULL, EVENTHANDLER_PRI_ANY); } SYSINIT(kcovdev, SI_SUB_LAST, SI_ORDER_ANY, kcov_init, NULL); Index: user/ngie/bug-237403/sys/mips/mediatek/mtk_spi_v1.c =================================================================== --- user/ngie/bug-237403/sys/mips/mediatek/mtk_spi_v1.c (revision 348022) +++ user/ngie/bug-237403/sys/mips/mediatek/mtk_spi_v1.c (revision 348023) @@ -1,350 +1,413 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * Copyright (c) 2011, Aleksandr Rybalko * Copyright (c) 2013, Alexander A. Mityaev * 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 unmodified, 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 #include +#include +#include "gpiobus_if.h" + +#include + #include #include #include "spibus_if.h" #include "opt_platform.h" #include #include #include +#include #include #include #undef MTK_SPI_DEBUG #ifdef MTK_SPI_DEBUG #define dprintf printf #else #define dprintf(x, arg...) #endif /* * register space access macros */ #define SPI_WRITE(sc, reg, val) do { \ bus_write_4(sc->sc_mem_res, (reg), (val)); \ } while (0) #define SPI_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg)) #define SPI_SET_BITS(sc, reg, bits) \ SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits)) #define SPI_CLEAR_BITS(sc, reg, bits) \ SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits)) struct mtk_spi_softc { device_t sc_dev; struct resource *sc_mem_res; + struct gpiobus_pin *gpio_cs; + int nonflash; }; static int mtk_spi_probe(device_t); static int mtk_spi_attach(device_t); static int mtk_spi_detach(device_t); static int mtk_spi_wait(struct mtk_spi_softc *); static void mtk_spi_chip_activate(struct mtk_spi_softc *); static void mtk_spi_chip_deactivate(struct mtk_spi_softc *); static uint8_t mtk_spi_txrx(struct mtk_spi_softc *, uint8_t *, int); static int mtk_spi_transfer(device_t, device_t, struct spi_command *); static phandle_t mtk_spi_get_node(device_t, device_t); static struct ofw_compat_data compat_data[] = { { "ralink,rt2880-spi", 1 }, { "ralink,rt3050-spi", 1 }, { "ralink,rt3352-spi", 1 }, { "ralink,rt3883-spi", 1 }, { "ralink,rt5350-spi", 1 }, { "ralink,mt7620a-spi", 1 }, { NULL, 0 } }; static int mtk_spi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return(ENXIO); device_set_desc(dev, "MTK SPI Controller (v1)"); return (0); } static int mtk_spi_attach(device_t dev) { struct mtk_spi_softc *sc = device_get_softc(dev); int rid; sc->sc_dev = dev; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); + RF_SHAREABLE | RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "Could not map memory\n"); return (ENXIO); } if (mtk_spi_wait(sc)) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (EBUSY); } - SPI_WRITE(sc, MTK_SPICFG, MSBFIRST | SPICLKPOL | TX_ON_CLK_FALL | - SPI_CLK_DIV8); /* XXX: make it configurable */ - /* - * W25Q64CV max 104MHz, bus 120-192 MHz, so divide by 2. - * Update: divide by 4, DEV2 to fast for flash. - */ + if (ofw_bus_has_prop(dev, "non-flash")) + sc->nonflash = 1; + else + sc->nonflash = 0; - device_add_child(dev, "spibus", 0); + ofw_gpiobus_parse_gpios(dev, "cs-gpios", &sc->gpio_cs); + + if (sc->gpio_cs != NULL) { + GPIO_PIN_SETFLAGS(sc->gpio_cs->dev, sc->gpio_cs->pin, + GPIO_PIN_OUTPUT); + GPIO_PIN_SET(sc->gpio_cs->dev, sc->gpio_cs->pin, 1); + } + + device_add_child(dev, "spibus", -1); return (bus_generic_attach(dev)); } static int mtk_spi_detach(device_t dev) { struct mtk_spi_softc *sc = device_get_softc(dev); SPI_SET_BITS(sc, MTK_SPICTL, HIZSMOSI | CS_HIGH); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static void mtk_spi_chip_activate(struct mtk_spi_softc *sc) { mtk_spi_wait(sc); /* * Put all CSx to low */ - SPI_CLEAR_BITS(sc, MTK_SPICTL, CS_HIGH | HIZSMOSI); + if (sc->gpio_cs != NULL) { + GPIO_PIN_SET(sc->gpio_cs->dev, sc->gpio_cs->pin, 0); + SPI_CLEAR_BITS(sc, MTK_SPICTL, HIZSMOSI); + } else { + SPI_CLEAR_BITS(sc, MTK_SPICTL, CS_HIGH | HIZSMOSI); + } } static void mtk_spi_chip_deactivate(struct mtk_spi_softc *sc) { mtk_spi_wait(sc); /* * Put all CSx to high */ - SPI_SET_BITS(sc, MTK_SPICTL, CS_HIGH | HIZSMOSI); + if (sc->gpio_cs != NULL) { + GPIO_PIN_SET(sc->gpio_cs->dev, sc->gpio_cs->pin, 1); + SPI_SET_BITS(sc, MTK_SPICTL, HIZSMOSI); + } else { + SPI_SET_BITS(sc, MTK_SPICTL, CS_HIGH | HIZSMOSI); + } } static int mtk_spi_wait(struct mtk_spi_softc *sc) { int i = 1000; while (i--) { if (!SPI_READ(sc, MTK_SPIBUSY)) break; } if (i == 0) { printf("busy\n"); return (1); } return (0); } static uint8_t mtk_spi_txrx(struct mtk_spi_softc *sc, uint8_t *data, int write) { if (mtk_spi_wait(sc)) return (EBUSY); if (write == MTK_SPI_WRITE) { SPI_WRITE(sc, MTK_SPIDATA, *data); SPI_SET_BITS(sc, MTK_SPICTL, START_WRITE); } else {/* MTK_SPI_READ */ SPI_SET_BITS(sc, MTK_SPICTL, START_READ); if (mtk_spi_wait(sc)) return (EBUSY); *data = SPI_READ(sc, MTK_SPIDATA) & 0xff; } return (0); } static int mtk_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct mtk_spi_softc *sc; uint8_t *buf, byte, *tx_buf; - uint32_t cs; + uint32_t cs, clock, mode; int i, sz, error = 0, write = 0; + int div, clk, cfgreg; sc = device_get_softc(dev); spibus_get_cs(child, &cs); + spibus_get_clock(child, &clock); + spibus_get_mode(child, &mode); cs &= ~SPIBUS_CS_HIGH; if (cs != 0) /* Only 1 CS */ return (ENXIO); - /* There is always a command to transfer. */ - tx_buf = (uint8_t *)(cmd->tx_cmd); - - /* Perform some fixup because MTK dont support duplex SPI */ - switch(tx_buf[0]) { - case CMD_READ_IDENT: - cmd->tx_cmd_sz = 1; - cmd->rx_cmd_sz = 3; + cfgreg = MSBFIRST; + switch(mode) { + case 0: /* This is workadound because of + mode 0 not work this soc. */ + case 3: + cfgreg |= SPICLKPOL | TX_ON_CLK_FALL; break; - case CMD_ENTER_4B_MODE: - case CMD_EXIT_4B_MODE: - case CMD_WRITE_ENABLE: - case CMD_WRITE_DISABLE: - cmd->tx_cmd_sz = 1; - cmd->rx_cmd_sz = 0; + case 1: + cfgreg |= TX_ON_CLK_FALL; break; - case CMD_READ_STATUS: - cmd->tx_cmd_sz = 1; - cmd->rx_cmd_sz = 1; + case 2: + cfgreg |= CAPT_ON_CLK_FALL; break; - case CMD_READ: - case CMD_FAST_READ: - cmd->rx_cmd_sz = cmd->tx_data_sz = 0; - break; - case CMD_SECTOR_ERASE: - cmd->rx_cmd_sz = 0; - break; - case CMD_PAGE_PROGRAM: - cmd->rx_cmd_sz = cmd->rx_data_sz = 0; - break; - } + } + + /* + * W25Q64CV max 104MHz, bus 120-192 MHz, so divide by 2. + * Update: divide by 4, DEV2 to fast for flash. + */ + if (clock != 0) { + div = (mtk_soc_get_cpuclk() + (clock - 1)) / clock; + clk = fls(div) - 2; + if (clk < 0) + clk = 0; + else if (clk > 6) + clk = 6; + } else { + clk = 6; + } + + SPI_WRITE(sc, MTK_SPICFG, cfgreg | clk); + + if (sc->nonflash == 0) { + /* There is always a command to transfer. */ + tx_buf = (uint8_t *)(cmd->tx_cmd); + + /* Perform some fixup because MTK dont support duplex SPI */ + switch(tx_buf[0]) { + case CMD_READ_IDENT: + cmd->tx_cmd_sz = 1; + cmd->rx_cmd_sz = 3; + break; + case CMD_ENTER_4B_MODE: + case CMD_EXIT_4B_MODE: + case CMD_WRITE_ENABLE: + case CMD_WRITE_DISABLE: + cmd->tx_cmd_sz = 1; + cmd->rx_cmd_sz = 0; + break; + case CMD_READ_STATUS: + cmd->tx_cmd_sz = 1; + cmd->rx_cmd_sz = 1; + break; + case CMD_READ: + case CMD_FAST_READ: + cmd->rx_cmd_sz = cmd->tx_data_sz = 0; + break; + case CMD_SECTOR_ERASE: + cmd->rx_cmd_sz = 0; + break; + case CMD_PAGE_PROGRAM: + cmd->rx_cmd_sz = cmd->rx_data_sz = 0; + break; + } + } mtk_spi_chip_activate(sc); if (cmd->tx_cmd_sz + cmd->rx_cmd_sz) { buf = (uint8_t *)(cmd->rx_cmd); tx_buf = (uint8_t *)(cmd->tx_cmd); - sz = cmd->tx_cmd_sz + cmd->rx_cmd_sz; + sz = cmd->tx_cmd_sz; + if (sc->nonflash == 0) + sz += cmd->rx_cmd_sz; for (i = 0; i < sz; i++) { if(i < cmd->tx_cmd_sz) { byte = tx_buf[i]; error = mtk_spi_txrx(sc, &byte, MTK_SPI_WRITE); if (error) goto mtk_spi_transfer_fail; continue; } error = mtk_spi_txrx(sc, &byte, MTK_SPI_READ); if (error) goto mtk_spi_transfer_fail; buf[i] = byte; } } /* * Transfer/Receive data */ if (cmd->tx_data_sz + cmd->rx_data_sz) { write = (cmd->tx_data_sz > 0)?1:0; buf = (uint8_t *)(write ? cmd->tx_data : cmd->rx_data); sz = write ? cmd->tx_data_sz : cmd->rx_data_sz; for (i = 0; i < sz; i++) { byte = buf[i]; error = mtk_spi_txrx(sc, &byte, write ? MTK_SPI_WRITE : MTK_SPI_READ); if (error) goto mtk_spi_transfer_fail; buf[i] = byte; } } mtk_spi_transfer_fail: mtk_spi_chip_deactivate(sc); return (error); } static phandle_t mtk_spi_get_node(device_t bus, device_t dev) { /* We only have one child, the SPI bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t mtk_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mtk_spi_probe), DEVMETHOD(device_attach, mtk_spi_attach), DEVMETHOD(device_detach, mtk_spi_detach), DEVMETHOD(spibus_transfer, mtk_spi_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, mtk_spi_get_node), DEVMETHOD_END }; static driver_t mtk_spi_driver = { .name = "spi", .methods = mtk_spi_methods, .size = sizeof(struct mtk_spi_softc), }; static devclass_t mtk_spi_devclass; DRIVER_MODULE(mtk_spi_v1, simplebus, mtk_spi_driver, mtk_spi_devclass, 0, 0); Index: user/ngie/bug-237403/sys/netinet/netdump/netdump_client.c =================================================================== --- user/ngie/bug-237403/sys/netinet/netdump/netdump_client.c (revision 348022) +++ user/ngie/bug-237403/sys/netinet/netdump/netdump_client.c (revision 348023) @@ -1,1510 +1,1511 @@ /*- * Copyright (c) 2005-2014 Sandvine Incorporated. All rights reserved. * Copyright (c) 2000 Darrell Anderson * 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. */ /* * netdump_client.c * FreeBSD subsystem supporting netdump network dumps. * A dedicated server must be running to accept client dumps. */ #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 #include #include #include #include #include #include #include #include #include #include #define NETDDEBUG(f, ...) do { \ if (nd_debug > 0) \ printf(("%s: " f), __func__, ## __VA_ARGS__); \ } while (0) #define NETDDEBUG_IF(i, f, ...) do { \ if (nd_debug > 0) \ if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__); \ } while (0) #define NETDDEBUGV(f, ...) do { \ if (nd_debug > 1) \ printf(("%s: " f), __func__, ## __VA_ARGS__); \ } while (0) #define NETDDEBUGV_IF(i, f, ...) do { \ if (nd_debug > 1) \ if_printf((i), ("%s: " f), __func__, ## __VA_ARGS__); \ } while (0) static int netdump_arp_gw(void); static void netdump_cleanup(void); static int netdump_configure(struct diocskerneldump_arg *, struct thread *); static int netdump_dumper(void *priv __unused, void *virtual, vm_offset_t physical __unused, off_t offset, size_t length); static bool netdump_enabled(void); static int netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS); static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst, u_short etype); static void netdump_handle_arp(struct mbuf **mb); static void netdump_handle_ip(struct mbuf **mb); static int netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr, int flags __unused, struct thread *td); static int netdump_modevent(module_t mod, int type, void *priv); static void netdump_network_poll(void); static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m); static void netdump_reinit_internal(struct ifnet *ifp); static int netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen); static int netdump_send_arp(in_addr_t dst); static int netdump_start(struct dumperinfo *di); static int netdump_udp_output(struct mbuf *m); static void netdump_unconfigure(void); /* Must be at least as big as the chunks dumpsys() gives us. */ static unsigned char nd_buf[MAXDUMPPGS * PAGE_SIZE]; static uint32_t nd_seqno; static int dump_failed, have_gw_mac; static void (*drv_if_input)(struct ifnet *, struct mbuf *); static int restore_gw_addr; static uint64_t rcvd_acks; CTASSERT(sizeof(rcvd_acks) * NBBY == NETDUMP_MAX_IN_FLIGHT); /* Configuration parameters. */ static struct { char ndc_iface[IFNAMSIZ]; union kd_ip ndc_server; union kd_ip ndc_client; union kd_ip ndc_gateway; uint8_t ndc_af; } nd_conf; #define nd_server nd_conf.ndc_server.in4 #define nd_client nd_conf.ndc_client.in4 #define nd_gateway nd_conf.ndc_gateway.in4 /* General dynamic settings. */ static struct sx nd_conf_lk; SX_SYSINIT(nd_conf, &nd_conf_lk, "netdump configuration lock"); #define NETDUMP_WLOCK() sx_xlock(&nd_conf_lk) #define NETDUMP_WUNLOCK() sx_xunlock(&nd_conf_lk) #define NETDUMP_RLOCK() sx_slock(&nd_conf_lk) #define NETDUMP_RUNLOCK() sx_sunlock(&nd_conf_lk) #define NETDUMP_ASSERT_WLOCKED() sx_assert(&nd_conf_lk, SA_XLOCKED) #define NETDUMP_ASSERT_LOCKED() sx_assert(&nd_conf_lk, SA_LOCKED) static struct ether_addr nd_gw_mac; static struct ifnet *nd_ifp; static eventhandler_tag nd_detach_cookie; static uint16_t nd_server_port = NETDUMP_PORT; FEATURE(netdump, "Netdump client support"); static SYSCTL_NODE(_net, OID_AUTO, netdump, CTLFLAG_RD, NULL, "netdump parameters"); static int nd_debug; SYSCTL_INT(_net_netdump, OID_AUTO, debug, CTLFLAG_RWTUN, &nd_debug, 0, "Debug message verbosity"); SYSCTL_PROC(_net_netdump, OID_AUTO, enabled, CTLFLAG_RD | CTLTYPE_INT, &nd_ifp, 0, netdump_enabled_sysctl, "I", "netdump configuration status"); static char nd_path[MAXPATHLEN]; SYSCTL_STRING(_net_netdump, OID_AUTO, path, CTLFLAG_RW, nd_path, sizeof(nd_path), "Server path for output files"); static int nd_polls = 2000; SYSCTL_INT(_net_netdump, OID_AUTO, polls, CTLFLAG_RWTUN, &nd_polls, 0, "Number of times to poll before assuming packet loss (0.5ms per poll)"); static int nd_retries = 10; SYSCTL_INT(_net_netdump, OID_AUTO, retries, CTLFLAG_RWTUN, &nd_retries, 0, "Number of retransmit attempts before giving up"); static int nd_arp_retries = 3; SYSCTL_INT(_net_netdump, OID_AUTO, arp_retries, CTLFLAG_RWTUN, &nd_arp_retries, 0, "Number of ARP attempts before giving up"); static bool netdump_enabled(void) { NETDUMP_ASSERT_LOCKED(); return (nd_ifp != NULL); } static int netdump_enabled_sysctl(SYSCTL_HANDLER_ARGS) { int en, error; NETDUMP_RLOCK(); en = netdump_enabled(); NETDUMP_RUNLOCK(); error = SYSCTL_OUT(req, &en, sizeof(en)); if (error != 0 || req->newptr == NULL) return (error); return (EPERM); } /* * Checks for netdump support on a network interface * * Parameters: * ifp The network interface that is being tested for support * * Returns: * int 1 if the interface is supported, 0 if not */ static bool netdump_supported_nic(struct ifnet *ifp) { return (ifp->if_netdump_methods != NULL); } /*- * Network specific primitives. * Following down the code they are divided ordered as: * - Packet buffer primitives * - Output primitives * - Input primitives * - Polling primitives */ /* * Handles creation of the ethernet header, then places outgoing packets into * the tx buffer for the NIC * * Parameters: * m The mbuf containing the packet to be sent (will be freed by * this function or the NIC driver) * ifp The interface to send on * dst The destination ethernet address (source address will be looked * up using ifp) * etype The ETHERTYPE_* value for the protocol that is being sent * * Returns: * int see errno.h, 0 for success */ static int netdump_ether_output(struct mbuf *m, struct ifnet *ifp, struct ether_addr dst, u_short etype) { struct ether_header *eh; if (((ifp->if_flags & (IFF_MONITOR | IFF_UP)) != IFF_UP) || (ifp->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) { if_printf(ifp, "netdump_ether_output: interface isn't up\n"); m_freem(m); return (ENETDOWN); } /* Fill in the ethernet header. */ M_PREPEND(m, ETHER_HDR_LEN, M_NOWAIT); if (m == NULL) { printf("%s: out of mbufs\n", __func__); return (ENOBUFS); } eh = mtod(m, struct ether_header *); memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); memcpy(eh->ether_dhost, dst.octet, ETHER_ADDR_LEN); eh->ether_type = htons(etype); return ((ifp->if_netdump_methods->nd_transmit)(ifp, m)); } /* * Unreliable transmission of an mbuf chain to the netdump server * Note: can't handle fragmentation; fails if the packet is larger than * nd_ifp->if_mtu after adding the UDP/IP headers * * Parameters: * m mbuf chain * * Returns: * int see errno.h, 0 for success */ static int netdump_udp_output(struct mbuf *m) { struct udpiphdr *ui; struct ip *ip; MPASS(netdump_enabled()); M_PREPEND(m, sizeof(struct udpiphdr), M_NOWAIT); if (m == NULL) { printf("%s: out of mbufs\n", __func__); return (ENOBUFS); } if (m->m_pkthdr.len > nd_ifp->if_mtu) { printf("netdump_udp_output: Packet is too big: %d > MTU %u\n", m->m_pkthdr.len, nd_ifp->if_mtu); m_freem(m); return (ENOBUFS); } ui = mtod(m, struct udpiphdr *); bzero(ui->ui_x1, sizeof(ui->ui_x1)); ui->ui_pr = IPPROTO_UDP; ui->ui_len = htons(m->m_pkthdr.len - sizeof(struct ip)); ui->ui_ulen = ui->ui_len; ui->ui_src = nd_client; ui->ui_dst = nd_server; /* Use this src port so that the server can connect() the socket */ ui->ui_sport = htons(NETDUMP_ACKPORT); ui->ui_dport = htons(nd_server_port); ui->ui_sum = 0; if ((ui->ui_sum = in_cksum(m, m->m_pkthdr.len)) == 0) ui->ui_sum = 0xffff; ip = mtod(m, struct ip *); ip->ip_v = IPVERSION; ip->ip_hl = sizeof(struct ip) >> 2; ip->ip_tos = 0; ip->ip_len = htons(m->m_pkthdr.len); ip->ip_id = 0; ip->ip_off = htons(IP_DF); ip->ip_ttl = 255; ip->ip_sum = 0; ip->ip_sum = in_cksum(m, sizeof(struct ip)); return (netdump_ether_output(m, nd_ifp, nd_gw_mac, ETHERTYPE_IP)); } /* * Builds and sends a single ARP request to locate the server * * Return value: * 0 on success * errno on error */ static int netdump_send_arp(in_addr_t dst) { struct ether_addr bcast; struct mbuf *m; struct arphdr *ah; int pktlen; MPASS(netdump_enabled()); /* Fill-up a broadcast address. */ memset(&bcast, 0xFF, ETHER_ADDR_LEN); m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) { printf("netdump_send_arp: Out of mbufs\n"); return (ENOBUFS); } pktlen = arphdr_len2(ETHER_ADDR_LEN, sizeof(struct in_addr)); m->m_len = pktlen; m->m_pkthdr.len = pktlen; MH_ALIGN(m, pktlen); ah = mtod(m, struct arphdr *); ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETHERTYPE_IP); ah->ar_hln = ETHER_ADDR_LEN; ah->ar_pln = sizeof(struct in_addr); ah->ar_op = htons(ARPOP_REQUEST); memcpy(ar_sha(ah), IF_LLADDR(nd_ifp), ETHER_ADDR_LEN); ((struct in_addr *)ar_spa(ah))->s_addr = nd_client.s_addr; bzero(ar_tha(ah), ETHER_ADDR_LEN); ((struct in_addr *)ar_tpa(ah))->s_addr = dst; return (netdump_ether_output(m, nd_ifp, bcast, ETHERTYPE_ARP)); } /* * Sends ARP requests to locate the server and waits for a response. * We first try to ARP the server itself, and fall back to the provided * gateway if the server appears to be off-link. * * Return value: * 0 on success * errno on error */ static int netdump_arp_gw(void) { in_addr_t dst; int error, polls, retries; dst = nd_server.s_addr; restart: for (retries = 0; retries < nd_arp_retries && have_gw_mac == 0; retries++) { error = netdump_send_arp(dst); if (error != 0) return (error); for (polls = 0; polls < nd_polls && have_gw_mac == 0; polls++) { netdump_network_poll(); DELAY(500); } if (have_gw_mac == 0) printf("(ARP retry)"); } if (have_gw_mac != 0) return (0); if (dst == nd_server.s_addr && nd_server.s_addr != nd_gateway.s_addr) { printf("Failed to ARP server, trying to reach gateway...\n"); dst = nd_gateway.s_addr; goto restart; } printf("\nARP timed out.\n"); return (ETIMEDOUT); } /* * Dummy free function for netdump clusters. */ static void netdump_mbuf_free(struct mbuf *m __unused) { } /* * Construct and reliably send a netdump packet. May fail from a resource * shortage or extreme number of unacknowledged retransmissions. Wait for * an acknowledgement before returning. Splits packets into chunks small * enough to be sent without fragmentation (looks up the interface MTU) * * Parameters: * type netdump packet type (HERALD, FINISHED, or VMCORE) * offset vmcore data offset (bytes) * data vmcore data * datalen vmcore data size (bytes) * * Returns: * int see errno.h, 0 for success */ static int netdump_send(uint32_t type, off_t offset, unsigned char *data, uint32_t datalen) { struct netdump_msg_hdr *nd_msg_hdr; struct mbuf *m, *m2; uint64_t want_acks; uint32_t i, pktlen, sent_so_far; int retries, polls, error; want_acks = 0; rcvd_acks = 0; retries = 0; MPASS(netdump_enabled()); retransmit: /* Chunks can be too big to fit in packets. */ for (i = sent_so_far = 0; sent_so_far < datalen || (i == 0 && datalen == 0); i++) { pktlen = datalen - sent_so_far; /* First bound: the packet structure. */ pktlen = min(pktlen, NETDUMP_DATASIZE); /* Second bound: the interface MTU (assume no IP options). */ pktlen = min(pktlen, nd_ifp->if_mtu - sizeof(struct udpiphdr) - sizeof(struct netdump_msg_hdr)); /* * Check if it is retransmitting and this has been ACKed * already. */ if ((rcvd_acks & (1 << i)) != 0) { sent_so_far += pktlen; continue; } /* * Get and fill a header mbuf, then chain data as an extended * mbuf. */ m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) { printf("netdump_send: Out of mbufs\n"); return (ENOBUFS); } m->m_len = sizeof(struct netdump_msg_hdr); m->m_pkthdr.len = sizeof(struct netdump_msg_hdr); MH_ALIGN(m, sizeof(struct netdump_msg_hdr)); nd_msg_hdr = mtod(m, struct netdump_msg_hdr *); nd_msg_hdr->mh_seqno = htonl(nd_seqno + i); nd_msg_hdr->mh_type = htonl(type); nd_msg_hdr->mh_offset = htobe64(offset + sent_so_far); nd_msg_hdr->mh_len = htonl(pktlen); nd_msg_hdr->mh__pad = 0; if (pktlen != 0) { m2 = m_get(M_NOWAIT, MT_DATA); if (m2 == NULL) { m_freem(m); printf("netdump_send: Out of mbufs\n"); return (ENOBUFS); } MEXTADD(m2, data + sent_so_far, pktlen, netdump_mbuf_free, NULL, NULL, 0, EXT_DISPOSABLE); m2->m_len = pktlen; m_cat(m, m2); m->m_pkthdr.len += pktlen; } error = netdump_udp_output(m); if (error != 0) return (error); /* Note that we're waiting for this packet in the bitfield. */ want_acks |= (1 << i); sent_so_far += pktlen; } if (i >= NETDUMP_MAX_IN_FLIGHT) printf("Warning: Sent more than %d packets (%d). " "Acknowledgements will fail unless the size of " "rcvd_acks/want_acks is increased.\n", NETDUMP_MAX_IN_FLIGHT, i); /* * Wait for acks. A *real* window would speed things up considerably. */ polls = 0; while (rcvd_acks != want_acks) { if (polls++ > nd_polls) { if (retries++ > nd_retries) return (ETIMEDOUT); printf(". "); goto retransmit; } netdump_network_poll(); DELAY(500); } nd_seqno += i; return (0); } /* * Handler for IP packets: checks their sanity and then processes any netdump * ACK packets it finds. * * It needs to replicate partially the behaviour of ip_input() and * udp_input(). * * Parameters: * mb a pointer to an mbuf * containing the packet received * Updates *mb if m_pullup et al change the pointer * Assumes the calling function will take care of freeing the mbuf */ static void netdump_handle_ip(struct mbuf **mb) { struct ip *ip; struct udpiphdr *udp; struct netdump_ack *nd_ack; struct mbuf *m; int rcv_ackno; unsigned short hlen; /* IP processing. */ m = *mb; if (m->m_pkthdr.len < sizeof(struct ip)) { NETDDEBUG("dropping packet too small for IP header\n"); return; } if (m->m_len < sizeof(struct ip)) { m = m_pullup(m, sizeof(struct ip)); *mb = m; if (m == NULL) { NETDDEBUG("m_pullup failed\n"); return; } } ip = mtod(m, struct ip *); /* IP version. */ if (ip->ip_v != IPVERSION) { NETDDEBUG("bad IP version %d\n", ip->ip_v); return; } /* Header length. */ hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) { NETDDEBUG("bad IP header length (%hu)\n", hlen); return; } if (hlen > m->m_len) { m = m_pullup(m, hlen); *mb = m; if (m == NULL) { NETDDEBUG("m_pullup failed\n"); return; } ip = mtod(m, struct ip *); } /* Ignore packets with IP options. */ if (hlen > sizeof(struct ip)) { NETDDEBUG("drop packet with IP options\n"); return; } #ifdef INVARIANTS if ((IN_LOOPBACK(ntohl(ip->ip_dst.s_addr)) || IN_LOOPBACK(ntohl(ip->ip_src.s_addr))) && (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) { NETDDEBUG("Bad IP header (RFC1122)\n"); return; } #endif /* Checksum. */ if ((m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) != 0) { if ((m->m_pkthdr.csum_flags & CSUM_IP_VALID) == 0) { NETDDEBUG("bad IP checksum\n"); return; } } else { /* XXX */ ; } /* Convert fields to host byte order. */ ip->ip_len = ntohs(ip->ip_len); if (ip->ip_len < hlen) { NETDDEBUG("IP packet smaller (%hu) than header (%hu)\n", ip->ip_len, hlen); return; } if (m->m_pkthdr.len < ip->ip_len) { NETDDEBUG("IP packet bigger (%hu) than ethernet packet (%d)\n", ip->ip_len, m->m_pkthdr.len); return; } if (m->m_pkthdr.len > ip->ip_len) { /* Truncate the packet to the IP length. */ if (m->m_len == m->m_pkthdr.len) { m->m_len = ip->ip_len; m->m_pkthdr.len = ip->ip_len; } else m_adj(m, ip->ip_len - m->m_pkthdr.len); } ip->ip_off = ntohs(ip->ip_off); /* Check that the source is the server's IP. */ if (ip->ip_src.s_addr != nd_server.s_addr) { NETDDEBUG("drop packet not from server (from 0x%x)\n", ip->ip_src.s_addr); return; } /* Check if the destination IP is ours. */ if (ip->ip_dst.s_addr != nd_client.s_addr) { NETDDEBUGV("drop packet not to our IP\n"); return; } if (ip->ip_p != IPPROTO_UDP) { NETDDEBUG("drop non-UDP packet\n"); return; } /* Do not deal with fragments. */ if ((ip->ip_off & (IP_MF | IP_OFFMASK)) != 0) { NETDDEBUG("drop fragmented packet\n"); return; } /* UDP custom is to have packet length not include IP header. */ ip->ip_len -= hlen; /* UDP processing. */ /* Get IP and UDP headers together, along with the netdump packet. */ if (m->m_pkthdr.len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) { NETDDEBUG("ignoring small packet\n"); return; } if (m->m_len < sizeof(struct udpiphdr) + sizeof(struct netdump_ack)) { m = m_pullup(m, sizeof(struct udpiphdr) + sizeof(struct netdump_ack)); *mb = m; if (m == NULL) { NETDDEBUG("m_pullup failed\n"); return; } } udp = mtod(m, struct udpiphdr *); if (ntohs(udp->ui_u.uh_dport) != NETDUMP_ACKPORT) { NETDDEBUG("not on the netdump port.\n"); return; } /* Netdump processing. */ /* * Packet is meant for us. Extract the ack sequence number and the * port number if necessary. */ nd_ack = (struct netdump_ack *)(mtod(m, caddr_t) + sizeof(struct udpiphdr)); rcv_ackno = ntohl(nd_ack->na_seqno); if (nd_server_port == NETDUMP_PORT) nd_server_port = ntohs(udp->ui_u.uh_sport); if (rcv_ackno >= nd_seqno + NETDUMP_MAX_IN_FLIGHT) printf("%s: ACK %d too far in future!\n", __func__, rcv_ackno); else if (rcv_ackno >= nd_seqno) { /* We're interested in this ack. Record it. */ rcvd_acks |= 1 << (rcv_ackno - nd_seqno); } } /* * Handler for ARP packets: checks their sanity and then * 1. If the ARP is a request for our IP, respond with our MAC address * 2. If the ARP is a response from our server, record its MAC address * * It needs to replicate partially the behaviour of arpintr() and * in_arpinput(). * * Parameters: * mb a pointer to an mbuf * containing the packet received * Updates *mb if m_pullup et al change the pointer * Assumes the calling function will take care of freeing the mbuf */ static void netdump_handle_arp(struct mbuf **mb) { char buf[INET_ADDRSTRLEN]; struct in_addr isaddr, itaddr, myaddr; struct ether_addr dst; struct mbuf *m; struct arphdr *ah; struct ifnet *ifp; uint8_t *enaddr; int req_len, op; m = *mb; ifp = m->m_pkthdr.rcvif; if (m->m_len < sizeof(struct arphdr)) { m = m_pullup(m, sizeof(struct arphdr)); *mb = m; if (m == NULL) { NETDDEBUG("runt packet: m_pullup failed\n"); return; } } ah = mtod(m, struct arphdr *); if (ntohs(ah->ar_hrd) != ARPHRD_ETHER) { NETDDEBUG("unknown hardware address 0x%2D)\n", (unsigned char *)&ah->ar_hrd, ""); return; } if (ntohs(ah->ar_pro) != ETHERTYPE_IP) { NETDDEBUG("drop ARP for unknown protocol %d\n", ntohs(ah->ar_pro)); return; } req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr)); if (m->m_len < req_len) { m = m_pullup(m, req_len); *mb = m; if (m == NULL) { NETDDEBUG("runt packet: m_pullup failed\n"); return; } } ah = mtod(m, struct arphdr *); op = ntohs(ah->ar_op); memcpy(&isaddr, ar_spa(ah), sizeof(isaddr)); memcpy(&itaddr, ar_tpa(ah), sizeof(itaddr)); enaddr = (uint8_t *)IF_LLADDR(ifp); myaddr = nd_client; if (memcmp(ar_sha(ah), enaddr, ifp->if_addrlen) == 0) { NETDDEBUG("ignoring ARP from myself\n"); return; } if (isaddr.s_addr == nd_client.s_addr) { printf("%s: %*D is using my IP address %s!\n", __func__, ifp->if_addrlen, (u_char *)ar_sha(ah), ":", inet_ntoa_r(isaddr, buf)); return; } if (memcmp(ar_sha(ah), ifp->if_broadcastaddr, ifp->if_addrlen) == 0) { NETDDEBUG("ignoring ARP from broadcast address\n"); return; } if (op == ARPOP_REPLY) { if (isaddr.s_addr != nd_gateway.s_addr && isaddr.s_addr != nd_server.s_addr) { inet_ntoa_r(isaddr, buf); NETDDEBUG( "ignoring ARP reply from %s (not netdump server)\n", buf); return; } memcpy(nd_gw_mac.octet, ar_sha(ah), min(ah->ar_hln, ETHER_ADDR_LEN)); have_gw_mac = 1; NETDDEBUG("got server MAC address %6D\n", nd_gw_mac.octet, ":"); return; } if (op != ARPOP_REQUEST) { NETDDEBUG("ignoring ARP non-request/reply\n"); return; } if (itaddr.s_addr != nd_client.s_addr) { NETDDEBUG("ignoring ARP not to our IP\n"); return; } memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); memcpy(ar_sha(ah), enaddr, ah->ar_hln); memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); memcpy(ar_spa(ah), &itaddr, ah->ar_pln); ah->ar_op = htons(ARPOP_REPLY); ah->ar_pro = htons(ETHERTYPE_IP); m->m_flags &= ~(M_BCAST|M_MCAST); m->m_len = arphdr_len(ah); m->m_pkthdr.len = m->m_len; memcpy(dst.octet, ar_tha(ah), ETHER_ADDR_LEN); netdump_ether_output(m, ifp, dst, ETHERTYPE_ARP); *mb = NULL; } /* * Handler for incoming packets directly from the network adapter * Identifies the packet type (IP or ARP) and passes it along to one of the * helper functions netdump_handle_ip or netdump_handle_arp. * * It needs to replicate partially the behaviour of ether_input() and * ether_demux(). * * Parameters: * ifp the interface the packet came from (should be nd_ifp) * m an mbuf containing the packet received */ static void netdump_pkt_in(struct ifnet *ifp, struct mbuf *m) { struct ifreq ifr; struct ether_header *eh; u_short etype; /* Ethernet processing. */ if ((m->m_flags & M_PKTHDR) == 0) { NETDDEBUG_IF(ifp, "discard frame without packet header\n"); goto done; } if (m->m_len < ETHER_HDR_LEN) { NETDDEBUG_IF(ifp, "discard frame without leading eth header (len %u pktlen %u)\n", m->m_len, m->m_pkthdr.len); goto done; } if ((m->m_flags & M_HASFCS) != 0) { m_adj(m, -ETHER_CRC_LEN); m->m_flags &= ~M_HASFCS; } eh = mtod(m, struct ether_header *); etype = ntohs(eh->ether_type); if ((m->m_flags & M_VLANTAG) != 0 || etype == ETHERTYPE_VLAN) { NETDDEBUG_IF(ifp, "ignoring vlan packets\n"); goto done; } if (if_gethwaddr(ifp, &ifr) != 0) { NETDDEBUG_IF(ifp, "failed to get hw addr for interface\n"); goto done; } if (memcmp(ifr.ifr_addr.sa_data, eh->ether_dhost, ETHER_ADDR_LEN) != 0) { NETDDEBUG_IF(ifp, "discard frame with incorrect destination addr\n"); goto done; } /* Done ethernet processing. Strip off the ethernet header. */ m_adj(m, ETHER_HDR_LEN); switch (etype) { case ETHERTYPE_ARP: netdump_handle_arp(&m); break; case ETHERTYPE_IP: netdump_handle_ip(&m); break; default: NETDDEBUG_IF(ifp, "dropping unknown ethertype %hu\n", etype); break; } done: if (m != NULL) m_freem(m); } /* * After trapping, instead of assuming that most of the network stack is sane, * we just poll the driver directly for packets. */ static void netdump_network_poll(void) { MPASS(netdump_enabled()); nd_ifp->if_netdump_methods->nd_poll(nd_ifp, 1000); } /*- * Dumping specific primitives. */ /* * Callback from dumpsys() to dump a chunk of memory. * Copies it out to our static buffer then sends it across the network. * Detects the initial KDH and makes sure it is given a special packet type. * * Parameters: * priv Unused. Optional private pointer. * virtual Virtual address (where to read the data from) * physical Unused. Physical memory address. * offset Offset from start of core file * length Data length * * Return value: * 0 on success * errno on error */ static int netdump_dumper(void *priv __unused, void *virtual, vm_offset_t physical __unused, off_t offset, size_t length) { int error; NETDDEBUGV("netdump_dumper(NULL, %p, NULL, %ju, %zu)\n", virtual, (uintmax_t)offset, length); if (virtual == NULL) { if (dump_failed != 0) printf("failed to dump the kernel core\n"); else if (netdump_send(NETDUMP_FINISHED, 0, NULL, 0) != 0) printf("failed to close the transaction\n"); else printf("\nnetdump finished.\n"); netdump_cleanup(); return (0); } if (length > sizeof(nd_buf)) return (ENOSPC); memmove(nd_buf, virtual, length); error = netdump_send(NETDUMP_VMCORE, offset, nd_buf, length); if (error != 0) { dump_failed = 1; return (error); } return (0); } /* * Perform any initalization needed prior to transmitting the kernel core. */ static int netdump_start(struct dumperinfo *di) { char *path; char buf[INET_ADDRSTRLEN]; uint32_t len; int error; error = 0; /* Check if the dumping is allowed to continue. */ if (!netdump_enabled()) return (EINVAL); if (panicstr == NULL) { printf( "netdump_start: netdump may only be used after a panic\n"); return (EINVAL); } if (nd_server.s_addr == INADDR_ANY) { printf("netdump_start: can't netdump; no server IP given\n"); return (EINVAL); } if (nd_client.s_addr == INADDR_ANY) { printf("netdump_start: can't netdump; no client IP given\n"); return (EINVAL); } /* We start dumping at offset 0. */ di->dumpoff = 0; nd_seqno = 1; /* * nd_server_port could have switched after the first ack the * first time it gets called. Adjust it accordingly. */ nd_server_port = NETDUMP_PORT; /* Switch to the netdump mbuf zones. */ netdump_mbuf_dump(); nd_ifp->if_netdump_methods->nd_event(nd_ifp, NETDUMP_START); /* Make the card use *our* receive callback. */ drv_if_input = nd_ifp->if_input; nd_ifp->if_input = netdump_pkt_in; if (nd_gateway.s_addr == INADDR_ANY) { restore_gw_addr = 1; nd_gateway.s_addr = nd_server.s_addr; } printf("netdump in progress. searching for server...\n"); if (netdump_arp_gw()) { printf("failed to locate server MAC address\n"); error = EINVAL; goto trig_abort; } if (nd_path[0] != '\0') { path = nd_path; len = strlen(path) + 1; } else { path = NULL; len = 0; } if (netdump_send(NETDUMP_HERALD, 0, path, len) != 0) { printf("failed to contact netdump server\n"); error = EINVAL; goto trig_abort; } printf("netdumping to %s (%6D)\n", inet_ntoa_r(nd_server, buf), nd_gw_mac.octet, ":"); return (0); trig_abort: netdump_cleanup(); return (error); } static int netdump_write_headers(struct dumperinfo *di, struct kerneldumpheader *kdh, void *key, uint32_t keysize) { int error; memcpy(nd_buf, kdh, sizeof(*kdh)); error = netdump_send(NETDUMP_KDH, 0, nd_buf, sizeof(*kdh)); if (error == 0 && keysize > 0) { if (keysize > sizeof(nd_buf)) return (EINVAL); memcpy(nd_buf, key, keysize); error = netdump_send(NETDUMP_EKCD_KEY, 0, nd_buf, keysize); } return (error); } /* * Cleanup routine for a possibly failed netdump. */ static void netdump_cleanup(void) { if (restore_gw_addr != 0) { nd_gateway.s_addr = INADDR_ANY; restore_gw_addr = 0; } if (drv_if_input != NULL) { nd_ifp->if_input = drv_if_input; drv_if_input = NULL; } nd_ifp->if_netdump_methods->nd_event(nd_ifp, NETDUMP_END); } /*- * KLD specific code. */ static struct cdevsw netdump_cdevsw = { .d_version = D_VERSION, .d_ioctl = netdump_ioctl, .d_name = "netdump", }; static struct cdev *netdump_cdev; static void netdump_unconfigure(void) { struct diocskerneldump_arg kda; NETDUMP_ASSERT_WLOCKED(); KASSERT(netdump_enabled(), ("%s: nd_ifp NULL", __func__)); bzero(&kda, sizeof(kda)); kda.kda_index = KDA_REMOVE_DEV; (void)dumper_remove(nd_conf.ndc_iface, &kda); netdump_mbuf_drain(); if_rele(nd_ifp); nd_ifp = NULL; bzero(&nd_conf, sizeof(nd_conf)); } static void netdump_ifdetach(void *arg __unused, struct ifnet *ifp) { NETDUMP_WLOCK(); if (ifp == nd_ifp) netdump_unconfigure(); NETDUMP_WUNLOCK(); } static int netdump_configure(struct diocskerneldump_arg *conf, struct thread *td) { struct ifnet *ifp; NETDUMP_ASSERT_WLOCKED(); CURVNET_SET(TD_TO_VNET(td)); if (!IS_DEFAULT_VNET(curvnet)) { CURVNET_RESTORE(); return (EINVAL); } ifp = ifunit_ref(conf->kda_iface); CURVNET_RESTORE(); if (ifp == NULL) return (ENOENT); if ((if_getflags(ifp) & IFF_UP) == 0) { if_rele(ifp); return (ENXIO); } if (!netdump_supported_nic(ifp) || ifp->if_type != IFT_ETHER) { if_rele(ifp); return (ENODEV); } if (netdump_enabled()) if_rele(nd_ifp); nd_ifp = ifp; netdump_reinit_internal(ifp); #define COPY_SIZED(elm) do { \ _Static_assert(sizeof(nd_conf.ndc_ ## elm) == \ sizeof(conf->kda_ ## elm), "elm " __XSTRING(elm) " mismatch"); \ memcpy(&nd_conf.ndc_ ## elm, &conf->kda_ ## elm, \ sizeof(nd_conf.ndc_ ## elm)); \ } while (0) COPY_SIZED(iface); COPY_SIZED(server); COPY_SIZED(client); COPY_SIZED(gateway); COPY_SIZED(af); #undef COPY_SIZED return (0); } /* * Reinitialize the mbuf pool used by drivers while dumping. This is called * from the generic ioctl handler for SIOCSIFMTU after any NIC driver has * reconfigured itself. (I.e., it may not be a configured netdump interface.) */ void netdump_reinit(struct ifnet *ifp) { NETDUMP_WLOCK(); if (ifp != nd_ifp) { NETDUMP_WUNLOCK(); return; } netdump_reinit_internal(ifp); NETDUMP_WUNLOCK(); } static void netdump_reinit_internal(struct ifnet *ifp) { int clsize, nmbuf, ncl, nrxr; NETDUMP_ASSERT_WLOCKED(); ifp->if_netdump_methods->nd_init(ifp, &nrxr, &ncl, &clsize); KASSERT(nrxr > 0, ("invalid receive ring count %d", nrxr)); /* * We need two headers per message on the transmit side. Multiply by * four to give us some breathing room. */ nmbuf = ncl * (4 + nrxr); ncl *= nrxr; netdump_mbuf_reinit(nmbuf, ncl, clsize); } /* * ioctl(2) handler for the netdump device. This is currently only used to * register netdump as a dump device. * * Parameters: * dev, Unused. * cmd, The ioctl to be handled. * addr, The parameter for the ioctl. * flags, Unused. * td, The thread invoking this ioctl. * * Returns: * 0 on success, and an errno value on failure. */ static int netdump_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr, int flags __unused, struct thread *td) { struct diocskerneldump_arg kda_copy, *conf; struct dumperinfo dumper; uint8_t *encryptedkey; int error; #ifdef COMPAT_FREEBSD11 u_int u; #endif #ifdef COMPAT_FREEBSD12 struct diocskerneldump_arg_freebsd12 *kda12; struct netdump_conf_freebsd12 *conf12; #endif conf = NULL; error = 0; NETDUMP_WLOCK(); switch (cmd) { #ifdef COMPAT_FREEBSD11 case DIOCSKERNELDUMP_FREEBSD11: gone_in(13, "11.x ABI compatibility"); u = *(u_int *)addr; if (u != 0) { error = ENXIO; break; } if (netdump_enabled()) netdump_unconfigure(); break; #endif #ifdef COMPAT_FREEBSD12 /* * Used by dumpon(8) in 12.x for clearing previous * configuration -- then NETDUMPSCONF_FREEBSD12 is used to * actually configure netdump. */ case DIOCSKERNELDUMP_FREEBSD12: gone_in(14, "12.x ABI compatibility"); kda12 = (void *)addr; if (kda12->kda12_enable) { error = ENXIO; break; } if (netdump_enabled()) netdump_unconfigure(); break; case NETDUMPGCONF_FREEBSD12: gone_in(14, "FreeBSD 12.x ABI compat"); conf12 = (void *)addr; if (!netdump_enabled()) { error = ENXIO; break; } if (nd_conf.ndc_af != AF_INET) { error = EOPNOTSUPP; break; } strlcpy(conf12->ndc12_iface, nd_ifp->if_xname, sizeof(conf12->ndc12_iface)); memcpy(&conf12->ndc12_server, &nd_server, sizeof(conf12->ndc12_server)); memcpy(&conf12->ndc12_client, &nd_client, sizeof(conf12->ndc12_client)); memcpy(&conf12->ndc12_gateway, &nd_gateway, sizeof(conf12->ndc12_gateway)); break; #endif case DIOCGKERNELDUMP: conf = (void *)addr; /* * For now, index is ignored; netdump doesn't support multiple * configurations (yet). */ if (!netdump_enabled()) { error = ENXIO; conf = NULL; break; } strlcpy(conf->kda_iface, nd_ifp->if_xname, sizeof(conf->kda_iface)); memcpy(&conf->kda_server, &nd_server, sizeof(nd_server)); memcpy(&conf->kda_client, &nd_client, sizeof(nd_client)); memcpy(&conf->kda_gateway, &nd_gateway, sizeof(nd_gateway)); conf->kda_af = nd_conf.ndc_af; conf = NULL; break; #ifdef COMPAT_FREEBSD12 case NETDUMPSCONF_FREEBSD12: gone_in(14, "FreeBSD 12.x ABI compat"); conf12 = (struct netdump_conf_freebsd12 *)addr; _Static_assert(offsetof(struct diocskerneldump_arg, kda_server) == offsetof(struct netdump_conf_freebsd12, ndc12_server), "simplifying assumption"); memset(&kda_copy, 0, sizeof(kda_copy)); memcpy(&kda_copy, conf12, offsetof(struct diocskerneldump_arg, kda_server)); /* 12.x ABI could only configure IPv4 (INET) netdump. */ kda_copy.kda_af = AF_INET; memcpy(&kda_copy.kda_server.in4, &conf12->ndc12_server, sizeof(struct in_addr)); memcpy(&kda_copy.kda_client.in4, &conf12->ndc12_client, sizeof(struct in_addr)); memcpy(&kda_copy.kda_gateway.in4, &conf12->ndc12_gateway, sizeof(struct in_addr)); kda_copy.kda_index = (conf12->ndc12_kda.kda12_enable ? 0 : KDA_REMOVE_ALL); conf = &kda_copy; explicit_bzero(conf12, sizeof(*conf12)); /* FALLTHROUGH */ #endif case DIOCSKERNELDUMP: encryptedkey = NULL; if (cmd == DIOCSKERNELDUMP) { conf = (void *)addr; memcpy(&kda_copy, conf, sizeof(kda_copy)); } /* Netdump only supports IP4 at this time. */ if (conf->kda_af != AF_INET) { error = EPROTONOSUPPORT; break; } conf->kda_iface[sizeof(conf->kda_iface) - 1] = '\0'; if (conf->kda_index == KDA_REMOVE || conf->kda_index == KDA_REMOVE_DEV || conf->kda_index == KDA_REMOVE_ALL) { if (netdump_enabled()) netdump_unconfigure(); if (conf->kda_index == KDA_REMOVE_ALL) error = dumper_remove(NULL, conf); break; } error = netdump_configure(conf, td); if (error != 0) break; if (conf->kda_encryption != KERNELDUMP_ENC_NONE) { if (conf->kda_encryptedkeysize <= 0 || conf->kda_encryptedkeysize > KERNELDUMP_ENCKEY_MAX_SIZE) { error = EINVAL; break; } encryptedkey = malloc(conf->kda_encryptedkeysize, M_TEMP, M_WAITOK); error = copyin(conf->kda_encryptedkey, encryptedkey, conf->kda_encryptedkeysize); if (error != 0) { free(encryptedkey, M_TEMP); break; } conf->kda_encryptedkey = encryptedkey; } memset(&dumper, 0, sizeof(dumper)); dumper.dumper_start = netdump_start; dumper.dumper_hdr = netdump_write_headers; dumper.dumper = netdump_dumper; dumper.priv = NULL; dumper.blocksize = NETDUMP_DATASIZE; dumper.maxiosize = MAXDUMPPGS * PAGE_SIZE; dumper.mediaoffset = 0; dumper.mediasize = 0; error = dumper_insert(&dumper, conf->kda_iface, conf); if (encryptedkey != NULL) { explicit_bzero(encryptedkey, conf->kda_encryptedkeysize); free(encryptedkey, M_TEMP); } if (error != 0) netdump_unconfigure(); break; default: error = ENOTTY; break; } explicit_bzero(&kda_copy, sizeof(kda_copy)); if (conf != NULL) explicit_bzero(conf, sizeof(*conf)); NETDUMP_WUNLOCK(); return (error); } /* * Called upon system init or kld load. Initializes the netdump parameters to * sane defaults (locates the first available NIC and uses the first IPv4 IP on * that card as the client IP). Leaves the server IP unconfigured. * * Parameters: * mod, Unused. * what, The module event type. * priv, Unused. * * Returns: * int, An errno value if an error occured, 0 otherwise. */ static int netdump_modevent(module_t mod __unused, int what, void *priv __unused) { struct diocskerneldump_arg conf; char *arg; int error; error = 0; switch (what) { case MOD_LOAD: error = make_dev_p(MAKEDEV_WAITOK, &netdump_cdev, &netdump_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "netdump"); if (error != 0) return (error); nd_detach_cookie = EVENTHANDLER_REGISTER(ifnet_departure_event, netdump_ifdetach, NULL, EVENTHANDLER_PRI_ANY); if ((arg = kern_getenv("net.dump.iface")) != NULL) { strlcpy(conf.kda_iface, arg, sizeof(conf.kda_iface)); freeenv(arg); if ((arg = kern_getenv("net.dump.server")) != NULL) { inet_aton(arg, &conf.kda_server.in4); freeenv(arg); } if ((arg = kern_getenv("net.dump.client")) != NULL) { inet_aton(arg, &conf.kda_client.in4); freeenv(arg); } if ((arg = kern_getenv("net.dump.gateway")) != NULL) { inet_aton(arg, &conf.kda_gateway.in4); freeenv(arg); } conf.kda_af = AF_INET; /* Ignore errors; we print a message to the console. */ NETDUMP_WLOCK(); (void)netdump_configure(&conf, curthread); NETDUMP_WUNLOCK(); } break; case MOD_UNLOAD: NETDUMP_WLOCK(); if (netdump_enabled()) { printf("netdump: disabling dump device for unload\n"); netdump_unconfigure(); } NETDUMP_WUNLOCK(); destroy_dev(netdump_cdev); EVENTHANDLER_DEREGISTER(ifnet_departure_event, nd_detach_cookie); break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t netdump_mod = { "netdump", netdump_modevent, NULL, }; MODULE_VERSION(netdump, 1); DECLARE_MODULE(netdump, netdump_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); Index: user/ngie/bug-237403/tests/sys/opencrypto/runtests.sh =================================================================== --- user/ngie/bug-237403/tests/sys/opencrypto/runtests.sh (revision 348022) +++ user/ngie/bug-237403/tests/sys/opencrypto/runtests.sh (revision 348023) @@ -1,88 +1,88 @@ #!/bin/sh - # # Copyright (c) 2014 The FreeBSD Foundation # All rights reserved. # # This software was developed by John-Mark Gurney under # the 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$ # : ${PYTHON=python} if [ ! -d /usr/local/share/nist-kat ]; then echo "1..0 # SKIP: nist-kat package not installed for test vectors" exit 0 fi if ! $PYTHON -c "from dpkt import dpkt"; then echo "1..0 # SKIP: py-dpkt package not installed" exit 0 fi loaded_modules= cleanup_tests() { trap - EXIT INT TERM set +e if [ -n "$oldcdas" ]; then sysctl "$oldcdas" 2>/dev/null fi # Unload modules in reverse order for loaded_module in $(echo $loaded_modules | tr ' ' '\n' | sort -r); do kldunload $loaded_module done } trap cleanup_tests EXIT INT TERM for required_module in nexus/aesni cryptodev; do if ! kldstat -q -m $required_module; then module_to_load=${required_module#nexus/} if ! kldload ${module_to_load}; then echo "1..0 # SKIP: could not load ${module_to_load}" exit 0 fi loaded_modules="$loaded_modules $required_module" fi done cdas_sysctl=kern.cryptodevallowsoft if ! oldcdas=$(sysctl -e $cdas_sysctl); then echo "1..0 # SKIP: could not resolve sysctl: $cdas_sysctl" exit 0 fi if ! sysctl $cdas_sysctl=1; then echo "1..0 # SKIP: could not enable /dev/crypto access via $cdas_sysctl sysctl." exit 0 fi echo "1..1" -if "$PYTHON" $(dirname $0)/cryptotest.py; then +if "$PYTHON" $(dirname $0)/cryptotest.py $CRYPTOTEST_ARGS; then echo "ok 1" else echo "not ok 1" fi Index: user/ngie/bug-237403/tests/sys/sys/rb_test.c =================================================================== --- user/ngie/bug-237403/tests/sys/sys/rb_test.c (revision 348022) +++ user/ngie/bug-237403/tests/sys/sys/rb_test.c (revision 348023) @@ -1,110 +1,112 @@ /* $OpenBSD: rb-test.c,v 1.4 2008/04/13 00:22:17 djm Exp $ */ /* * Copyright 2002 Niels Provos * 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. 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. * * $FreeBSD$ */ #include #include #include #include struct node { RB_ENTRY(node) node; int key; }; RB_HEAD(tree, node) root; static int compare(struct node *a, struct node *b) { if (a->key < b->key) return (-1); else if (a->key > b->key) return (1); return (0); } RB_PROTOTYPE(tree, node, node, compare); RB_GENERATE(tree, node, node, compare); #define ITER 150 #define MIN 5 #define MAX 5000 ATF_TC_WITHOUT_HEAD(rb_test); ATF_TC_BODY(rb_test, tc) { struct node *tmp, *ins; int i, max, min; max = min = 42; /* pacify gcc */ RB_INIT(&root); for (i = 0; i < ITER; i++) { tmp = malloc(sizeof(struct node)); - ATF_CHECK_MSG(tmp != NULL, "malloc failed"); + ATF_REQUIRE_MSG(tmp != NULL, "malloc failed"); do { tmp->key = arc4random_uniform(MAX-MIN); tmp->key += MIN; } while (RB_FIND(tree, &root, tmp) != NULL); if (i == 0) max = min = tmp->key; else { if (tmp->key > max) max = tmp->key; if (tmp->key < min) min = tmp->key; } - ATF_CHECK_EQ(NULL, RB_INSERT(tree, &root, tmp)); + ATF_REQUIRE_EQ(NULL, RB_INSERT(tree, &root, tmp)); } ins = RB_MIN(tree, &root); + ATF_REQUIRE_MSG(ins != NULL, "RB_MIN error"); ATF_CHECK_EQ(min, ins->key); tmp = ins; ins = RB_MAX(tree, &root); + ATF_REQUIRE_MSG(ins != NULL, "RB_MAX error"); ATF_CHECK_EQ(max, ins->key); ATF_CHECK_EQ(tmp, RB_REMOVE(tree, &root, tmp)); for (i = 0; i < ITER - 1; i++) { tmp = RB_ROOT(&root); - ATF_CHECK_MSG(tmp != NULL, "RB_ROOT error"); + ATF_REQUIRE_MSG(tmp != NULL, "RB_ROOT error"); ATF_CHECK_EQ(tmp, RB_REMOVE(tree, &root, tmp)); free(tmp); } } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, rb_test); return (atf_no_error()); } Index: user/ngie/bug-237403/tests/sys/sys/splay_test.c =================================================================== --- user/ngie/bug-237403/tests/sys/sys/splay_test.c (revision 348022) +++ user/ngie/bug-237403/tests/sys/sys/splay_test.c (revision 348023) @@ -1,110 +1,112 @@ /* $OpenBSD: splay-test.c,v 1.4 2008/04/13 00:22:17 djm Exp $ */ /* * Copyright 2002 Niels Provos * 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. 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. * * $FreeBSD$ */ #include #include #include #include struct node { SPLAY_ENTRY(node) node; int key; }; SPLAY_HEAD(tree, node) root; static int compare(struct node *a, struct node *b) { if (a->key < b->key) return (-1); else if (a->key > b->key) return (1); return (0); } SPLAY_PROTOTYPE(tree, node, node, compare); SPLAY_GENERATE(tree, node, node, compare); #define ITER 150 #define MIN 5 #define MAX 5000 ATF_TC_WITHOUT_HEAD(splay_test); ATF_TC_BODY(splay_test, tc) { struct node *tmp, *ins; int i, max, min; SPLAY_INIT(&root); max = min = 42; /* pacify gcc */ for (i = 0; i < ITER; i++) { tmp = malloc(sizeof(struct node)); - ATF_CHECK_MSG(tmp != NULL, "malloc failed"); + ATF_REQUIRE_MSG(tmp != NULL, "malloc failed"); do { tmp->key = arc4random_uniform(MAX-MIN); tmp->key += MIN; } while (SPLAY_FIND(tree, &root, tmp) != NULL); if (i == 0) max = min = tmp->key; else { if (tmp->key > max) max = tmp->key; if (tmp->key < min) min = tmp->key; } - ATF_CHECK_EQ(NULL, SPLAY_INSERT(tree, &root, tmp)); + ATF_REQUIRE_EQ(NULL, SPLAY_INSERT(tree, &root, tmp)); } ins = SPLAY_MIN(tree, &root); + ATF_REQUIRE_MSG(ins != NULL, "SPLAY_MIN error"); ATF_CHECK_EQ(min, ins->key); tmp = ins; ins = SPLAY_MAX(tree, &root); + ATF_REQUIRE_MSG(ins != NULL, "SPLAY_MAX error"); ATF_CHECK_EQ(max, ins->key); ATF_CHECK_EQ(tmp, SPLAY_REMOVE(tree, &root, tmp)); for (i = 0; i < ITER - 1; i++) { tmp = SPLAY_ROOT(&root); - ATF_CHECK_MSG(tmp != NULL, "SPLAY_ROOT error"); + ATF_REQUIRE_MSG(tmp != NULL, "SPLAY_ROOT error"); ATF_CHECK_EQ(tmp, SPLAY_REMOVE(tree, &root, tmp)); free(tmp); } } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, splay_test); return (atf_no_error()); } Index: user/ngie/bug-237403 =================================================================== --- user/ngie/bug-237403 (revision 348022) +++ user/ngie/bug-237403 (revision 348023) Property changes on: user/ngie/bug-237403 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r347997-348022