Index: head/lib/libfetch/ftp.c =================================================================== --- head/lib/libfetch/ftp.c (revision 326407) +++ head/lib/libfetch/ftp.c (revision 326408) @@ -1,1208 +1,1208 @@ /*- - * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: (BSD-3-Clause AND Beerware) * * Copyright (c) 1998-2011 Dag-Erling Smørgrav * 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 * in this position and unchanged. * 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. */ #include __FBSDID("$FreeBSD$"); /* * Portions of this code were taken from or based on ftpio.c: * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * Major Changelog: * * Dag-Erling Smørgrav * 9 Jun 1998 * * Incorporated into libfetch * * Jordan K. Hubbard * 17 Jan 1996 * * Turned inside out. Now returns xfers as new file ids, not as a special * `state' of FTP_t * * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fetch.h" #include "common.h" #include "ftperr.h" #define FTP_ANONYMOUS_USER "anonymous" #define FTP_CONNECTION_ALREADY_OPEN 125 #define FTP_OPEN_DATA_CONNECTION 150 #define FTP_OK 200 #define FTP_FILE_STATUS 213 #define FTP_SERVICE_READY 220 #define FTP_TRANSFER_COMPLETE 226 #define FTP_PASSIVE_MODE 227 #define FTP_LPASSIVE_MODE 228 #define FTP_EPASSIVE_MODE 229 #define FTP_LOGGED_IN 230 #define FTP_FILE_ACTION_OK 250 #define FTP_DIRECTORY_CREATED 257 /* multiple meanings */ #define FTP_FILE_CREATED 257 /* multiple meanings */ #define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ #define FTP_NEED_PASSWORD 331 #define FTP_NEED_ACCOUNT 332 #define FTP_FILE_OK 350 #define FTP_SYNTAX_ERROR 500 #define FTP_PROTOCOL_ERROR 999 static struct url cached_host; static conn_t *cached_connection; #define isftpreply(foo) \ (isdigit((unsigned char)foo[0]) && \ isdigit((unsigned char)foo[1]) && \ isdigit((unsigned char)foo[2]) && \ (foo[3] == ' ' || foo[3] == '\0')) #define isftpinfo(foo) \ (isdigit((unsigned char)foo[0]) && \ isdigit((unsigned char)foo[1]) && \ isdigit((unsigned char)foo[2]) && \ foo[3] == '-') /* * Translate IPv4 mapped IPv6 address to IPv4 address */ static void unmappedaddr(struct sockaddr_in6 *sin6) { struct sockaddr_in *sin4; u_int32_t addr; int port; if (sin6->sin6_family != AF_INET6 || !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) return; sin4 = (struct sockaddr_in *)sin6; addr = *(u_int32_t *)(uintptr_t)&sin6->sin6_addr.s6_addr[12]; port = sin6->sin6_port; memset(sin4, 0, sizeof(struct sockaddr_in)); sin4->sin_addr.s_addr = addr; sin4->sin_port = port; sin4->sin_family = AF_INET; sin4->sin_len = sizeof(struct sockaddr_in); } /* * Get server response */ static int ftp_chkerr(conn_t *conn) { if (fetch_getln(conn) == -1) { fetch_syserr(); return (-1); } if (isftpinfo(conn->buf)) { while (conn->buflen && !isftpreply(conn->buf)) { if (fetch_getln(conn) == -1) { fetch_syserr(); return (-1); } } } while (conn->buflen && isspace((unsigned char)conn->buf[conn->buflen - 1])) conn->buflen--; conn->buf[conn->buflen] = '\0'; if (!isftpreply(conn->buf)) { ftp_seterr(FTP_PROTOCOL_ERROR); return (-1); } conn->err = (conn->buf[0] - '0') * 100 + (conn->buf[1] - '0') * 10 + (conn->buf[2] - '0'); return (conn->err); } /* * Send a command and check reply */ static int ftp_cmd(conn_t *conn, const char *fmt, ...) { va_list ap; size_t len; char *msg; int r; va_start(ap, fmt); len = vasprintf(&msg, fmt, ap); va_end(ap); if (msg == NULL) { errno = ENOMEM; fetch_syserr(); return (-1); } r = fetch_putln(conn, msg, len); free(msg); if (r == -1) { fetch_syserr(); return (-1); } return (ftp_chkerr(conn)); } /* * Return a pointer to the filename part of a path */ static const char * ftp_filename(const char *file, int *len, int *type) { const char *s; if ((s = strrchr(file, '/')) == NULL) s = file; else s = s + 1; *len = strlen(s); if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { *type = s[*len - 1]; *len -= 7; } else { *type = '\0'; } return (s); } /* * Get current working directory from the reply to a CWD, PWD or CDUP * command. */ static int ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen) { char *src, *dst, *end; int q; if (conn->err != FTP_WORKING_DIRECTORY && conn->err != FTP_FILE_ACTION_OK) return (FTP_PROTOCOL_ERROR); end = conn->buf + conn->buflen; src = conn->buf + 4; if (src >= end || *src++ != '"') return (FTP_PROTOCOL_ERROR); for (q = 0, dst = pwd; src < end && pwdlen--; ++src) { if (!q && *src == '"') q = 1; else if (q && *src != '"') break; else if (q) *dst++ = '"', q = 0; else *dst++ = *src; } if (!pwdlen) return (FTP_PROTOCOL_ERROR); *dst = '\0'; #if 0 DEBUG(fprintf(stderr, "pwd: [%s]\n", pwd)); #endif return (FTP_OK); } /* * Change working directory to the directory that contains the specified * file. */ static int ftp_cwd(conn_t *conn, const char *file) { const char *beg, *end; char pwd[PATH_MAX]; int e, i, len; /* If no slashes in name, no need to change dirs. */ if ((end = strrchr(file, '/')) == NULL) return (0); if ((e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { ftp_seterr(e); return (-1); } for (;;) { len = strlen(pwd); /* Look for a common prefix between PWD and dir to fetch. */ for (i = 0; i <= len && i <= end - file; ++i) if (pwd[i] != file[i]) break; #if 0 DEBUG(fprintf(stderr, "have: [%.*s|%s]\n", i, pwd, pwd + i)); DEBUG(fprintf(stderr, "want: [%.*s|%s]\n", i, file, file + i)); #endif /* Keep going up a dir until we have a matching prefix. */ if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/')) break; if ((e = ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK || (e = ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || (e = ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { ftp_seterr(e); return (-1); } } #ifdef FTP_COMBINE_CWDS /* Skip leading slashes, even "////". */ for (beg = file + i; beg < end && *beg == '/'; ++beg, ++i) /* nothing */ ; /* If there is no trailing dir, we're already there. */ if (beg >= end) return (0); /* Change to the directory all in one chunk (e.g., foo/bar/baz). */ e = ftp_cmd(conn, "CWD %.*s", (int)(end - beg), beg); if (e == FTP_FILE_ACTION_OK) return (0); #endif /* FTP_COMBINE_CWDS */ /* That didn't work so go back to legacy behavior (multiple CWDs). */ for (beg = file + i; beg < end; beg = file + i + 1) { while (*beg == '/') ++beg, ++i; for (++i; file + i < end && file[i] != '/'; ++i) /* nothing */ ; e = ftp_cmd(conn, "CWD %.*s", file + i - beg, beg); if (e != FTP_FILE_ACTION_OK) { ftp_seterr(e); return (-1); } } return (0); } /* * Set transfer mode and data type */ static int ftp_mode_type(conn_t *conn, int mode, int type) { int e; switch (mode) { case 0: case 's': mode = 'S'; case 'S': break; default: return (FTP_PROTOCOL_ERROR); } if ((e = ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) { if (mode == 'S') { /* * Stream mode is supposed to be the default - so * much so that some servers not only do not * support any other mode, but do not support the * MODE command at all. * * If "MODE S" fails, it is unlikely that we * previously succeeded in setting a different * mode. Therefore, we simply hope that the * server is already in the correct mode, and * silently ignore the failure. */ } else { return (e); } } switch (type) { case 0: case 'i': type = 'I'; case 'I': break; case 'a': type = 'A'; case 'A': break; case 'd': type = 'D'; case 'D': /* can't handle yet */ default: return (FTP_PROTOCOL_ERROR); } if ((e = ftp_cmd(conn, "TYPE %c", type)) != FTP_OK) return (e); return (FTP_OK); } /* * Request and parse file stats */ static int ftp_stat(conn_t *conn, const char *file, struct url_stat *us) { char *ln; const char *filename; int filenamelen, type; struct tm tm; time_t t; int e; us->size = -1; us->atime = us->mtime = 0; filename = ftp_filename(file, &filenamelen, &type); if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) { ftp_seterr(e); return (-1); } e = ftp_cmd(conn, "SIZE %.*s", filenamelen, filename); if (e != FTP_FILE_STATUS) { ftp_seterr(e); return (-1); } for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) /* nothing */ ; for (us->size = 0; *ln && isdigit((unsigned char)*ln); ln++) us->size = us->size * 10 + *ln - '0'; if (*ln && !isspace((unsigned char)*ln)) { ftp_seterr(FTP_PROTOCOL_ERROR); us->size = -1; return (-1); } if (us->size == 0) us->size = -1; DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size)); e = ftp_cmd(conn, "MDTM %.*s", filenamelen, filename); if (e != FTP_FILE_STATUS) { ftp_seterr(e); return (-1); } for (ln = conn->buf + 4; *ln && isspace((unsigned char)*ln); ln++) /* nothing */ ; switch (strspn(ln, "0123456789")) { case 14: break; case 15: ln++; ln[0] = '2'; ln[1] = '0'; break; default: ftp_seterr(FTP_PROTOCOL_ERROR); return (-1); } if (sscanf(ln, "%04d%02d%02d%02d%02d%02d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) { ftp_seterr(FTP_PROTOCOL_ERROR); return (-1); } tm.tm_mon--; tm.tm_year -= 1900; tm.tm_isdst = -1; t = timegm(&tm); if (t == (time_t)-1) t = time(NULL); us->mtime = t; us->atime = t; DEBUG(fprintf(stderr, "last modified: [%04d-%02d-%02d %02d:%02d:%02d]\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec)); return (0); } /* * I/O functions for FTP */ struct ftpio { conn_t *cconn; /* Control connection */ conn_t *dconn; /* Data connection */ int dir; /* Direction */ int eof; /* EOF reached */ int err; /* Error code */ }; static int ftp_readfn(void *, char *, int); static int ftp_writefn(void *, const char *, int); static fpos_t ftp_seekfn(void *, fpos_t, int); static int ftp_closefn(void *); static int ftp_readfn(void *v, char *buf, int len) { struct ftpio *io; int r; io = (struct ftpio *)v; if (io == NULL) { errno = EBADF; return (-1); } if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) { errno = EBADF; return (-1); } if (io->err) { errno = io->err; return (-1); } if (io->eof) return (0); r = fetch_read(io->dconn, buf, len); if (r > 0) return (r); if (r == 0) { io->eof = 1; return (0); } if (errno != EINTR) io->err = errno; return (-1); } static int ftp_writefn(void *v, const char *buf, int len) { struct ftpio *io; int w; io = (struct ftpio *)v; if (io == NULL) { errno = EBADF; return (-1); } if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) { errno = EBADF; return (-1); } if (io->err) { errno = io->err; return (-1); } w = fetch_write(io->dconn, buf, len); if (w >= 0) return (w); if (errno != EINTR) io->err = errno; return (-1); } static fpos_t ftp_seekfn(void *v, fpos_t pos __unused, int whence __unused) { struct ftpio *io; io = (struct ftpio *)v; if (io == NULL) { errno = EBADF; return (-1); } errno = ESPIPE; return (-1); } static int ftp_closefn(void *v) { struct ftpio *io; int r; io = (struct ftpio *)v; if (io == NULL) { errno = EBADF; return (-1); } if (io->dir == -1) return (0); if (io->cconn == NULL || io->dconn == NULL) { errno = EBADF; return (-1); } fetch_close(io->dconn); io->dir = -1; io->dconn = NULL; DEBUG(fprintf(stderr, "Waiting for final status\n")); r = ftp_chkerr(io->cconn); if (io->cconn == cached_connection && io->cconn->ref == 1) cached_connection = NULL; fetch_close(io->cconn); free(io); return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1; } static FILE * ftp_setup(conn_t *cconn, conn_t *dconn, int mode) { struct ftpio *io; FILE *f; if (cconn == NULL || dconn == NULL) return (NULL); if ((io = malloc(sizeof(*io))) == NULL) return (NULL); io->cconn = cconn; io->dconn = dconn; io->dir = mode; io->eof = io->err = 0; f = funopen(io, ftp_readfn, ftp_writefn, ftp_seekfn, ftp_closefn); if (f == NULL) free(io); return (f); } /* * Transfer file */ static FILE * ftp_transfer(conn_t *conn, const char *oper, const char *file, int mode, off_t offset, const char *flags) { struct sockaddr_storage sa; struct sockaddr_in6 *sin6; struct sockaddr_in *sin4; const char *bindaddr; const char *filename; int filenamelen, type; int low, pasv, verbose; int e, sd = -1; socklen_t l; char *s; FILE *df; /* check flags */ low = CHECK_FLAG('l'); pasv = CHECK_FLAG('p') || !CHECK_FLAG('P'); verbose = CHECK_FLAG('v'); /* passive mode */ if ((s = getenv("FTP_PASSIVE_MODE")) != NULL) pasv = (strncasecmp(s, "no", 2) != 0); /* isolate filename */ filename = ftp_filename(file, &filenamelen, &type); /* set transfer mode and data type */ if ((e = ftp_mode_type(conn, 0, type)) != FTP_OK) goto ouch; /* find our own address, bind, and listen */ l = sizeof(sa); if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1) goto sysouch; if (sa.ss_family == AF_INET6) unmappedaddr((struct sockaddr_in6 *)&sa); /* open data socket */ if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { fetch_syserr(); return (NULL); } if (pasv) { u_char addr[64]; char *ln, *p; unsigned int i; int port; /* send PASV command */ if (verbose) fetch_info("setting passive mode"); switch (sa.ss_family) { case AF_INET: if ((e = ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE) goto ouch; break; case AF_INET6: if ((e = ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) { if (e == -1) goto ouch; if ((e = ftp_cmd(conn, "LPSV")) != FTP_LPASSIVE_MODE) goto ouch; } break; default: e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ goto ouch; } /* * Find address and port number. The reply to the PASV command * is IMHO the one and only weak point in the FTP protocol. */ ln = conn->buf; switch (e) { case FTP_PASSIVE_MODE: case FTP_LPASSIVE_MODE: for (p = ln + 3; *p && !isdigit((unsigned char)*p); p++) /* nothing */ ; if (!*p) { e = FTP_PROTOCOL_ERROR; goto ouch; } l = (e == FTP_PASSIVE_MODE ? 6 : 21); for (i = 0; *p && i < l; i++, p++) addr[i] = strtol(p, &p, 10); if (i < l) { e = FTP_PROTOCOL_ERROR; goto ouch; } break; case FTP_EPASSIVE_MODE: for (p = ln + 3; *p && *p != '('; p++) /* nothing */ ; if (!*p) { e = FTP_PROTOCOL_ERROR; goto ouch; } ++p; if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2], &port, &addr[3]) != 5 || addr[0] != addr[1] || addr[0] != addr[2] || addr[0] != addr[3]) { e = FTP_PROTOCOL_ERROR; goto ouch; } break; } /* seek to required offset */ if (offset) if (ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK) goto sysouch; /* construct sockaddr for data socket */ l = sizeof(sa); if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1) goto sysouch; if (sa.ss_family == AF_INET6) unmappedaddr((struct sockaddr_in6 *)&sa); switch (sa.ss_family) { case AF_INET6: sin6 = (struct sockaddr_in6 *)&sa; if (e == FTP_EPASSIVE_MODE) sin6->sin6_port = htons(port); else { memcpy(&sin6->sin6_addr, addr + 2, 16); memcpy(&sin6->sin6_port, addr + 19, 2); } break; case AF_INET: sin4 = (struct sockaddr_in *)&sa; if (e == FTP_EPASSIVE_MODE) sin4->sin_port = htons(port); else { memcpy(&sin4->sin_addr, addr, 4); memcpy(&sin4->sin_port, addr + 4, 2); } break; default: e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ break; } /* connect to data port */ if (verbose) fetch_info("opening data connection"); bindaddr = getenv("FETCH_BIND_ADDRESS"); if (bindaddr != NULL && *bindaddr != '\0' && (e = fetch_bind(sd, sa.ss_family, bindaddr)) != 0) goto ouch; if (connect(sd, (struct sockaddr *)&sa, sa.ss_len) == -1) goto sysouch; /* make the server initiate the transfer */ if (verbose) fetch_info("initiating transfer"); e = ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename); if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) goto ouch; } else { u_int32_t a; u_short p; int arg, d; char *ap; char hname[INET6_ADDRSTRLEN]; switch (sa.ss_family) { case AF_INET6: ((struct sockaddr_in6 *)&sa)->sin6_port = 0; #ifdef IPV6_PORTRANGE arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH; if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE, (char *)&arg, sizeof(arg)) == -1) goto sysouch; #endif break; case AF_INET: ((struct sockaddr_in *)&sa)->sin_port = 0; arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH; if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, (char *)&arg, sizeof(arg)) == -1) goto sysouch; break; } if (verbose) fetch_info("binding data socket"); if (bind(sd, (struct sockaddr *)&sa, sa.ss_len) == -1) goto sysouch; if (listen(sd, 1) == -1) goto sysouch; /* find what port we're on and tell the server */ if (getsockname(sd, (struct sockaddr *)&sa, &l) == -1) goto sysouch; switch (sa.ss_family) { case AF_INET: sin4 = (struct sockaddr_in *)&sa; a = ntohl(sin4->sin_addr.s_addr); p = ntohs(sin4->sin_port); e = ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d", (a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff, (p >> 8) & 0xff, p & 0xff); break; case AF_INET6: #define UC(b) (((int)b)&0xff) e = -1; sin6 = (struct sockaddr_in6 *)&sa; sin6->sin6_scope_id = 0; if (getnameinfo((struct sockaddr *)&sa, sa.ss_len, hname, sizeof(hname), NULL, 0, NI_NUMERICHOST) == 0) { e = ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname, htons(sin6->sin6_port)); if (e == -1) goto ouch; } if (e != FTP_OK) { ap = (char *)&sin6->sin6_addr; e = ftp_cmd(conn, "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 6, 16, UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]), UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]), UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]), UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]), 2, (ntohs(sin6->sin6_port) >> 8) & 0xff, ntohs(sin6->sin6_port) & 0xff); } break; default: e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */ goto ouch; } if (e != FTP_OK) goto ouch; /* seek to required offset */ if (offset) if (ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK) goto sysouch; /* make the server initiate the transfer */ if (verbose) fetch_info("initiating transfer"); e = ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename); if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) goto ouch; /* accept the incoming connection and go to town */ if ((d = accept(sd, NULL, NULL)) == -1) goto sysouch; close(sd); sd = d; } if ((df = ftp_setup(conn, fetch_reopen(sd), mode)) == NULL) goto sysouch; return (df); sysouch: fetch_syserr(); if (sd >= 0) close(sd); return (NULL); ouch: if (e != -1) ftp_seterr(e); if (sd >= 0) close(sd); return (NULL); } /* * Authenticate */ static int ftp_authenticate(conn_t *conn, struct url *url, struct url *purl) { const char *user, *pwd, *logname; char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1]; int e, len; /* XXX FTP_AUTH, and maybe .netrc */ /* send user name and password */ if (url->user[0] == '\0') fetch_netrc_auth(url); user = url->user; if (*user == '\0') user = getenv("FTP_LOGIN"); if (user == NULL || *user == '\0') user = FTP_ANONYMOUS_USER; if (purl && url->port == fetch_default_port(url->scheme)) e = ftp_cmd(conn, "USER %s@%s", user, url->host); else if (purl) e = ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port); else e = ftp_cmd(conn, "USER %s", user); /* did the server request a password? */ if (e == FTP_NEED_PASSWORD) { pwd = url->pwd; if (*pwd == '\0') pwd = getenv("FTP_PASSWORD"); if (pwd == NULL || *pwd == '\0') { if ((logname = getlogin()) == NULL) logname = FTP_ANONYMOUS_USER; if ((len = snprintf(pbuf, MAXLOGNAME + 1, "%s@", logname)) < 0) len = 0; else if (len > MAXLOGNAME) len = MAXLOGNAME; gethostname(pbuf + len, sizeof(pbuf) - len); pwd = pbuf; } e = ftp_cmd(conn, "PASS %s", pwd); } return (e); } /* * Log on to FTP server */ static conn_t * ftp_connect(struct url *url, struct url *purl, const char *flags) { conn_t *conn; int e, direct, verbose; #ifdef INET6 int af = AF_UNSPEC; #else int af = AF_INET; #endif direct = CHECK_FLAG('d'); verbose = CHECK_FLAG('v'); if (CHECK_FLAG('4')) af = AF_INET; else if (CHECK_FLAG('6')) af = AF_INET6; if (direct) purl = NULL; /* check for proxy */ if (purl) { /* XXX proxy authentication! */ conn = fetch_connect(purl->host, purl->port, af, verbose); } else { /* no proxy, go straight to target */ conn = fetch_connect(url->host, url->port, af, verbose); purl = NULL; } /* check connection */ if (conn == NULL) /* fetch_connect() has already set an error code */ return (NULL); /* expect welcome message */ if ((e = ftp_chkerr(conn)) != FTP_SERVICE_READY) goto fouch; /* authenticate */ if ((e = ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) goto fouch; /* TODO: Request extended features supported, if any (RFC 3659). */ /* done */ return (conn); fouch: if (e != -1) ftp_seterr(e); fetch_close(conn); return (NULL); } /* * Disconnect from server */ static void ftp_disconnect(conn_t *conn) { (void)ftp_cmd(conn, "QUIT"); if (conn == cached_connection && conn->ref == 1) cached_connection = NULL; fetch_close(conn); } /* * Check if we're already connected */ static int ftp_isconnected(struct url *url) { return (cached_connection && (strcmp(url->host, cached_host.host) == 0) && (strcmp(url->user, cached_host.user) == 0) && (strcmp(url->pwd, cached_host.pwd) == 0) && (url->port == cached_host.port)); } /* * Check the cache, reconnect if no luck */ static conn_t * ftp_cached_connect(struct url *url, struct url *purl, const char *flags) { conn_t *conn; int e; /* set default port */ if (!url->port) url->port = fetch_default_port(url->scheme); /* try to use previously cached connection */ if (ftp_isconnected(url)) { e = ftp_cmd(cached_connection, "NOOP"); if (e == FTP_OK || e == FTP_SYNTAX_ERROR) return (fetch_ref(cached_connection)); } /* connect to server */ if ((conn = ftp_connect(url, purl, flags)) == NULL) return (NULL); if (cached_connection) ftp_disconnect(cached_connection); cached_connection = fetch_ref(conn); memcpy(&cached_host, url, sizeof(*url)); return (conn); } /* * Check the proxy settings */ static struct url * ftp_get_proxy(struct url * url, const char *flags) { struct url *purl; char *p; if (flags != NULL && strchr(flags, 'd') != NULL) return (NULL); if (fetch_no_proxy_match(url->host)) return (NULL); if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) || (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && *p && (purl = fetchParseURL(p)) != NULL) { if (!*purl->scheme) { if (getenv("FTP_PROXY") || getenv("ftp_proxy")) strcpy(purl->scheme, SCHEME_FTP); else strcpy(purl->scheme, SCHEME_HTTP); } if (!purl->port) purl->port = fetch_default_proxy_port(purl->scheme); if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 || strcasecmp(purl->scheme, SCHEME_HTTP) == 0) return (purl); fetchFreeURL(purl); } return (NULL); } /* * Process an FTP request */ FILE * ftp_request(struct url *url, const char *op, struct url_stat *us, struct url *purl, const char *flags) { conn_t *conn; int oflag; /* check if we should use HTTP instead */ if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) { if (strcmp(op, "STAT") == 0) return (http_request(url, "HEAD", us, purl, flags)); else if (strcmp(op, "RETR") == 0) return (http_request(url, "GET", us, purl, flags)); /* * Our HTTP code doesn't support PUT requests yet, so try * a direct connection. */ } /* connect to server */ conn = ftp_cached_connect(url, purl, flags); if (purl) fetchFreeURL(purl); if (conn == NULL) return (NULL); /* change directory */ if (ftp_cwd(conn, url->doc) == -1) goto errsock; /* stat file */ if (us && ftp_stat(conn, url->doc, us) == -1 && fetchLastErrCode != FETCH_PROTO && fetchLastErrCode != FETCH_UNAVAIL) goto errsock; /* just a stat */ if (strcmp(op, "STAT") == 0) { --conn->ref; ftp_disconnect(conn); return (FILE *)1; /* bogus return value */ } if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0) oflag = O_WRONLY; else oflag = O_RDONLY; /* initiate the transfer */ return (ftp_transfer(conn, op, url->doc, oflag, url->offset, flags)); errsock: ftp_disconnect(conn); return (NULL); } /* * Get and stat file */ FILE * fetchXGetFTP(struct url *url, struct url_stat *us, const char *flags) { return (ftp_request(url, "RETR", us, ftp_get_proxy(url, flags), flags)); } /* * Get file */ FILE * fetchGetFTP(struct url *url, const char *flags) { return (fetchXGetFTP(url, NULL, flags)); } /* * Put file */ FILE * fetchPutFTP(struct url *url, const char *flags) { return (ftp_request(url, CHECK_FLAG('a') ? "APPE" : "STOR", NULL, ftp_get_proxy(url, flags), flags)); } /* * Get file stats */ int fetchStatFTP(struct url *url, struct url_stat *us, const char *flags) { FILE *f; f = ftp_request(url, "STAT", us, ftp_get_proxy(url, flags), flags); if (f == NULL) return (-1); /* * When op is "STAT", ftp_request() will return either NULL or * (FILE *)1, never a valid FILE *, so we mustn't fclose(f) before * returning, as it would cause a segfault. */ return (0); } /* * List a directory */ struct url_ent * fetchListFTP(struct url *url __unused, const char *flags __unused) { warnx("fetchListFTP(): not implemented"); return (NULL); } Index: head/sbin/recoverdisk/recoverdisk.c =================================================================== --- head/sbin/recoverdisk/recoverdisk.c (revision 326407) +++ head/sbin/recoverdisk/recoverdisk.c (revision 326408) @@ -1,322 +1,324 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static volatile sig_atomic_t aborting = 0; static size_t bigsize = 1024 * 1024; static size_t medsize; static size_t minsize = 512; struct lump { off_t start; off_t len; int state; TAILQ_ENTRY(lump) list; }; static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps); static void new_lump(off_t start, off_t len, int state) { struct lump *lp; lp = malloc(sizeof *lp); if (lp == NULL) err(1, "Malloc failed"); lp->start = start; lp->len = len; lp->state = state; TAILQ_INSERT_TAIL(&lumps, lp, list); } static struct lump *lp; static char *wworklist = NULL; static char *rworklist = NULL; #define PRINT_HEADER \ printf("%13s %7s %13s %5s %13s %13s %9s\n", \ "start", "size", "block-len", "state", "done", "remaining", "% done") #define PRINT_STATUS(start, i, len, state, d, t) \ printf("\r%13jd %7zu %13jd %5d %13jd %13jd %9.5f", \ (intmax_t)start, \ i, \ (intmax_t)len, \ state, \ (intmax_t)d, \ (intmax_t)(t - d), \ 100*(double)d/(double)t) /* Save the worklist if -w was given */ static void save_worklist(void) { FILE *file; struct lump *llp; if (wworklist != NULL) { (void)fprintf(stderr, "\nSaving worklist ..."); fflush(stderr); file = fopen(wworklist, "w"); if (file == NULL) err(1, "Error opening file %s", wworklist); TAILQ_FOREACH(llp, &lumps, list) fprintf(file, "%jd %jd %d\n", (intmax_t)llp->start, (intmax_t)llp->len, llp->state); fclose(file); (void)fprintf(stderr, " done.\n"); } } /* Read the worklist if -r was given */ static off_t read_worklist(off_t t) { off_t s, l, d; int state, lines; FILE *file; (void)fprintf(stderr, "Reading worklist ..."); fflush(stderr); file = fopen(rworklist, "r"); if (file == NULL) err(1, "Error opening file %s", rworklist); lines = 0; d = t; for (;;) { ++lines; if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) { if (!feof(file)) err(1, "Error parsing file %s at line %d", rworklist, lines); else break; } new_lump(s, l, state); d -= l; } (void)fprintf(stderr, " done.\n"); /* * Return the number of bytes already read * (at least not in worklist). */ return (d); } static void usage(void) { (void)fprintf(stderr, "usage: recoverdisk [-b bigsize] [-r readlist] " "[-s interval] [-w writelist] source [destination]\n"); exit(1); } static void sighandler(__unused int sig) { aborting = 1; } int main(int argc, char * const argv[]) { int ch; int fdr, fdw; off_t t, d, start, len; size_t i, j; int error, state; u_char *buf; u_int sectorsize; off_t stripesize; time_t t1, t2; struct stat sb; u_int n, snapshot = 60; while ((ch = getopt(argc, argv, "b:r:w:s:")) != -1) { switch (ch) { case 'b': bigsize = strtoul(optarg, NULL, 0); break; case 'r': rworklist = strdup(optarg); if (rworklist == NULL) err(1, "Cannot allocate enough memory"); break; case 's': snapshot = strtoul(optarg, NULL, 0); break; case 'w': wworklist = strdup(optarg); if (wworklist == NULL) err(1, "Cannot allocate enough memory"); break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc < 1 || argc > 2) usage(); fdr = open(argv[0], O_RDONLY); if (fdr < 0) err(1, "Cannot open read descriptor %s", argv[0]); error = fstat(fdr, &sb); if (error < 0) err(1, "fstat failed"); if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { error = ioctl(fdr, DIOCGSECTORSIZE, §orsize); if (error < 0) err(1, "DIOCGSECTORSIZE failed"); error = ioctl(fdr, DIOCGSTRIPESIZE, &stripesize); if (error == 0 && stripesize > sectorsize) sectorsize = stripesize; minsize = sectorsize; bigsize = rounddown(bigsize, sectorsize); error = ioctl(fdr, DIOCGMEDIASIZE, &t); if (error < 0) err(1, "DIOCGMEDIASIZE failed"); } else { t = sb.st_size; } if (bigsize < minsize) bigsize = minsize; for (ch = 0; (bigsize >> ch) > minsize; ch++) continue; medsize = bigsize >> (ch / 2); medsize = rounddown(medsize, minsize); fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n", bigsize, medsize, minsize); buf = malloc(bigsize); if (buf == NULL) err(1, "Cannot allocate %zu bytes buffer", bigsize); if (argc > 1) { fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE); if (fdw < 0) err(1, "Cannot open write descriptor %s", argv[1]); if (ftruncate(fdw, t) < 0) err(1, "Cannot truncate output %s to %jd bytes", argv[1], (intmax_t)t); } else fdw = -1; if (rworklist != NULL) { d = read_worklist(t); } else { new_lump(0, t, 0); d = 0; } if (wworklist != NULL) signal(SIGINT, sighandler); t1 = 0; start = len = i = state = 0; PRINT_HEADER; n = 0; for (;;) { lp = TAILQ_FIRST(&lumps); if (lp == NULL) break; while (lp->len > 0 && !aborting) { /* These are only copied for printing stats */ start = lp->start; len = lp->len; state = lp->state; i = MIN(lp->len, (off_t)bigsize); if (lp->state == 1) i = MIN(lp->len, (off_t)medsize); if (lp->state > 1) i = MIN(lp->len, (off_t)minsize); time(&t2); if (t1 != t2 || lp->len < (off_t)bigsize) { PRINT_STATUS(start, i, len, state, d, t); t1 = t2; if (++n == snapshot) { save_worklist(); n = 0; } } if (i == 0) { errx(1, "BOGUS i %10jd", (intmax_t)i); } fflush(stdout); j = pread(fdr, buf, i, lp->start); if (j == i) { d += i; if (fdw >= 0) j = pwrite(fdw, buf, i, lp->start); else j = i; if (j != i) printf("\nWrite error at %jd/%zu\n", lp->start, i); lp->start += i; lp->len -= i; continue; } printf("\n%jd %zu failed (%s)\n", lp->start, i, strerror(errno)); if (errno == EINVAL) { printf("read() size too big? Try with -b 131072"); aborting = 1; } if (errno == ENXIO) aborting = 1; new_lump(lp->start, i, lp->state + 1); lp->start += i; lp->len -= i; } if (aborting) { save_worklist(); return (0); } TAILQ_REMOVE(&lumps, lp, list); free(lp); } PRINT_STATUS(start, i, len, state, d, t); save_worklist(); printf("\nCompleted\n"); return (0); } Index: head/share/examples/BSD_daemon/beastie.fig =================================================================== --- head/share/examples/BSD_daemon/beastie.fig (revision 326407) +++ head/share/examples/BSD_daemon/beastie.fig (revision 326408) @@ -1,269 +1,271 @@ #FIG 3.2 +# SPDX-License-Identifier: Beerware +# # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): # wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp # ---------------------------------------------------------------------------- # # $FreeBSD$ # Landscape Center Inches A4 100.00 Single -2 1200 2 1 1 0 1 0 0 47 0 20 0.000 1 6.0388 2748 2543 130 238 2748 2543 2878 2305 1 1 0 1 0 7 46 0 20 0.000 1 6.1087 2738 2495 55 105 2738 2495 2793 2495 1 1 0 1 0 0 47 0 20 0.000 1 6.0388 3291 2663 179 279 3291 2663 3470 2384 1 1 0 1 0 7 46 0 20 0.000 1 6.1087 3260 2603 55 105 3260 2603 3315 2603 3 0 0 3 0 7 49 0 -1 0.000 0 0 0 16 4102 4658 4050 4695 4005 4703 3933 4706 3881 4706 3821 4688 3746 4658 3671 4601 3615 4556 3536 4515 3450 4481 3333 4463 3228 4459 3127 4481 3086 4511 2981 4574 0.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 3 1 0 3 0 20 50 0 20 0.000 0 0 0 18 2628 4864 2636 4898 2666 4928 2696 4939 2745 4943 2782 4950 2812 4943 2895 4928 2943 4894 2973 4860 3011 4804 3052 4751 3078 4729 3078 4695 3067 4661 3052 4628 3030 4594 3003 4553 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 1 0 3 0 20 47 0 20 0.000 0 0 0 21 2250 4691 2302 4766 2358 4819 2403 4856 2478 4868 2565 4871 2640 4856 2726 4834 2778 4808 2835 4748 2910 4665 2970 4594 3000 4538 2996 4474 2985 4399 2925 4346 2868 4313 2801 4290 2733 4290 2673 4305 2632 4346 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 1 0 3 0 20 46 0 20 0.000 0 0 0 19 2287 4215 2231 4238 2182 4264 2141 4305 2096 4391 2096 4455 2092 4534 2122 4598 2208 4665 2272 4680 2377 4688 2460 4691 2561 4643 2636 4571 2651 4511 2658 4459 2632 4361 2591 4305 2505 4256 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 1 0 3 0 20 45 0 20 0.000 0 0 0 20 2250 4088 2242 4054 2257 3986 2310 3938 2366 3908 2441 3893 2508 3893 2568 3889 2621 3896 2670 3919 2741 3975 2763 4031 2775 4099 2771 4200 2715 4260 2617 4290 2490 4294 2377 4283 2302 4223 2253 4140 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 0 0 3 0 7 49 0 -1 0.000 0 0 0 22 2940 2986 2861 2930 2771 2915 2677 2919 2595 2960 2546 3020 2531 3106 2535 3185 2583 3260 2647 3305 2718 3335 2805 3342 2880 3342 2955 3331 3007 3320 3045 3324 3093 3357 3165 3350 3202 3339 3210 3279 3195 3211 3157 3181 0.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 3 0 0 3 0 7 50 0 -1 0.000 0 0 0 8 2891 5190 2947 5209 3000 5228 3056 5235 3101 5239 3146 5239 3187 5239 3187 5235 0.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 3 0 0 3 0 7 50 0 -1 0.000 0 0 0 8 2876 5202 2951 5306 3015 5351 3108 5404 3176 5419 3240 5434 3315 5438 3352 5434 0.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 3 0 0 3 0 7 50 0 -1 0.000 0 0 0 14 3161 5427 3326 5528 3416 5546 3540 5565 3637 5535 3753 5501 3862 5464 4005 5449 4132 5434 4293 5359 4421 5303 4533 5258 4597 5198 4661 5093 0.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 3 1 0 3 0 31 48 0 20 0.000 0 0 0 22 3851 5483 4050 5625 4166 5696 4278 5768 4372 5831 4413 5854 4447 5850 4473 5835 4488 5813 4515 5779 4526 5749 4522 5704 4503 5678 4462 5648 4413 5618 4331 5565 4252 5513 4211 5494 4121 5426 4121 5426 3993 5445 3866 5464 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 1 0 3 0 13 51 0 20 0.000 0 0 0 55 2291 6462 2420 6447 2549 6444 2672 6444 2774 6441 2849 6387 2951 6357 3026 6321 3068 6294 3059 6210 3026 6153 2996 6063 2981 5991 3017 5991 3077 6030 3113 6075 3179 6057 3251 6060 3308 6063 3347 6096 3392 6150 3455 6132 3500 6135 3521 6186 3536 6255 3539 6309 3563 6237 3593 6159 3629 6090 3692 6060 3764 6054 3827 6069 3893 6111 3956 6159 3980 6204 3983 6156 4022 6138 4109 6138 4199 6132 4283 6102 4346 6081 4421 6042 4466 6144 4523 6264 4583 6402 4625 6546 4667 6663 4682 6717 4520 6837 4004 7044 3467 7131 2816 7128 2456 7047 2246 6816 2006 6510 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 1 0 3 0 7 49 0 20 0.000 0 0 0 13 3168 2255 3315 2009 3424 1894 3629 1784 3784 1943 3806 2158 3786 2470 3693 2773 3553 2975 3328 3070 3103 2971 3034 2731 3043 2543 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 0 0 3 0 7 45 0 -1 0.000 0 0 0 7 1481 6669 1601 6744 1744 6777 1919 6787 2111 6780 2363 6769 2327 6771 0.000 1.000 1.000 1.000 1.000 1.000 0.000 3 1 0 3 0 7 44 0 20 0.000 0 0 0 36 2294 6825 2258 6885 2252 6930 2252 6993 2258 7044 2288 7092 2462 7179 2618 7215 2900 7233 3104 7242 3407 7254 3617 7236 3926 7194 4094 7155 4280 7086 4505 6987 4637 6927 4676 6906 4697 6861 4697 6822 4691 6783 4691 6738 4679 6669 4553 6741 4358 6834 4220 6879 4037 6930 3770 6978 3560 6993 3338 7002 2957 7011 2666 6993 2522 6960 2414 6921 2363 6876 2315 6843 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 1 0 3 0 7 49 0 20 0.000 0 0 0 21 2504 6663 2474 6609 2408 6552 2339 6501 2243 6477 2138 6471 2006 6480 1874 6510 1669 6567 1475 6654 1418 6711 1397 6762 1412 6843 1445 6921 1523 6978 1637 7017 1781 7029 1946 7032 2069 7038 2177 7038 2321 7038 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 0 0 3 3 7 43 0 -1 0.000 0 0 0 3 3599 6276 3776 6291 3959 6330 0.000 1.000 0.000 3 1 0 2 0 7 43 0 20 0.000 0 0 0 14 4232 6357 4280 6294 4331 6276 4373 6282 4415 6315 4451 6369 4445 6444 4424 6516 4367 6567 4304 6561 4253 6531 4223 6501 4214 6456 4220 6399 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 0 0 3 0 7 45 0 -1 0.000 0 0 0 7 3533 6267 3542 6357 3524 6399 3491 6435 3428 6459 3309 6482 3059 6582 0.000 1.000 1.000 1.000 1.000 1.000 0.000 3 0 0 3 3 7 43 0 -1 0.000 0 0 0 3 3440 6504 3544 6502 3653 6537 0.000 1.000 0.000 3 0 0 3 3 7 43 0 -1 0.000 0 0 0 3 3248 6546 3359 6552 3470 6603 0.000 1.000 0.000 3 0 0 3 3 7 43 0 -1 0.000 0 0 0 3 3560 6402 3755 6417 3904 6454 0.000 1.000 0.000 3 0 0 3 3 7 43 0 -1 0.000 0 0 0 4 2861 6429 2861 6429 2979 6447 3122 6504 0.000 1.000 1.000 0.000 3 0 0 3 3 7 43 0 -1 0.000 0 0 0 3 2594 6492 2769 6497 2972 6546 0.000 1.000 0.000 3 0 0 3 3 7 43 0 -1 0.000 0 0 0 3 3023 6360 3173 6383 3335 6438 0.000 1.000 0.000 3 0 0 3 0 7 45 0 -1 0.000 0 0 0 8 3983 6181 3998 6342 4001 6447 3971 6504 3887 6528 3758 6567 3599 6588 3339 6707 0.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 3 0 0 3 3 7 43 0 -1 0.000 0 0 0 3 3095 6246 3240 6248 3410 6306 0.000 1.000 0.000 3 1 0 3 0 7 46 0 20 0.000 0 0 0 17 2291 6976 2265 6844 2375 6738 2450 6702 2561 6645 2666 6612 2795 6588 2915 6579 3029 6585 3152 6612 3239 6645 3320 6705 3389 6786 3422 6876 3440 6984 3437 7056 2957 7152 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 0 0 4 0 7 49 0 -1 0.000 0 0 0 18 2498 3459 2578 3459 2613 3489 2673 3554 2728 3614 2793 3669 2853 3739 2948 3799 3053 3854 3173 3879 3328 3884 3453 3864 3583 3799 3688 3749 3818 3669 3858 3614 3943 3609 3983 3609 0.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 3 1 0 3 0 7 49 0 20 0.000 0 0 0 17 2786 2845 2877 2727 2925 2661 3009 2376 3075 2175 3131 2050 3174 1929 3192 1869 3114 1746 2940 1824 2871 1890 2712 2103 2598 2340 2562 2427 2538 2709 2616 2814 2679 2850 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 1 0 3 0 20 52 0 20 0.000 0 0 0 142 2136 3087 2170 2963 2229 2861 2391 2682 2418 2498 2498 2228 2476 2126 2452 2000 2401 1725 2407 1577 2476 1395 2483 1365 2719 1094 2978 878 3235 725 3394 659 3490 731 3394 806 3277 932 3166 1080 3112 1163 3103 1253 3112 1364 3151 1425 3235 1418 3397 1334 3616 1277 3871 1265 4150 1298 4628 1478 4793 1646 4861 1725 5006 1706 5273 1568 5288 1490 5281 1223 5141 998 5101 908 5189 866 5326 893 5465 1010 5708 1328 5789 1577 5836 1830 5765 2093 5513 2438 5207 2654 5056 2760 5078 2729 5072 3058 4988 3368 4829 3607 4565 3850 4502 3970 4486 4088 4511 4213 4589 4330 4643 4455 4679 4633 4688 4958 4667 5107 4607 5230 4613 5344 4733 5685 4928 5985 5221 6195 5570 6348 5993 6443 6480 6542 6773 6623 6966 6698 7141 6825 7206 6977 7208 7110 7062 7250 6923 7320 6513 7397 6481 7403 6518 7493 6468 7502 6120 7505 5571 7526 5298 7547 5241 7532 5298 7487 5958 7130 6174 6998 6249 6935 6291 6995 6342 7076 6368 7155 6408 7205 6608 7170 6756 7115 6878 7035 6873 6959 6750 6851 6543 6779 5985 6713 5568 6632 5271 6554 5003 6462 4821 6389 4673 6278 4545 6194 4431 6068 4371 6089 3675 6375 3016 5993 2997 5942 2859 5732 2802 5591 2798 5438 2820 5318 2873 5220 2883 5186 2856 5153 2778 5075 2768 4988 2791 4943 2701 4943 2661 4925 2633 4883 2577 4868 2273 4422 2457 4299 2763 4274 3001 4305 3128 4328 3203 4230 3216 4124 3173 4028 3081 3995 2986 3945 2880 3851 2733 3770 2634 3698 2493 3665 2334 3566 2221 3413 2166 3323 2133 3179 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 3 0 0 3 0 7 49 0 -1 0.000 0 0 0 5 3489 1649 3649 1554 3825 1575 3953 1675 4005 1902 0.000 1.000 1.000 1.000 0.000 3 0 0 3 0 7 49 0 -1 0.000 0 0 0 10 3278 1680 3254 1569 3149 1539 3009 1544 2883 1665 2696 1865 2564 2129 2505 2334 2441 2484 2379 2670 0.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 3 0 0 3 0 31 51 0 20 0.000 0 0 0 71 2452 4170 2238 4009 2122 3911 2078 3867 2096 3836 2128 3795 2148 3725 2153 3675 2151 3587 2131 3510 2101 3423 2013 3341 1954 3332 2041 3258 1914 3207 1843 3180 1773 3140 1668 3097 1557 3040 1621 3132 1671 3192 1718 3287 1753 3350 1783 3475 1836 3430 1891 3385 1943 3442 2003 3515 2021 3595 2023 3642 2006 3702 1953 3735 1921 3767 1873 3730 1833 3695 1791 3665 1738 3622 1669 3576 1731 3501 1731 3474 1529 3466 1222 3248 1344 3469 1433 3645 1446 3735 1468 3765 1503 3740 1563 3675 1616 3722 1703 3792 1758 3842 1786 3927 1718 3977 1518 4045 1229 3950 1297 3894 1364 3856 1357 3792 1147 3759 903 3564 884 3646 1008 3875 1218 4119 1413 4179 1641 4200 1796 4130 1926 4060 1956 4027 2076 4125 2216 4253 2332 4339 0.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 0.000 Index: head/share/examples/libusb20/bulk.c =================================================================== --- head/share/examples/libusb20/bulk.c (revision 326407) +++ head/share/examples/libusb20/bulk.c (revision 326408) @@ -1,243 +1,246 @@ -/* ---------------------------------------------------------------------------- +/*- + * SPDX-License-Identifier: Beerware + * + * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42) (by Poul-Henning Kamp): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Joerg Wunsch * ---------------------------------------------------------------------------- * * $FreeBSD$ */ /* * Simple demo program to illustrate the handling of FreeBSD's * libusb20. * * Issues a bulk output, and then requests a bulk input. */ /* * Examples: * Just list all VID:PID pairs * ./bulk * * Say "hello" to an Atmel JTAGICEmkII. * ./bulk -o 2 -i 0x82 -v 0x03eb -p 0x2103 0x1b 0 0 1 0 0 0 0x0e 1 0xf3 0x97 * * Return the INQUIRY data of an USB mass storage device. * (It's best to have the umass(4) driver unloaded while doing such * experiments, and perform a "usbconfig reset" for the device if it * gets stuck.) * ./bulk -v 0x5e3 -p 0x723 -i 0x81 -o 2 0x55 0x53 0x42 0x43 1 2 3 4 31 12 0x80 0x24 0 0 0 0x12 0 0 0 36 0 0 0 0 0 0 0 0 0 0 */ #include #include #include #include #include #include #include #include #include "util.h" /* * If you want to see the details of the internal datastructures * in the debugger, unifdef the following. */ #ifdef DEBUG # include # include "/usr/src/lib/libusb/libusb20_int.h" #endif #define BUFLEN 64 #define TIMEOUT 5000 /* 5 s */ int in_ep, out_ep; /* endpoints */ uint8_t out_buf[BUFLEN]; uint16_t out_len; static void doit(struct libusb20_device *dev) { int rv; /* * Open the device, allocating memory for two possible (bulk or * interrupt) transfers. * * If only control transfers are intended (via * libusb20_dev_request_sync()), transfer_max can be given as 0. */ if ((rv = libusb20_dev_open(dev, 2)) != 0) { fprintf(stderr, "libusb20_dev_open: %s\n", libusb20_strerror(rv)); return; } /* * If the device has more than one configuration, select the desired * one here. */ if ((rv = libusb20_dev_set_config_index(dev, 0)) != 0) { fprintf(stderr, "libusb20_dev_set_config_index: %s\n", libusb20_strerror(rv)); return; } /* * Two transfers have been requested in libusb20_dev_open() above; * obtain the corresponding transfer struct pointers. */ struct libusb20_transfer *xfr_out = libusb20_tr_get_pointer(dev, 0); struct libusb20_transfer *xfr_in = libusb20_tr_get_pointer(dev, 1); if (xfr_in == NULL || xfr_out == NULL) { fprintf(stderr, "libusb20_tr_get_pointer: %s\n", libusb20_strerror(rv)); return; } /* * Open both transfers, the "out" one for the write endpoint, the * "in" one for the read endpoint (ep | 0x80). */ if ((rv = libusb20_tr_open(xfr_out, 0, 1, out_ep)) != 0) { fprintf(stderr, "libusb20_tr_open: %s\n", libusb20_strerror(rv)); return; } if ((rv = libusb20_tr_open(xfr_in, 0, 1, in_ep)) != 0) { fprintf(stderr, "libusb20_tr_open: %s\n", libusb20_strerror(rv)); return; } uint8_t in_buf[BUFLEN]; uint32_t rlen; if (out_len > 0) { if ((rv = libusb20_tr_bulk_intr_sync(xfr_out, out_buf, out_len, &rlen, TIMEOUT)) != 0) { fprintf(stderr, "libusb20_tr_bulk_intr_sync (OUT): %s\n", libusb20_strerror(rv)); } printf("sent %d bytes\n", rlen); } if ((rv = libusb20_tr_bulk_intr_sync(xfr_in, in_buf, BUFLEN, &rlen, TIMEOUT)) != 0) { fprintf(stderr, "libusb20_tr_bulk_intr_sync: %s\n", libusb20_strerror(rv)); } printf("received %d bytes\n", rlen); if (rlen > 0) print_formatted(in_buf, rlen); libusb20_tr_close(xfr_out); libusb20_tr_close(xfr_in); libusb20_dev_close(dev); } static void usage(void) { fprintf(stderr, "Usage ./usb -i -o -v -p [ ...\n]"); exit(EX_USAGE); } int main(int argc, char **argv) { unsigned int vid = UINT_MAX, pid = UINT_MAX; /* impossible VID:PID */ int c; while ((c = getopt(argc, argv, "i:o:p:v:")) != -1) switch (c) { case 'i': in_ep = strtol(optarg, NULL, 0); break; case 'o': out_ep = strtol(optarg, NULL, 0); break; case 'p': pid = strtol(optarg, NULL, 0); break; case 'v': vid = strtol(optarg, NULL, 0); break; default: usage(); break; } argc -= optind; argv += optind; if (vid != UINT_MAX || pid != UINT_MAX) { if (in_ep == 0 || out_ep == 0) { usage(); } if ((in_ep & 0x80) == 0) { fprintf(stderr, "IN_EP must have bit 7 set\n"); return (EX_USAGE); } if (argc > 0) { for (out_len = 0; argc > 0 && out_len < BUFLEN; out_len++, argc--) { unsigned n = strtoul(argv[out_len], 0, 0); if (n > 255) fprintf(stderr, "Warning: data #%d 0x%0x > 0xff, truncating\n", out_len, n); out_buf[out_len] = (uint8_t)n; } out_len++; if (argc > 0) fprintf(stderr, "Data count exceeds maximum of %d, ignoring %d elements\n", BUFLEN, optind); } } struct libusb20_backend *be; struct libusb20_device *dev; if ((be = libusb20_be_alloc_default()) == NULL) { perror("libusb20_be_alloc()"); return 1; } dev = NULL; while ((dev = libusb20_be_device_foreach(be, dev)) != NULL) { struct LIBUSB20_DEVICE_DESC_DECODED *ddp = libusb20_dev_get_device_desc(dev); printf("Found device %s (VID:PID = 0x%04x:0x%04x)\n", libusb20_dev_get_desc(dev), ddp->idVendor, ddp->idProduct); if (ddp->idVendor == vid && ddp->idProduct == pid) doit(dev); } libusb20_be_free(be); return 0; } Index: head/share/examples/libusb20/control.c =================================================================== --- head/share/examples/libusb20/control.c (revision 326407) +++ head/share/examples/libusb20/control.c (revision 326408) @@ -1,415 +1,418 @@ -/* ---------------------------------------------------------------------------- +/*- + * SPDX-License-Identifier: Beerware + * + * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42) (by Poul-Henning Kamp): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Joerg Wunsch * ---------------------------------------------------------------------------- * * $FreeBSD$ */ /* * Simple demo program to illustrate the handling of FreeBSD's * libusb20. */ /* * Examples: * Just list all VID:PID pairs * ./control * * Standard device request GET_STATUS, report two bytes of status * (bit 0 in the first byte returned is the "self powered" bit) * ./control -v 0x3eb -p 0x2103 in std dev get_status 0 0 2 * * Request input reports through the interrupt pipe from a mouse * device (move the mouse around after issuing the command): * ./control -v 0x093a -p 0x2516 -i 0x81 * */ #include #include #include #include #include #include #include #include #include #include #include #include "util.h" /* * If you want to see the details of the internal datastructures * in the debugger, unifdef the following. */ #ifdef DEBUG # include "/usr/src/lib/libusb/libusb20_int.h" #endif #define BUFLEN 64 #define TIMEOUT 5000 /* 5 s */ int intr_ep; /* endpoints */ struct LIBUSB20_CONTROL_SETUP_DECODED setup; uint8_t out_buf[BUFLEN]; uint16_t out_len; bool do_request; static void doit(struct libusb20_device *dev) { int rv; if (do_request) printf("doit(): bmRequestType 0x%02x, bRequest 0x%02x, wValue 0x%04x, wIndex 0x%04x, wLength 0x%04x\n", setup.bmRequestType, setup.bRequest, setup.wValue, setup.wIndex, setup.wLength); /* * Open the device, allocating memory for two possible (bulk or * interrupt) transfers. * * If only control transfers are intended (via * libusb20_dev_request_sync()), transfer_max can be given as 0. */ if ((rv = libusb20_dev_open(dev, 1)) != 0) { fprintf(stderr, "libusb20_dev_open: %s\n", libusb20_strerror(rv)); return; } /* * If the device has more than one configuration, select the desired * one here. */ if ((rv = libusb20_dev_set_config_index(dev, 0)) != 0) { fprintf(stderr, "libusb20_dev_set_config_index: %s\n", libusb20_strerror(rv)); return; } uint8_t *data = 0; uint16_t actlen; if ((setup.bmRequestType & 0x80) != 0) { /* this is an IN request, allocate a buffer */ data = malloc(setup.wLength); if (data == 0) { fprintf(stderr, "Out of memory allocating %u bytes of reply buffer\n", setup.wLength); return; } } else data = out_buf; if (do_request) { if ((rv = libusb20_dev_request_sync(dev, &setup, data, &actlen, TIMEOUT, 0 /* flags */)) != 0) { fprintf(stderr, "libusb20_dev_request_sync: %s\n", libusb20_strerror(rv)); } printf("sent %d bytes\n", actlen); if ((setup.bmRequestType & 0x80) != 0) { print_formatted(data, (uint32_t)setup.wLength); free(data); } } if (intr_ep != 0) { /* * One transfer has been requested in libusb20_dev_open() above; * obtain the corresponding transfer struct pointer. */ struct libusb20_transfer *xfr_intr = libusb20_tr_get_pointer(dev, 0); if (xfr_intr == NULL) { fprintf(stderr, "libusb20_tr_get_pointer: %s\n", libusb20_strerror(rv)); return; } /* * Open the interrupt transfer. */ if ((rv = libusb20_tr_open(xfr_intr, 0, 1, intr_ep)) != 0) { fprintf(stderr, "libusb20_tr_open: %s\n", libusb20_strerror(rv)); return; } uint8_t in_buf[BUFLEN]; uint32_t rlen; if ((rv = libusb20_tr_bulk_intr_sync(xfr_intr, in_buf, BUFLEN, &rlen, TIMEOUT)) != 0) { fprintf(stderr, "libusb20_tr_bulk_intr_sync: %s\n", libusb20_strerror(rv)); } printf("received %d bytes\n", rlen); if (rlen > 0) print_formatted(in_buf, rlen); libusb20_tr_close(xfr_intr); } libusb20_dev_close(dev); } static void usage(void) { fprintf(stderr, "Usage ./usb [-i ] -v -p [dir type rcpt req wValue wIndex wLength [ ...]]\n"); exit(EX_USAGE); } static const char *reqnames[] = { "get_status", "clear_feature", "res1", "set_feature", "res2", "set_address", "get_descriptor", "set_descriptor", "get_configuration", "set_configuration", "get_interface", "set_interface", "synch_frame", }; static int get_req(const char *reqname) { size_t i; size_t l = strlen(reqname); for (i = 0; i < sizeof reqnames / sizeof reqnames[0]; i++) if (strncasecmp(reqname, reqnames[i], l) == 0) return i; return strtoul(reqname, 0, 0); } static int parse_req(int argc, char **argv) { int idx; uint8_t rt = 0; for (idx = 0; argc != 0 && idx <= 6; argc--, idx++) switch (idx) { case 0: /* dir[ection]: i[n] | o[ut] */ if (*argv[idx] == 'i') rt |= 0x80; else if (*argv[idx] == 'o') /* nop */; else { fprintf(stderr, "request direction must be \"in\" or \"out\" (got %s)\n", argv[idx]); return -1; } break; case 1: /* type: s[tandard] | c[lass] | v[endor] */ if (*argv[idx] == 's') /* nop */; else if (*argv[idx] == 'c') rt |= 0x20; else if (*argv[idx] == 'v') rt |= 0x40; else { fprintf(stderr, "request type must be one of \"standard\", \"class\", or \"vendor\" (got %s)\n", argv[idx]); return -1; } break; case 2: /* rcpt: d[evice], i[nterface], e[ndpoint], o[ther] */ if (*argv[idx] == 'd') /* nop */; else if (*argv[idx] == 'i') rt |= 1; else if (*argv[idx] == 'e') rt |= 2; else if (*argv[idx] == 'o') rt |= 3; else { fprintf(stderr, "recipient must be one of \"device\", \"interface\", \"endpoint\", or \"other\" (got %s)\n", argv[idx]); return -1; } setup.bmRequestType = rt; break; case 3: setup.bRequest = get_req(argv[idx]); break; case 4: setup.wValue = strtoul(argv[idx], 0, 0); break; case 5: setup.wIndex = strtoul(argv[idx], 0, 0); break; case 6: setup.wLength = strtoul(argv[idx], 0, 0); break; } return argc; } int main(int argc, char **argv) { unsigned int vid = UINT_MAX, pid = UINT_MAX; /* impossible VID:PID */ int c; /* * Initialize setup struct. This step is required, and initializes * internal fields in the struct. * * All the "public" fields are named exactly the way as the USB * standard describes them, namely: * * setup.bmRequestType: bitmask, bit 7 is direction * bits 6/5 is request type * (standard, class, vendor) * bits 4..0 is recipient * (device, interface, endpoint, * other) * setup.bRequest: the request itself (see get_req() for standard * requests, or specific value) * setup.wValue: a 16-bit value * setup.wIndex: another 16-bit value * setup.wLength: length of associated data transfer, direction * depends on bit 7 of bmRequestType */ LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &setup); while ((c = getopt(argc, argv, "i:p:v:")) != -1) switch (c) { case 'i': intr_ep = strtol(optarg, NULL, 0); break; case 'p': pid = strtol(optarg, NULL, 0); break; case 'v': vid = strtol(optarg, NULL, 0); break; default: usage(); break; } argc -= optind; argv += optind; if (vid != UINT_MAX || pid != UINT_MAX) { if (intr_ep != 0 && (intr_ep & 0x80) == 0) { fprintf(stderr, "Interrupt endpoint must be of type IN\n"); usage(); } if (argc > 0) { do_request = true; int rv = parse_req(argc, argv); if (rv < 0) return EX_USAGE; argc = rv; if (argc > 0) { for (out_len = 0; argc > 0 && out_len < BUFLEN; out_len++, argc--) { unsigned n = strtoul(argv[out_len], 0, 0); if (n > 255) fprintf(stderr, "Warning: data #%d 0x%0x > 0xff, truncating\n", out_len, n); out_buf[out_len] = (uint8_t)n; } out_len++; if (argc > 0) fprintf(stderr, "Data count exceeds maximum of %d, ignoring %d elements\n", BUFLEN, optind); } } } struct libusb20_backend *be; struct libusb20_device *dev; if ((be = libusb20_be_alloc_default()) == NULL) { perror("libusb20_be_alloc()"); return 1; } dev = NULL; while ((dev = libusb20_be_device_foreach(be, dev)) != NULL) { struct LIBUSB20_DEVICE_DESC_DECODED *ddp = libusb20_dev_get_device_desc(dev); printf("Found device %s (VID:PID = 0x%04x:0x%04x)\n", libusb20_dev_get_desc(dev), ddp->idVendor, ddp->idProduct); if (ddp->idVendor == vid && ddp->idProduct == pid) doit(dev); } libusb20_be_free(be); return 0; } Index: head/sys/dev/led/led.c =================================================================== --- head/sys/dev/led/led.c (revision 326407) +++ head/sys/dev/led/led.c (revision 326408) @@ -1,338 +1,340 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include struct ledsc { LIST_ENTRY(ledsc) list; char *name; void *private; int unit; led_t *func; struct cdev *dev; struct sbuf *spec; char *str; char *ptr; int count; time_t last_second; }; static struct unrhdr *led_unit; static struct mtx led_mtx; static struct sx led_sx; static LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(led_list); static struct callout led_ch; static int blinkers = 0; static MALLOC_DEFINE(M_LED, "LED", "LED driver"); static void led_timeout(void *p) { struct ledsc *sc; LIST_FOREACH(sc, &led_list, list) { if (sc->ptr == NULL) continue; if (sc->count > 0) { sc->count--; continue; } if (*sc->ptr == '.') { sc->ptr = NULL; blinkers--; continue; } else if (*sc->ptr == 'U' || *sc->ptr == 'u') { if (sc->last_second == time_second) continue; sc->last_second = time_second; sc->func(sc->private, *sc->ptr == 'U'); } else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') { sc->func(sc->private, 0); sc->count = (*sc->ptr & 0xf) - 1; } else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') { sc->func(sc->private, 1); sc->count = (*sc->ptr & 0xf) - 1; } sc->ptr++; if (*sc->ptr == '\0') sc->ptr = sc->str; } if (blinkers > 0) callout_reset(&led_ch, hz / 10, led_timeout, p); } static int led_state(struct ledsc *sc, struct sbuf **sb, int state) { struct sbuf *sb2 = NULL; sb2 = sc->spec; sc->spec = *sb; if (*sb != NULL) { sc->str = sbuf_data(*sb); if (sc->ptr == NULL) { blinkers++; callout_reset(&led_ch, hz / 10, led_timeout, NULL); } sc->ptr = sc->str; } else { sc->str = NULL; if (sc->ptr != NULL) blinkers--; sc->ptr = NULL; sc->func(sc->private, state); } sc->count = 0; *sb = sb2; return(0); } static int led_parse(const char *s, struct sbuf **sb, int *state) { int i, error; /* * Handle "on" and "off" immediately so people can flash really * fast from userland if they want to */ if (*s == '0' || *s == '1') { *state = *s & 1; return (0); } *state = 0; *sb = sbuf_new_auto(); if (*sb == NULL) return (ENOMEM); switch(s[0]) { /* * Flash, default is 100msec/100msec. * 'f2' sets 200msec/200msec etc. */ case 'f': if (s[1] >= '1' && s[1] <= '9') i = s[1] - '1'; else i = 0; sbuf_printf(*sb, "%c%c", 'A' + i, 'a' + i); break; /* * Digits, flashes out numbers. * 'd12' becomes -__________-_-______________________________ */ case 'd': for(s++; *s; s++) { if (!isdigit(*s)) continue; i = *s - '0'; if (i == 0) i = 10; for (; i > 1; i--) sbuf_cat(*sb, "Aa"); sbuf_cat(*sb, "Aj"); } sbuf_cat(*sb, "jj"); break; /* * String, roll your own. * 'a-j' gives "off" for n/10 sec. * 'A-J' gives "on" for n/10 sec. * no delay before repeat * 'sAaAbBa' becomes _-_--__- */ case 's': for(s++; *s; s++) { if ((*s >= 'a' && *s <= 'j') || (*s >= 'A' && *s <= 'J') || *s == 'U' || *s <= 'u' || *s == '.') sbuf_bcat(*sb, s, 1); } break; /* * Morse. * '.' becomes _- * '-' becomes _--- * ' ' becomes __ * '\n' becomes ____ * 1sec pause between repeats * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________ */ case 'm': for(s++; *s; s++) { if (*s == '.') sbuf_cat(*sb, "aA"); else if (*s == '-') sbuf_cat(*sb, "aC"); else if (*s == ' ') sbuf_cat(*sb, "b"); else if (*s == '\n') sbuf_cat(*sb, "d"); } sbuf_cat(*sb, "j"); break; default: sbuf_delete(*sb); return (EINVAL); } error = sbuf_finish(*sb); if (error != 0 || sbuf_len(*sb) == 0) { *sb = NULL; return (error); } return (0); } static int led_write(struct cdev *dev, struct uio *uio, int ioflag) { struct ledsc *sc; char *s; struct sbuf *sb = NULL; int error, state = 0; if (uio->uio_resid > 512) return (EINVAL); s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK); s[uio->uio_resid] = '\0'; error = uiomove(s, uio->uio_resid, uio); if (error) { free(s, M_DEVBUF); return (error); } error = led_parse(s, &sb, &state); free(s, M_DEVBUF); if (error) return (error); mtx_lock(&led_mtx); sc = dev->si_drv1; if (sc != NULL) error = led_state(sc, &sb, state); mtx_unlock(&led_mtx); if (sb != NULL) sbuf_delete(sb); return (error); } int led_set(char const *name, char const *cmd) { struct ledsc *sc; struct sbuf *sb = NULL; int error, state = 0; error = led_parse(cmd, &sb, &state); if (error) return (error); mtx_lock(&led_mtx); LIST_FOREACH(sc, &led_list, list) { if (strcmp(sc->name, name) == 0) break; } if (sc != NULL) error = led_state(sc, &sb, state); else error = ENOENT; mtx_unlock(&led_mtx); if (sb != NULL) sbuf_delete(sb); return (0); } static struct cdevsw led_cdevsw = { .d_version = D_VERSION, .d_write = led_write, .d_name = "LED", }; struct cdev * led_create(led_t *func, void *priv, char const *name) { return (led_create_state(func, priv, name, 0)); } struct cdev * led_create_state(led_t *func, void *priv, char const *name, int state) { struct ledsc *sc; sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO); sx_xlock(&led_sx); sc->name = strdup(name, M_LED); sc->unit = alloc_unr(led_unit); sc->private = priv; sc->func = func; sc->dev = make_dev(&led_cdevsw, sc->unit, UID_ROOT, GID_WHEEL, 0600, "led/%s", name); sx_xunlock(&led_sx); mtx_lock(&led_mtx); sc->dev->si_drv1 = sc; LIST_INSERT_HEAD(&led_list, sc, list); if (state != -1) sc->func(sc->private, state != 0); mtx_unlock(&led_mtx); return (sc->dev); } void led_destroy(struct cdev *dev) { struct ledsc *sc; mtx_lock(&led_mtx); sc = dev->si_drv1; dev->si_drv1 = NULL; if (sc->ptr != NULL) blinkers--; LIST_REMOVE(sc, list); if (LIST_EMPTY(&led_list)) callout_stop(&led_ch); mtx_unlock(&led_mtx); sx_xlock(&led_sx); free_unr(led_unit, sc->unit); destroy_dev(dev); if (sc->spec != NULL) sbuf_delete(sc->spec); free(sc->name, M_LED); free(sc, M_LED); sx_xunlock(&led_sx); } static void led_drvinit(void *unused) { led_unit = new_unrhdr(0, INT_MAX, NULL); mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF); sx_init(&led_sx, "LED sx"); callout_init_mtx(&led_ch, &led_mtx, 0); } SYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL); Index: head/sys/dev/md/md.c =================================================================== --- head/sys/dev/md/md.c (revision 326407) +++ head/sys/dev/md/md.c (revision 326408) @@ -1,1944 +1,1944 @@ /*- + * SPDX-License-Identifier: (Beerware AND BSD-3-Clause) + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ /*- - * SPDX-License-Identifier: BSD-3-Clause - * * The following functions are based in the vn(4) driver: mdstart_swap(), * mdstart_vnode(), mdcreate_swap(), mdcreate_vnode() and mddestroy(), * and as such under the following copyright: * * Copyright (c) 1988 University of Utah. * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Portions of this software were developed by Konstantin Belousov * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 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. * * from: Utah Hdr: vn.c 1.13 94/04/02 * * from: @(#)vn.c 8.6 (Berkeley) 4/1/94 * From: src/sys/dev/vn/vn.c,v 1.122 2000/12/16 16:06:03 */ #include "opt_rootdevname.h" #include "opt_geom.h" #include "opt_md.h" #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 #include #include #include #include #define MD_MODVER 1 #define MD_SHUTDOWN 0x10000 /* Tell worker thread to terminate. */ #define MD_EXITING 0x20000 /* Worker thread is exiting. */ #ifndef MD_NSECT #define MD_NSECT (10000 * 2) #endif static MALLOC_DEFINE(M_MD, "md_disk", "Memory Disk"); static MALLOC_DEFINE(M_MDSECT, "md_sectors", "Memory Disk Sectors"); static int md_debug; SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, "Enable md(4) debug messages"); static int md_malloc_wait; SYSCTL_INT(_vm, OID_AUTO, md_malloc_wait, CTLFLAG_RW, &md_malloc_wait, 0, "Allow malloc to wait for memory allocations"); #if defined(MD_ROOT) && !defined(MD_ROOT_FSTYPE) #define MD_ROOT_FSTYPE "ufs" #endif #if defined(MD_ROOT) /* * Preloaded image gets put here. */ #if defined(MD_ROOT_SIZE) /* * We put the mfs_root symbol into the oldmfs section of the kernel object file. * Applications that patch the object with the image can determine * the size looking at the oldmfs section size within the kernel. */ u_char mfs_root[MD_ROOT_SIZE*1024] __attribute__ ((section ("oldmfs"))); const int mfs_root_size = sizeof(mfs_root); #else extern volatile u_char __weak_symbol mfs_root; extern volatile u_char __weak_symbol mfs_root_end; __GLOBL(mfs_root); __GLOBL(mfs_root_end); #define mfs_root_size ((uintptr_t)(&mfs_root_end - &mfs_root)) #endif #endif static g_init_t g_md_init; static g_fini_t g_md_fini; static g_start_t g_md_start; static g_access_t g_md_access; static void g_md_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp); static struct cdev *status_dev = NULL; static struct sx md_sx; static struct unrhdr *md_uh; static d_ioctl_t mdctlioctl; static struct cdevsw mdctl_cdevsw = { .d_version = D_VERSION, .d_ioctl = mdctlioctl, .d_name = MD_NAME, }; struct g_class g_md_class = { .name = "MD", .version = G_VERSION, .init = g_md_init, .fini = g_md_fini, .start = g_md_start, .access = g_md_access, .dumpconf = g_md_dumpconf, }; DECLARE_GEOM_CLASS(g_md_class, g_md); static LIST_HEAD(, md_s) md_softc_list = LIST_HEAD_INITIALIZER(md_softc_list); #define NINDIR (PAGE_SIZE / sizeof(uintptr_t)) #define NMASK (NINDIR-1) static int nshift; static int md_vnode_pbuf_freecnt; struct indir { uintptr_t *array; u_int total; u_int used; u_int shift; }; struct md_s { int unit; LIST_ENTRY(md_s) list; struct bio_queue_head bio_queue; struct mtx queue_mtx; struct mtx stat_mtx; struct cdev *dev; enum md_types type; off_t mediasize; unsigned sectorsize; unsigned opencount; unsigned fwheads; unsigned fwsectors; char ident[32]; unsigned flags; char name[20]; struct proc *procp; struct g_geom *gp; struct g_provider *pp; int (*start)(struct md_s *sc, struct bio *bp); struct devstat *devstat; /* MD_MALLOC related fields */ struct indir *indir; uma_zone_t uma; /* MD_PRELOAD related fields */ u_char *pl_ptr; size_t pl_len; /* MD_VNODE related fields */ struct vnode *vnode; char file[PATH_MAX]; char label[PATH_MAX]; struct ucred *cred; /* MD_SWAP related fields */ vm_object_t object; }; static struct indir * new_indir(u_int shift) { struct indir *ip; ip = malloc(sizeof *ip, M_MD, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (ip == NULL) return (NULL); ip->array = malloc(sizeof(uintptr_t) * NINDIR, M_MDSECT, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (ip->array == NULL) { free(ip, M_MD); return (NULL); } ip->total = NINDIR; ip->shift = shift; return (ip); } static void del_indir(struct indir *ip) { free(ip->array, M_MDSECT); free(ip, M_MD); } static void destroy_indir(struct md_s *sc, struct indir *ip) { int i; for (i = 0; i < NINDIR; i++) { if (!ip->array[i]) continue; if (ip->shift) destroy_indir(sc, (struct indir*)(ip->array[i])); else if (ip->array[i] > 255) uma_zfree(sc->uma, (void *)(ip->array[i])); } del_indir(ip); } /* * This function does the math and allocates the top level "indir" structure * for a device of "size" sectors. */ static struct indir * dimension(off_t size) { off_t rcnt; struct indir *ip; int layer; rcnt = size; layer = 0; while (rcnt > NINDIR) { rcnt /= NINDIR; layer++; } /* * XXX: the top layer is probably not fully populated, so we allocate * too much space for ip->array in here. */ ip = malloc(sizeof *ip, M_MD, M_WAITOK | M_ZERO); ip->array = malloc(sizeof(uintptr_t) * NINDIR, M_MDSECT, M_WAITOK | M_ZERO); ip->total = NINDIR; ip->shift = layer * nshift; return (ip); } /* * Read a given sector */ static uintptr_t s_read(struct indir *ip, off_t offset) { struct indir *cip; int idx; uintptr_t up; if (md_debug > 1) printf("s_read(%jd)\n", (intmax_t)offset); up = 0; for (cip = ip; cip != NULL;) { if (cip->shift) { idx = (offset >> cip->shift) & NMASK; up = cip->array[idx]; cip = (struct indir *)up; continue; } idx = offset & NMASK; return (cip->array[idx]); } return (0); } /* * Write a given sector, prune the tree if the value is 0 */ static int s_write(struct indir *ip, off_t offset, uintptr_t ptr) { struct indir *cip, *lip[10]; int idx, li; uintptr_t up; if (md_debug > 1) printf("s_write(%jd, %p)\n", (intmax_t)offset, (void *)ptr); up = 0; li = 0; cip = ip; for (;;) { lip[li++] = cip; if (cip->shift) { idx = (offset >> cip->shift) & NMASK; up = cip->array[idx]; if (up != 0) { cip = (struct indir *)up; continue; } /* Allocate branch */ cip->array[idx] = (uintptr_t)new_indir(cip->shift - nshift); if (cip->array[idx] == 0) return (ENOSPC); cip->used++; up = cip->array[idx]; cip = (struct indir *)up; continue; } /* leafnode */ idx = offset & NMASK; up = cip->array[idx]; if (up != 0) cip->used--; cip->array[idx] = ptr; if (ptr != 0) cip->used++; break; } if (cip->used != 0 || li == 1) return (0); li--; while (cip->used == 0 && cip != ip) { li--; idx = (offset >> lip[li]->shift) & NMASK; up = lip[li]->array[idx]; KASSERT(up == (uintptr_t)cip, ("md screwed up")); del_indir(cip); lip[li]->array[idx] = 0; lip[li]->used--; cip = lip[li]; } return (0); } static int g_md_access(struct g_provider *pp, int r, int w, int e) { struct md_s *sc; sc = pp->geom->softc; if (sc == NULL) { if (r <= 0 && w <= 0 && e <= 0) return (0); return (ENXIO); } r += pp->acr; w += pp->acw; e += pp->ace; if ((sc->flags & MD_READONLY) != 0 && w > 0) return (EROFS); if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) { sc->opencount = 1; } else if ((pp->acr + pp->acw + pp->ace) > 0 && (r + w + e) == 0) { sc->opencount = 0; } return (0); } static void g_md_start(struct bio *bp) { struct md_s *sc; sc = bp->bio_to->geom->softc; if ((bp->bio_cmd == BIO_READ) || (bp->bio_cmd == BIO_WRITE)) { mtx_lock(&sc->stat_mtx); devstat_start_transaction_bio(sc->devstat, bp); mtx_unlock(&sc->stat_mtx); } mtx_lock(&sc->queue_mtx); bioq_disksort(&sc->bio_queue, bp); mtx_unlock(&sc->queue_mtx); wakeup(sc); } #define MD_MALLOC_MOVE_ZERO 1 #define MD_MALLOC_MOVE_FILL 2 #define MD_MALLOC_MOVE_READ 3 #define MD_MALLOC_MOVE_WRITE 4 #define MD_MALLOC_MOVE_CMP 5 static int md_malloc_move_ma(vm_page_t **mp, int *ma_offs, unsigned sectorsize, void *ptr, u_char fill, int op) { struct sf_buf *sf; vm_page_t m, *mp1; char *p, first; off_t *uc; unsigned n; int error, i, ma_offs1, sz, first_read; m = NULL; error = 0; sf = NULL; /* if (op == MD_MALLOC_MOVE_CMP) { gcc */ first = 0; first_read = 0; uc = ptr; mp1 = *mp; ma_offs1 = *ma_offs; /* } */ sched_pin(); for (n = sectorsize; n != 0; n -= sz) { sz = imin(PAGE_SIZE - *ma_offs, n); if (m != **mp) { if (sf != NULL) sf_buf_free(sf); m = **mp; sf = sf_buf_alloc(m, SFB_CPUPRIVATE | (md_malloc_wait ? 0 : SFB_NOWAIT)); if (sf == NULL) { error = ENOMEM; break; } } p = (char *)sf_buf_kva(sf) + *ma_offs; switch (op) { case MD_MALLOC_MOVE_ZERO: bzero(p, sz); break; case MD_MALLOC_MOVE_FILL: memset(p, fill, sz); break; case MD_MALLOC_MOVE_READ: bcopy(ptr, p, sz); cpu_flush_dcache(p, sz); break; case MD_MALLOC_MOVE_WRITE: bcopy(p, ptr, sz); break; case MD_MALLOC_MOVE_CMP: for (i = 0; i < sz; i++, p++) { if (!first_read) { *uc = (u_char)*p; first = *p; first_read = 1; } else if (*p != first) { error = EDOOFUS; break; } } break; default: KASSERT(0, ("md_malloc_move_ma unknown op %d\n", op)); break; } if (error != 0) break; *ma_offs += sz; *ma_offs %= PAGE_SIZE; if (*ma_offs == 0) (*mp)++; ptr = (char *)ptr + sz; } if (sf != NULL) sf_buf_free(sf); sched_unpin(); if (op == MD_MALLOC_MOVE_CMP && error != 0) { *mp = mp1; *ma_offs = ma_offs1; } return (error); } static int md_malloc_move_vlist(bus_dma_segment_t **pvlist, int *pma_offs, unsigned len, void *ptr, u_char fill, int op) { bus_dma_segment_t *vlist; uint8_t *p, *end, first; off_t *uc; int ma_offs, seg_len; vlist = *pvlist; ma_offs = *pma_offs; uc = ptr; for (; len != 0; len -= seg_len) { seg_len = imin(vlist->ds_len - ma_offs, len); p = (uint8_t *)(uintptr_t)vlist->ds_addr + ma_offs; switch (op) { case MD_MALLOC_MOVE_ZERO: bzero(p, seg_len); break; case MD_MALLOC_MOVE_FILL: memset(p, fill, seg_len); break; case MD_MALLOC_MOVE_READ: bcopy(ptr, p, seg_len); cpu_flush_dcache(p, seg_len); break; case MD_MALLOC_MOVE_WRITE: bcopy(p, ptr, seg_len); break; case MD_MALLOC_MOVE_CMP: end = p + seg_len; first = *uc = *p; /* Confirm all following bytes match the first */ while (++p < end) { if (*p != first) return (EDOOFUS); } break; default: KASSERT(0, ("md_malloc_move_vlist unknown op %d\n", op)); break; } ma_offs += seg_len; if (ma_offs == vlist->ds_len) { ma_offs = 0; vlist++; } ptr = (uint8_t *)ptr + seg_len; } *pvlist = vlist; *pma_offs = ma_offs; return (0); } static int mdstart_malloc(struct md_s *sc, struct bio *bp) { u_char *dst; vm_page_t *m; bus_dma_segment_t *vlist; int i, error, error1, ma_offs, notmapped; off_t secno, nsec, uc; uintptr_t sp, osp; switch (bp->bio_cmd) { case BIO_READ: case BIO_WRITE: case BIO_DELETE: break; default: return (EOPNOTSUPP); } notmapped = (bp->bio_flags & BIO_UNMAPPED) != 0; vlist = (bp->bio_flags & BIO_VLIST) != 0 ? (bus_dma_segment_t *)bp->bio_data : NULL; if (notmapped) { m = bp->bio_ma; ma_offs = bp->bio_ma_offset; dst = NULL; KASSERT(vlist == NULL, ("vlists cannot be unmapped")); } else if (vlist != NULL) { ma_offs = bp->bio_ma_offset; dst = NULL; } else { dst = bp->bio_data; } nsec = bp->bio_length / sc->sectorsize; secno = bp->bio_offset / sc->sectorsize; error = 0; while (nsec--) { osp = s_read(sc->indir, secno); if (bp->bio_cmd == BIO_DELETE) { if (osp != 0) error = s_write(sc->indir, secno, 0); } else if (bp->bio_cmd == BIO_READ) { if (osp == 0) { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, NULL, 0, MD_MALLOC_MOVE_ZERO); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, NULL, 0, MD_MALLOC_MOVE_ZERO); } else bzero(dst, sc->sectorsize); } else if (osp <= 255) { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, NULL, osp, MD_MALLOC_MOVE_FILL); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, NULL, osp, MD_MALLOC_MOVE_FILL); } else memset(dst, osp, sc->sectorsize); } else { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_READ); } else if (vlist != NULL) { error = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_READ); } else { bcopy((void *)osp, dst, sc->sectorsize); cpu_flush_dcache(dst, sc->sectorsize); } } osp = 0; } else if (bp->bio_cmd == BIO_WRITE) { if (sc->flags & MD_COMPRESS) { if (notmapped) { error1 = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, &uc, 0, MD_MALLOC_MOVE_CMP); i = error1 == 0 ? sc->sectorsize : 0; } else if (vlist != NULL) { error1 = md_malloc_move_vlist(&vlist, &ma_offs, sc->sectorsize, &uc, 0, MD_MALLOC_MOVE_CMP); i = error1 == 0 ? sc->sectorsize : 0; } else { uc = dst[0]; for (i = 1; i < sc->sectorsize; i++) { if (dst[i] != uc) break; } } } else { i = 0; uc = 0; } if (i == sc->sectorsize) { if (osp != uc) error = s_write(sc->indir, secno, uc); } else { if (osp <= 255) { sp = (uintptr_t)uma_zalloc(sc->uma, md_malloc_wait ? M_WAITOK : M_NOWAIT); if (sp == 0) { error = ENOSPC; break; } if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)sp, 0, MD_MALLOC_MOVE_WRITE); } else if (vlist != NULL) { error = md_malloc_move_vlist( &vlist, &ma_offs, sc->sectorsize, (void *)sp, 0, MD_MALLOC_MOVE_WRITE); } else { bcopy(dst, (void *)sp, sc->sectorsize); } error = s_write(sc->indir, secno, sp); } else { if (notmapped) { error = md_malloc_move_ma(&m, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_WRITE); } else if (vlist != NULL) { error = md_malloc_move_vlist( &vlist, &ma_offs, sc->sectorsize, (void *)osp, 0, MD_MALLOC_MOVE_WRITE); } else { bcopy(dst, (void *)osp, sc->sectorsize); } osp = 0; } } } else { error = EOPNOTSUPP; } if (osp > 255) uma_zfree(sc->uma, (void*)osp); if (error != 0) break; secno++; if (!notmapped && vlist == NULL) dst += sc->sectorsize; } bp->bio_resid = 0; return (error); } static void mdcopyto_vlist(void *src, bus_dma_segment_t *vlist, off_t offset, off_t len) { off_t seg_len; while (offset >= vlist->ds_len) { offset -= vlist->ds_len; vlist++; } while (len != 0) { seg_len = omin(len, vlist->ds_len - offset); bcopy(src, (void *)(uintptr_t)(vlist->ds_addr + offset), seg_len); offset = 0; src = (uint8_t *)src + seg_len; len -= seg_len; vlist++; } } static void mdcopyfrom_vlist(bus_dma_segment_t *vlist, off_t offset, void *dst, off_t len) { off_t seg_len; while (offset >= vlist->ds_len) { offset -= vlist->ds_len; vlist++; } while (len != 0) { seg_len = omin(len, vlist->ds_len - offset); bcopy((void *)(uintptr_t)(vlist->ds_addr + offset), dst, seg_len); offset = 0; dst = (uint8_t *)dst + seg_len; len -= seg_len; vlist++; } } static int mdstart_preload(struct md_s *sc, struct bio *bp) { uint8_t *p; p = sc->pl_ptr + bp->bio_offset; switch (bp->bio_cmd) { case BIO_READ: if ((bp->bio_flags & BIO_VLIST) != 0) { mdcopyto_vlist(p, (bus_dma_segment_t *)bp->bio_data, bp->bio_ma_offset, bp->bio_length); } else { bcopy(p, bp->bio_data, bp->bio_length); } cpu_flush_dcache(bp->bio_data, bp->bio_length); break; case BIO_WRITE: if ((bp->bio_flags & BIO_VLIST) != 0) { mdcopyfrom_vlist((bus_dma_segment_t *)bp->bio_data, bp->bio_ma_offset, p, bp->bio_length); } else { bcopy(bp->bio_data, p, bp->bio_length); } break; } bp->bio_resid = 0; return (0); } static int mdstart_vnode(struct md_s *sc, struct bio *bp) { int error; struct uio auio; struct iovec aiov; struct iovec *piov; struct mount *mp; struct vnode *vp; struct buf *pb; bus_dma_segment_t *vlist; struct thread *td; off_t iolen, len, zerosize; int ma_offs, npages; switch (bp->bio_cmd) { case BIO_READ: auio.uio_rw = UIO_READ; break; case BIO_WRITE: case BIO_DELETE: auio.uio_rw = UIO_WRITE; break; case BIO_FLUSH: break; default: return (EOPNOTSUPP); } td = curthread; vp = sc->vnode; pb = NULL; piov = NULL; ma_offs = bp->bio_ma_offset; len = bp->bio_length; /* * VNODE I/O * * If an error occurs, we set BIO_ERROR but we do not set * B_INVAL because (for a write anyway), the buffer is * still valid. */ if (bp->bio_cmd == BIO_FLUSH) { (void) vn_start_write(vp, &mp, V_WAIT); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_FSYNC(vp, MNT_WAIT, td); VOP_UNLOCK(vp, 0); vn_finished_write(mp); return (error); } auio.uio_offset = (vm_ooffset_t)bp->bio_offset; auio.uio_resid = bp->bio_length; auio.uio_segflg = UIO_SYSSPACE; auio.uio_td = td; if (bp->bio_cmd == BIO_DELETE) { /* * Emulate BIO_DELETE by writing zeros. */ zerosize = ZERO_REGION_SIZE - (ZERO_REGION_SIZE % sc->sectorsize); auio.uio_iovcnt = howmany(bp->bio_length, zerosize); piov = malloc(sizeof(*piov) * auio.uio_iovcnt, M_MD, M_WAITOK); auio.uio_iov = piov; while (len > 0) { piov->iov_base = __DECONST(void *, zero_region); piov->iov_len = len; if (len > zerosize) piov->iov_len = zerosize; len -= piov->iov_len; piov++; } piov = auio.uio_iov; } else if ((bp->bio_flags & BIO_VLIST) != 0) { piov = malloc(sizeof(*piov) * bp->bio_ma_n, M_MD, M_WAITOK); auio.uio_iov = piov; vlist = (bus_dma_segment_t *)bp->bio_data; while (len > 0) { piov->iov_base = (void *)(uintptr_t)(vlist->ds_addr + ma_offs); piov->iov_len = vlist->ds_len - ma_offs; if (piov->iov_len > len) piov->iov_len = len; len -= piov->iov_len; ma_offs = 0; vlist++; piov++; } auio.uio_iovcnt = piov - auio.uio_iov; piov = auio.uio_iov; } else if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pb = getpbuf(&md_vnode_pbuf_freecnt); bp->bio_resid = len; unmapped_step: npages = atop(min(MAXPHYS, round_page(len + (ma_offs & PAGE_MASK)))); iolen = min(ptoa(npages) - (ma_offs & PAGE_MASK), len); KASSERT(iolen > 0, ("zero iolen")); pmap_qenter((vm_offset_t)pb->b_data, &bp->bio_ma[atop(ma_offs)], npages); aiov.iov_base = (void *)((vm_offset_t)pb->b_data + (ma_offs & PAGE_MASK)); aiov.iov_len = iolen; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = iolen; } else { aiov.iov_base = bp->bio_data; aiov.iov_len = bp->bio_length; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; } /* * When reading set IO_DIRECT to try to avoid double-caching * the data. When writing IO_DIRECT is not optimal. */ if (auio.uio_rw == UIO_READ) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_READ(vp, &auio, IO_DIRECT, sc->cred); VOP_UNLOCK(vp, 0); } else { (void) vn_start_write(vp, &mp, V_WAIT); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); error = VOP_WRITE(vp, &auio, sc->flags & MD_ASYNC ? 0 : IO_SYNC, sc->cred); VOP_UNLOCK(vp, 0); vn_finished_write(mp); if (error == 0) sc->flags &= ~MD_VERIFY; } if (pb != NULL) { pmap_qremove((vm_offset_t)pb->b_data, npages); if (error == 0) { len -= iolen; bp->bio_resid -= iolen; ma_offs += iolen; if (len > 0) goto unmapped_step; } relpbuf(pb, &md_vnode_pbuf_freecnt); } free(piov, M_MD); if (pb == NULL) bp->bio_resid = auio.uio_resid; return (error); } static void md_swap_page_free(vm_page_t m) { vm_page_xunbusy(m); vm_page_lock(m); vm_page_free(m); vm_page_unlock(m); } static int mdstart_swap(struct md_s *sc, struct bio *bp) { vm_page_t m; u_char *p; vm_pindex_t i, lastp; bus_dma_segment_t *vlist; int rv, ma_offs, offs, len, lastend; switch (bp->bio_cmd) { case BIO_READ: case BIO_WRITE: case BIO_DELETE: break; default: return (EOPNOTSUPP); } p = bp->bio_data; ma_offs = (bp->bio_flags & (BIO_UNMAPPED|BIO_VLIST)) != 0 ? bp->bio_ma_offset : 0; vlist = (bp->bio_flags & BIO_VLIST) != 0 ? (bus_dma_segment_t *)bp->bio_data : NULL; /* * offs is the offset at which to start operating on the * next (ie, first) page. lastp is the last page on * which we're going to operate. lastend is the ending * position within that last page (ie, PAGE_SIZE if * we're operating on complete aligned pages). */ offs = bp->bio_offset % PAGE_SIZE; lastp = (bp->bio_offset + bp->bio_length - 1) / PAGE_SIZE; lastend = (bp->bio_offset + bp->bio_length - 1) % PAGE_SIZE + 1; rv = VM_PAGER_OK; VM_OBJECT_WLOCK(sc->object); vm_object_pip_add(sc->object, 1); for (i = bp->bio_offset / PAGE_SIZE; i <= lastp; i++) { len = ((i == lastp) ? lastend : PAGE_SIZE) - offs; m = vm_page_grab(sc->object, i, VM_ALLOC_SYSTEM); if (bp->bio_cmd == BIO_READ) { if (m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) { /* * Pager does not have the page. Zero * the allocated page, and mark it as * valid. Do not set dirty, the page * can be recreated if thrown out. */ pmap_zero_page(m); m->valid = VM_PAGE_BITS_ALL; } if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pmap_copy_pages(&m, offs, bp->bio_ma, ma_offs, len); } else if ((bp->bio_flags & BIO_VLIST) != 0) { physcopyout_vlist(VM_PAGE_TO_PHYS(m) + offs, vlist, ma_offs, len); cpu_flush_dcache(p, len); } else { physcopyout(VM_PAGE_TO_PHYS(m) + offs, p, len); cpu_flush_dcache(p, len); } } else if (bp->bio_cmd == BIO_WRITE) { if (len == PAGE_SIZE || m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) pmap_zero_page(m); if ((bp->bio_flags & BIO_UNMAPPED) != 0) { pmap_copy_pages(bp->bio_ma, ma_offs, &m, offs, len); } else if ((bp->bio_flags & BIO_VLIST) != 0) { physcopyin_vlist(vlist, ma_offs, VM_PAGE_TO_PHYS(m) + offs, len); } else { physcopyin(p, VM_PAGE_TO_PHYS(m) + offs, len); } m->valid = VM_PAGE_BITS_ALL; if (m->dirty != VM_PAGE_BITS_ALL) { vm_page_dirty(m); vm_pager_page_unswapped(m); } } else if (bp->bio_cmd == BIO_DELETE) { if (len == PAGE_SIZE || m->valid == VM_PAGE_BITS_ALL) rv = VM_PAGER_OK; else rv = vm_pager_get_pages(sc->object, &m, 1, NULL, NULL); if (rv == VM_PAGER_ERROR) { md_swap_page_free(m); break; } else if (rv == VM_PAGER_FAIL) { md_swap_page_free(m); m = NULL; } else { /* Page is valid. */ if (len != PAGE_SIZE) { pmap_zero_page_area(m, offs, len); if (m->dirty != VM_PAGE_BITS_ALL) { vm_page_dirty(m); vm_pager_page_unswapped(m); } } else { vm_pager_page_unswapped(m); md_swap_page_free(m); m = NULL; } } } if (m != NULL) { vm_page_xunbusy(m); vm_page_lock(m); if (vm_page_active(m)) vm_page_reference(m); else vm_page_activate(m); vm_page_unlock(m); } /* Actions on further pages start at offset 0 */ p += PAGE_SIZE - offs; offs = 0; ma_offs += len; } vm_object_pip_wakeup(sc->object); VM_OBJECT_WUNLOCK(sc->object); return (rv != VM_PAGER_ERROR ? 0 : ENOSPC); } static int mdstart_null(struct md_s *sc, struct bio *bp) { switch (bp->bio_cmd) { case BIO_READ: bzero(bp->bio_data, bp->bio_length); cpu_flush_dcache(bp->bio_data, bp->bio_length); break; case BIO_WRITE: break; } bp->bio_resid = 0; return (0); } static void md_kthread(void *arg) { struct md_s *sc; struct bio *bp; int error; sc = arg; thread_lock(curthread); sched_prio(curthread, PRIBIO); thread_unlock(curthread); if (sc->type == MD_VNODE) curthread->td_pflags |= TDP_NORUNNINGBUF; for (;;) { mtx_lock(&sc->queue_mtx); if (sc->flags & MD_SHUTDOWN) { sc->flags |= MD_EXITING; mtx_unlock(&sc->queue_mtx); kproc_exit(0); } bp = bioq_takefirst(&sc->bio_queue); if (!bp) { msleep(sc, &sc->queue_mtx, PRIBIO | PDROP, "mdwait", 0); continue; } mtx_unlock(&sc->queue_mtx); if (bp->bio_cmd == BIO_GETATTR) { int isv = ((sc->flags & MD_VERIFY) != 0); if ((sc->fwsectors && sc->fwheads && (g_handleattr_int(bp, "GEOM::fwsectors", sc->fwsectors) || g_handleattr_int(bp, "GEOM::fwheads", sc->fwheads))) || g_handleattr_int(bp, "GEOM::candelete", 1)) error = -1; else if (sc->ident[0] != '\0' && g_handleattr_str(bp, "GEOM::ident", sc->ident)) error = -1; else if (g_handleattr_int(bp, "MNT::verified", isv)) error = -1; else error = EOPNOTSUPP; } else { error = sc->start(sc, bp); } if (error != -1) { bp->bio_completed = bp->bio_length; if ((bp->bio_cmd == BIO_READ) || (bp->bio_cmd == BIO_WRITE)) devstat_end_transaction_bio(sc->devstat, bp); g_io_deliver(bp, error); } } } static struct md_s * mdfind(int unit) { struct md_s *sc; LIST_FOREACH(sc, &md_softc_list, list) { if (sc->unit == unit) break; } return (sc); } static struct md_s * mdnew(int unit, int *errp, enum md_types type) { struct md_s *sc; int error; *errp = 0; if (unit == -1) unit = alloc_unr(md_uh); else unit = alloc_unr_specific(md_uh, unit); if (unit == -1) { *errp = EBUSY; return (NULL); } sc = (struct md_s *)malloc(sizeof *sc, M_MD, M_WAITOK | M_ZERO); sc->type = type; bioq_init(&sc->bio_queue); mtx_init(&sc->queue_mtx, "md bio queue", NULL, MTX_DEF); mtx_init(&sc->stat_mtx, "md stat", NULL, MTX_DEF); sc->unit = unit; sprintf(sc->name, "md%d", unit); LIST_INSERT_HEAD(&md_softc_list, sc, list); error = kproc_create(md_kthread, sc, &sc->procp, 0, 0,"%s", sc->name); if (error == 0) return (sc); LIST_REMOVE(sc, list); mtx_destroy(&sc->stat_mtx); mtx_destroy(&sc->queue_mtx); free_unr(md_uh, sc->unit); free(sc, M_MD); *errp = error; return (NULL); } static void mdinit(struct md_s *sc) { struct g_geom *gp; struct g_provider *pp; g_topology_lock(); gp = g_new_geomf(&g_md_class, "md%d", sc->unit); gp->softc = sc; pp = g_new_providerf(gp, "md%d", sc->unit); pp->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE; pp->mediasize = sc->mediasize; pp->sectorsize = sc->sectorsize; switch (sc->type) { case MD_MALLOC: case MD_VNODE: case MD_SWAP: pp->flags |= G_PF_ACCEPT_UNMAPPED; break; case MD_PRELOAD: case MD_NULL: break; } sc->gp = gp; sc->pp = pp; g_error_provider(pp, 0); g_topology_unlock(); sc->devstat = devstat_new_entry("md", sc->unit, sc->sectorsize, DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_DIRECT, DEVSTAT_PRIORITY_MAX); } static int mdcreate_malloc(struct md_s *sc, struct md_ioctl *mdio) { uintptr_t sp; int error; off_t u; error = 0; if (mdio->md_options & ~(MD_AUTOUNIT | MD_COMPRESS | MD_RESERVE)) return (EINVAL); if (mdio->md_sectorsize != 0 && !powerof2(mdio->md_sectorsize)) return (EINVAL); /* Compression doesn't make sense if we have reserved space */ if (mdio->md_options & MD_RESERVE) mdio->md_options &= ~MD_COMPRESS; if (mdio->md_fwsectors != 0) sc->fwsectors = mdio->md_fwsectors; if (mdio->md_fwheads != 0) sc->fwheads = mdio->md_fwheads; sc->flags = mdio->md_options & (MD_COMPRESS | MD_FORCE); sc->indir = dimension(sc->mediasize / sc->sectorsize); sc->uma = uma_zcreate(sc->name, sc->sectorsize, NULL, NULL, NULL, NULL, 0x1ff, 0); if (mdio->md_options & MD_RESERVE) { off_t nsectors; nsectors = sc->mediasize / sc->sectorsize; for (u = 0; u < nsectors; u++) { sp = (uintptr_t)uma_zalloc(sc->uma, (md_malloc_wait ? M_WAITOK : M_NOWAIT) | M_ZERO); if (sp != 0) error = s_write(sc->indir, u, sp); else error = ENOMEM; if (error != 0) break; } } return (error); } static int mdsetcred(struct md_s *sc, struct ucred *cred) { char *tmpbuf; int error = 0; /* * Set credits in our softc */ if (sc->cred) crfree(sc->cred); sc->cred = crhold(cred); /* * Horrible kludge to establish credentials for NFS XXX. */ if (sc->vnode) { struct uio auio; struct iovec aiov; tmpbuf = malloc(sc->sectorsize, M_TEMP, M_WAITOK); bzero(&auio, sizeof(auio)); aiov.iov_base = tmpbuf; aiov.iov_len = sc->sectorsize; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = aiov.iov_len; vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY); error = VOP_READ(sc->vnode, &auio, 0, sc->cred); VOP_UNLOCK(sc->vnode, 0); free(tmpbuf, M_TEMP); } return (error); } static int mdcreate_vnode(struct md_s *sc, struct md_ioctl *mdio, struct thread *td) { struct vattr vattr; struct nameidata nd; char *fname; int error, flags; /* * Kernel-originated requests must have the filename appended * to the mdio structure to protect against malicious software. */ fname = mdio->md_file; if ((void *)fname != (void *)(mdio + 1)) { error = copyinstr(fname, sc->file, sizeof(sc->file), NULL); if (error != 0) return (error); } else strlcpy(sc->file, fname, sizeof(sc->file)); /* * If the user specified that this is a read only device, don't * set the FWRITE mask before trying to open the backing store. */ flags = FREAD | ((mdio->md_options & MD_READONLY) ? 0 : FWRITE) \ | ((mdio->md_options & MD_VERIFY) ? 0 : O_VERIFY); NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, sc->file, td); error = vn_open(&nd, &flags, 0, NULL); if (error != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp->v_type != VREG) { error = EINVAL; goto bad; } error = VOP_GETATTR(nd.ni_vp, &vattr, td->td_ucred); if (error != 0) goto bad; if (VOP_ISLOCKED(nd.ni_vp) != LK_EXCLUSIVE) { vn_lock(nd.ni_vp, LK_UPGRADE | LK_RETRY); if (nd.ni_vp->v_iflag & VI_DOOMED) { /* Forced unmount. */ error = EBADF; goto bad; } } nd.ni_vp->v_vflag |= VV_MD; VOP_UNLOCK(nd.ni_vp, 0); if (mdio->md_fwsectors != 0) sc->fwsectors = mdio->md_fwsectors; if (mdio->md_fwheads != 0) sc->fwheads = mdio->md_fwheads; snprintf(sc->ident, sizeof(sc->ident), "MD-DEV%ju-INO%ju", (uintmax_t)vattr.va_fsid, (uintmax_t)vattr.va_fileid); sc->flags = mdio->md_options & (MD_FORCE | MD_ASYNC | MD_VERIFY); if (!(flags & FWRITE)) sc->flags |= MD_READONLY; sc->vnode = nd.ni_vp; error = mdsetcred(sc, td->td_ucred); if (error != 0) { sc->vnode = NULL; vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY); nd.ni_vp->v_vflag &= ~VV_MD; goto bad; } return (0); bad: VOP_UNLOCK(nd.ni_vp, 0); (void)vn_close(nd.ni_vp, flags, td->td_ucred, td); return (error); } static int mddestroy(struct md_s *sc, struct thread *td) { if (sc->gp) { sc->gp->softc = NULL; g_topology_lock(); g_wither_geom(sc->gp, ENXIO); g_topology_unlock(); sc->gp = NULL; sc->pp = NULL; } if (sc->devstat) { devstat_remove_entry(sc->devstat); sc->devstat = NULL; } mtx_lock(&sc->queue_mtx); sc->flags |= MD_SHUTDOWN; wakeup(sc); while (!(sc->flags & MD_EXITING)) msleep(sc->procp, &sc->queue_mtx, PRIBIO, "mddestroy", hz / 10); mtx_unlock(&sc->queue_mtx); mtx_destroy(&sc->stat_mtx); mtx_destroy(&sc->queue_mtx); if (sc->vnode != NULL) { vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY); sc->vnode->v_vflag &= ~VV_MD; VOP_UNLOCK(sc->vnode, 0); (void)vn_close(sc->vnode, sc->flags & MD_READONLY ? FREAD : (FREAD|FWRITE), sc->cred, td); } if (sc->cred != NULL) crfree(sc->cred); if (sc->object != NULL) vm_object_deallocate(sc->object); if (sc->indir) destroy_indir(sc, sc->indir); if (sc->uma) uma_zdestroy(sc->uma); LIST_REMOVE(sc, list); free_unr(md_uh, sc->unit); free(sc, M_MD); return (0); } static int mdresize(struct md_s *sc, struct md_ioctl *mdio) { int error, res; vm_pindex_t oldpages, newpages; switch (sc->type) { case MD_VNODE: case MD_NULL: break; case MD_SWAP: if (mdio->md_mediasize <= 0 || (mdio->md_mediasize % PAGE_SIZE) != 0) return (EDOM); oldpages = OFF_TO_IDX(round_page(sc->mediasize)); newpages = OFF_TO_IDX(round_page(mdio->md_mediasize)); if (newpages < oldpages) { VM_OBJECT_WLOCK(sc->object); vm_object_page_remove(sc->object, newpages, 0, 0); swap_pager_freespace(sc->object, newpages, oldpages - newpages); swap_release_by_cred(IDX_TO_OFF(oldpages - newpages), sc->cred); sc->object->charge = IDX_TO_OFF(newpages); sc->object->size = newpages; VM_OBJECT_WUNLOCK(sc->object); } else if (newpages > oldpages) { res = swap_reserve_by_cred(IDX_TO_OFF(newpages - oldpages), sc->cred); if (!res) return (ENOMEM); if ((mdio->md_options & MD_RESERVE) || (sc->flags & MD_RESERVE)) { error = swap_pager_reserve(sc->object, oldpages, newpages - oldpages); if (error < 0) { swap_release_by_cred( IDX_TO_OFF(newpages - oldpages), sc->cred); return (EDOM); } } VM_OBJECT_WLOCK(sc->object); sc->object->charge = IDX_TO_OFF(newpages); sc->object->size = newpages; VM_OBJECT_WUNLOCK(sc->object); } break; default: return (EOPNOTSUPP); } sc->mediasize = mdio->md_mediasize; g_topology_lock(); g_resize_provider(sc->pp, sc->mediasize); g_topology_unlock(); return (0); } static int mdcreate_swap(struct md_s *sc, struct md_ioctl *mdio, struct thread *td) { vm_ooffset_t npage; int error; /* * Range check. Disallow negative sizes and sizes not being * multiple of page size. */ if (sc->mediasize <= 0 || (sc->mediasize % PAGE_SIZE) != 0) return (EDOM); /* * Allocate an OBJT_SWAP object. * * Note the truncation. */ if ((mdio->md_options & MD_VERIFY) != 0) return (EINVAL); npage = mdio->md_mediasize / PAGE_SIZE; if (mdio->md_fwsectors != 0) sc->fwsectors = mdio->md_fwsectors; if (mdio->md_fwheads != 0) sc->fwheads = mdio->md_fwheads; sc->object = vm_pager_allocate(OBJT_SWAP, NULL, PAGE_SIZE * npage, VM_PROT_DEFAULT, 0, td->td_ucred); if (sc->object == NULL) return (ENOMEM); sc->flags = mdio->md_options & (MD_FORCE | MD_RESERVE); if (mdio->md_options & MD_RESERVE) { if (swap_pager_reserve(sc->object, 0, npage) < 0) { error = EDOM; goto finish; } } error = mdsetcred(sc, td->td_ucred); finish: if (error != 0) { vm_object_deallocate(sc->object); sc->object = NULL; } return (error); } static int mdcreate_null(struct md_s *sc, struct md_ioctl *mdio, struct thread *td) { /* * Range check. Disallow negative sizes and sizes not being * multiple of page size. */ if (sc->mediasize <= 0 || (sc->mediasize % PAGE_SIZE) != 0) return (EDOM); return (0); } static int xmdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct md_ioctl *mdio; struct md_s *sc; int error, i; unsigned sectsize; if (md_debug) printf("mdctlioctl(%s %lx %p %x %p)\n", devtoname(dev), cmd, addr, flags, td); mdio = (struct md_ioctl *)addr; if (mdio->md_version != MDIOVERSION) return (EINVAL); /* * We assert the version number in the individual ioctl * handlers instead of out here because (a) it is possible we * may add another ioctl in the future which doesn't read an * mdio, and (b) the correct return value for an unknown ioctl * is ENOIOCTL, not EINVAL. */ error = 0; switch (cmd) { case MDIOCATTACH: switch (mdio->md_type) { case MD_MALLOC: case MD_PRELOAD: case MD_VNODE: case MD_SWAP: case MD_NULL: break; default: return (EINVAL); } if (mdio->md_sectorsize == 0) sectsize = DEV_BSIZE; else sectsize = mdio->md_sectorsize; if (sectsize > MAXPHYS || mdio->md_mediasize < sectsize) return (EINVAL); if (mdio->md_options & MD_AUTOUNIT) sc = mdnew(-1, &error, mdio->md_type); else { if (mdio->md_unit > INT_MAX) return (EINVAL); sc = mdnew(mdio->md_unit, &error, mdio->md_type); } if (sc == NULL) return (error); if (mdio->md_label != NULL) error = copyinstr(mdio->md_label, sc->label, sizeof(sc->label), NULL); if (error != 0) goto err_after_new; if (mdio->md_options & MD_AUTOUNIT) mdio->md_unit = sc->unit; sc->mediasize = mdio->md_mediasize; sc->sectorsize = sectsize; error = EDOOFUS; switch (sc->type) { case MD_MALLOC: sc->start = mdstart_malloc; error = mdcreate_malloc(sc, mdio); break; case MD_PRELOAD: /* * We disallow attaching preloaded memory disks via * ioctl. Preloaded memory disks are automatically * attached in g_md_init(). */ error = EOPNOTSUPP; break; case MD_VNODE: sc->start = mdstart_vnode; error = mdcreate_vnode(sc, mdio, td); break; case MD_SWAP: sc->start = mdstart_swap; error = mdcreate_swap(sc, mdio, td); break; case MD_NULL: sc->start = mdstart_null; error = mdcreate_null(sc, mdio, td); break; } err_after_new: if (error != 0) { mddestroy(sc, td); return (error); } /* Prune off any residual fractional sector */ i = sc->mediasize % sc->sectorsize; sc->mediasize -= i; mdinit(sc); return (0); case MDIOCDETACH: if (mdio->md_mediasize != 0 || (mdio->md_options & ~MD_FORCE) != 0) return (EINVAL); sc = mdfind(mdio->md_unit); if (sc == NULL) return (ENOENT); if (sc->opencount != 0 && !(sc->flags & MD_FORCE) && !(mdio->md_options & MD_FORCE)) return (EBUSY); return (mddestroy(sc, td)); case MDIOCRESIZE: if ((mdio->md_options & ~(MD_FORCE | MD_RESERVE)) != 0) return (EINVAL); sc = mdfind(mdio->md_unit); if (sc == NULL) return (ENOENT); if (mdio->md_mediasize < sc->sectorsize) return (EINVAL); if (mdio->md_mediasize < sc->mediasize && !(sc->flags & MD_FORCE) && !(mdio->md_options & MD_FORCE)) return (EBUSY); return (mdresize(sc, mdio)); case MDIOCQUERY: sc = mdfind(mdio->md_unit); if (sc == NULL) return (ENOENT); mdio->md_type = sc->type; mdio->md_options = sc->flags; mdio->md_mediasize = sc->mediasize; mdio->md_sectorsize = sc->sectorsize; error = 0; if (mdio->md_label != NULL) { error = copyout(sc->label, mdio->md_label, strlen(sc->label) + 1); } if (sc->type == MD_VNODE || (sc->type == MD_PRELOAD && mdio->md_file != NULL)) error = copyout(sc->file, mdio->md_file, strlen(sc->file) + 1); return (error); case MDIOCLIST: i = 1; LIST_FOREACH(sc, &md_softc_list, list) { if (i == MDNPAD - 1) mdio->md_pad[i] = -1; else mdio->md_pad[i++] = sc->unit; } mdio->md_pad[0] = i - 1; return (0); default: return (ENOIOCTL); }; } static int mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { int error; sx_xlock(&md_sx); error = xmdctlioctl(dev, cmd, addr, flags, td); sx_xunlock(&md_sx); return (error); } static void md_preloaded(u_char *image, size_t length, const char *name) { struct md_s *sc; int error; sc = mdnew(-1, &error, MD_PRELOAD); if (sc == NULL) return; sc->mediasize = length; sc->sectorsize = DEV_BSIZE; sc->pl_ptr = image; sc->pl_len = length; sc->start = mdstart_preload; if (name != NULL) strlcpy(sc->file, name, sizeof(sc->file)); #if defined(MD_ROOT) && !defined(ROOTDEVNAME) if (sc->unit == 0) rootdevnames[0] = MD_ROOT_FSTYPE ":/dev/md0"; #endif mdinit(sc); if (name != NULL) { printf("%s%d: Preloaded image <%s> %zd bytes at %p\n", MD_NAME, sc->unit, name, length, image); } else { printf("%s%d: Embedded image %zd bytes at %p\n", MD_NAME, sc->unit, length, image); } } static void g_md_init(struct g_class *mp __unused) { caddr_t mod; u_char *ptr, *name, *type; unsigned len; int i; /* figure out log2(NINDIR) */ for (i = NINDIR, nshift = -1; i; nshift++) i >>= 1; mod = NULL; sx_init(&md_sx, "MD config lock"); g_topology_unlock(); md_uh = new_unrhdr(0, INT_MAX, NULL); #ifdef MD_ROOT if (mfs_root_size != 0) { sx_xlock(&md_sx); md_preloaded(__DEVOLATILE(u_char *, &mfs_root), mfs_root_size, NULL); sx_xunlock(&md_sx); } #endif /* XXX: are preload_* static or do they need Giant ? */ while ((mod = preload_search_next_name(mod)) != NULL) { name = (char *)preload_search_info(mod, MODINFO_NAME); if (name == NULL) continue; type = (char *)preload_search_info(mod, MODINFO_TYPE); if (type == NULL) continue; if (strcmp(type, "md_image") && strcmp(type, "mfs_root")) continue; ptr = preload_fetch_addr(mod); len = preload_fetch_size(mod); if (ptr != NULL && len != 0) { sx_xlock(&md_sx); md_preloaded(ptr, len, name); sx_xunlock(&md_sx); } } md_vnode_pbuf_freecnt = nswbuf / 10; status_dev = make_dev(&mdctl_cdevsw, INT_MAX, UID_ROOT, GID_WHEEL, 0600, MDCTL_NAME); g_topology_lock(); } static void g_md_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, struct g_consumer *cp __unused, struct g_provider *pp) { struct md_s *mp; char *type; mp = gp->softc; if (mp == NULL) return; switch (mp->type) { case MD_MALLOC: type = "malloc"; break; case MD_PRELOAD: type = "preload"; break; case MD_VNODE: type = "vnode"; break; case MD_SWAP: type = "swap"; break; case MD_NULL: type = "null"; break; default: type = "unknown"; break; } if (pp != NULL) { if (indent == NULL) { sbuf_printf(sb, " u %d", mp->unit); sbuf_printf(sb, " s %ju", (uintmax_t) mp->sectorsize); sbuf_printf(sb, " f %ju", (uintmax_t) mp->fwheads); sbuf_printf(sb, " fs %ju", (uintmax_t) mp->fwsectors); sbuf_printf(sb, " l %ju", (uintmax_t) mp->mediasize); sbuf_printf(sb, " t %s", type); if ((mp->type == MD_VNODE && mp->vnode != NULL) || (mp->type == MD_PRELOAD && mp->file[0] != '\0')) sbuf_printf(sb, " file %s", mp->file); sbuf_printf(sb, " label %s", mp->label); } else { sbuf_printf(sb, "%s%d\n", indent, mp->unit); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->sectorsize); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->fwheads); sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->fwsectors); if (mp->ident[0] != '\0') { sbuf_printf(sb, "%s", indent); g_conf_printf_escaped(sb, "%s", mp->ident); sbuf_printf(sb, "\n"); } sbuf_printf(sb, "%s%ju\n", indent, (uintmax_t) mp->mediasize); sbuf_printf(sb, "%s%s\n", indent, (mp->flags & MD_COMPRESS) == 0 ? "off": "on"); sbuf_printf(sb, "%s%s\n", indent, (mp->flags & MD_READONLY) == 0 ? "read-write": "read-only"); sbuf_printf(sb, "%s%s\n", indent, type); if ((mp->type == MD_VNODE && mp->vnode != NULL) || (mp->type == MD_PRELOAD && mp->file[0] != '\0')) { sbuf_printf(sb, "%s", indent); g_conf_printf_escaped(sb, "%s", mp->file); sbuf_printf(sb, "\n"); } sbuf_printf(sb, "%s\n"); } } } static void g_md_fini(struct g_class *mp __unused) { sx_destroy(&md_sx); if (status_dev != NULL) destroy_dev(status_dev); delete_unrhdr(md_uh); } Index: head/sys/dev/mn/if_mn.c =================================================================== --- head/sys/dev/mn/if_mn.c (revision 326407) +++ head/sys/dev/mn/if_mn.c (revision 326408) @@ -1,1431 +1,1433 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- */ /* * Driver for Siemens reference design card "Easy321-R1". * * This card contains a FALC54 E1/T1 framer and a MUNICH32X 32-channel HDLC * controller. * * The driver supports E1 mode with up to 31 channels. We send CRC4 but don't * check it coming in. * * The FALC54 and MUNICH32X have far too many registers and weird modes for * comfort, so I have not bothered typing it all into a "fooreg.h" file, * you will (badly!) need the documentation anyway if you want to mess with * this gadget. */ #include __FBSDID("$FreeBSD$"); /* * Stuff to describe the MUNIC32X and FALC54 chips. */ #define M32_CHAN 32 /* We have 32 channels */ #define M32_TS 32 /* We have 32 timeslots */ #define NG_MN_NODE_TYPE "mn" #include #include #include #include #include #include #include #include #include #include "pci_if.h" #include #include #include #include #include #include #include static int mn_maxlatency = 1000; SYSCTL_INT(_debug, OID_AUTO, mn_maxlatency, CTLFLAG_RW, &mn_maxlatency, 0, "The number of milliseconds a packet is allowed to spend in the output queue. " "If the output queue is longer than this number of milliseconds when the packet " "arrives for output, the packet will be dropped." ); #ifndef NMN /* Most machines don't support more than 4 busmaster PCI slots, if even that many */ #define NMN 4 #endif /* From: PEB 20321 data sheet, p187, table 22 */ struct m32xreg { u_int32_t conf, cmd, stat, imask; u_int32_t fill10, piqba, piql, fill1c; u_int32_t mode1, mode2, ccba, txpoll; u_int32_t tiqba, tiql, riqba, riql; u_int32_t lconf, lccba, fill48, ltran; u_int32_t ltiqba, ltiql, lriqba, lriql; u_int32_t lreg0, lreg1, lreg2, lreg3; u_int32_t lreg4, lreg5, lre6, lstat; u_int32_t gpdir, gpdata, gpod, fill8c; u_int32_t ssccon, sscbr, ssctb, sscrb; u_int32_t ssccse, sscim, fillab, fillac; u_int32_t iomcon1, iomcon2, iomstat, fillbc; u_int32_t iomcit0, iomcit1, iomcir0, iomcir1; u_int32_t iomtmo, iomrmo, filld8, filldc; u_int32_t mbcmd, mbdata1, mbdata2, mbdata3; u_int32_t mbdata4, mbdata5, mbdata6, mbdata7; }; /* From: PEB 2254 data sheet, p80, table 10 */ struct f54wreg { u_int16_t xfifo; u_int8_t cmdr, mode, rah1, rah2, ral1, ral2; u_int8_t ipc, ccr1, ccr3, pre, rtr1, rtr2, rtr3, rtr4; u_int8_t ttr1, ttr2, ttr3, ttr4, imr0, imr1, imr2, imr3; u_int8_t imr4, fill19, fmr0, fmr1, fmr2, loop, xsw, xsp; u_int8_t xc0, xc1, rc0, rc1, xpm0, xpm1, xpm2, tswm; u_int8_t test1, idle, xsa4, xsa5, xsa6, xsa7, xsa8, fmr3; u_int8_t icb1, icb2, icb3, icb4, lim0, lim1, pcd, pcr; u_int8_t lim2, fill39[7]; u_int8_t fill40[8]; u_int8_t fill48[8]; u_int8_t fill50[8]; u_int8_t fill58[8]; u_int8_t dec, fill61, test2, fill63[5]; u_int8_t fill68[8]; u_int8_t xs[16]; }; /* From: PEB 2254 data sheet, p117, table 10 */ struct f54rreg { u_int16_t rfifo; u_int8_t fill2, mode, rah1, rah2, ral1, ral2; u_int8_t ipc, ccr1, ccr3, pre, rtr1, rtr2, rtr3, rtr4; u_int8_t ttr1, ttr2, ttr3, ttr4, imr0, imr1, imr2, imr3; u_int8_t imr4, fill19, fmr0, fmr1, fmr2, loop, xsw, xsp; u_int8_t xc0, xc1, rc0, rc1, xpm0, xpm1, xpm2, tswm; u_int8_t test, idle, xsa4, xsa5, xsa6, xsa7, xsa8, fmr13; u_int8_t icb1, icb2, icb3, icb4, lim0, lim1, pcd, pcr; u_int8_t lim2, fill39[7]; u_int8_t fill40[8]; u_int8_t fill48[4], frs0, frs1, rsw, rsp; u_int16_t fec, cvc, cec1, ebc; u_int16_t cec2, cec3; u_int8_t rsa4, rsa5, rsa6, rsa7; u_int8_t rsa8, rsa6s, tsr0, tsr1, sis, rsis; u_int16_t rbc; u_int8_t isr0, isr1, isr2, isr3, fill6c, fill6d, gis, vstr; u_int8_t rs[16]; }; /* Transmit & receive descriptors */ struct trxd { u_int32_t flags; vm_offset_t next; vm_offset_t data; u_int32_t status; /* only used for receive */ struct mbuf *m; /* software use only */ struct trxd *vnext; /* software use only */ }; /* Channel specification */ struct cspec { u_int32_t flags; vm_offset_t rdesc; vm_offset_t tdesc; u_int32_t itbs; }; struct m32_mem { vm_offset_t csa; u_int32_t ccb; u_int32_t reserve1[2]; u_int32_t ts[M32_TS]; struct cspec cs[M32_CHAN]; vm_offset_t crxd[M32_CHAN]; vm_offset_t ctxd[M32_CHAN]; }; struct mn_softc; struct sockaddr; struct rtentry; static int mn_probe(device_t self); static int mn_attach(device_t self); static void mn_create_channel(struct mn_softc *sc, int chan); static int mn_reset(struct mn_softc *sc); static struct trxd * mn_alloc_desc(void); static void mn_free_desc(struct trxd *dp); static void mn_intr(void *xsc); static u_int32_t mn_parse_ts(const char *s, int *nbit); #ifdef notyet static void m32_dump(struct mn_softc *sc); static void f54_dump(struct mn_softc *sc); static void mn_fmt_ts(char *p, u_int32_t ts); #endif /* notyet */ static void f54_init(struct mn_softc *sc); static ng_constructor_t ngmn_constructor; static ng_rcvmsg_t ngmn_rcvmsg; static ng_shutdown_t ngmn_shutdown; static ng_newhook_t ngmn_newhook; static ng_connect_t ngmn_connect; static ng_rcvdata_t ngmn_rcvdata; static ng_disconnect_t ngmn_disconnect; static struct ng_type mntypestruct = { .version = NG_ABI_VERSION, .name = NG_MN_NODE_TYPE, .constructor = ngmn_constructor, .rcvmsg = ngmn_rcvmsg, .shutdown = ngmn_shutdown, .newhook = ngmn_newhook, .connect = ngmn_connect, .rcvdata = ngmn_rcvdata, .disconnect = ngmn_disconnect, }; static MALLOC_DEFINE(M_MN, "mn", "Mx driver related"); #define NIQB 64 struct schan { enum {DOWN, UP} state; struct mn_softc *sc; int chan; u_int32_t ts; char name[8]; struct trxd *r1, *rl; struct trxd *x1, *xl; hook_p hook; time_t last_recv; time_t last_rxerr; time_t last_xmit; u_long rx_error; u_long short_error; u_long crc_error; u_long dribble_error; u_long long_error; u_long abort_error; u_long overflow_error; int last_error; int prev_error; u_long tx_pending; u_long tx_limit; }; enum framing {WHOKNOWS, E1, E1U, T1, T1U}; struct mn_softc { int unit; device_t dev; struct resource *irq; void *intrhand; enum framing framing; int nhooks; void *m0v, *m1v; vm_offset_t m0p, m1p; struct m32xreg *m32x; struct f54wreg *f54w; struct f54rreg *f54r; struct m32_mem m32_mem; u_int32_t tiqb[NIQB]; u_int32_t riqb[NIQB]; u_int32_t piqb[NIQB]; u_int32_t ltiqb[NIQB]; u_int32_t lriqb[NIQB]; char name[8]; u_int32_t falc_irq, falc_state, framer_state; struct schan *ch[M32_CHAN]; char nodename[NG_NODESIZ]; node_p node; u_long cnt_fec; u_long cnt_cvc; u_long cnt_cec1; u_long cnt_ebc; u_long cnt_cec2; u_long cnt_cec3; u_long cnt_rbc; }; static int ngmn_constructor(node_p node) { return (EINVAL); } static int ngmn_shutdown(node_p nodep) { return (EINVAL); } static void ngmn_config(node_p node, char *set, char *ret) { struct mn_softc *sc; enum framing wframing; sc = NG_NODE_PRIVATE(node); if (set != NULL) { if (!strncmp(set, "line ", 5)) { wframing = sc->framing; if (!strcmp(set, "line e1")) { wframing = E1; } else if (!strcmp(set, "line e1u")) { wframing = E1U; } else { strcat(ret, "ENOGROK\n"); return; } if (wframing == sc->framing) return; if (sc->nhooks > 0) { sprintf(ret, "Cannot change line when %d hooks open\n", sc->nhooks); return; } sc->framing = wframing; #if 1 f54_init(sc); #else mn_reset(sc); #endif } else { printf("%s CONFIG SET [%s]\n", sc->nodename, set); strcat(ret, "ENOGROK\n"); return; } } } static int ngmn_rcvmsg(node_p node, item_p item, hook_p lasthook) { struct mn_softc *sc; struct ng_mesg *resp = NULL; struct schan *sch; char *s, *r; int pos, i; struct ng_mesg *msg; NGI_GET_MSG(item, msg); sc = NG_NODE_PRIVATE(node); if (msg->header.typecookie != NGM_GENERIC_COOKIE) { NG_FREE_ITEM(item); NG_FREE_MSG(msg); return (EINVAL); } if (msg->header.cmd != NGM_TEXT_CONFIG && msg->header.cmd != NGM_TEXT_STATUS) { NG_FREE_ITEM(item); NG_FREE_MSG(msg); return (EINVAL); } NG_MKRESPONSE(resp, msg, sizeof(struct ng_mesg) + NG_TEXTRESPONSE, M_NOWAIT); if (resp == NULL) { NG_FREE_ITEM(item); NG_FREE_MSG(msg); return (ENOMEM); } if (msg->header.arglen) s = (char *)msg->data; else s = NULL; r = (char *)resp->data; *r = '\0'; if (msg->header.cmd == NGM_TEXT_CONFIG) { ngmn_config(node, s, r); resp->header.arglen = strlen(r) + 1; NG_RESPOND_MSG(i, node, item, resp); NG_FREE_MSG(msg); return (0); } pos = 0; pos += sprintf(pos + r,"Framer status %b;\n", sc->framer_state, "\20" "\40LOS\37AIS\36LFA\35RRA" "\34AUXP\33NMF\32LMFA\31frs0.0" "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS" "\24TS16LFA\23frs1.2\22XLS\21XLO" "\20RS1\17rsw.6\16RRA\15RY0" "\14RY1\13RY2\12RY3\11RY4" "\10SI1\7SI2\6rsp.5\5rsp.4" "\4rsp.3\3RSIF\2RS13\1RS15"); pos += sprintf(pos + r," Framing errors: %lu", sc->cnt_fec); pos += sprintf(pos + r," Code Violations: %lu\n", sc->cnt_cvc); pos += sprintf(pos + r," Falc State %b;\n", sc->falc_state, "\20" "\40LOS\37AIS\36LFA\35RRA" "\34AUXP\33NMF\32LMFA\31frs0.0" "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS" "\24TS16LFA\23frs1.2\22XLS\21XLO" "\20RS1\17rsw.6\16RRA\15RY0" "\14RY1\13RY2\12RY3\11RY4" "\10SI1\7SI2\6rsp.5\5rsp.4" "\4rsp.3\3RSIF\2RS13\1RS15"); pos += sprintf(pos + r, " Falc IRQ %b\n", sc->falc_irq, "\20" "\40RME\37RFS\36T8MS\35RMB\34CASC\33CRC4\32SA6SC\31RPF" "\30b27\27RDO\26ALLS\25XDU\24XMB\23b22\22XLSC\21XPR" "\20FAR\17LFA\16MFAR\15T400MS\14AIS\13LOS\12RAR\11RA" "\10ES\7SEC\6LMFA16\5AIS16\4RA16\3API\2SLN\1SLP"); for (i = 0; i < M32_CHAN; i++) { if (!sc->ch[i]) continue; sch = sc->ch[i]; pos += sprintf(r + pos, " Chan %d <%s> ", i, NG_HOOK_NAME(sch->hook)); pos += sprintf(r + pos, " Last Rx: "); if (sch->last_recv) pos += sprintf(r + pos, "%lu s", (unsigned long)(time_second - sch->last_recv)); else pos += sprintf(r + pos, "never"); pos += sprintf(r + pos, ", last RxErr: "); if (sch->last_rxerr) pos += sprintf(r + pos, "%lu s", (unsigned long)(time_second - sch->last_rxerr)); else pos += sprintf(r + pos, "never"); pos += sprintf(r + pos, ", last Tx: "); if (sch->last_xmit) pos += sprintf(r + pos, "%lu s\n", (unsigned long)(time_second - sch->last_xmit)); else pos += sprintf(r + pos, "never\n"); pos += sprintf(r + pos, " RX error(s) %lu", sch->rx_error); pos += sprintf(r + pos, " Short: %lu", sch->short_error); pos += sprintf(r + pos, " CRC: %lu", sch->crc_error); pos += sprintf(r + pos, " Mod8: %lu", sch->dribble_error); pos += sprintf(r + pos, " Long: %lu", sch->long_error); pos += sprintf(r + pos, " Abort: %lu", sch->abort_error); pos += sprintf(r + pos, " Overflow: %lu\n", sch->overflow_error); pos += sprintf(r + pos, " Last error: %b Prev error: %b\n", sch->last_error, "\20\7SHORT\5CRC\4MOD8\3LONG\2ABORT\1OVERRUN", sch->prev_error, "\20\7SHORT\5CRC\4MOD8\3LONG\2ABORT\1OVERRUN"); pos += sprintf(r + pos, " Xmit bytes pending %ld\n", sch->tx_pending); } resp->header.arglen = pos + 1; /* Take care of synchronous response, if any */ NG_RESPOND_MSG(i, node, item, resp); NG_FREE_MSG(msg); return (0); } static int ngmn_newhook(node_p node, hook_p hook, const char *name) { u_int32_t ts, chan; struct mn_softc *sc; int nbit; sc = NG_NODE_PRIVATE(node); if (name[0] != 't' || name[1] != 's') return (EINVAL); ts = mn_parse_ts(name + 2, &nbit); printf("%d bits %x\n", nbit, ts); if (sc->framing == E1 && (ts & 1)) return (EINVAL); if (sc->framing == E1U && nbit != 32) return (EINVAL); if (ts == 0) return (EINVAL); if (sc->framing == E1) chan = ffs(ts) - 1; else chan = 1; if (!sc->ch[chan]) mn_create_channel(sc, chan); else if (sc->ch[chan]->state == UP) return (EBUSY); sc->ch[chan]->ts = ts; sc->ch[chan]->hook = hook; sc->ch[chan]->tx_limit = nbit * 8; NG_HOOK_SET_PRIVATE(hook, sc->ch[chan]); sc->nhooks++; return(0); } static struct trxd *mn_desc_free; static struct trxd * mn_alloc_desc(void) { struct trxd *dp; dp = mn_desc_free; if (dp) mn_desc_free = dp->vnext; else dp = (struct trxd *)malloc(sizeof *dp, M_MN, M_NOWAIT); return (dp); } static void mn_free_desc(struct trxd *dp) { dp->vnext = mn_desc_free; mn_desc_free = dp; } static u_int32_t mn_parse_ts(const char *s, int *nbit) { unsigned r; int i, j; char *p; r = 0; j = -1; *nbit = 0; while(*s) { i = strtol(s, &p, 0); if (i < 0 || i > 31) return (0); while (j != -1 && j < i) { r |= 1 << j++; (*nbit)++; } j = -1; r |= 1 << i; (*nbit)++; if (*p == ',') { s = p + 1; continue; } else if (*p == '-') { j = i + 1; s = p + 1; continue; } else if (!*p) { break; } else { return (0); } } return (r); } #ifdef notyet static void mn_fmt_ts(char *p, u_int32_t ts) { char *s; int j; s = ""; ts &= 0xffffffff; for (j = 0; j < 32; j++) { if (!(ts & (1 << j))) continue; sprintf(p, "%s%d", s, j); p += strlen(p); s = ","; if (!(ts & (1 << (j+1)))) continue; for (; j < 32; j++) if (!(ts & (1 << (j+1)))) break; sprintf(p, "-%d", j); p += strlen(p); s = ","; } } #endif /* notyet */ /* * OUTPUT */ static int ngmn_rcvdata(hook_p hook, item_p item) { struct mbuf *m2; struct trxd *dp, *dp2; struct schan *sch; struct mn_softc *sc; int chan, pitch, len; struct mbuf *m; sch = NG_HOOK_PRIVATE(hook); sc = sch->sc; chan = sch->chan; if (sch->state != UP) { NG_FREE_ITEM(item); return (0); } NGI_GET_M(item, m); if (sch->tx_pending + m->m_pkthdr.len > sch->tx_limit * mn_maxlatency) { NG_FREE_M(m); NG_FREE_ITEM(item); return (0); } NG_FREE_ITEM(item); pitch = 0; m2 = m; dp2 = sc->ch[chan]->xl; len = m->m_pkthdr.len; while (len) { dp = mn_alloc_desc(); if (!dp) { pitch++; m_freem(m); sc->ch[chan]->xl = dp2; dp = dp2->vnext; while (dp) { dp2 = dp->vnext; mn_free_desc(dp); dp = dp2; } sc->ch[chan]->xl->vnext = NULL; break; } dp->data = vtophys(m2->m_data); dp->flags = m2->m_len << 16; dp->flags += 1; len -= m2->m_len; dp->next = vtophys(dp); dp->vnext = NULL; sc->ch[chan]->xl->next = vtophys(dp); sc->ch[chan]->xl->vnext = dp; sc->ch[chan]->xl = dp; if (!len) { dp->m = m; dp->flags |= 0xc0000000; dp2->flags &= ~0x40000000; } else { dp->m = NULL; m2 = m2->m_next; } } if (pitch) printf("%s%d: Short on mem, pitched %d packets\n", sc->name, chan, pitch); else { #if 0 printf("%d = %d + %d (%p)\n", sch->tx_pending + m->m_pkthdr.len, sch->tx_pending , m->m_pkthdr.len, m); #endif sch->tx_pending += m->m_pkthdr.len; sc->m32x->txpoll &= ~(1 << chan); } return (0); } /* * OPEN */ static int ngmn_connect(hook_p hook) { int i, nts, chan; struct trxd *dp, *dp2; struct mbuf *m; struct mn_softc *sc; struct schan *sch; u_int32_t u; sch = NG_HOOK_PRIVATE(hook); chan = sch->chan; sc = sch->sc; if (sch->state == UP) return (0); sch->state = UP; /* Count and configure the timeslots for this channel */ for (nts = i = 0; i < 32; i++) if (sch->ts & (1 << i)) { sc->m32_mem.ts[i] = 0x00ff00ff | (chan << 24) | (chan << 8); nts++; } /* Init the receiver & xmitter to HDLC */ sc->m32_mem.cs[chan].flags = 0x80e90006; /* Allocate two buffers per timeslot */ if (nts == 32) sc->m32_mem.cs[chan].itbs = 63; else sc->m32_mem.cs[chan].itbs = nts * 2; /* Setup a transmit chain with one descriptor */ /* XXX: we actually send a 1 byte packet */ dp = mn_alloc_desc(); MGETHDR(m, M_WAITOK, MT_DATA); m->m_pkthdr.len = 0; dp->m = m; dp->flags = 0xc0000000 + (1 << 16); dp->next = vtophys(dp); dp->vnext = NULL; dp->data = vtophys(sc->name); sc->m32_mem.cs[chan].tdesc = vtophys(dp); sc->ch[chan]->x1 = dp; sc->ch[chan]->xl = dp; /* Setup a receive chain with 5 + NTS descriptors */ dp = mn_alloc_desc(); m = NULL; MGETHDR(m, M_WAITOK, MT_DATA); MCLGET(m, M_WAITOK); dp->m = m; dp->data = vtophys(m->m_data); dp->flags = 0x40000000; dp->flags += 1600 << 16; dp->next = vtophys(dp); dp->vnext = NULL; sc->ch[chan]->rl = dp; for (i = 0; i < (nts + 10); i++) { dp2 = dp; dp = mn_alloc_desc(); m = NULL; MGETHDR(m, M_WAITOK, MT_DATA); MCLGET(m, M_WAITOK); dp->m = m; dp->data = vtophys(m->m_data); dp->flags = 0x00000000; dp->flags += 1600 << 16; dp->next = vtophys(dp2); dp->vnext = dp2; } sc->m32_mem.cs[chan].rdesc = vtophys(dp); sc->ch[chan]->r1 = dp; /* Initialize this channel */ sc->m32_mem.ccb = 0x00008000 + (chan << 8); sc->m32x->cmd = 0x1; DELAY(1000); u = sc->m32x->stat; if (!(u & 1)) printf("%s: init chan %d stat %08x\n", sc->name, chan, u); sc->m32x->stat = 1; /* probably not at splnet, force outward queueing */ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); return (0); } /* * CLOSE */ static int ngmn_disconnect(hook_p hook) { int chan, i; struct mn_softc *sc; struct schan *sch; struct trxd *dp, *dp2; u_int32_t u; sch = NG_HOOK_PRIVATE(hook); chan = sch->chan; sc = sch->sc; if (sch->state == DOWN) return (0); sch->state = DOWN; /* Set receiver & transmitter off */ sc->m32_mem.cs[chan].flags = 0x80920006; sc->m32_mem.cs[chan].itbs = 0; /* free the timeslots */ for (i = 0; i < 32; i++) if (sc->ch[chan]->ts & (1 << i)) sc->m32_mem.ts[i] = 0x20002000; /* Initialize this channel */ sc->m32_mem.ccb = 0x00008000 + (chan << 8); sc->m32x->cmd = 0x1; DELAY(30); u = sc->m32x->stat; if (!(u & 1)) printf("%s: zap chan %d stat %08x\n", sc->name, chan, u); sc->m32x->stat = 1; /* Free all receive descriptors and mbufs */ for (dp = sc->ch[chan]->r1; dp ; dp = dp2) { if (dp->m) m_freem(dp->m); sc->ch[chan]->r1 = dp2 = dp->vnext; mn_free_desc(dp); } /* Free all transmit descriptors and mbufs */ for (dp = sc->ch[chan]->x1; dp ; dp = dp2) { if (dp->m) { sc->ch[chan]->tx_pending -= dp->m->m_pkthdr.len; m_freem(dp->m); } sc->ch[chan]->x1 = dp2 = dp->vnext; mn_free_desc(dp); } sc->nhooks--; return(0); } /* * Create a new channel. */ static void mn_create_channel(struct mn_softc *sc, int chan) { struct schan *sch; sch = sc->ch[chan] = (struct schan *)malloc(sizeof *sc->ch[chan], M_MN, M_WAITOK | M_ZERO); sch->sc = sc; sch->state = DOWN; sch->chan = chan; sprintf(sch->name, "%s%d", sc->name, chan); return; } #ifdef notyet /* * Dump Munich32x state */ static void m32_dump(struct mn_softc *sc) { u_int32_t *tp4; int i, j; printf("mn%d: MUNICH32X dump\n", sc->unit); tp4 = (u_int32_t *)sc->m0v; for(j = 0; j < 64; j += 8) { printf("%02x", j * sizeof *tp4); for(i = 0; i < 8; i++) printf(" %08x", tp4[i+j]); printf("\n"); } for(j = 0; j < M32_CHAN; j++) { if (!sc->ch[j]) continue; printf("CH%d: state %d ts %08x", j, sc->ch[j]->state, sc->ch[j]->ts); printf(" %08x %08x %08x %08x %08x %08x\n", sc->m32_mem.cs[j].flags, sc->m32_mem.cs[j].rdesc, sc->m32_mem.cs[j].tdesc, sc->m32_mem.cs[j].itbs, sc->m32_mem.crxd[j], sc->m32_mem.ctxd[j] ); } } /* * Dump Falch54 state */ static void f54_dump(struct mn_softc *sc) { u_int8_t *tp1; int i, j; printf("%s: FALC54 dump\n", sc->name); tp1 = (u_int8_t *)sc->m1v; for(j = 0; j < 128; j += 16) { printf("%s: %02x |", sc->name, j * sizeof *tp1); for(i = 0; i < 16; i++) printf(" %02x", tp1[i+j]); printf("\n"); } } #endif /* notyet */ /* * Init Munich32x */ static void m32_init(struct mn_softc *sc) { sc->m32x->conf = 0x00000000; sc->m32x->mode1 = 0x81048000 + 1600; /* XXX: temp */ #if 1 sc->m32x->mode2 = 0x00000081; sc->m32x->txpoll = 0xffffffff; #elif 1 sc->m32x->mode2 = 0x00000081; sc->m32x->txpoll = 0xffffffff; #else sc->m32x->mode2 = 0x00000101; #endif sc->m32x->lconf = 0x6060009B; sc->m32x->imask = 0x00000000; } /* * Init the Falc54 */ static void f54_init(struct mn_softc *sc) { sc->f54w->ipc = 0x07; sc->f54w->xpm0 = 0xbd; sc->f54w->xpm1 = 0x03; sc->f54w->xpm2 = 0x00; sc->f54w->imr0 = 0x18; /* RMB, CASC */ sc->f54w->imr1 = 0x08; /* XMB */ sc->f54w->imr2 = 0x00; sc->f54w->imr3 = 0x38; /* LMFA16, AIS16, RA16 */ sc->f54w->imr4 = 0x00; sc->f54w->fmr0 = 0xf0; /* X: HDB3, R: HDB3 */ sc->f54w->fmr1 = 0x0e; /* Send CRC4, 2Mbit, ECM */ if (sc->framing == E1) sc->f54w->fmr2 = 0x03; /* Auto Rem-Alarm, Auto resync */ else if (sc->framing == E1U) sc->f54w->fmr2 = 0x33; /* dais, rtm, Auto Rem-Alarm, Auto resync */ sc->f54w->lim1 = 0xb0; /* XCLK=8kHz, .62V threshold */ sc->f54w->pcd = 0x0a; sc->f54w->pcr = 0x15; sc->f54w->xsw = 0x9f; /* fmr4 */ if (sc->framing == E1) sc->f54w->xsp = 0x1c; /* fmr5 */ else if (sc->framing == E1U) sc->f54w->xsp = 0x3c; /* tt0, fmr5 */ sc->f54w->xc0 = 0x07; sc->f54w->xc1 = 0x3d; sc->f54w->rc0 = 0x05; sc->f54w->rc1 = 0x00; sc->f54w->cmdr = 0x51; } static int mn_reset(struct mn_softc *sc) { u_int32_t u; int i; sc->m32x->ccba = vtophys(&sc->m32_mem.csa); sc->m32_mem.csa = vtophys(&sc->m32_mem.ccb); bzero(sc->tiqb, sizeof sc->tiqb); sc->m32x->tiqba = vtophys(&sc->tiqb); sc->m32x->tiql = NIQB / 16 - 1; bzero(sc->riqb, sizeof sc->riqb); sc->m32x->riqba = vtophys(&sc->riqb); sc->m32x->riql = NIQB / 16 - 1; bzero(sc->ltiqb, sizeof sc->ltiqb); sc->m32x->ltiqba = vtophys(&sc->ltiqb); sc->m32x->ltiql = NIQB / 16 - 1; bzero(sc->lriqb, sizeof sc->lriqb); sc->m32x->lriqba = vtophys(&sc->lriqb); sc->m32x->lriql = NIQB / 16 - 1; bzero(sc->piqb, sizeof sc->piqb); sc->m32x->piqba = vtophys(&sc->piqb); sc->m32x->piql = NIQB / 16 - 1; m32_init(sc); f54_init(sc); u = sc->m32x->stat; sc->m32x->stat = u; sc->m32_mem.ccb = 0x4; sc->m32x->cmd = 0x1; DELAY(1000); u = sc->m32x->stat; sc->m32x->stat = u; /* set all timeslots to known state */ for (i = 0; i < 32; i++) sc->m32_mem.ts[i] = 0x20002000; if (!(u & 1)) { printf( "mn%d: WARNING: Controller failed the PCI bus-master test.\n" "mn%d: WARNING: Use a PCI slot which can support bus-master cards.\n", sc->unit, sc->unit); return (0); } return (1); } /* * FALC54 interrupt handling */ static void f54_intr(struct mn_softc *sc) { unsigned g, u, s; g = sc->f54r->gis; u = sc->f54r->isr0 << 24; u |= sc->f54r->isr1 << 16; u |= sc->f54r->isr2 << 8; u |= sc->f54r->isr3; sc->falc_irq = u; /* don't chat about the 1 sec heart beat */ if (u & ~0x40) { #if 0 printf("%s*: FALC54 IRQ GIS:%02x %b\n", sc->name, g, u, "\20" "\40RME\37RFS\36T8MS\35RMB\34CASC\33CRC4\32SA6SC\31RPF" "\30b27\27RDO\26ALLS\25XDU\24XMB\23b22\22XLSC\21XPR" "\20FAR\17LFA\16MFAR\15T400MS\14AIS\13LOS\12RAR\11RA" "\10ES\7SEC\6LMFA16\5AIS16\4RA16\3API\2SLN\1SLP"); #endif s = sc->f54r->frs0 << 24; s |= sc->f54r->frs1 << 16; s |= sc->f54r->rsw << 8; s |= sc->f54r->rsp; sc->falc_state = s; s &= ~0x01844038; /* undefined or static bits */ s &= ~0x00009fc7; /* bits we don't care about */ s &= ~0x00780000; /* XXX: TS16 related */ s &= ~0x06000000; /* XXX: Multiframe related */ #if 0 printf("%s*: FALC54 Status %b\n", sc->name, s, "\20" "\40LOS\37AIS\36LFA\35RRA\34AUXP\33NMF\32LMFA\31frs0.0" "\30frs1.7\27TS16RA\26TS16LOS\25TS16AIS\24TS16LFA\23frs1.2\22XLS\21XLO" "\20RS1\17rsw.6\16RRA\15RY0\14RY1\13RY2\12RY3\11RY4" "\10SI1\7SI2\6rsp.5\5rsp.4\4rsp.3\3RSIF\2RS13\1RS15"); #endif if (s != sc->framer_state) { #if 0 for (i = 0; i < M32_CHAN; i++) { if (!sc->ch[i]) continue; sp = &sc->ch[i]->ifsppp; if (!(SP2IFP(sp)->if_flags & IFF_UP)) continue; if (s) timeout((timeout_t *)sp->pp_down, sp, 1 * hz); else timeout((timeout_t *)sp->pp_up, sp, 1 * hz); } #endif sc->framer_state = s; } } /* Once per second check error counters */ /* XXX: not clear if this is actually ok */ if (!(u & 0x40)) return; sc->cnt_fec += sc->f54r->fec; sc->cnt_cvc += sc->f54r->cvc; sc->cnt_cec1 += sc->f54r->cec1; sc->cnt_ebc += sc->f54r->ebc; sc->cnt_cec2 += sc->f54r->cec2; sc->cnt_cec3 += sc->f54r->cec3; sc->cnt_rbc += sc->f54r->rbc; } /* * Transmit interrupt for one channel */ static void mn_tx_intr(struct mn_softc *sc, u_int32_t vector) { u_int32_t chan; struct trxd *dp; struct mbuf *m; chan = vector & 0x1f; if (!sc->ch[chan]) return; if (sc->ch[chan]->state != UP) { printf("%s: tx_intr when not UP\n", sc->name); return; } for (;;) { dp = sc->ch[chan]->x1; if (vtophys(dp) == sc->m32_mem.ctxd[chan]) return; m = dp->m; if (m) { #if 0 printf("%d = %d - %d (%p)\n", sc->ch[chan]->tx_pending - m->m_pkthdr.len, sc->ch[chan]->tx_pending , m->m_pkthdr.len, m); #endif sc->ch[chan]->tx_pending -= m->m_pkthdr.len; m_freem(m); } sc->ch[chan]->last_xmit = time_second; sc->ch[chan]->x1 = dp->vnext; mn_free_desc(dp); } } /* * Receive interrupt for one channel */ static void mn_rx_intr(struct mn_softc *sc, u_int32_t vector) { u_int32_t chan, err; struct trxd *dp; struct mbuf *m; struct schan *sch; chan = vector & 0x1f; if (!sc->ch[chan]) return; sch = sc->ch[chan]; if (sch->state != UP) { printf("%s: rx_intr when not UP\n", sc->name); return; } vector &= ~0x1f; if (vector == 0x30000b00) sch->rx_error++; for (;;) { dp = sch->r1; if (vtophys(dp) == sc->m32_mem.crxd[chan]) return; m = dp->m; dp->m = NULL; m->m_pkthdr.len = m->m_len = (dp->status >> 16) & 0x1fff; err = (dp->status >> 8) & 0xff; if (!err) { int error; NG_SEND_DATA_ONLY(error, sch->hook, m); sch->last_recv = time_second; /* we could be down by now... */ if (sch->state != UP) return; } else if (err & 0x40) { sch->short_error++; } else if (err & 0x10) { sch->crc_error++; } else if (err & 0x08) { sch->dribble_error++; } else if (err & 0x04) { sch->long_error++; } else if (err & 0x02) { sch->abort_error++; } else if (err & 0x01) { sch->overflow_error++; } if (err) { sch->last_rxerr = time_second; sch->prev_error = sch->last_error; sch->last_error = err; } sc->ch[chan]->r1 = dp->vnext; /* Replenish desc + mbuf supplies */ if (!m) { MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { mn_free_desc(dp); return; /* ENOBUFS */ } if (!(MCLGET(m, M_NOWAIT))) { mn_free_desc(dp); m_freem(m); return; /* ENOBUFS */ } } dp->m = m; dp->data = vtophys(m->m_data); dp->flags = 0x40000000; dp->flags += 1600 << 16; dp->next = vtophys(dp); dp->vnext = NULL; sc->ch[chan]->rl->next = vtophys(dp); sc->ch[chan]->rl->vnext = dp; sc->ch[chan]->rl->flags &= ~0x40000000; sc->ch[chan]->rl = dp; } } /* * Interrupt handler */ static void mn_intr(void *xsc) { struct mn_softc *sc; u_int32_t stat, lstat, u; int i, j; sc = xsc; stat = sc->m32x->stat; lstat = sc->m32x->lstat; #if 0 if (!stat && !(lstat & 2)) return; #endif if (stat & ~0xc200) { printf("%s: I stat=%08x lstat=%08x\n", sc->name, stat, lstat); } if ((stat & 0x200) || (lstat & 2)) f54_intr(sc); for (j = i = 0; i < 64; i ++) { u = sc->riqb[i]; if (u) { sc->riqb[i] = 0; mn_rx_intr(sc, u); if ((u & ~0x1f) == 0x30000800 || (u & ~0x1f) == 0x30000b00) continue; u &= ~0x30000400; /* bits we don't care about */ if ((u & ~0x1f) == 0x00000900) continue; if (!(u & ~0x1f)) continue; if (!j) printf("%s*: RIQB:", sc->name); printf(" [%d]=%08x", i, u); j++; } } if (j) printf("\n"); for (j = i = 0; i < 64; i ++) { u = sc->tiqb[i]; if (u) { sc->tiqb[i] = 0; mn_tx_intr(sc, u); if ((u & ~0x1f) == 0x20000800) continue; u &= ~0x20000000; /* bits we don't care about */ if (!u) continue; if (!j) printf("%s*: TIQB:", sc->name); printf(" [%d]=%08x", i, u); j++; } } if (j) printf("\n"); sc->m32x->stat = stat; } /* * PCI initialization stuff */ static int mn_probe (device_t self) { u_int id = pci_get_devid(self); if (sizeof (struct m32xreg) != 256) { printf("MN: sizeof(struct m32xreg) = %zd, should have been 256\n", sizeof (struct m32xreg)); return (ENXIO); } if (sizeof (struct f54rreg) != 128) { printf("MN: sizeof(struct f54rreg) = %zd, should have been 128\n", sizeof (struct f54rreg)); return (ENXIO); } if (sizeof (struct f54wreg) != 128) { printf("MN: sizeof(struct f54wreg) = %zd, should have been 128\n", sizeof (struct f54wreg)); return (ENXIO); } if (id != 0x2101110a) return (ENXIO); device_set_desc_copy(self, "Munich32X E1/T1 HDLC Controller"); return (BUS_PROBE_DEFAULT); } static int mn_attach (device_t self) { struct mn_softc *sc; u_int32_t u; u_int32_t ver; static int once; int rid, error; struct resource *res; if (!once) { if (ng_newtype(&mntypestruct)) printf("ng_newtype failed\n"); once++; } sc = (struct mn_softc *)malloc(sizeof *sc, M_MN, M_WAITOK | M_ZERO); device_set_softc(self, sc); sc->dev = self; sc->unit = device_get_unit(self); sc->framing = E1; sprintf(sc->name, "mn%d", sc->unit); rid = PCIR_BAR(0); res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (res == NULL) { device_printf(self, "Could not map memory\n"); free(sc, M_MN); return ENXIO; } sc->m0v = rman_get_virtual(res); sc->m0p = rman_get_start(res); rid = PCIR_BAR(1); res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (res == NULL) { device_printf(self, "Could not map memory\n"); free(sc, M_MN); return ENXIO; } sc->m1v = rman_get_virtual(res); sc->m1p = rman_get_start(res); /* Allocate interrupt */ rid = 0; sc->irq = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->irq == NULL) { printf("couldn't map interrupt\n"); free(sc, M_MN); return(ENXIO); } error = bus_setup_intr(self, sc->irq, INTR_TYPE_NET, NULL, mn_intr, sc, &sc->intrhand); if (error) { printf("couldn't set up irq\n"); free(sc, M_MN); return(ENXIO); } u = pci_read_config(self, PCIR_COMMAND, 2); printf("%x\n", u); pci_write_config(self, PCIR_COMMAND, u | PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN, 2); #if 0 pci_write_config(self, PCIR_COMMAND, 0x02800046, 4); #endif u = pci_read_config(self, PCIR_COMMAND, 1); printf("%x\n", u); ver = pci_get_revid(self); sc->m32x = (struct m32xreg *) sc->m0v; sc->f54w = (struct f54wreg *) sc->m1v; sc->f54r = (struct f54rreg *) sc->m1v; /* We must reset before poking at FALC54 registers */ u = mn_reset(sc); if (!u) return (0); printf("mn%d: Munich32X", sc->unit); switch (ver) { case 0x13: printf(" Rev 2.2"); break; default: printf(" Rev 0x%x\n", ver); } printf(", Falc54"); switch (sc->f54r->vstr) { case 0: printf(" Rev < 1.3\n"); break; case 1: printf(" Rev 1.3\n"); break; case 2: printf(" Rev 1.4\n"); break; case 0x10: printf("-LH Rev 1.1\n"); break; case 0x13: printf("-LH Rev 1.3\n"); break; default: printf(" Rev 0x%x\n", sc->f54r->vstr); } if (ng_make_node_common(&mntypestruct, &sc->node) != 0) { printf("ng_make_node_common failed\n"); return (0); } NG_NODE_SET_PRIVATE(sc->node, sc); sprintf(sc->nodename, "%s%d", NG_MN_NODE_TYPE, sc->unit); if (ng_name_node(sc->node, sc->nodename)) { NG_NODE_UNREF(sc->node); return (0); } return (0); } static device_method_t mn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mn_probe), DEVMETHOD(device_attach, mn_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD_END }; static driver_t mn_driver = { "mn", mn_methods, 0 }; static devclass_t mn_devclass; DRIVER_MODULE(mn, pci, mn_driver, mn_devclass, 0, 0); Index: head/sys/dev/ppbus/pps.c =================================================================== --- head/sys/dev/ppbus/pps.c (revision 326407) +++ head/sys/dev/ppbus/pps.c (revision 326408) @@ -1,344 +1,346 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * * This driver implements a draft-mogul-pps-api-02.txt PPS source. * * The input pin is pin#10 * The echo output pin is pin#14 * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ppbus_if.h" #include #define PPS_NAME "pps" /* our official name */ #define PRVERBOSE(fmt, arg...) if (bootverbose) printf(fmt, ##arg); struct pps_data { struct ppb_device pps_dev; struct pps_state pps[9]; struct cdev *devs[9]; device_t ppsdev; device_t ppbus; int busy; struct callout timeout; int lastdata; struct sx lock; struct resource *intr_resource; /* interrupt resource */ void *intr_cookie; /* interrupt registration cookie */ }; static void ppsintr(void *arg); static void ppshcpoll(void *arg); #define DEVTOSOFTC(dev) \ ((struct pps_data *)device_get_softc(dev)) static devclass_t pps_devclass; static d_open_t ppsopen; static d_close_t ppsclose; static d_ioctl_t ppsioctl; static struct cdevsw pps_cdevsw = { .d_version = D_VERSION, .d_open = ppsopen, .d_close = ppsclose, .d_ioctl = ppsioctl, .d_name = PPS_NAME, }; static void ppsidentify(driver_t *driver, device_t parent) { device_t dev; dev = device_find_child(parent, PPS_NAME, -1); if (!dev) BUS_ADD_CHILD(parent, 0, PPS_NAME, -1); } static int ppstry(device_t ppbus, int send, int expect) { int i; ppb_wdtr(ppbus, send); i = ppb_rdtr(ppbus); PRVERBOSE("S: %02x E: %02x G: %02x\n", send, expect, i); return (i != expect); } static int ppsprobe(device_t ppsdev) { device_set_desc(ppsdev, "Pulse per second Timing Interface"); return (0); } static int ppsattach(device_t dev) { struct pps_data *sc = DEVTOSOFTC(dev); device_t ppbus = device_get_parent(dev); struct cdev *d; int error, i, unit, rid = 0; /* declare our interrupt handler */ sc->intr_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE); /* interrupts seem mandatory */ if (sc->intr_resource == NULL) { device_printf(dev, "Unable to allocate interrupt resource\n"); return (ENXIO); } error = bus_setup_intr(dev, sc->intr_resource, INTR_TYPE_TTY | INTR_MPSAFE, NULL, ppsintr, sc, &sc->intr_cookie); if (error) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->intr_resource); device_printf(dev, "Unable to register interrupt handler\n"); return (error); } sx_init(&sc->lock, "pps"); ppb_init_callout(ppbus, &sc->timeout, 0); sc->ppsdev = dev; sc->ppbus = ppbus; unit = device_get_unit(ppbus); d = make_dev(&pps_cdevsw, unit, UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%d", unit); sc->devs[0] = d; sc->pps[0].ppscap = PPS_CAPTUREASSERT | PPS_ECHOASSERT; d->si_drv1 = sc; d->si_drv2 = (void*)0; pps_init(&sc->pps[0]); ppb_lock(ppbus); if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { ppb_unlock(ppbus); return (0); } do { i = ppb_set_mode(sc->ppbus, PPB_EPP); PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); if (i == -1) break; i = 0; ppb_wctr(ppbus, i); if (ppstry(ppbus, 0x00, 0x00)) break; if (ppstry(ppbus, 0x55, 0x55)) break; if (ppstry(ppbus, 0xaa, 0xaa)) break; if (ppstry(ppbus, 0xff, 0xff)) break; i = IRQENABLE | PCD | STROBE | nINIT | SELECTIN; ppb_wctr(ppbus, i); PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); if (ppstry(ppbus, 0x00, 0x00)) break; if (ppstry(ppbus, 0x55, 0x00)) break; if (ppstry(ppbus, 0xaa, 0x00)) break; if (ppstry(ppbus, 0xff, 0x00)) break; i = IRQENABLE | PCD | nINIT | SELECTIN; ppb_wctr(ppbus, i); PRVERBOSE("CTR = %02x (%02x)\n", ppb_rctr(ppbus), i); ppstry(ppbus, 0x00, 0xff); ppstry(ppbus, 0x55, 0xff); ppstry(ppbus, 0xaa, 0xff); ppstry(ppbus, 0xff, 0xff); ppb_unlock(ppbus); for (i = 1; i < 9; i++) { d = make_dev(&pps_cdevsw, unit + 0x10000 * i, UID_ROOT, GID_WHEEL, 0600, PPS_NAME "%db%d", unit, i - 1); sc->devs[i] = d; sc->pps[i].ppscap = PPS_CAPTUREASSERT | PPS_CAPTURECLEAR; d->si_drv1 = sc; d->si_drv2 = (void *)(intptr_t)i; pps_init(&sc->pps[i]); } ppb_lock(ppbus); } while (0); i = ppb_set_mode(sc->ppbus, PPB_COMPATIBLE); ppb_release_bus(ppbus, dev); ppb_unlock(ppbus); return (0); } static int ppsopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct pps_data *sc = dev->si_drv1; device_t ppbus = sc->ppbus; int subdev = (intptr_t)dev->si_drv2; int i; /* * The sx lock is here solely to serialize open()'s to close * the race of concurrent open()'s when pps(4) doesn't own the * ppbus. */ sx_xlock(&sc->lock); ppb_lock(ppbus); if (!sc->busy) { device_t ppsdev = sc->ppsdev; if (ppb_request_bus(ppbus, ppsdev, PPB_WAIT|PPB_INTR)) { ppb_unlock(ppbus); sx_xunlock(&sc->lock); return (EINTR); } i = ppb_set_mode(sc->ppbus, PPB_PS2); PRVERBOSE("EPP: %d %d\n", i, PPB_IN_EPP_MODE(sc->ppbus)); i = IRQENABLE | PCD | nINIT | SELECTIN; ppb_wctr(ppbus, i); } if (subdev > 0 && !(sc->busy & ~1)) { /* XXX: Timeout of 1? hz/100 instead perhaps? */ callout_reset(&sc->timeout, 1, ppshcpoll, sc); sc->lastdata = ppb_rdtr(sc->ppbus); } sc->busy |= (1 << subdev); ppb_unlock(ppbus); sx_xunlock(&sc->lock); return(0); } static int ppsclose(struct cdev *dev, int flags, int fmt, struct thread *td) { struct pps_data *sc = dev->si_drv1; int subdev = (intptr_t)dev->si_drv2; sx_xlock(&sc->lock); sc->pps[subdev].ppsparam.mode = 0; /* PHK ??? */ ppb_lock(sc->ppbus); sc->busy &= ~(1 << subdev); if (subdev > 0 && !(sc->busy & ~1)) callout_stop(&sc->timeout); if (!sc->busy) { device_t ppsdev = sc->ppsdev; device_t ppbus = sc->ppbus; ppb_wdtr(ppbus, 0); ppb_wctr(ppbus, 0); ppb_set_mode(ppbus, PPB_COMPATIBLE); ppb_release_bus(ppbus, ppsdev); } ppb_unlock(sc->ppbus); sx_xunlock(&sc->lock); return(0); } static void ppshcpoll(void *arg) { struct pps_data *sc = arg; int i, j, k, l; KASSERT(sc->busy & ~1, ("pps polling w/o opened devices")); i = ppb_rdtr(sc->ppbus); if (i == sc->lastdata) return; l = sc->lastdata ^ i; k = 1; for (j = 1; j < 9; j ++) { if (l & k) { pps_capture(&sc->pps[j]); pps_event(&sc->pps[j], i & k ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR); } k += k; } sc->lastdata = i; callout_reset(&sc->timeout, 1, ppshcpoll, sc); } static void ppsintr(void *arg) { struct pps_data *sc = (struct pps_data *)arg; ppb_assert_locked(sc->ppbus); pps_capture(&sc->pps[0]); if (!(ppb_rstr(sc->ppbus) & nACK)) return; if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) ppb_wctr(sc->ppbus, IRQENABLE | AUTOFEED); pps_event(&sc->pps[0], PPS_CAPTUREASSERT); if (sc->pps[0].ppsparam.mode & PPS_ECHOASSERT) ppb_wctr(sc->ppbus, IRQENABLE); } static int ppsioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct pps_data *sc = dev->si_drv1; int subdev = (intptr_t)dev->si_drv2; int err; ppb_lock(sc->ppbus); err = pps_ioctl(cmd, data, &sc->pps[subdev]); ppb_unlock(sc->ppbus); return (err); } static device_method_t pps_methods[] = { /* device interface */ DEVMETHOD(device_identify, ppsidentify), DEVMETHOD(device_probe, ppsprobe), DEVMETHOD(device_attach, ppsattach), { 0, 0 } }; static driver_t pps_driver = { PPS_NAME, pps_methods, sizeof(struct pps_data), }; DRIVER_MODULE(pps, ppbus, pps_driver, pps_devclass, 0, 0); MODULE_DEPEND(pps, ppbus, 1, 1, 1); Index: head/sys/i386/i386/elan-mmcr.c =================================================================== --- head/sys/i386/i386/elan-mmcr.c (revision 326407) +++ head/sys/i386/i386/elan-mmcr.c (revision 326408) @@ -1,505 +1,507 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * * The AMD Elan sc520 is a system-on-chip gadget which is used in embedded * kind of things, see www.soekris.com for instance, and it has a few quirks * we need to deal with. * Unfortunately we cannot identify the gadget by CPUID output because it * depends on strapping options and only the stepping field may be useful * and those are undocumented from AMDs side. * * So instead we recognize the on-chip host-PCI bridge and call back from * sys/i386/pci/pci_bus.c to here if we find it. * * #ifdef CPU_ELAN_PPS * The Elan has three general purpose counters, and when two of these * are used just right they can hardware timestamp external events with * approx 125 nsec resolution and +/- 125 nsec precision. * * Connect the signal to TMR1IN and a GPIO pin, and configure the GPIO pin * with a 'P' in sysctl machdep.elan_gpio_config. * * The rising edge of the signal will start timer 1 counting up from * zero, and when the timecounter polls for PPS, both counter 1 & 2 is * read, as well as the GPIO bit. If a rising edge has happened, the * contents of timer 1 which is how long time ago the edge happened, * is subtracted from timer 2 to give us a "true time stamp". * * Echoing the PPS signal on any GPIO pin is supported (set it to 'e' * or 'E' (inverted) in the sysctl) The echo signal should only be * used as a visual indication, not for calibration since it suffers * from 1/hz (or more) jitter which the timestamps are compensated for. * #endif CPU_ELAN_PPS */ #include __FBSDID("$FreeBSD$"); #include "opt_cpu.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char gpio_config[33]; static volatile uint16_t *mmcrptr; volatile struct elan_mmcr *elan_mmcr; #ifdef CPU_ELAN_PPS static struct pps_state elan_pps; static volatile uint16_t *pps_ap[3]; static u_int pps_a, pps_d; static u_int echo_a, echo_d; #endif /* CPU_ELAN_PPS */ #ifdef CPU_SOEKRIS static struct bios_oem bios_soekris = { { 0xf0000, 0xf1000 }, { { "Soekris", 0, 8 }, /* Soekris Engineering. */ { "net4", 0, 8 }, /* net45xx */ { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ { NULL, 0, 0 }, } }; #endif static u_int led_cookie[32]; static struct cdev *led_dev[32]; static void gpio_led(void *cookie, int state) { u_int u, v; u = *(int *)cookie; v = u & 0xffff; u >>= 16; if (!state) v ^= 0xc; mmcrptr[v / 2] = u; } static int sysctl_machdep_elan_gpio_config(SYSCTL_HANDLER_ARGS) { u_int u, v; int i, np, ne; int error; char buf[32]; char tmp[10]; error = SYSCTL_OUT(req, gpio_config, 33); if (error != 0 || req->newptr == NULL) return (error); if (req->newlen != 32) return (EINVAL); error = SYSCTL_IN(req, buf, 32); if (error != 0) return (error); /* Disallow any disabled pins and count pps and echo */ np = ne = 0; for (i = 0; i < 32; i++) { if (gpio_config[i] == '-' && buf[i] == '.') buf[i] = gpio_config[i]; if (gpio_config[i] == '-' && buf[i] != '-') return (EPERM); if (buf[i] == 'P') { np++; if (np > 1) return (EINVAL); } if (buf[i] == 'e' || buf[i] == 'E') { ne++; if (ne > 1) return (EINVAL); } if (buf[i] != 'L' && buf[i] != 'l' #ifdef CPU_ELAN_PPS && buf[i] != 'P' && buf[i] != 'E' && buf[i] != 'e' #endif /* CPU_ELAN_PPS */ && buf[i] != '.' && buf[i] != '-') return (EINVAL); } #ifdef CPU_ELAN_PPS if (np == 0) pps_a = pps_d = 0; if (ne == 0) echo_a = echo_d = 0; #endif for (i = 0; i < 32; i++) { u = 1 << (i & 0xf); if (i >= 16) v = 2; else v = 0; #ifdef CPU_SOEKRIS if (i == 9) ; else #endif if (buf[i] != 'l' && buf[i] != 'L' && led_dev[i] != NULL) { led_destroy(led_dev[i]); led_dev[i] = NULL; mmcrptr[(0xc2a + v) / 2] &= ~u; } switch (buf[i]) { #ifdef CPU_ELAN_PPS case 'P': pps_d = u; pps_a = 0xc30 + v; pps_ap[0] = &mmcrptr[pps_a / 2]; pps_ap[1] = &elan_mmcr->GPTMR2CNT; pps_ap[2] = &elan_mmcr->GPTMR1CNT; mmcrptr[(0xc2a + v) / 2] &= ~u; gpio_config[i] = buf[i]; break; case 'e': case 'E': echo_d = u; if (buf[i] == 'E') echo_a = 0xc34 + v; else echo_a = 0xc38 + v; mmcrptr[(0xc2a + v) / 2] |= u; gpio_config[i] = buf[i]; break; #endif /* CPU_ELAN_PPS */ case 'l': case 'L': if (buf[i] == 'L') led_cookie[i] = (0xc34 + v) | (u << 16); else led_cookie[i] = (0xc38 + v) | (u << 16); if (led_dev[i]) break; sprintf(tmp, "gpio%d", i); mmcrptr[(0xc2a + v) / 2] |= u; gpio_config[i] = buf[i]; led_dev[i] = led_create(gpio_led, &led_cookie[i], tmp); break; case '.': gpio_config[i] = buf[i]; break; case '-': default: break; } } return (0); } SYSCTL_OID(_machdep, OID_AUTO, elan_gpio_config, CTLTYPE_STRING | CTLFLAG_RW, NULL, 0, sysctl_machdep_elan_gpio_config, "A", "Elan CPU GPIO pin config"); #ifdef CPU_ELAN_PPS static void elan_poll_pps(struct timecounter *tc) { static int state; int i; uint16_t u, x, y, z; register_t saveintr; /* * Grab the HW state as quickly and compactly as we can. Disable * interrupts to avoid measuring our interrupt service time on * hw with quality clock sources. */ saveintr = intr_disable(); x = *pps_ap[0]; /* state, must be first, see below */ y = *pps_ap[1]; /* timer2 */ z = *pps_ap[2]; /* timer1 */ intr_restore(saveintr); /* * Order is important here. We need to check the state of the GPIO * pin first, in order to avoid reading timer 1 right before the * state change. Technically pps_a may be zero in which case we * harmlessly read the REVID register and the contents of pps_d is * of no concern. */ i = x & pps_d; /* If state did not change or we don't have a GPIO pin, return */ if (i == state || pps_a == 0) return; state = i; /* If the state is "low", flip the echo GPIO and return. */ if (!i) { if (echo_a) mmcrptr[(echo_a ^ 0xc) / 2] = echo_d; return; } /* * Subtract timer1 from timer2 to compensate for time from the * edge until we read the counters. */ u = y - z; pps_capture(&elan_pps); elan_pps.capcount = u; pps_event(&elan_pps, PPS_CAPTUREASSERT); /* Twiddle echo bit */ if (echo_a) mmcrptr[echo_a / 2] = echo_d; } #endif /* CPU_ELAN_PPS */ static unsigned elan_get_timecount(struct timecounter *tc) { /* Read timer2, end of story */ return (elan_mmcr->GPTMR2CNT); } /* * The Elan CPU can be run from a number of clock frequencies, this * allows you to override the default 33.3 MHZ. */ #ifndef CPU_ELAN_XTAL #define CPU_ELAN_XTAL 33333333 #endif static struct timecounter elan_timecounter = { elan_get_timecount, NULL, 0xffff, CPU_ELAN_XTAL / 4, "ELAN", 1000 }; static int sysctl_machdep_elan_freq(SYSCTL_HANDLER_ARGS) { u_int f; int error; f = elan_timecounter.tc_frequency * 4; error = sysctl_handle_int(oidp, &f, 0, req); if (error == 0 && req->newptr != NULL) elan_timecounter.tc_frequency = (f + 3) / 4; return (error); } SYSCTL_PROC(_machdep, OID_AUTO, elan_freq, CTLTYPE_UINT | CTLFLAG_RW, 0, sizeof (u_int), sysctl_machdep_elan_freq, "IU", ""); /* * Positively identifying the Elan can only be done through the PCI id of * the host-bridge, this function is called from i386/pci/pci_bus.c. */ void init_AMD_Elan_sc520(void) { u_int new; int i; mmcrptr = pmap_mapdev(0xfffef000, 0x1000); elan_mmcr = (volatile struct elan_mmcr *)mmcrptr; /*- * The i8254 is driven with a nonstandard frequency which is * derived thusly: * f = 32768 * 45 * 25 / 31 = 1189161.29... * We use the sysctl to get the i8254 (timecounter etc) into whack. */ new = 1189161; i = kernel_sysctlbyname(&thread0, "machdep.i8254_freq", NULL, 0, &new, sizeof new, NULL, 0); if (bootverbose || 1) printf("sysctl machdep.i8254_freq=%d returns %d\n", new, i); /* Start GP timer #2 and use it as timecounter, hz permitting */ elan_mmcr->GPTMR2MAXCMPA = 0; elan_mmcr->GPTMR2CTL = 0xc001; #ifdef CPU_ELAN_PPS /* Set up GP timer #1 as pps counter */ elan_mmcr->CSPFS &= ~0x10; elan_mmcr->GPTMR1CTL = 0x8000 | 0x4000 | 0x10 | 0x1; elan_mmcr->GPTMR1MAXCMPA = 0x0; elan_mmcr->GPTMR1MAXCMPB = 0x0; elan_pps.ppscap |= PPS_CAPTUREASSERT; pps_init(&elan_pps); #endif tc_init(&elan_timecounter); } static void elan_watchdog(void *foo __unused, u_int spec, int *error) { u_int u, v, w; static u_int cur; u = spec & WD_INTERVAL; if (u > 0 && u <= 35) { u = imax(u - 5, 24); v = 2 << (u - 24); v |= 0xc000; /* * There is a bug in some silicon which prevents us from * writing to the WDTMRCTL register if the GP echo mode is * enabled. GP echo mode on the other hand is desirable * for other reasons. Save and restore the GP echo mode * around our hardware tom-foolery. */ w = elan_mmcr->GPECHO; elan_mmcr->GPECHO = 0; if (v != cur) { /* Clear the ENB bit */ elan_mmcr->WDTMRCTL = 0x3333; elan_mmcr->WDTMRCTL = 0xcccc; elan_mmcr->WDTMRCTL = 0; /* Set new value */ elan_mmcr->WDTMRCTL = 0x3333; elan_mmcr->WDTMRCTL = 0xcccc; elan_mmcr->WDTMRCTL = v; cur = v; } else { /* Just reset timer */ elan_mmcr->WDTMRCTL = 0xaaaa; elan_mmcr->WDTMRCTL = 0x5555; } elan_mmcr->GPECHO = w; *error = 0; } else { w = elan_mmcr->GPECHO; elan_mmcr->GPECHO = 0; elan_mmcr->WDTMRCTL = 0x3333; elan_mmcr->WDTMRCTL = 0xcccc; elan_mmcr->WDTMRCTL = 0x4080; elan_mmcr->WDTMRCTL = w; /* XXX What does this statement do? */ elan_mmcr->GPECHO = w; cur = 0; } } static int elan_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr) { if (offset >= 0x1000) return (-1); *paddr = 0xfffef000; return (0); } static int elan_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *tdr) { int error; error = ENOIOCTL; #ifdef CPU_ELAN_PPS if (pps_a != 0) error = pps_ioctl(cmd, arg, &elan_pps); /* * We only want to incur the overhead of the PPS polling if we * are actually asked to timestamp. */ if (elan_pps.ppsparam.mode & PPS_CAPTUREASSERT) { elan_timecounter.tc_poll_pps = elan_poll_pps; } else { elan_timecounter.tc_poll_pps = NULL; } if (error != ENOIOCTL) return (error); #endif return(error); } static struct cdevsw elan_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_ioctl = elan_ioctl, .d_mmap = elan_mmap, .d_name = "elan", }; static void elan_drvinit(void) { #ifdef CPU_SOEKRIS #define BIOS_OEM_MAXLEN 72 static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; #endif /* CPU_SOEKRIS */ /* If no elan found, just return */ if (mmcrptr == NULL) return; printf("Elan-mmcr driver: MMCR at %p.%s\n", mmcrptr, #ifdef CPU_ELAN_PPS " PPS support." #else "" #endif ); make_dev(&elan_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "elan-mmcr"); #ifdef CPU_SOEKRIS if ( bios_oem_strings(&bios_soekris, bios_oem, BIOS_OEM_MAXLEN) > 0 ) printf("Elan-mmcr %s\n", bios_oem); /* Create the error LED on GPIO9 */ led_cookie[9] = 0x02000c34; led_dev[9] = led_create(gpio_led, &led_cookie[9], "error"); /* Disable the unavailable GPIO pins */ strcpy(gpio_config, "-----....--..--------..---------"); #else /* !CPU_SOEKRIS */ /* We don't know which pins are available so enable them all */ strcpy(gpio_config, "................................"); #endif /* CPU_SOEKRIS */ EVENTHANDLER_REGISTER(watchdog_list, elan_watchdog, NULL, 0); } SYSINIT(elan, SI_SUB_PSEUDO, SI_ORDER_MIDDLE, elan_drvinit, NULL); Index: head/sys/i386/i386/mp_clock.c =================================================================== --- head/sys/i386/i386/mp_clock.c (revision 326407) +++ head/sys/i386/i386/mp_clock.c (revision 326408) @@ -1,150 +1,152 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- */ #include __FBSDID("$FreeBSD$"); /*- * Just when we thought life were beautiful, reality pops its grim face over * the edge again: * * ] 20. ACPI Timer Errata * ] * ] Problem: The power management timer may return improper result when * ] read. Although the timer value settles properly after incrementing, * ] while incrementing there is a 3nS window every 69.8nS where the * ] timer value is indeterminate (a 4.2% chance that the data will be * ] incorrect when read). As a result, the ACPI free running count up * ] timer specification is violated due to erroneous reads. Implication: * ] System hangs due to the "inaccuracy" of the timer when used by * ] software for time critical events and delays. * ] * ] Workaround: Read the register twice and compare. * ] Status: This will not be fixed in the PIIX4 or PIIX4E. * * The counter is in other words not latched to the PCI bus clock when * read. Notice the workaround isn't: We need to read until we have * three monotonic samples and then use the middle one, otherwise we are * not protected against the fact that the bits can be wrong in two * directions. If we only cared about monosity two reads would be enough. */ /* #include "opt_bus.h" */ #include #include #include #include #include #include #include #include #include static unsigned piix_get_timecount(struct timecounter *tc); static u_int32_t piix_timecounter_address; static u_int piix_freq = 14318182/4; static struct timecounter piix_timecounter = { piix_get_timecount, /* get_timecount */ 0, /* no poll_pps */ 0xffffff, /* counter_mask */ 0, /* frequency */ "PIIX" /* name */ }; static int sysctl_machdep_piix_freq(SYSCTL_HANDLER_ARGS) { int error; u_int freq; if (piix_timecounter.tc_frequency == 0) return (EOPNOTSUPP); freq = piix_freq; error = sysctl_handle_int(oidp, &freq, 0, req); if (error == 0 && req->newptr != NULL) { piix_freq = freq; piix_timecounter.tc_frequency = piix_freq; } return (error); } SYSCTL_PROC(_machdep, OID_AUTO, piix_freq, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(u_int), sysctl_machdep_piix_freq, "I", ""); static unsigned piix_get_timecount(struct timecounter *tc) { unsigned u1, u2, u3; u2 = inl(piix_timecounter_address); u3 = inl(piix_timecounter_address); do { u1 = u2; u2 = u3; u3 = inl(piix_timecounter_address); } while (u1 > u2 || u2 > u3); return (u2); } static int piix_probe(device_t dev) { u_int32_t d; if (devclass_get_device(devclass_find("acpi"), 0) != NULL) return (ENXIO); switch (pci_get_devid(dev)) { case 0x71138086: device_set_desc(dev, "PIIX Timecounter"); break; default: return (ENXIO); } d = pci_read_config(dev, PCIR_COMMAND, 2); if (!(d & PCIM_CMD_PORTEN)) { device_printf(dev, "PIIX I/O space not mapped\n"); return (ENXIO); } return (0); } static int piix_attach(device_t dev) { u_int32_t d; d = pci_read_config(dev, 0x40, 4); piix_timecounter_address = (d & 0xffc0) + 8; piix_timecounter.tc_frequency = piix_freq; tc_init(&piix_timecounter); return (0); } static device_method_t piix_methods[] = { /* Device interface */ DEVMETHOD(device_probe, piix_probe), DEVMETHOD(device_attach, piix_attach), { 0, 0 } }; static driver_t piix_driver = { "piix", piix_methods, 1, }; static devclass_t piix_devclass; DRIVER_MODULE(piix, pci, piix_driver, piix_devclass, 0, 0); Index: head/sys/kern/imgact_gzip.c =================================================================== --- head/sys/kern/imgact_gzip.c (revision 326407) +++ head/sys/kern/imgact_gzip.c (revision 326408) @@ -1,390 +1,392 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- */ /* * This module handles execution of a.out files which have been run through * "gzip". This saves diskspace, but wastes cpu-cycles and VM. * * TODO: * text-segments should be made R/O after being filled * is the vm-stuff safe ? * should handle the entire header of gzip'ed stuff. * inflate isn't quite reentrant yet... * error-handling is a mess... * so is the rest... * tidy up unnecessary includes */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct imgact_gzip { struct image_params *ip; struct exec a_out; int error; int gotheader; int where; u_char *inbuf; u_long offset; u_long output; u_long len; int idx; u_long virtual_offset, file_offset, file_end, bss_size; }; static int exec_gzip_imgact(struct image_params *imgp); static int NextByte(void *vp); static int do_aout_hdr(struct imgact_gzip *); static int Flush(void *vp, u_char *, u_long siz); static int exec_gzip_imgact(imgp) struct image_params *imgp; { int error; const u_char *p = (const u_char *) imgp->image_header; struct imgact_gzip igz; struct inflate infl; struct vmspace *vmspace; /* If these four are not OK, it isn't a gzip file */ if (p[0] != 0x1f) return -1; /* 0 Simply magic */ if (p[1] != 0x8b) return -1; /* 1 Simply magic */ if (p[2] != 0x08) return -1; /* 2 Compression method */ if (p[9] != 0x03) return -1; /* 9 OS compressed on */ /* * If this one contains anything but a comment or a filename marker, * we don't want to chew on it */ if (p[3] & ~(0x18)) return ENOEXEC; /* 3 Flags */ /* These are of no use to us */ /* 4-7 Timestamp */ /* 8 Extra flags */ bzero(&igz, sizeof igz); bzero(&infl, sizeof infl); infl.gz_private = (void *) &igz; infl.gz_input = NextByte; infl.gz_output = Flush; igz.ip = imgp; igz.idx = 10; if (p[3] & 0x08) { /* skip a filename */ while (p[igz.idx++]) if (igz.idx >= PAGE_SIZE) return ENOEXEC; } if (p[3] & 0x10) { /* skip a comment */ while (p[igz.idx++]) if (igz.idx >= PAGE_SIZE) return ENOEXEC; } igz.len = imgp->attr->va_size; error = inflate(&infl); /* * The unzipped file may not even have been long enough to contain * a header giving Flush() a chance to return error. Check for this. */ if ( !igz.gotheader ) return ENOEXEC; if ( !error ) { vmspace = imgp->proc->p_vmspace; error = vm_map_protect(&vmspace->vm_map, (vm_offset_t) vmspace->vm_taddr, (vm_offset_t) (vmspace->vm_taddr + (vmspace->vm_tsize << PAGE_SHIFT)) , VM_PROT_READ|VM_PROT_EXECUTE,0); } if (igz.inbuf) kmap_free_wakeup(exec_map, (vm_offset_t)igz.inbuf, PAGE_SIZE); if (igz.error || error) { printf("Output=%lu ", igz.output); printf("Inflate_error=%d igz.error=%d where=%d\n", error, igz.error, igz.where); } if (igz.error) return igz.error; if (error) return ENOEXEC; return 0; } static int do_aout_hdr(struct imgact_gzip * gz) { int error; struct vmspace *vmspace; vm_offset_t vmaddr; /* * Set file/virtual offset based on a.out variant. We do two cases: * host byte order and network byte order (for NetBSD compatibility) */ switch ((int) (gz->a_out.a_midmag & 0xffff)) { case ZMAGIC: gz->virtual_offset = 0; if (gz->a_out.a_text) { gz->file_offset = PAGE_SIZE; } else { /* Bill's "screwball mode" */ gz->file_offset = 0; } break; case QMAGIC: gz->virtual_offset = PAGE_SIZE; gz->file_offset = 0; break; default: /* NetBSD compatibility */ switch ((int) (ntohl(gz->a_out.a_midmag) & 0xffff)) { case ZMAGIC: case QMAGIC: gz->virtual_offset = PAGE_SIZE; gz->file_offset = 0; break; default: gz->where = __LINE__; return (-1); } } gz->bss_size = roundup(gz->a_out.a_bss, PAGE_SIZE); /* * Check various fields in header for validity/bounds. */ if ( /* entry point must lay with text region */ gz->a_out.a_entry < gz->virtual_offset || gz->a_out.a_entry >= gz->virtual_offset + gz->a_out.a_text || /* text and data size must each be page rounded */ gz->a_out.a_text & PAGE_MASK || gz->a_out.a_data & PAGE_MASK) { gz->where = __LINE__; return (-1); } /* * text/data/bss must not exceed limits */ PROC_LOCK(gz->ip->proc); if ( /* text can't exceed maximum text size */ gz->a_out.a_text > maxtsiz || /* data + bss can't exceed rlimit */ gz->a_out.a_data + gz->bss_size > lim_cur_proc(gz->ip->proc, RLIMIT_DATA) || racct_set(gz->ip->proc, RACCT_DATA, gz->a_out.a_data + gz->bss_size) != 0) { PROC_UNLOCK(gz->ip->proc); gz->where = __LINE__; return (ENOMEM); } PROC_UNLOCK(gz->ip->proc); /* Find out how far we should go */ gz->file_end = gz->file_offset + gz->a_out.a_text + gz->a_out.a_data; /* * Avoid a possible deadlock if the current address space is destroyed * and that address space maps the locked vnode. In the common case, * the locked vnode's v_usecount is decremented but remains greater * than zero. Consequently, the vnode lock is not needed by vrele(). * However, in cases where the vnode lock is external, such as nullfs, * v_usecount may become zero. */ VOP_UNLOCK(gz->ip->vp, 0); /* * Destroy old process VM and create a new one (with a new stack) */ error = exec_new_vmspace(gz->ip, &aout_sysvec); vn_lock(gz->ip->vp, LK_EXCLUSIVE | LK_RETRY); if (error) { gz->where = __LINE__; return (error); } vmspace = gz->ip->proc->p_vmspace; vmaddr = gz->virtual_offset; error = vm_mmap(&vmspace->vm_map, &vmaddr, gz->a_out.a_text + gz->a_out.a_data, VM_PROT_ALL, VM_PROT_ALL, MAP_ANON | MAP_FIXED, OBJT_DEFAULT, NULL, 0); if (error) { gz->where = __LINE__; return (error); } if (gz->bss_size != 0) { /* * Allocate demand-zeroed area for uninitialized data. * "bss" = 'block started by symbol' - named after the * IBM 7090 instruction of the same name. */ vmaddr = gz->virtual_offset + gz->a_out.a_text + gz->a_out.a_data; error = vm_map_find(&vmspace->vm_map, NULL, 0, &vmaddr, gz->bss_size, 0, VMFS_NO_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); if (error) { gz->where = __LINE__; return (error); } } /* Fill in process VM information */ vmspace->vm_tsize = gz->a_out.a_text >> PAGE_SHIFT; vmspace->vm_dsize = (gz->a_out.a_data + gz->bss_size) >> PAGE_SHIFT; vmspace->vm_taddr = (caddr_t) (uintptr_t) gz->virtual_offset; vmspace->vm_daddr = (caddr_t) (uintptr_t) (gz->virtual_offset + gz->a_out.a_text); /* Fill in image_params */ gz->ip->interpreted = 0; gz->ip->entry_addr = gz->a_out.a_entry; gz->ip->proc->p_sysent = &aout_sysvec; return 0; } static int NextByte(void *vp) { int error; struct imgact_gzip *igz = (struct imgact_gzip *) vp; if (igz->idx >= igz->len) { igz->where = __LINE__; return GZ_EOF; } if (igz->inbuf && igz->idx < (igz->offset + PAGE_SIZE)) { return igz->inbuf[(igz->idx++) - igz->offset]; } if (igz->inbuf) kmap_free_wakeup(exec_map, (vm_offset_t)igz->inbuf, PAGE_SIZE); igz->offset = igz->idx & ~PAGE_MASK; error = vm_mmap(exec_map, /* map */ (vm_offset_t *) & igz->inbuf, /* address */ PAGE_SIZE, /* size */ VM_PROT_READ, /* protection */ VM_PROT_READ, /* max protection */ 0, /* flags */ OBJT_VNODE, /* handle type */ igz->ip->vp, /* vnode */ igz->offset); /* offset */ if (error) { igz->where = __LINE__; igz->error = error; return GZ_EOF; } return igz->inbuf[(igz->idx++) - igz->offset]; } static int Flush(void *vp, u_char * ptr, u_long siz) { struct imgact_gzip *gz = (struct imgact_gzip *) vp; u_char *p = ptr, *q; int i; /* First, find an a.out-header. */ if (gz->output < sizeof gz->a_out) { q = (u_char *) & gz->a_out; i = min(siz, sizeof gz->a_out - gz->output); bcopy(p, q + gz->output, i); gz->output += i; p += i; siz -= i; if (gz->output == sizeof gz->a_out) { gz->gotheader = 1; i = do_aout_hdr(gz); if (i == -1) { if (!gz->where) gz->where = __LINE__; gz->error = ENOEXEC; return ENOEXEC; } else if (i) { gz->where = __LINE__; gz->error = i; return ENOEXEC; } if (gz->file_offset == 0) { q = (u_char *) (uintptr_t) gz->virtual_offset; copyout(&gz->a_out, q, sizeof gz->a_out); } } } /* Skip over zero-padded first PAGE if needed */ if (gz->output < gz->file_offset && gz->output + siz > gz->file_offset) { i = min(siz, gz->file_offset - gz->output); gz->output += i; p += i; siz -= i; } if (gz->output >= gz->file_offset && gz->output < gz->file_end) { i = min(siz, gz->file_end - gz->output); q = (u_char *) (uintptr_t) (gz->virtual_offset + gz->output - gz->file_offset); copyout(p, q, i); gz->output += i; p += i; siz -= i; } gz->output += siz; return 0; } /* * Tell kern_execve.c about it, with a little help from the linker. */ static struct execsw gzip_execsw = {exec_gzip_imgact, "gzip"}; EXEC_SET(execgzip, gzip_execsw); Index: head/sys/kern/inflate.c =================================================================== --- head/sys/kern/inflate.c (revision 326407) +++ head/sys/kern/inflate.c (revision 326408) @@ -1,1082 +1,1084 @@ -/* +/*- * Most parts of this file are not covered by: + * + * SPDX-License-Identifier: Beerware * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- */ #include __FBSDID("$FreeBSD$"); #include #include #ifdef _KERNEL #include #include #endif #include #ifdef _KERNEL static MALLOC_DEFINE(M_GZIP, "gzip_trees", "Gzip trees"); #endif /* needed to make inflate() work */ #define uch u_char #define ush u_short #define ulg u_long /* Stuff to make inflate() work */ #ifdef _KERNEL #define memzero(dest,len) bzero(dest,len) #endif #define NOMEMCPY #ifdef _KERNEL #define FPRINTF printf #else extern void putstr (char *); #define FPRINTF putstr #endif #define FLUSH(x,y) { \ int foo = (*x->gz_output)(x->gz_private,x->gz_slide,y); \ if (foo) \ return foo; \ } static const int qflag = 0; #ifndef _KERNEL /* want to use this file in kzip also */ extern unsigned char *kzipmalloc (int); extern void kzipfree (void*); #define malloc(x, y, z) kzipmalloc((x)) #define free(x, y) kzipfree((x)) #endif /* * This came from unzip-5.12. I have changed it the flow to pass * a structure pointer around, thus hopefully making it re-entrant. * Poul-Henning */ /* inflate.c -- put in the public domain by Mark Adler version c14o, 23 August 1994 */ /* You can do whatever you like with this source file, though I would prefer that if you modify it and redistribute it that you include comments to that effect with your name and the date. Thank you. History: vers date who what ---- --------- -------------- ------------------------------------ a ~~ Feb 92 M. Adler used full (large, one-step) lookup table b1 21 Mar 92 M. Adler first version with partial lookup tables b2 21 Mar 92 M. Adler fixed bug in fixed-code blocks b3 22 Mar 92 M. Adler sped up match copies, cleaned up some b4 25 Mar 92 M. Adler added prototypes; removed window[] (now is the responsibility of unzip.h--also changed name to slide[]), so needs diffs for unzip.c and unzip.h (this allows compiling in the small model on MSDOS); fixed cast of q in huft_build(); b5 26 Mar 92 M. Adler got rid of unintended macro recursion. b6 27 Mar 92 M. Adler got rid of nextbyte() routine. fixed bug in inflate_fixed(). c1 30 Mar 92 M. Adler removed lbits, dbits environment variables. changed BMAX to 16 for explode. Removed OUTB usage, and replaced it with flush()-- this was a 20% speed improvement! Added an explode.c (to replace unimplod.c) that uses the huft routines here. Removed register union. c2 4 Apr 92 M. Adler fixed bug for file sizes a multiple of 32k. c3 10 Apr 92 M. Adler reduced memory of code tables made by huft_build significantly (factor of two to three). c4 15 Apr 92 M. Adler added NOMEMCPY do kill use of memcpy(). worked around a Turbo C optimization bug. c5 21 Apr 92 M. Adler added the GZ_WSIZE #define to allow reducing the 32K window size for specialized applications. c6 31 May 92 M. Adler added some typecasts to eliminate warnings c7 27 Jun 92 G. Roelofs added some more typecasts (444: MSC bug). c8 5 Oct 92 J-l. Gailly added ifdef'd code to deal with PKZIP bug. c9 9 Oct 92 M. Adler removed a memory error message (~line 416). c10 17 Oct 92 G. Roelofs changed ULONG/UWORD/byte to ulg/ush/uch, removed old inflate, renamed inflate_entry to inflate, added Mark's fix to a comment. c10.5 14 Dec 92 M. Adler fix up error messages for incomplete trees. c11 2 Jan 93 M. Adler fixed bug in detection of incomplete tables, and removed assumption that EOB is the longest code (bad assumption). c12 3 Jan 93 M. Adler make tables for fixed blocks only once. c13 5 Jan 93 M. Adler allow all zero length codes (pkzip 2.04c outputs one zero length code for an empty distance tree). c14 12 Mar 93 M. Adler made inflate.c standalone with the introduction of inflate.h. c14b 16 Jul 93 G. Roelofs added (unsigned) typecast to w at 470. c14c 19 Jul 93 J. Bush changed v[N_MAX], l[288], ll[28x+3x] arrays to static for Amiga. c14d 13 Aug 93 J-l. Gailly de-complicatified Mark's c[*p++]++ thing. c14e 8 Oct 93 G. Roelofs changed memset() to memzero(). c14f 22 Oct 93 G. Roelofs renamed quietflg to qflag; made Trace() conditional; added inflate_free(). c14g 28 Oct 93 G. Roelofs changed l/(lx+1) macro to pointer (Cray bug) c14h 7 Dec 93 C. Ghisler huft_build() optimizations. c14i 9 Jan 94 A. Verheijen set fixed_t{d,l} to NULL after freeing; G. Roelofs check NEXTBYTE macro for GZ_EOF. c14j 23 Jan 94 G. Roelofs removed Ghisler "optimizations"; ifdef'd GZ_EOF check. c14k 27 Feb 94 G. Roelofs added some typecasts to avoid warnings. c14l 9 Apr 94 G. Roelofs fixed split comments on preprocessor lines to avoid bug in Encore compiler. c14m 7 Jul 94 P. Kienitz modified to allow assembler version of inflate_codes() (define ASM_INFLATECODES) c14n 22 Jul 94 G. Roelofs changed fprintf to FPRINTF for DLL versions c14o 23 Aug 94 C. Spieler added a newline to a debug statement; G. Roelofs added another typecast to avoid MSC warning */ /* Inflate deflated (PKZIP's method 8 compressed) data. The compression method searches for as much of the current string of bytes (up to a length of 258) in the previous 32K bytes. If it doesn't find any matches (of at least length 3), it codes the next byte. Otherwise, it codes the length of the matched string and its distance backwards from the current position. There is a single Huffman code that codes both single bytes (called "literals") and match lengths. A second Huffman code codes the distance information, which follows a length code. Each length or distance code actually represents a base value and a number of "extra" (sometimes zero) bits to get to add to the base value. At the end of each deflated block is a special end-of-block (EOB) literal/ length code. The decoding process is basically: get a literal/length code; if EOB then done; if a literal, emit the decoded byte; if a length then get the distance and emit the referred-to bytes from the sliding window of previously emitted data. There are (currently) three kinds of inflate blocks: stored, fixed, and dynamic. The compressor outputs a chunk of data at a time and decides which method to use on a chunk-by-chunk basis. A chunk might typically be 32K to 64K, uncompressed. If the chunk is uncompressible, then the "stored" method is used. In this case, the bytes are simply stored as is, eight bits per byte, with none of the above coding. The bytes are preceded by a count, since there is no longer an EOB code. If the data is compressible, then either the fixed or dynamic methods are used. In the dynamic method, the compressed data is preceded by an encoding of the literal/length and distance Huffman codes that are to be used to decode this block. The representation is itself Huffman coded, and so is preceded by a description of that code. These code descriptions take up a little space, and so for small blocks, there is a predefined set of codes, called the fixed codes. The fixed method is used if the block ends up smaller that way (usually for quite small chunks); otherwise the dynamic method is used. In the latter case, the codes are customized to the probabilities in the current block and so can code it much better than the pre-determined fixed codes can. The Huffman codes themselves are decoded using a mutli-level table lookup, in order to maximize the speed of decoding plus the speed of building the decoding tables. See the comments below that precede the lbits and dbits tuning parameters. */ /* Notes beyond the 1.93a appnote.txt: 1. Distance pointers never point before the beginning of the output stream. 2. Distance pointers can point back across blocks, up to 32k away. 3. There is an implied maximum of 7 bits for the bit length table and 15 bits for the actual data. 4. If only one code exists, then it is encoded using one bit. (Zero would be more efficient, but perhaps a little confusing.) If two codes exist, they are coded using one bit each (0 and 1). 5. There is no way of sending zero distance codes--a dummy must be sent if there are none. (History: a pre 2.0 version of PKZIP would store blocks with no distance codes, but this was discovered to be too harsh a criterion.) Valid only for 1.93a. 2.04c does allow zero distance codes, which is sent as one code of zero bits in length. 6. There are up to 286 literal/length codes. Code 256 represents the end-of-block. Note however that the static length tree defines 288 codes just to fill out the Huffman codes. Codes 286 and 287 cannot be used though, since there is no length base or extra bits defined for them. Similarly, there are up to 30 distance codes. However, static trees define 32 codes (all 5 bits) to fill out the Huffman codes, but the last two had better not show up in the data. 7. Unzip can check dynamic Huffman blocks for complete code sets. The exception is that a single code would not be complete (see #4). 8. The five bits following the block type is really the number of literal codes sent minus 257. 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits (1+6+6). Therefore, to output three times the length, you output three codes (1+1+1), whereas to output four times the same length, you only need two codes (1+3). Hmm. 10. In the tree reconstruction algorithm, Code = Code + Increment only if BitLength(i) is not zero. (Pretty obvious.) 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) 12. Note: length code 284 can represent 227-258, but length code 285 really is 258. The last length deserves its own, short code since it gets used a lot in very redundant files. The length 258 is special since 258 - 3 (the min match length) is 255. 13. The literal/length and distance code bit lengths are read as a single stream of lengths. It is possible (and advantageous) for a repeat code (16, 17, or 18) to go across the boundary between the two sets of lengths. */ #define PKZIP_BUG_WORKAROUND /* PKZIP 1.93a problem--live with it */ /* inflate.h must supply the uch slide[GZ_WSIZE] array and the NEXTBYTE, FLUSH() and memzero macros. If the window size is not 32K, it should also define GZ_WSIZE. If INFMOD is defined, it can include compiled functions to support the NEXTBYTE and/or FLUSH() macros. There are defaults for NEXTBYTE and FLUSH() below for use as examples of what those functions need to do. Normally, you would also want FLUSH() to compute a crc on the data. inflate.h also needs to provide these typedefs: typedef unsigned char uch; typedef unsigned short ush; typedef unsigned long ulg; This module uses the external functions malloc() and free() (and probably memset() or bzero() in the memzero() macro). Their prototypes are normally found in and . */ #define INFMOD /* tell inflate.h to include code to be * compiled */ /* Huffman code lookup table entry--this entry is four bytes for machines that have 16-bit pointers (e.g. PC's in the small or medium model). Valid extra bits are 0..13. e == 15 is EOB (end of block), e == 16 means that v is a literal, 16 < e < 32 means that v is a pointer to the next table, which codes e - 16 bits, and lastly e == 99 indicates an unused code. If a code with e == 99 is looked up, this implies an error in the data. */ struct huft { uch e; /* number of extra bits or operation */ uch b; /* number of bits in this code or subcode */ union { ush n; /* literal, length base, or distance * base */ struct huft *t; /* pointer to next level of table */ } v; }; /* Function prototypes */ static int huft_build(struct inflate *, unsigned *, unsigned, unsigned, const ush *, const ush *, struct huft **, int *); static int huft_free(struct inflate *, struct huft *); static int inflate_codes(struct inflate *, struct huft *, struct huft *, int, int); static int inflate_stored(struct inflate *); static int xinflate(struct inflate *); static int inflate_fixed(struct inflate *); static int inflate_dynamic(struct inflate *); static int inflate_block(struct inflate *, int *); /* The inflate algorithm uses a sliding 32K byte window on the uncompressed stream to find repeated byte strings. This is implemented here as a circular buffer. The index is updated simply by incrementing and then and'ing with 0x7fff (32K-1). */ /* It is left to other modules to supply the 32K area. It is assumed to be usable as if it were declared "uch slide[32768];" or as just "uch *slide;" and then malloc'ed in the latter case. The definition must be in unzip.h, included above. */ /* Tables for deflate from PKZIP's appnote.txt. */ /* Order of the bit length code lengths */ static const unsigned border[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; static const ush cplens[] = { /* Copy lengths for literal codes 257..285 */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; /* note: see note #13 above about the 258 in this list. */ static const ush cplext[] = { /* Extra bits for literal codes 257..285 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */ static const ush cpdist[] = { /* Copy offsets for distance codes 0..29 */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; static const ush cpdext[] = { /* Extra bits for distance codes */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; /* And'ing with mask[n] masks the lower n bits */ static const ush mask[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; /* Macros for inflate() bit peeking and grabbing. The usage is: NEEDBITS(glbl,j) x = b & mask[j]; DUMPBITS(j) where NEEDBITS makes sure that b has at least j bits in it, and DUMPBITS removes the bits from b. The macros use the variable k for the number of bits in b. Normally, b and k are register variables for speed, and are initialized at the beginning of a routine that uses these macros from a global bit buffer and count. In order to not ask for more bits than there are in the compressed stream, the Huffman tables are constructed to only ask for just enough bits to make up the end-of-block code (value 256). Then no bytes need to be "returned" to the buffer at the end of the last block. See the huft_build() routine. */ /* * The following 2 were global variables. * They are now fields of the inflate structure. */ #define NEEDBITS(glbl,n) { \ while(k<(n)) { \ int c=(*glbl->gz_input)(glbl->gz_private); \ if(c==GZ_EOF) \ return 1; \ b|=((ulg)c)<>=(n);k-=(n);} /* Huffman code decoding is performed using a multi-level table lookup. The fastest way to decode is to simply build a lookup table whose size is determined by the longest code. However, the time it takes to build this table can also be a factor if the data being decoded is not very long. The most common codes are necessarily the shortest codes, so those codes dominate the decoding time, and hence the speed. The idea is you can have a shorter table that decodes the shorter, more probable codes, and then point to subsidiary tables for the longer codes. The time it costs to decode the longer codes is then traded against the time it takes to make longer tables. This results of this trade are in the variables lbits and dbits below. lbits is the number of bits the first level table for literal/ length codes can decode in one step, and dbits is the same thing for the distance codes. Subsequent tables are also less than or equal to those sizes. These values may be adjusted either when all of the codes are shorter than that, in which case the longest code length in bits is used, or when the shortest code is *longer* than the requested table size, in which case the length of the shortest code in bits is used. There are two different values for the two tables, since they code a different number of possibilities each. The literal/length table codes 286 possible values, or in a flat code, a little over eight bits. The distance table codes 30 possible values, or a little less than five bits, flat. The optimum values for speed end up being about one bit more than those, so lbits is 8+1 and dbits is 5+1. The optimum values may differ though from machine to machine, and possibly even between compilers. Your mileage may vary. */ static const int lbits = 9; /* bits in base literal/length lookup table */ static const int dbits = 6; /* bits in base distance lookup table */ /* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ #define BMAX 16 /* maximum bit length of any code (16 for * explode) */ #define N_MAX 288 /* maximum number of codes in any set */ /* Given a list of code lengths and a maximum table size, make a set of tables to decode that set of codes. Return zero on success, one if the given code set is incomplete (the tables are still built in this case), two if the input is invalid (all zero length codes or an oversubscribed set of lengths), and three if not enough memory. The code with value 256 is special, and the tables are constructed so that no bits beyond that code are fetched when that code is decoded. */ /* * Arguments: * b code lengths in bits (all assumed <= BMAX) * n number of codes (assumed <= N_MAX) * s number of simple-valued codes (0..s-1) * d list of base values for non-simple codes * e list of extra bits for non-simple codes * t result: starting table * m maximum lookup bits, returns actual */ static int huft_build(struct inflate *glbl, unsigned *b, unsigned n, unsigned s, const ush *d, const ush *e, struct huft **t, int *m) { unsigned a; /* counter for codes of length k */ unsigned c[BMAX + 1]; /* bit length count table */ unsigned el; /* length of EOB code (value 256) */ unsigned f; /* i repeats in table every f entries */ int g; /* maximum code length */ int h; /* table level */ unsigned i; /* counter, current code */ unsigned j; /* counter */ int k; /* number of bits in current code */ int lx[BMAX + 1]; /* memory for l[-1..BMAX-1] */ int *l = lx + 1; /* stack of bits per table */ unsigned *p; /* pointer into c[], b[], or v[] */ struct huft *q; /* points to current table */ struct huft r; /* table entry for structure assignment */ struct huft *u[BMAX];/* table stack */ unsigned v[N_MAX]; /* values in order of bit length */ int w; /* bits before this table == (l * h) */ unsigned x[BMAX + 1]; /* bit offsets, then code stack */ unsigned *xp; /* pointer into x */ int y; /* number of dummy codes added */ unsigned z; /* number of entries in current table */ /* Generate counts for each bit length */ el = n > 256 ? b[256] : BMAX; /* set length of EOB code, if any */ #ifdef _KERNEL memzero((char *) c, sizeof(c)); #else for (i = 0; i < BMAX+1; i++) c [i] = 0; #endif p = b; i = n; do { c[*p]++; p++; /* assume all entries <= BMAX */ } while (--i); if (c[0] == n) { /* null input--all zero length codes */ *t = (struct huft *) NULL; *m = 0; return 0; } /* Find minimum and maximum length, bound *m by those */ for (j = 1; j <= BMAX; j++) if (c[j]) break; k = j; /* minimum code length */ if ((unsigned) *m < j) *m = j; for (i = BMAX; i; i--) if (c[i]) break; g = i; /* maximum code length */ if ((unsigned) *m > i) *m = i; /* Adjust last length count to fill out codes, if needed */ for (y = 1 << j; j < i; j++, y <<= 1) if ((y -= c[j]) < 0) return 2; /* bad input: more codes than bits */ if ((y -= c[i]) < 0) return 2; c[i] += y; /* Generate starting offsets into the value table for each length */ x[1] = j = 0; p = c + 1; xp = x + 2; while (--i) { /* note that i == g from above */ *xp++ = (j += *p++); } /* Make a table of values in order of bit lengths */ p = b; i = 0; do { if ((j = *p++) != 0) v[x[j]++] = i; } while (++i < n); /* Generate the Huffman codes and for each, make the table entries */ x[0] = i = 0; /* first Huffman code is zero */ p = v; /* grab values in bit order */ h = -1; /* no tables yet--level -1 */ w = l[-1] = 0; /* no bits decoded yet */ u[0] = (struct huft *) NULL; /* just to keep compilers happy */ q = (struct huft *) NULL; /* ditto */ z = 0; /* ditto */ /* go through the bit lengths (k already is bits in shortest code) */ for (; k <= g; k++) { a = c[k]; while (a--) { /* * here i is the Huffman code of length k bits for * value *p */ /* make tables up to required level */ while (k > w + l[h]) { w += l[h++]; /* add bits already decoded */ /* * compute minimum size table less than or * equal to *m bits */ z = (z = g - w) > (unsigned) *m ? *m : z; /* upper limit */ if ((f = 1 << (j = k - w)) > a + 1) { /* try a k-w bit table *//* t * oo few codes for k-w * bit table */ f -= a + 1; /* deduct codes from * patterns left */ xp = c + k; while (++j < z) { /* try smaller tables up * to z bits */ if ((f <<= 1) <= *++xp) break; /* enough codes to use * up j bits */ f -= *xp; /* else deduct codes * from patterns */ } } if ((unsigned) w + j > el && (unsigned) w < el) j = el - w; /* make EOB code end at * table */ z = 1 << j; /* table entries for j-bit * table */ l[h] = j; /* set table size in stack */ /* allocate and link in new table */ if ((q = (struct huft *) malloc((z + 1) * sizeof(struct huft), M_GZIP, M_WAITOK)) == (struct huft *) NULL) { if (h) huft_free(glbl, u[0]); return 3; /* not enough memory */ } glbl->gz_hufts += z + 1; /* track memory usage */ *t = q + 1; /* link to list for * huft_free() */ *(t = &(q->v.t)) = (struct huft *) NULL; u[h] = ++q; /* table starts after link */ /* connect to last table, if there is one */ if (h) { x[h] = i; /* save pattern for * backing up */ r.b = (uch) l[h - 1]; /* bits to dump before * this table */ r.e = (uch) (16 + j); /* bits in this table */ r.v.t = q; /* pointer to this table */ j = (i & ((1 << w) - 1)) >> (w - l[h - 1]); u[h - 1][j] = r; /* connect to last table */ } } /* set up table entry in r */ r.b = (uch) (k - w); if (p >= v + n) r.e = 99; /* out of values--invalid * code */ else if (*p < s) { r.e = (uch) (*p < 256 ? 16 : 15); /* 256 is end-of-block * code */ r.v.n = *p++; /* simple code is just the * value */ } else { r.e = (uch) e[*p - s]; /* non-simple--look up * in lists */ r.v.n = d[*p++ - s]; } /* fill code-like entries with r */ f = 1 << (k - w); for (j = i >> w; j < z; j += f) q[j] = r; /* backwards increment the k-bit code i */ for (j = 1 << (k - 1); i & j; j >>= 1) i ^= j; i ^= j; /* backup over finished tables */ while ((i & ((1 << w) - 1)) != x[h]) w -= l[--h]; /* don't need to update q */ } } /* return actual size of base table */ *m = l[0]; /* Return true (1) if we were given an incomplete table */ return y != 0 && g != 1; } /* * Arguments: * t table to free */ static int huft_free(struct inflate *glbl, struct huft *t) /* Free the malloc'ed tables built by huft_build(), which makes a linked list of the tables it made, with the links in a dummy first entry of each table. */ { struct huft *p, *q; /* Go through linked list, freeing from the malloced (t[-1]) address. */ p = t; while (p != (struct huft *) NULL) { q = (--p)->v.t; free(p, M_GZIP); p = q; } return 0; } /* inflate (decompress) the codes in a deflated (compressed) block. Return an error code or zero if it all goes ok. */ /* * Arguments: * tl, td literal/length and distance decoder tables * bl, bd number of bits decoded by tl[] and td[] */ static int inflate_codes(struct inflate *glbl, struct huft *tl, struct huft*td, int bl, int bd) { unsigned e; /* table entry flag/number of extra bits */ unsigned n, d; /* length and index for copy */ unsigned w; /* current window position */ struct huft *t; /* pointer to table entry */ unsigned ml, md; /* masks for bl and bd bits */ ulg b; /* bit buffer */ unsigned k; /* number of bits in bit buffer */ /* make local copies of globals */ b = glbl->gz_bb; /* initialize bit buffer */ k = glbl->gz_bk; w = glbl->gz_wp; /* initialize window position */ /* inflate the coded data */ ml = mask[bl]; /* precompute masks for speed */ md = mask[bd]; while (1) { /* do until end of block */ NEEDBITS(glbl, (unsigned) bl) if ((e = (t = tl + ((unsigned) b & ml))->e) > 16) do { if (e == 99) return 1; DUMPBITS(t->b) e -= 16; NEEDBITS(glbl, e) } while ((e = (t = t->v.t + ((unsigned) b & mask[e]))->e) > 16); DUMPBITS(t->b) if (e == 16) { /* then it's a literal */ glbl->gz_slide[w++] = (uch) t->v.n; if (w == GZ_WSIZE) { FLUSH(glbl, w); w = 0; } } else { /* it's an EOB or a length */ /* exit if end of block */ if (e == 15) break; /* get length of block to copy */ NEEDBITS(glbl, e) n = t->v.n + ((unsigned) b & mask[e]); DUMPBITS(e); /* decode distance of block to copy */ NEEDBITS(glbl, (unsigned) bd) if ((e = (t = td + ((unsigned) b & md))->e) > 16) do { if (e == 99) return 1; DUMPBITS(t->b) e -= 16; NEEDBITS(glbl, e) } while ((e = (t = t->v.t + ((unsigned) b & mask[e]))->e) > 16); DUMPBITS(t->b) NEEDBITS(glbl, e) d = w - t->v.n - ((unsigned) b & mask[e]); DUMPBITS(e) /* do the copy */ do { n -= (e = (e = GZ_WSIZE - ((d &= GZ_WSIZE - 1) > w ? d : w)) > n ? n : e); #ifndef NOMEMCPY if (w - d >= e) { /* (this test assumes * unsigned comparison) */ memcpy(glbl->gz_slide + w, glbl->gz_slide + d, e); w += e; d += e; } else /* do it slow to avoid memcpy() * overlap */ #endif /* !NOMEMCPY */ do { glbl->gz_slide[w++] = glbl->gz_slide[d++]; } while (--e); if (w == GZ_WSIZE) { FLUSH(glbl, w); w = 0; } } while (n); } } /* restore the globals from the locals */ glbl->gz_wp = w; /* restore global window pointer */ glbl->gz_bb = b; /* restore global bit buffer */ glbl->gz_bk = k; /* done */ return 0; } /* "decompress" an inflated type 0 (stored) block. */ static int inflate_stored(struct inflate *glbl) { unsigned n; /* number of bytes in block */ unsigned w; /* current window position */ ulg b; /* bit buffer */ unsigned k; /* number of bits in bit buffer */ /* make local copies of globals */ b = glbl->gz_bb; /* initialize bit buffer */ k = glbl->gz_bk; w = glbl->gz_wp; /* initialize window position */ /* go to byte boundary */ n = k & 7; DUMPBITS(n); /* get the length and its complement */ NEEDBITS(glbl, 16) n = ((unsigned) b & 0xffff); DUMPBITS(16) NEEDBITS(glbl, 16) if (n != (unsigned) ((~b) & 0xffff)) return 1; /* error in compressed data */ DUMPBITS(16) /* read and output the compressed data */ while (n--) { NEEDBITS(glbl, 8) glbl->gz_slide[w++] = (uch) b; if (w == GZ_WSIZE) { FLUSH(glbl, w); w = 0; } DUMPBITS(8) } /* restore the globals from the locals */ glbl->gz_wp = w; /* restore global window pointer */ glbl->gz_bb = b; /* restore global bit buffer */ glbl->gz_bk = k; return 0; } /* decompress an inflated type 1 (fixed Huffman codes) block. We should either replace this with a custom decoder, or at least precompute the Huffman tables. */ static int inflate_fixed(struct inflate *glbl) { /* if first time, set up tables for fixed blocks */ if (glbl->gz_fixed_tl == (struct huft *) NULL) { int i; /* temporary variable */ static unsigned l[288]; /* length list for huft_build */ /* literal table */ for (i = 0; i < 144; i++) l[i] = 8; for (; i < 256; i++) l[i] = 9; for (; i < 280; i++) l[i] = 7; for (; i < 288; i++) /* make a complete, but wrong code * set */ l[i] = 8; glbl->gz_fixed_bl = 7; if ((i = huft_build(glbl, l, 288, 257, cplens, cplext, &glbl->gz_fixed_tl, &glbl->gz_fixed_bl)) != 0) { glbl->gz_fixed_tl = (struct huft *) NULL; return i; } /* distance table */ for (i = 0; i < 30; i++) /* make an incomplete code * set */ l[i] = 5; glbl->gz_fixed_bd = 5; if ((i = huft_build(glbl, l, 30, 0, cpdist, cpdext, &glbl->gz_fixed_td, &glbl->gz_fixed_bd)) > 1) { huft_free(glbl, glbl->gz_fixed_tl); glbl->gz_fixed_tl = (struct huft *) NULL; return i; } } /* decompress until an end-of-block code */ return inflate_codes(glbl, glbl->gz_fixed_tl, glbl->gz_fixed_td, glbl->gz_fixed_bl, glbl->gz_fixed_bd) != 0; } /* decompress an inflated type 2 (dynamic Huffman codes) block. */ static int inflate_dynamic(struct inflate *glbl) { int i; /* temporary variables */ unsigned j; unsigned l; /* last length */ unsigned m; /* mask for bit lengths table */ unsigned n; /* number of lengths to get */ struct huft *tl; /* literal/length code table */ struct huft *td; /* distance code table */ int bl; /* lookup bits for tl */ int bd; /* lookup bits for td */ unsigned nb; /* number of bit length codes */ unsigned nl; /* number of literal/length codes */ unsigned nd; /* number of distance codes */ #ifdef PKZIP_BUG_WORKAROUND unsigned ll[288 + 32]; /* literal/length and distance code * lengths */ #else unsigned ll[286 + 30]; /* literal/length and distance code * lengths */ #endif ulg b; /* bit buffer */ unsigned k; /* number of bits in bit buffer */ /* make local bit buffer */ b = glbl->gz_bb; k = glbl->gz_bk; /* read in table lengths */ NEEDBITS(glbl, 5) nl = 257 + ((unsigned) b & 0x1f); /* number of * literal/length codes */ DUMPBITS(5) NEEDBITS(glbl, 5) nd = 1 + ((unsigned) b & 0x1f); /* number of distance codes */ DUMPBITS(5) NEEDBITS(glbl, 4) nb = 4 + ((unsigned) b & 0xf); /* number of bit length codes */ DUMPBITS(4) #ifdef PKZIP_BUG_WORKAROUND if (nl > 288 || nd > 32) #else if (nl > 286 || nd > 30) #endif return 1; /* bad lengths */ /* read in bit-length-code lengths */ for (j = 0; j < nb; j++) { NEEDBITS(glbl, 3) ll[border[j]] = (unsigned) b & 7; DUMPBITS(3) } for (; j < 19; j++) ll[border[j]] = 0; /* build decoding table for trees--single level, 7 bit lookup */ bl = 7; if ((i = huft_build(glbl, ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) { if (i == 1) huft_free(glbl, tl); return i; /* incomplete code set */ } /* read in literal and distance code lengths */ n = nl + nd; m = mask[bl]; i = l = 0; while ((unsigned) i < n) { NEEDBITS(glbl, (unsigned) bl) j = (td = tl + ((unsigned) b & m))->b; DUMPBITS(j) j = td->v.n; if (j < 16) /* length of code in bits (0..15) */ ll[i++] = l = j; /* save last length in l */ else if (j == 16) { /* repeat last length 3 to 6 times */ NEEDBITS(glbl, 2) j = 3 + ((unsigned) b & 3); DUMPBITS(2) if ((unsigned) i + j > n) return 1; while (j--) ll[i++] = l; } else if (j == 17) { /* 3 to 10 zero length codes */ NEEDBITS(glbl, 3) j = 3 + ((unsigned) b & 7); DUMPBITS(3) if ((unsigned) i + j > n) return 1; while (j--) ll[i++] = 0; l = 0; } else { /* j == 18: 11 to 138 zero length codes */ NEEDBITS(glbl, 7) j = 11 + ((unsigned) b & 0x7f); DUMPBITS(7) if ((unsigned) i + j > n) return 1; while (j--) ll[i++] = 0; l = 0; } } /* free decoding table for trees */ huft_free(glbl, tl); /* restore the global bit buffer */ glbl->gz_bb = b; glbl->gz_bk = k; /* build the decoding tables for literal/length and distance codes */ bl = lbits; i = huft_build(glbl, ll, nl, 257, cplens, cplext, &tl, &bl); if (i != 0) { if (i == 1 && !qflag) { FPRINTF("(incomplete l-tree) "); huft_free(glbl, tl); } return i; /* incomplete code set */ } bd = dbits; i = huft_build(glbl, ll + nl, nd, 0, cpdist, cpdext, &td, &bd); if (i != 0) { if (i == 1 && !qflag) { FPRINTF("(incomplete d-tree) "); #ifdef PKZIP_BUG_WORKAROUND i = 0; } #else huft_free(glbl, td); } huft_free(glbl, tl); return i; /* incomplete code set */ #endif } /* decompress until an end-of-block code */ if (inflate_codes(glbl, tl, td, bl, bd)) return 1; /* free the decoding tables, return */ huft_free(glbl, tl); huft_free(glbl, td); return 0; } /* decompress an inflated block */ /* * Arguments: * e last block flag */ static int inflate_block(struct inflate *glbl, int *e) { unsigned t; /* block type */ ulg b; /* bit buffer */ unsigned k; /* number of bits in bit buffer */ /* make local bit buffer */ b = glbl->gz_bb; k = glbl->gz_bk; /* read in last block bit */ NEEDBITS(glbl, 1) * e = (int) b & 1; DUMPBITS(1) /* read in block type */ NEEDBITS(glbl, 2) t = (unsigned) b & 3; DUMPBITS(2) /* restore the global bit buffer */ glbl->gz_bb = b; glbl->gz_bk = k; /* inflate that block type */ if (t == 2) return inflate_dynamic(glbl); if (t == 0) return inflate_stored(glbl); if (t == 1) return inflate_fixed(glbl); /* bad block type */ return 2; } /* decompress an inflated entry */ static int xinflate(struct inflate *glbl) { int e; /* last block flag */ int r; /* result code */ unsigned h; /* maximum struct huft's malloc'ed */ glbl->gz_fixed_tl = (struct huft *) NULL; /* initialize window, bit buffer */ glbl->gz_wp = 0; glbl->gz_bk = 0; glbl->gz_bb = 0; /* decompress until the last block */ h = 0; do { glbl->gz_hufts = 0; if ((r = inflate_block(glbl, &e)) != 0) return r; if (glbl->gz_hufts > h) h = glbl->gz_hufts; } while (!e); /* flush out slide */ FLUSH(glbl, glbl->gz_wp); /* return success */ return 0; } /* Nobody uses this - why not? */ int inflate(struct inflate *glbl) { int i; #ifdef _KERNEL u_char *p = NULL; if (!glbl->gz_slide) p = glbl->gz_slide = malloc(GZ_WSIZE, M_GZIP, M_WAITOK); #endif if (!glbl->gz_slide) #ifdef _KERNEL return(ENOMEM); #else return 3; /* kzip expects 3 */ #endif i = xinflate(glbl); if (glbl->gz_fixed_td != (struct huft *) NULL) { huft_free(glbl, glbl->gz_fixed_td); glbl->gz_fixed_td = (struct huft *) NULL; } if (glbl->gz_fixed_tl != (struct huft *) NULL) { huft_free(glbl, glbl->gz_fixed_tl); glbl->gz_fixed_tl = (struct huft *) NULL; } #ifdef _KERNEL if (p == glbl->gz_slide) { free(glbl->gz_slide, M_GZIP); glbl->gz_slide = NULL; } #endif return i; } /* ----------------------- END INFLATE.C */ Index: head/sys/kern/kern_tc.c =================================================================== --- head/sys/kern/kern_tc.c (revision 326407) +++ head/sys/kern/kern_tc.c (revision 326408) @@ -1,2201 +1,2203 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * Copyright (c) 2011, 2015, 2016 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Julien Ridoux at the University * of Melbourne under sponsorship from the FreeBSD Foundation. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_ntp.h" #include "opt_ffclock.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * A large step happens on boot. This constant detects such steps. * It is relatively small so that ntp_update_second gets called enough * in the typical 'missed a couple of seconds' case, but doesn't loop * forever when the time step is large. */ #define LARGE_STEP 200 /* * Implement a dummy timecounter which we can use until we get a real one * in the air. This allows the console and other early stuff to use * time services. */ static u_int dummy_get_timecount(struct timecounter *tc) { static u_int now; return (++now); } static struct timecounter dummy_timecounter = { dummy_get_timecount, 0, ~0u, 1000000, "dummy", -1000000 }; struct timehands { /* These fields must be initialized by the driver. */ struct timecounter *th_counter; int64_t th_adjustment; uint64_t th_scale; u_int th_offset_count; struct bintime th_offset; struct bintime th_bintime; struct timeval th_microtime; struct timespec th_nanotime; struct bintime th_boottime; /* Fields not to be copied in tc_windup start with th_generation. */ u_int th_generation; struct timehands *th_next; }; static struct timehands th0; static struct timehands th1 = { .th_next = &th0 }; static struct timehands th0 = { .th_counter = &dummy_timecounter, .th_scale = (uint64_t)-1 / 1000000, .th_offset = { .sec = 1 }, .th_generation = 1, .th_next = &th1 }; static struct timehands *volatile timehands = &th0; struct timecounter *timecounter = &dummy_timecounter; static struct timecounter *timecounters = &dummy_timecounter; int tc_min_ticktock_freq = 1; volatile time_t time_second = 1; volatile time_t time_uptime = 1; static int sysctl_kern_boottime(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_kern, KERN_BOOTTIME, boottime, CTLTYPE_STRUCT|CTLFLAG_RD, NULL, 0, sysctl_kern_boottime, "S,timeval", "System boottime"); SYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW, 0, ""); static SYSCTL_NODE(_kern_timecounter, OID_AUTO, tc, CTLFLAG_RW, 0, ""); static int timestepwarnings; SYSCTL_INT(_kern_timecounter, OID_AUTO, stepwarnings, CTLFLAG_RW, ×tepwarnings, 0, "Log time steps"); struct bintime bt_timethreshold; struct bintime bt_tickthreshold; sbintime_t sbt_timethreshold; sbintime_t sbt_tickthreshold; struct bintime tc_tick_bt; sbintime_t tc_tick_sbt; int tc_precexp; int tc_timepercentage = TC_DEFAULTPERC; static int sysctl_kern_timecounter_adjprecision(SYSCTL_HANDLER_ARGS); SYSCTL_PROC(_kern_timecounter, OID_AUTO, alloweddeviation, CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 0, 0, sysctl_kern_timecounter_adjprecision, "I", "Allowed time interval deviation in percents"); volatile int rtc_generation = 1; static int tc_chosen; /* Non-zero if a specific tc was chosen via sysctl. */ static void tc_windup(struct bintime *new_boottimebin); static void cpu_tick_calibrate(int); void dtrace_getnanotime(struct timespec *tsp); static int sysctl_kern_boottime(SYSCTL_HANDLER_ARGS) { struct timeval boottime; getboottime(&boottime); #ifndef __mips__ #ifdef SCTL_MASK32 int tv[2]; if (req->flags & SCTL_MASK32) { tv[0] = boottime.tv_sec; tv[1] = boottime.tv_usec; return (SYSCTL_OUT(req, tv, sizeof(tv))); } #endif #endif return (SYSCTL_OUT(req, &boottime, sizeof(boottime))); } static int sysctl_kern_timecounter_get(SYSCTL_HANDLER_ARGS) { u_int ncount; struct timecounter *tc = arg1; ncount = tc->tc_get_timecount(tc); return (sysctl_handle_int(oidp, &ncount, 0, req)); } static int sysctl_kern_timecounter_freq(SYSCTL_HANDLER_ARGS) { uint64_t freq; struct timecounter *tc = arg1; freq = tc->tc_frequency; return (sysctl_handle_64(oidp, &freq, 0, req)); } /* * Return the difference between the timehands' counter value now and what * was when we copied it to the timehands' offset_count. */ static __inline u_int tc_delta(struct timehands *th) { struct timecounter *tc; tc = th->th_counter; return ((tc->tc_get_timecount(tc) - th->th_offset_count) & tc->tc_counter_mask); } /* * Functions for reading the time. We have to loop until we are sure that * the timehands that we operated on was not updated under our feet. See * the comment in for a description of these 12 functions. */ #ifdef FFCLOCK void fbclock_binuptime(struct bintime *bt) { struct timehands *th; unsigned int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *bt = th->th_offset; bintime_addx(bt, th->th_scale * tc_delta(th)); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void fbclock_nanouptime(struct timespec *tsp) { struct bintime bt; fbclock_binuptime(&bt); bintime2timespec(&bt, tsp); } void fbclock_microuptime(struct timeval *tvp) { struct bintime bt; fbclock_binuptime(&bt); bintime2timeval(&bt, tvp); } void fbclock_bintime(struct bintime *bt) { struct timehands *th; unsigned int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *bt = th->th_bintime; bintime_addx(bt, th->th_scale * tc_delta(th)); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void fbclock_nanotime(struct timespec *tsp) { struct bintime bt; fbclock_bintime(&bt); bintime2timespec(&bt, tsp); } void fbclock_microtime(struct timeval *tvp) { struct bintime bt; fbclock_bintime(&bt); bintime2timeval(&bt, tvp); } void fbclock_getbinuptime(struct bintime *bt) { struct timehands *th; unsigned int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *bt = th->th_offset; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void fbclock_getnanouptime(struct timespec *tsp) { struct timehands *th; unsigned int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); bintime2timespec(&th->th_offset, tsp); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void fbclock_getmicrouptime(struct timeval *tvp) { struct timehands *th; unsigned int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); bintime2timeval(&th->th_offset, tvp); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void fbclock_getbintime(struct bintime *bt) { struct timehands *th; unsigned int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *bt = th->th_bintime; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void fbclock_getnanotime(struct timespec *tsp) { struct timehands *th; unsigned int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *tsp = th->th_nanotime; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void fbclock_getmicrotime(struct timeval *tvp) { struct timehands *th; unsigned int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *tvp = th->th_microtime; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } #else /* !FFCLOCK */ void binuptime(struct bintime *bt) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *bt = th->th_offset; bintime_addx(bt, th->th_scale * tc_delta(th)); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void nanouptime(struct timespec *tsp) { struct bintime bt; binuptime(&bt); bintime2timespec(&bt, tsp); } void microuptime(struct timeval *tvp) { struct bintime bt; binuptime(&bt); bintime2timeval(&bt, tvp); } void bintime(struct bintime *bt) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *bt = th->th_bintime; bintime_addx(bt, th->th_scale * tc_delta(th)); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void nanotime(struct timespec *tsp) { struct bintime bt; bintime(&bt); bintime2timespec(&bt, tsp); } void microtime(struct timeval *tvp) { struct bintime bt; bintime(&bt); bintime2timeval(&bt, tvp); } void getbinuptime(struct bintime *bt) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *bt = th->th_offset; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void getnanouptime(struct timespec *tsp) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); bintime2timespec(&th->th_offset, tsp); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void getmicrouptime(struct timeval *tvp) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); bintime2timeval(&th->th_offset, tvp); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void getbintime(struct bintime *bt) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *bt = th->th_bintime; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void getnanotime(struct timespec *tsp) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *tsp = th->th_nanotime; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } void getmicrotime(struct timeval *tvp) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *tvp = th->th_microtime; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } #endif /* FFCLOCK */ void getboottime(struct timeval *boottime) { struct bintime boottimebin; getboottimebin(&boottimebin); bintime2timeval(&boottimebin, boottime); } void getboottimebin(struct bintime *boottimebin) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *boottimebin = th->th_boottime; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } #ifdef FFCLOCK /* * Support for feed-forward synchronization algorithms. This is heavily inspired * by the timehands mechanism but kept independent from it. *_windup() functions * have some connection to avoid accessing the timecounter hardware more than * necessary. */ /* Feed-forward clock estimates kept updated by the synchronization daemon. */ struct ffclock_estimate ffclock_estimate; struct bintime ffclock_boottime; /* Feed-forward boot time estimate. */ uint32_t ffclock_status; /* Feed-forward clock status. */ int8_t ffclock_updated; /* New estimates are available. */ struct mtx ffclock_mtx; /* Mutex on ffclock_estimate. */ struct fftimehands { struct ffclock_estimate cest; struct bintime tick_time; struct bintime tick_time_lerp; ffcounter tick_ffcount; uint64_t period_lerp; volatile uint8_t gen; struct fftimehands *next; }; #define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) static struct fftimehands ffth[10]; static struct fftimehands *volatile fftimehands = ffth; static void ffclock_init(void) { struct fftimehands *cur; struct fftimehands *last; memset(ffth, 0, sizeof(ffth)); last = ffth + NUM_ELEMENTS(ffth) - 1; for (cur = ffth; cur < last; cur++) cur->next = cur + 1; last->next = ffth; ffclock_updated = 0; ffclock_status = FFCLOCK_STA_UNSYNC; mtx_init(&ffclock_mtx, "ffclock lock", NULL, MTX_DEF); } /* * Reset the feed-forward clock estimates. Called from inittodr() to get things * kick started and uses the timecounter nominal frequency as a first period * estimate. Note: this function may be called several time just after boot. * Note: this is the only function that sets the value of boot time for the * monotonic (i.e. uptime) version of the feed-forward clock. */ void ffclock_reset_clock(struct timespec *ts) { struct timecounter *tc; struct ffclock_estimate cest; tc = timehands->th_counter; memset(&cest, 0, sizeof(struct ffclock_estimate)); timespec2bintime(ts, &ffclock_boottime); timespec2bintime(ts, &(cest.update_time)); ffclock_read_counter(&cest.update_ffcount); cest.leapsec_next = 0; cest.period = ((1ULL << 63) / tc->tc_frequency) << 1; cest.errb_abs = 0; cest.errb_rate = 0; cest.status = FFCLOCK_STA_UNSYNC; cest.leapsec_total = 0; cest.leapsec = 0; mtx_lock(&ffclock_mtx); bcopy(&cest, &ffclock_estimate, sizeof(struct ffclock_estimate)); ffclock_updated = INT8_MAX; mtx_unlock(&ffclock_mtx); printf("ffclock reset: %s (%llu Hz), time = %ld.%09lu\n", tc->tc_name, (unsigned long long)tc->tc_frequency, (long)ts->tv_sec, (unsigned long)ts->tv_nsec); } /* * Sub-routine to convert a time interval measured in RAW counter units to time * in seconds stored in bintime format. * NOTE: bintime_mul requires u_int, but the value of the ffcounter may be * larger than the max value of u_int (on 32 bit architecture). Loop to consume * extra cycles. */ static void ffclock_convert_delta(ffcounter ffdelta, uint64_t period, struct bintime *bt) { struct bintime bt2; ffcounter delta, delta_max; delta_max = (1ULL << (8 * sizeof(unsigned int))) - 1; bintime_clear(bt); do { if (ffdelta > delta_max) delta = delta_max; else delta = ffdelta; bt2.sec = 0; bt2.frac = period; bintime_mul(&bt2, (unsigned int)delta); bintime_add(bt, &bt2); ffdelta -= delta; } while (ffdelta > 0); } /* * Update the fftimehands. * Push the tick ffcount and time(s) forward based on current clock estimate. * The conversion from ffcounter to bintime relies on the difference clock * principle, whose accuracy relies on computing small time intervals. If a new * clock estimate has been passed by the synchronisation daemon, make it * current, and compute the linear interpolation for monotonic time if needed. */ static void ffclock_windup(unsigned int delta) { struct ffclock_estimate *cest; struct fftimehands *ffth; struct bintime bt, gap_lerp; ffcounter ffdelta; uint64_t frac; unsigned int polling; uint8_t forward_jump, ogen; /* * Pick the next timehand, copy current ffclock estimates and move tick * times and counter forward. */ forward_jump = 0; ffth = fftimehands->next; ogen = ffth->gen; ffth->gen = 0; cest = &ffth->cest; bcopy(&fftimehands->cest, cest, sizeof(struct ffclock_estimate)); ffdelta = (ffcounter)delta; ffth->period_lerp = fftimehands->period_lerp; ffth->tick_time = fftimehands->tick_time; ffclock_convert_delta(ffdelta, cest->period, &bt); bintime_add(&ffth->tick_time, &bt); ffth->tick_time_lerp = fftimehands->tick_time_lerp; ffclock_convert_delta(ffdelta, ffth->period_lerp, &bt); bintime_add(&ffth->tick_time_lerp, &bt); ffth->tick_ffcount = fftimehands->tick_ffcount + ffdelta; /* * Assess the status of the clock, if the last update is too old, it is * likely the synchronisation daemon is dead and the clock is free * running. */ if (ffclock_updated == 0) { ffdelta = ffth->tick_ffcount - cest->update_ffcount; ffclock_convert_delta(ffdelta, cest->period, &bt); if (bt.sec > 2 * FFCLOCK_SKM_SCALE) ffclock_status |= FFCLOCK_STA_UNSYNC; } /* * If available, grab updated clock estimates and make them current. * Recompute time at this tick using the updated estimates. The clock * estimates passed the feed-forward synchronisation daemon may result * in time conversion that is not monotonically increasing (just after * the update). time_lerp is a particular linear interpolation over the * synchronisation algo polling period that ensures monotonicity for the * clock ids requesting it. */ if (ffclock_updated > 0) { bcopy(&ffclock_estimate, cest, sizeof(struct ffclock_estimate)); ffdelta = ffth->tick_ffcount - cest->update_ffcount; ffth->tick_time = cest->update_time; ffclock_convert_delta(ffdelta, cest->period, &bt); bintime_add(&ffth->tick_time, &bt); /* ffclock_reset sets ffclock_updated to INT8_MAX */ if (ffclock_updated == INT8_MAX) ffth->tick_time_lerp = ffth->tick_time; if (bintime_cmp(&ffth->tick_time, &ffth->tick_time_lerp, >)) forward_jump = 1; else forward_jump = 0; bintime_clear(&gap_lerp); if (forward_jump) { gap_lerp = ffth->tick_time; bintime_sub(&gap_lerp, &ffth->tick_time_lerp); } else { gap_lerp = ffth->tick_time_lerp; bintime_sub(&gap_lerp, &ffth->tick_time); } /* * The reset from the RTC clock may be far from accurate, and * reducing the gap between real time and interpolated time * could take a very long time if the interpolated clock insists * on strict monotonicity. The clock is reset under very strict * conditions (kernel time is known to be wrong and * synchronization daemon has been restarted recently. * ffclock_boottime absorbs the jump to ensure boot time is * correct and uptime functions stay consistent. */ if (((ffclock_status & FFCLOCK_STA_UNSYNC) == FFCLOCK_STA_UNSYNC) && ((cest->status & FFCLOCK_STA_UNSYNC) == 0) && ((cest->status & FFCLOCK_STA_WARMUP) == FFCLOCK_STA_WARMUP)) { if (forward_jump) bintime_add(&ffclock_boottime, &gap_lerp); else bintime_sub(&ffclock_boottime, &gap_lerp); ffth->tick_time_lerp = ffth->tick_time; bintime_clear(&gap_lerp); } ffclock_status = cest->status; ffth->period_lerp = cest->period; /* * Compute corrected period used for the linear interpolation of * time. The rate of linear interpolation is capped to 5000PPM * (5ms/s). */ if (bintime_isset(&gap_lerp)) { ffdelta = cest->update_ffcount; ffdelta -= fftimehands->cest.update_ffcount; ffclock_convert_delta(ffdelta, cest->period, &bt); polling = bt.sec; bt.sec = 0; bt.frac = 5000000 * (uint64_t)18446744073LL; bintime_mul(&bt, polling); if (bintime_cmp(&gap_lerp, &bt, >)) gap_lerp = bt; /* Approximate 1 sec by 1-(1/2^64) to ease arithmetic */ frac = 0; if (gap_lerp.sec > 0) { frac -= 1; frac /= ffdelta / gap_lerp.sec; } frac += gap_lerp.frac / ffdelta; if (forward_jump) ffth->period_lerp += frac; else ffth->period_lerp -= frac; } ffclock_updated = 0; } if (++ogen == 0) ogen = 1; ffth->gen = ogen; fftimehands = ffth; } /* * Adjust the fftimehands when the timecounter is changed. Stating the obvious, * the old and new hardware counter cannot be read simultaneously. tc_windup() * does read the two counters 'back to back', but a few cycles are effectively * lost, and not accumulated in tick_ffcount. This is a fairly radical * operation for a feed-forward synchronization daemon, and it is its job to not * pushing irrelevant data to the kernel. Because there is no locking here, * simply force to ignore pending or next update to give daemon a chance to * realize the counter has changed. */ static void ffclock_change_tc(struct timehands *th) { struct fftimehands *ffth; struct ffclock_estimate *cest; struct timecounter *tc; uint8_t ogen; tc = th->th_counter; ffth = fftimehands->next; ogen = ffth->gen; ffth->gen = 0; cest = &ffth->cest; bcopy(&(fftimehands->cest), cest, sizeof(struct ffclock_estimate)); cest->period = ((1ULL << 63) / tc->tc_frequency ) << 1; cest->errb_abs = 0; cest->errb_rate = 0; cest->status |= FFCLOCK_STA_UNSYNC; ffth->tick_ffcount = fftimehands->tick_ffcount; ffth->tick_time_lerp = fftimehands->tick_time_lerp; ffth->tick_time = fftimehands->tick_time; ffth->period_lerp = cest->period; /* Do not lock but ignore next update from synchronization daemon. */ ffclock_updated--; if (++ogen == 0) ogen = 1; ffth->gen = ogen; fftimehands = ffth; } /* * Retrieve feed-forward counter and time of last kernel tick. */ void ffclock_last_tick(ffcounter *ffcount, struct bintime *bt, uint32_t flags) { struct fftimehands *ffth; uint8_t gen; /* * No locking but check generation has not changed. Also need to make * sure ffdelta is positive, i.e. ffcount > tick_ffcount. */ do { ffth = fftimehands; gen = ffth->gen; if ((flags & FFCLOCK_LERP) == FFCLOCK_LERP) *bt = ffth->tick_time_lerp; else *bt = ffth->tick_time; *ffcount = ffth->tick_ffcount; } while (gen == 0 || gen != ffth->gen); } /* * Absolute clock conversion. Low level function to convert ffcounter to * bintime. The ffcounter is converted using the current ffclock period estimate * or the "interpolated period" to ensure monotonicity. * NOTE: this conversion may have been deferred, and the clock updated since the * hardware counter has been read. */ void ffclock_convert_abs(ffcounter ffcount, struct bintime *bt, uint32_t flags) { struct fftimehands *ffth; struct bintime bt2; ffcounter ffdelta; uint8_t gen; /* * No locking but check generation has not changed. Also need to make * sure ffdelta is positive, i.e. ffcount > tick_ffcount. */ do { ffth = fftimehands; gen = ffth->gen; if (ffcount > ffth->tick_ffcount) ffdelta = ffcount - ffth->tick_ffcount; else ffdelta = ffth->tick_ffcount - ffcount; if ((flags & FFCLOCK_LERP) == FFCLOCK_LERP) { *bt = ffth->tick_time_lerp; ffclock_convert_delta(ffdelta, ffth->period_lerp, &bt2); } else { *bt = ffth->tick_time; ffclock_convert_delta(ffdelta, ffth->cest.period, &bt2); } if (ffcount > ffth->tick_ffcount) bintime_add(bt, &bt2); else bintime_sub(bt, &bt2); } while (gen == 0 || gen != ffth->gen); } /* * Difference clock conversion. * Low level function to Convert a time interval measured in RAW counter units * into bintime. The difference clock allows measuring small intervals much more * reliably than the absolute clock. */ void ffclock_convert_diff(ffcounter ffdelta, struct bintime *bt) { struct fftimehands *ffth; uint8_t gen; /* No locking but check generation has not changed. */ do { ffth = fftimehands; gen = ffth->gen; ffclock_convert_delta(ffdelta, ffth->cest.period, bt); } while (gen == 0 || gen != ffth->gen); } /* * Access to current ffcounter value. */ void ffclock_read_counter(ffcounter *ffcount) { struct timehands *th; struct fftimehands *ffth; unsigned int gen, delta; /* * ffclock_windup() called from tc_windup(), safe to rely on * th->th_generation only, for correct delta and ffcounter. */ do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); ffth = fftimehands; delta = tc_delta(th); *ffcount = ffth->tick_ffcount; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); *ffcount += delta; } void binuptime(struct bintime *bt) { binuptime_fromclock(bt, sysclock_active); } void nanouptime(struct timespec *tsp) { nanouptime_fromclock(tsp, sysclock_active); } void microuptime(struct timeval *tvp) { microuptime_fromclock(tvp, sysclock_active); } void bintime(struct bintime *bt) { bintime_fromclock(bt, sysclock_active); } void nanotime(struct timespec *tsp) { nanotime_fromclock(tsp, sysclock_active); } void microtime(struct timeval *tvp) { microtime_fromclock(tvp, sysclock_active); } void getbinuptime(struct bintime *bt) { getbinuptime_fromclock(bt, sysclock_active); } void getnanouptime(struct timespec *tsp) { getnanouptime_fromclock(tsp, sysclock_active); } void getmicrouptime(struct timeval *tvp) { getmicrouptime_fromclock(tvp, sysclock_active); } void getbintime(struct bintime *bt) { getbintime_fromclock(bt, sysclock_active); } void getnanotime(struct timespec *tsp) { getnanotime_fromclock(tsp, sysclock_active); } void getmicrotime(struct timeval *tvp) { getmicrouptime_fromclock(tvp, sysclock_active); } #endif /* FFCLOCK */ /* * This is a clone of getnanotime and used for walltimestamps. * The dtrace_ prefix prevents fbt from creating probes for * it so walltimestamp can be safely used in all fbt probes. */ void dtrace_getnanotime(struct timespec *tsp) { struct timehands *th; u_int gen; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); *tsp = th->th_nanotime; atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); } /* * System clock currently providing time to the system. Modifiable via sysctl * when the FFCLOCK option is defined. */ int sysclock_active = SYSCLOCK_FBCK; /* Internal NTP status and error estimates. */ extern int time_status; extern long time_esterror; /* * Take a snapshot of sysclock data which can be used to compare system clocks * and generate timestamps after the fact. */ void sysclock_getsnapshot(struct sysclock_snap *clock_snap, int fast) { struct fbclock_info *fbi; struct timehands *th; struct bintime bt; unsigned int delta, gen; #ifdef FFCLOCK ffcounter ffcount; struct fftimehands *ffth; struct ffclock_info *ffi; struct ffclock_estimate cest; ffi = &clock_snap->ff_info; #endif fbi = &clock_snap->fb_info; delta = 0; do { th = timehands; gen = atomic_load_acq_int(&th->th_generation); fbi->th_scale = th->th_scale; fbi->tick_time = th->th_offset; #ifdef FFCLOCK ffth = fftimehands; ffi->tick_time = ffth->tick_time_lerp; ffi->tick_time_lerp = ffth->tick_time_lerp; ffi->period = ffth->cest.period; ffi->period_lerp = ffth->period_lerp; clock_snap->ffcount = ffth->tick_ffcount; cest = ffth->cest; #endif if (!fast) delta = tc_delta(th); atomic_thread_fence_acq(); } while (gen == 0 || gen != th->th_generation); clock_snap->delta = delta; clock_snap->sysclock_active = sysclock_active; /* Record feedback clock status and error. */ clock_snap->fb_info.status = time_status; /* XXX: Very crude estimate of feedback clock error. */ bt.sec = time_esterror / 1000000; bt.frac = ((time_esterror - bt.sec) * 1000000) * (uint64_t)18446744073709ULL; clock_snap->fb_info.error = bt; #ifdef FFCLOCK if (!fast) clock_snap->ffcount += delta; /* Record feed-forward clock leap second adjustment. */ ffi->leapsec_adjustment = cest.leapsec_total; if (clock_snap->ffcount > cest.leapsec_next) ffi->leapsec_adjustment -= cest.leapsec; /* Record feed-forward clock status and error. */ clock_snap->ff_info.status = cest.status; ffcount = clock_snap->ffcount - cest.update_ffcount; ffclock_convert_delta(ffcount, cest.period, &bt); /* 18446744073709 = int(2^64/1e12), err_bound_rate in [ps/s]. */ bintime_mul(&bt, cest.errb_rate * (uint64_t)18446744073709ULL); /* 18446744073 = int(2^64 / 1e9), since err_abs in [ns]. */ bintime_addx(&bt, cest.errb_abs * (uint64_t)18446744073ULL); clock_snap->ff_info.error = bt; #endif } /* * Convert a sysclock snapshot into a struct bintime based on the specified * clock source and flags. */ int sysclock_snap2bintime(struct sysclock_snap *cs, struct bintime *bt, int whichclock, uint32_t flags) { struct bintime boottimebin; #ifdef FFCLOCK struct bintime bt2; uint64_t period; #endif switch (whichclock) { case SYSCLOCK_FBCK: *bt = cs->fb_info.tick_time; /* If snapshot was created with !fast, delta will be >0. */ if (cs->delta > 0) bintime_addx(bt, cs->fb_info.th_scale * cs->delta); if ((flags & FBCLOCK_UPTIME) == 0) { getboottimebin(&boottimebin); bintime_add(bt, &boottimebin); } break; #ifdef FFCLOCK case SYSCLOCK_FFWD: if (flags & FFCLOCK_LERP) { *bt = cs->ff_info.tick_time_lerp; period = cs->ff_info.period_lerp; } else { *bt = cs->ff_info.tick_time; period = cs->ff_info.period; } /* If snapshot was created with !fast, delta will be >0. */ if (cs->delta > 0) { ffclock_convert_delta(cs->delta, period, &bt2); bintime_add(bt, &bt2); } /* Leap second adjustment. */ if (flags & FFCLOCK_LEAPSEC) bt->sec -= cs->ff_info.leapsec_adjustment; /* Boot time adjustment, for uptime/monotonic clocks. */ if (flags & FFCLOCK_UPTIME) bintime_sub(bt, &ffclock_boottime); break; #endif default: return (EINVAL); break; } return (0); } /* * Initialize a new timecounter and possibly use it. */ void tc_init(struct timecounter *tc) { u_int u; struct sysctl_oid *tc_root; u = tc->tc_frequency / tc->tc_counter_mask; /* XXX: We need some margin here, 10% is a guess */ u *= 11; u /= 10; if (u > hz && tc->tc_quality >= 0) { tc->tc_quality = -2000; if (bootverbose) { printf("Timecounter \"%s\" frequency %ju Hz", tc->tc_name, (uintmax_t)tc->tc_frequency); printf(" -- Insufficient hz, needs at least %u\n", u); } } else if (tc->tc_quality >= 0 || bootverbose) { printf("Timecounter \"%s\" frequency %ju Hz quality %d\n", tc->tc_name, (uintmax_t)tc->tc_frequency, tc->tc_quality); } tc->tc_next = timecounters; timecounters = tc; /* * Set up sysctl tree for this counter. */ tc_root = SYSCTL_ADD_NODE_WITH_LABEL(NULL, SYSCTL_STATIC_CHILDREN(_kern_timecounter_tc), OID_AUTO, tc->tc_name, CTLFLAG_RW, 0, "timecounter description", "timecounter"); SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, "mask", CTLFLAG_RD, &(tc->tc_counter_mask), 0, "mask for implemented bits"); SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, "counter", CTLTYPE_UINT | CTLFLAG_RD, tc, sizeof(*tc), sysctl_kern_timecounter_get, "IU", "current timecounter value"); SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, "frequency", CTLTYPE_U64 | CTLFLAG_RD, tc, sizeof(*tc), sysctl_kern_timecounter_freq, "QU", "timecounter frequency"); SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, "quality", CTLFLAG_RD, &(tc->tc_quality), 0, "goodness of time counter"); /* * Do not automatically switch if the current tc was specifically * chosen. Never automatically use a timecounter with negative quality. * Even though we run on the dummy counter, switching here may be * worse since this timecounter may not be monotonic. */ if (tc_chosen) return; if (tc->tc_quality < 0) return; if (tc->tc_quality < timecounter->tc_quality) return; if (tc->tc_quality == timecounter->tc_quality && tc->tc_frequency < timecounter->tc_frequency) return; (void)tc->tc_get_timecount(tc); (void)tc->tc_get_timecount(tc); timecounter = tc; } /* Report the frequency of the current timecounter. */ uint64_t tc_getfrequency(void) { return (timehands->th_counter->tc_frequency); } static bool sleeping_on_old_rtc(struct thread *td) { /* * td_rtcgen is modified by curthread when it is running, * and by other threads in this function. By finding the thread * on a sleepqueue and holding the lock on the sleepqueue * chain, we guarantee that the thread is not running and that * modifying td_rtcgen is safe. Setting td_rtcgen to zero informs * the thread that it was woken due to a real-time clock adjustment. * (The declaration of td_rtcgen refers to this comment.) */ if (td->td_rtcgen != 0 && td->td_rtcgen != rtc_generation) { td->td_rtcgen = 0; return (true); } return (false); } static struct mtx tc_setclock_mtx; MTX_SYSINIT(tc_setclock_init, &tc_setclock_mtx, "tcsetc", MTX_SPIN); /* * Step our concept of UTC. This is done by modifying our estimate of * when we booted. */ void tc_setclock(struct timespec *ts) { struct timespec tbef, taft; struct bintime bt, bt2; timespec2bintime(ts, &bt); nanotime(&tbef); mtx_lock_spin(&tc_setclock_mtx); cpu_tick_calibrate(1); binuptime(&bt2); bintime_sub(&bt, &bt2); /* XXX fiddle all the little crinkly bits around the fiords... */ tc_windup(&bt); mtx_unlock_spin(&tc_setclock_mtx); /* Avoid rtc_generation == 0, since td_rtcgen == 0 is special. */ atomic_add_rel_int(&rtc_generation, 2); sleepq_chains_remove_matching(sleeping_on_old_rtc); if (timestepwarnings) { nanotime(&taft); log(LOG_INFO, "Time stepped from %jd.%09ld to %jd.%09ld (%jd.%09ld)\n", (intmax_t)tbef.tv_sec, tbef.tv_nsec, (intmax_t)taft.tv_sec, taft.tv_nsec, (intmax_t)ts->tv_sec, ts->tv_nsec); } } /* * Initialize the next struct timehands in the ring and make * it the active timehands. Along the way we might switch to a different * timecounter and/or do seconds processing in NTP. Slightly magic. */ static void tc_windup(struct bintime *new_boottimebin) { struct bintime bt; struct timehands *th, *tho; uint64_t scale; u_int delta, ncount, ogen; int i; time_t t; /* * Make the next timehands a copy of the current one, but do * not overwrite the generation or next pointer. While we * update the contents, the generation must be zero. We need * to ensure that the zero generation is visible before the * data updates become visible, which requires release fence. * For similar reasons, re-reading of the generation after the * data is read should use acquire fence. */ tho = timehands; th = tho->th_next; ogen = th->th_generation; th->th_generation = 0; atomic_thread_fence_rel(); bcopy(tho, th, offsetof(struct timehands, th_generation)); if (new_boottimebin != NULL) th->th_boottime = *new_boottimebin; /* * Capture a timecounter delta on the current timecounter and if * changing timecounters, a counter value from the new timecounter. * Update the offset fields accordingly. */ delta = tc_delta(th); if (th->th_counter != timecounter) ncount = timecounter->tc_get_timecount(timecounter); else ncount = 0; #ifdef FFCLOCK ffclock_windup(delta); #endif th->th_offset_count += delta; th->th_offset_count &= th->th_counter->tc_counter_mask; while (delta > th->th_counter->tc_frequency) { /* Eat complete unadjusted seconds. */ delta -= th->th_counter->tc_frequency; th->th_offset.sec++; } if ((delta > th->th_counter->tc_frequency / 2) && (th->th_scale * delta < ((uint64_t)1 << 63))) { /* The product th_scale * delta just barely overflows. */ th->th_offset.sec++; } bintime_addx(&th->th_offset, th->th_scale * delta); /* * Hardware latching timecounters may not generate interrupts on * PPS events, so instead we poll them. There is a finite risk that * the hardware might capture a count which is later than the one we * got above, and therefore possibly in the next NTP second which might * have a different rate than the current NTP second. It doesn't * matter in practice. */ if (tho->th_counter->tc_poll_pps) tho->th_counter->tc_poll_pps(tho->th_counter); /* * Deal with NTP second processing. The for loop normally * iterates at most once, but in extreme situations it might * keep NTP sane if timeouts are not run for several seconds. * At boot, the time step can be large when the TOD hardware * has been read, so on really large steps, we call * ntp_update_second only twice. We need to call it twice in * case we missed a leap second. */ bt = th->th_offset; bintime_add(&bt, &th->th_boottime); i = bt.sec - tho->th_microtime.tv_sec; if (i > LARGE_STEP) i = 2; for (; i > 0; i--) { t = bt.sec; ntp_update_second(&th->th_adjustment, &bt.sec); if (bt.sec != t) th->th_boottime.sec += bt.sec - t; } /* Update the UTC timestamps used by the get*() functions. */ th->th_bintime = bt; bintime2timeval(&bt, &th->th_microtime); bintime2timespec(&bt, &th->th_nanotime); /* Now is a good time to change timecounters. */ if (th->th_counter != timecounter) { #ifndef __arm__ if ((timecounter->tc_flags & TC_FLAGS_C2STOP) != 0) cpu_disable_c2_sleep++; if ((th->th_counter->tc_flags & TC_FLAGS_C2STOP) != 0) cpu_disable_c2_sleep--; #endif th->th_counter = timecounter; th->th_offset_count = ncount; tc_min_ticktock_freq = max(1, timecounter->tc_frequency / (((uint64_t)timecounter->tc_counter_mask + 1) / 3)); #ifdef FFCLOCK ffclock_change_tc(th); #endif } /*- * Recalculate the scaling factor. We want the number of 1/2^64 * fractions of a second per period of the hardware counter, taking * into account the th_adjustment factor which the NTP PLL/adjtime(2) * processing provides us with. * * The th_adjustment is nanoseconds per second with 32 bit binary * fraction and we want 64 bit binary fraction of second: * * x = a * 2^32 / 10^9 = a * 4.294967296 * * The range of th_adjustment is +/- 5000PPM so inside a 64bit int * we can only multiply by about 850 without overflowing, that * leaves no suitably precise fractions for multiply before divide. * * Divide before multiply with a fraction of 2199/512 results in a * systematic undercompensation of 10PPM of th_adjustment. On a * 5000PPM adjustment this is a 0.05PPM error. This is acceptable. * * We happily sacrifice the lowest of the 64 bits of our result * to the goddess of code clarity. * */ scale = (uint64_t)1 << 63; scale += (th->th_adjustment / 1024) * 2199; scale /= th->th_counter->tc_frequency; th->th_scale = scale * 2; /* * Now that the struct timehands is again consistent, set the new * generation number, making sure to not make it zero. */ if (++ogen == 0) ogen = 1; atomic_store_rel_int(&th->th_generation, ogen); /* Go live with the new struct timehands. */ #ifdef FFCLOCK switch (sysclock_active) { case SYSCLOCK_FBCK: #endif time_second = th->th_microtime.tv_sec; time_uptime = th->th_offset.sec; #ifdef FFCLOCK break; case SYSCLOCK_FFWD: time_second = fftimehands->tick_time_lerp.sec; time_uptime = fftimehands->tick_time_lerp.sec - ffclock_boottime.sec; break; } #endif timehands = th; timekeep_push_vdso(); } /* Report or change the active timecounter hardware. */ static int sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS) { char newname[32]; struct timecounter *newtc, *tc; int error; tc = timecounter; strlcpy(newname, tc->tc_name, sizeof(newname)); error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req); if (error != 0 || req->newptr == NULL) return (error); /* Record that the tc in use now was specifically chosen. */ tc_chosen = 1; if (strcmp(newname, tc->tc_name) == 0) return (0); for (newtc = timecounters; newtc != NULL; newtc = newtc->tc_next) { if (strcmp(newname, newtc->tc_name) != 0) continue; /* Warm up new timecounter. */ (void)newtc->tc_get_timecount(newtc); (void)newtc->tc_get_timecount(newtc); timecounter = newtc; /* * The vdso timehands update is deferred until the next * 'tc_windup()'. * * This is prudent given that 'timekeep_push_vdso()' does not * use any locking and that it can be called in hard interrupt * context via 'tc_windup()'. */ return (0); } return (EINVAL); } SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, sysctl_kern_timecounter_hardware, "A", "Timecounter hardware selected"); /* Report the available timecounter hardware. */ static int sysctl_kern_timecounter_choice(SYSCTL_HANDLER_ARGS) { struct sbuf sb; struct timecounter *tc; int error; sbuf_new_for_sysctl(&sb, NULL, 0, req); for (tc = timecounters; tc != NULL; tc = tc->tc_next) { if (tc != timecounters) sbuf_putc(&sb, ' '); sbuf_printf(&sb, "%s(%d)", tc->tc_name, tc->tc_quality); } error = sbuf_finish(&sb); sbuf_delete(&sb); return (error); } SYSCTL_PROC(_kern_timecounter, OID_AUTO, choice, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_kern_timecounter_choice, "A", "Timecounter hardware detected"); /* * RFC 2783 PPS-API implementation. */ /* * Return true if the driver is aware of the abi version extensions in the * pps_state structure, and it supports at least the given abi version number. */ static inline int abi_aware(struct pps_state *pps, int vers) { return ((pps->kcmode & KCMODE_ABIFLAG) && pps->driver_abi >= vers); } static int pps_fetch(struct pps_fetch_args *fapi, struct pps_state *pps) { int err, timo; pps_seq_t aseq, cseq; struct timeval tv; if (fapi->tsformat && fapi->tsformat != PPS_TSFMT_TSPEC) return (EINVAL); /* * If no timeout is requested, immediately return whatever values were * most recently captured. If timeout seconds is -1, that's a request * to block without a timeout. WITNESS won't let us sleep forever * without a lock (we really don't need a lock), so just repeatedly * sleep a long time. */ if (fapi->timeout.tv_sec || fapi->timeout.tv_nsec) { if (fapi->timeout.tv_sec == -1) timo = 0x7fffffff; else { tv.tv_sec = fapi->timeout.tv_sec; tv.tv_usec = fapi->timeout.tv_nsec / 1000; timo = tvtohz(&tv); } aseq = pps->ppsinfo.assert_sequence; cseq = pps->ppsinfo.clear_sequence; while (aseq == pps->ppsinfo.assert_sequence && cseq == pps->ppsinfo.clear_sequence) { if (abi_aware(pps, 1) && pps->driver_mtx != NULL) { if (pps->flags & PPSFLAG_MTX_SPIN) { err = msleep_spin(pps, pps->driver_mtx, "ppsfch", timo); } else { err = msleep(pps, pps->driver_mtx, PCATCH, "ppsfch", timo); } } else { err = tsleep(pps, PCATCH, "ppsfch", timo); } if (err == EWOULDBLOCK) { if (fapi->timeout.tv_sec == -1) { continue; } else { return (ETIMEDOUT); } } else if (err != 0) { return (err); } } } pps->ppsinfo.current_mode = pps->ppsparam.mode; fapi->pps_info_buf = pps->ppsinfo; return (0); } int pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps) { pps_params_t *app; struct pps_fetch_args *fapi; #ifdef FFCLOCK struct pps_fetch_ffc_args *fapi_ffc; #endif #ifdef PPS_SYNC struct pps_kcbind_args *kapi; #endif KASSERT(pps != NULL, ("NULL pps pointer in pps_ioctl")); switch (cmd) { case PPS_IOC_CREATE: return (0); case PPS_IOC_DESTROY: return (0); case PPS_IOC_SETPARAMS: app = (pps_params_t *)data; if (app->mode & ~pps->ppscap) return (EINVAL); #ifdef FFCLOCK /* Ensure only a single clock is selected for ffc timestamp. */ if ((app->mode & PPS_TSCLK_MASK) == PPS_TSCLK_MASK) return (EINVAL); #endif pps->ppsparam = *app; return (0); case PPS_IOC_GETPARAMS: app = (pps_params_t *)data; *app = pps->ppsparam; app->api_version = PPS_API_VERS_1; return (0); case PPS_IOC_GETCAP: *(int*)data = pps->ppscap; return (0); case PPS_IOC_FETCH: fapi = (struct pps_fetch_args *)data; return (pps_fetch(fapi, pps)); #ifdef FFCLOCK case PPS_IOC_FETCH_FFCOUNTER: fapi_ffc = (struct pps_fetch_ffc_args *)data; if (fapi_ffc->tsformat && fapi_ffc->tsformat != PPS_TSFMT_TSPEC) return (EINVAL); if (fapi_ffc->timeout.tv_sec || fapi_ffc->timeout.tv_nsec) return (EOPNOTSUPP); pps->ppsinfo_ffc.current_mode = pps->ppsparam.mode; fapi_ffc->pps_info_buf_ffc = pps->ppsinfo_ffc; /* Overwrite timestamps if feedback clock selected. */ switch (pps->ppsparam.mode & PPS_TSCLK_MASK) { case PPS_TSCLK_FBCK: fapi_ffc->pps_info_buf_ffc.assert_timestamp = pps->ppsinfo.assert_timestamp; fapi_ffc->pps_info_buf_ffc.clear_timestamp = pps->ppsinfo.clear_timestamp; break; case PPS_TSCLK_FFWD: break; default: break; } return (0); #endif /* FFCLOCK */ case PPS_IOC_KCBIND: #ifdef PPS_SYNC kapi = (struct pps_kcbind_args *)data; /* XXX Only root should be able to do this */ if (kapi->tsformat && kapi->tsformat != PPS_TSFMT_TSPEC) return (EINVAL); if (kapi->kernel_consumer != PPS_KC_HARDPPS) return (EINVAL); if (kapi->edge & ~pps->ppscap) return (EINVAL); pps->kcmode = (kapi->edge & KCMODE_EDGEMASK) | (pps->kcmode & KCMODE_ABIFLAG); return (0); #else return (EOPNOTSUPP); #endif default: return (ENOIOCTL); } } void pps_init(struct pps_state *pps) { pps->ppscap |= PPS_TSFMT_TSPEC | PPS_CANWAIT; if (pps->ppscap & PPS_CAPTUREASSERT) pps->ppscap |= PPS_OFFSETASSERT; if (pps->ppscap & PPS_CAPTURECLEAR) pps->ppscap |= PPS_OFFSETCLEAR; #ifdef FFCLOCK pps->ppscap |= PPS_TSCLK_MASK; #endif pps->kcmode &= ~KCMODE_ABIFLAG; } void pps_init_abi(struct pps_state *pps) { pps_init(pps); if (pps->driver_abi > 0) { pps->kcmode |= KCMODE_ABIFLAG; pps->kernel_abi = PPS_ABI_VERSION; } } void pps_capture(struct pps_state *pps) { struct timehands *th; KASSERT(pps != NULL, ("NULL pps pointer in pps_capture")); th = timehands; pps->capgen = atomic_load_acq_int(&th->th_generation); pps->capth = th; #ifdef FFCLOCK pps->capffth = fftimehands; #endif pps->capcount = th->th_counter->tc_get_timecount(th->th_counter); atomic_thread_fence_acq(); if (pps->capgen != th->th_generation) pps->capgen = 0; } void pps_event(struct pps_state *pps, int event) { struct bintime bt; struct timespec ts, *tsp, *osp; u_int tcount, *pcount; int foff; pps_seq_t *pseq; #ifdef FFCLOCK struct timespec *tsp_ffc; pps_seq_t *pseq_ffc; ffcounter *ffcount; #endif #ifdef PPS_SYNC int fhard; #endif KASSERT(pps != NULL, ("NULL pps pointer in pps_event")); /* Nothing to do if not currently set to capture this event type. */ if ((event & pps->ppsparam.mode) == 0) return; /* If the timecounter was wound up underneath us, bail out. */ if (pps->capgen == 0 || pps->capgen != atomic_load_acq_int(&pps->capth->th_generation)) return; /* Things would be easier with arrays. */ if (event == PPS_CAPTUREASSERT) { tsp = &pps->ppsinfo.assert_timestamp; osp = &pps->ppsparam.assert_offset; foff = pps->ppsparam.mode & PPS_OFFSETASSERT; #ifdef PPS_SYNC fhard = pps->kcmode & PPS_CAPTUREASSERT; #endif pcount = &pps->ppscount[0]; pseq = &pps->ppsinfo.assert_sequence; #ifdef FFCLOCK ffcount = &pps->ppsinfo_ffc.assert_ffcount; tsp_ffc = &pps->ppsinfo_ffc.assert_timestamp; pseq_ffc = &pps->ppsinfo_ffc.assert_sequence; #endif } else { tsp = &pps->ppsinfo.clear_timestamp; osp = &pps->ppsparam.clear_offset; foff = pps->ppsparam.mode & PPS_OFFSETCLEAR; #ifdef PPS_SYNC fhard = pps->kcmode & PPS_CAPTURECLEAR; #endif pcount = &pps->ppscount[1]; pseq = &pps->ppsinfo.clear_sequence; #ifdef FFCLOCK ffcount = &pps->ppsinfo_ffc.clear_ffcount; tsp_ffc = &pps->ppsinfo_ffc.clear_timestamp; pseq_ffc = &pps->ppsinfo_ffc.clear_sequence; #endif } /* * If the timecounter changed, we cannot compare the count values, so * we have to drop the rest of the PPS-stuff until the next event. */ if (pps->ppstc != pps->capth->th_counter) { pps->ppstc = pps->capth->th_counter; *pcount = pps->capcount; pps->ppscount[2] = pps->capcount; return; } /* Convert the count to a timespec. */ tcount = pps->capcount - pps->capth->th_offset_count; tcount &= pps->capth->th_counter->tc_counter_mask; bt = pps->capth->th_bintime; bintime_addx(&bt, pps->capth->th_scale * tcount); bintime2timespec(&bt, &ts); /* If the timecounter was wound up underneath us, bail out. */ atomic_thread_fence_acq(); if (pps->capgen != pps->capth->th_generation) return; *pcount = pps->capcount; (*pseq)++; *tsp = ts; if (foff) { timespecadd(tsp, osp); if (tsp->tv_nsec < 0) { tsp->tv_nsec += 1000000000; tsp->tv_sec -= 1; } } #ifdef FFCLOCK *ffcount = pps->capffth->tick_ffcount + tcount; bt = pps->capffth->tick_time; ffclock_convert_delta(tcount, pps->capffth->cest.period, &bt); bintime_add(&bt, &pps->capffth->tick_time); bintime2timespec(&bt, &ts); (*pseq_ffc)++; *tsp_ffc = ts; #endif #ifdef PPS_SYNC if (fhard) { uint64_t scale; /* * Feed the NTP PLL/FLL. * The FLL wants to know how many (hardware) nanoseconds * elapsed since the previous event. */ tcount = pps->capcount - pps->ppscount[2]; pps->ppscount[2] = pps->capcount; tcount &= pps->capth->th_counter->tc_counter_mask; scale = (uint64_t)1 << 63; scale /= pps->capth->th_counter->tc_frequency; scale *= 2; bt.sec = 0; bt.frac = 0; bintime_addx(&bt, scale * tcount); bintime2timespec(&bt, &ts); hardpps(tsp, ts.tv_nsec + 1000000000 * ts.tv_sec); } #endif /* Wakeup anyone sleeping in pps_fetch(). */ wakeup(pps); } /* * Timecounters need to be updated every so often to prevent the hardware * counter from overflowing. Updating also recalculates the cached values * used by the get*() family of functions, so their precision depends on * the update frequency. */ static int tc_tick; SYSCTL_INT(_kern_timecounter, OID_AUTO, tick, CTLFLAG_RD, &tc_tick, 0, "Approximate number of hardclock ticks in a millisecond"); void tc_ticktock(int cnt) { static int count; if (mtx_trylock_spin(&tc_setclock_mtx)) { count += cnt; if (count >= tc_tick) { count = 0; tc_windup(NULL); } mtx_unlock_spin(&tc_setclock_mtx); } } static void __inline tc_adjprecision(void) { int t; if (tc_timepercentage > 0) { t = (99 + tc_timepercentage) / tc_timepercentage; tc_precexp = fls(t + (t >> 1)) - 1; FREQ2BT(hz / tc_tick, &bt_timethreshold); FREQ2BT(hz, &bt_tickthreshold); bintime_shift(&bt_timethreshold, tc_precexp); bintime_shift(&bt_tickthreshold, tc_precexp); } else { tc_precexp = 31; bt_timethreshold.sec = INT_MAX; bt_timethreshold.frac = ~(uint64_t)0; bt_tickthreshold = bt_timethreshold; } sbt_timethreshold = bttosbt(bt_timethreshold); sbt_tickthreshold = bttosbt(bt_tickthreshold); } static int sysctl_kern_timecounter_adjprecision(SYSCTL_HANDLER_ARGS) { int error, val; val = tc_timepercentage; error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); tc_timepercentage = val; if (cold) goto done; tc_adjprecision(); done: return (0); } static void inittimecounter(void *dummy) { u_int p; int tick_rate; /* * Set the initial timeout to * max(1, ). * People should probably not use the sysctl to set the timeout * to smaller than its initial value, since that value is the * smallest reasonable one. If they want better timestamps they * should use the non-"get"* functions. */ if (hz > 1000) tc_tick = (hz + 500) / 1000; else tc_tick = 1; tc_adjprecision(); FREQ2BT(hz, &tick_bt); tick_sbt = bttosbt(tick_bt); tick_rate = hz / tc_tick; FREQ2BT(tick_rate, &tc_tick_bt); tc_tick_sbt = bttosbt(tc_tick_bt); p = (tc_tick * 1000000) / hz; printf("Timecounters tick every %d.%03u msec\n", p / 1000, p % 1000); #ifdef FFCLOCK ffclock_init(); #endif /* warm up new timecounter (again) and get rolling. */ (void)timecounter->tc_get_timecount(timecounter); (void)timecounter->tc_get_timecount(timecounter); mtx_lock_spin(&tc_setclock_mtx); tc_windup(NULL); mtx_unlock_spin(&tc_setclock_mtx); } SYSINIT(timecounter, SI_SUB_CLOCKS, SI_ORDER_SECOND, inittimecounter, NULL); /* Cpu tick handling -------------------------------------------------*/ static int cpu_tick_variable; static uint64_t cpu_tick_frequency; static DPCPU_DEFINE(uint64_t, tc_cpu_ticks_base); static DPCPU_DEFINE(unsigned, tc_cpu_ticks_last); static uint64_t tc_cpu_ticks(void) { struct timecounter *tc; uint64_t res, *base; unsigned u, *last; critical_enter(); base = DPCPU_PTR(tc_cpu_ticks_base); last = DPCPU_PTR(tc_cpu_ticks_last); tc = timehands->th_counter; u = tc->tc_get_timecount(tc) & tc->tc_counter_mask; if (u < *last) *base += (uint64_t)tc->tc_counter_mask + 1; *last = u; res = u + *base; critical_exit(); return (res); } void cpu_tick_calibration(void) { static time_t last_calib; if (time_uptime != last_calib && !(time_uptime & 0xf)) { cpu_tick_calibrate(0); last_calib = time_uptime; } } /* * This function gets called every 16 seconds on only one designated * CPU in the system from hardclock() via cpu_tick_calibration()(). * * Whenever the real time clock is stepped we get called with reset=1 * to make sure we handle suspend/resume and similar events correctly. */ static void cpu_tick_calibrate(int reset) { static uint64_t c_last; uint64_t c_this, c_delta; static struct bintime t_last; struct bintime t_this, t_delta; uint32_t divi; if (reset) { /* The clock was stepped, abort & reset */ t_last.sec = 0; return; } /* we don't calibrate fixed rate cputicks */ if (!cpu_tick_variable) return; getbinuptime(&t_this); c_this = cpu_ticks(); if (t_last.sec != 0) { c_delta = c_this - c_last; t_delta = t_this; bintime_sub(&t_delta, &t_last); /* * Headroom: * 2^(64-20) / 16[s] = * 2^(44) / 16[s] = * 17.592.186.044.416 / 16 = * 1.099.511.627.776 [Hz] */ divi = t_delta.sec << 20; divi |= t_delta.frac >> (64 - 20); c_delta <<= 20; c_delta /= divi; if (c_delta > cpu_tick_frequency) { if (0 && bootverbose) printf("cpu_tick increased to %ju Hz\n", c_delta); cpu_tick_frequency = c_delta; } } c_last = c_this; t_last = t_this; } void set_cputicker(cpu_tick_f *func, uint64_t freq, unsigned var) { if (func == NULL) { cpu_ticks = tc_cpu_ticks; } else { cpu_tick_frequency = freq; cpu_tick_variable = var; cpu_ticks = func; } } uint64_t cpu_tickrate(void) { if (cpu_ticks == tc_cpu_ticks) return (tc_getfrequency()); return (cpu_tick_frequency); } /* * We need to be slightly careful converting cputicks to microseconds. * There is plenty of margin in 64 bits of microseconds (half a million * years) and in 64 bits at 4 GHz (146 years), but if we do a multiply * before divide conversion (to retain precision) we find that the * margin shrinks to 1.5 hours (one millionth of 146y). * With a three prong approach we never lose significant bits, no * matter what the cputick rate and length of timeinterval is. */ uint64_t cputick2usec(uint64_t tick) { if (tick > 18446744073709551LL) /* floor(2^64 / 1000) */ return (tick / (cpu_tickrate() / 1000000LL)); else if (tick > 18446744073709LL) /* floor(2^64 / 1000000) */ return ((tick * 1000LL) / (cpu_tickrate() / 1000LL)); else return ((tick * 1000000LL) / cpu_tickrate()); } cpu_tick_f *cpu_ticks = tc_cpu_ticks; static int vdso_th_enable = 1; static int sysctl_fast_gettime(SYSCTL_HANDLER_ARGS) { int old_vdso_th_enable, error; old_vdso_th_enable = vdso_th_enable; error = sysctl_handle_int(oidp, &old_vdso_th_enable, 0, req); if (error != 0) return (error); vdso_th_enable = old_vdso_th_enable; return (0); } SYSCTL_PROC(_kern_timecounter, OID_AUTO, fast_gettime, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0, sysctl_fast_gettime, "I", "Enable fast time of day"); uint32_t tc_fill_vdso_timehands(struct vdso_timehands *vdso_th) { struct timehands *th; uint32_t enabled; th = timehands; vdso_th->th_scale = th->th_scale; vdso_th->th_offset_count = th->th_offset_count; vdso_th->th_counter_mask = th->th_counter->tc_counter_mask; vdso_th->th_offset = th->th_offset; vdso_th->th_boottime = th->th_boottime; if (th->th_counter->tc_fill_vdso_timehands != NULL) { enabled = th->th_counter->tc_fill_vdso_timehands(vdso_th, th->th_counter); } else enabled = 0; if (!vdso_th_enable) enabled = 0; return (enabled); } #ifdef COMPAT_FREEBSD32 uint32_t tc_fill_vdso_timehands32(struct vdso_timehands32 *vdso_th32) { struct timehands *th; uint32_t enabled; th = timehands; *(uint64_t *)&vdso_th32->th_scale[0] = th->th_scale; vdso_th32->th_offset_count = th->th_offset_count; vdso_th32->th_counter_mask = th->th_counter->tc_counter_mask; vdso_th32->th_offset.sec = th->th_offset.sec; *(uint64_t *)&vdso_th32->th_offset.frac[0] = th->th_offset.frac; vdso_th32->th_boottime.sec = th->th_boottime.sec; *(uint64_t *)&vdso_th32->th_boottime.frac[0] = th->th_boottime.frac; if (th->th_counter->tc_fill_vdso_timehands32 != NULL) { enabled = th->th_counter->tc_fill_vdso_timehands32(vdso_th32, th->th_counter); } else enabled = 0; if (!vdso_th_enable) enabled = 0; return (enabled); } #endif Index: head/sys/kern/subr_disk.c =================================================================== --- head/sys/kern/subr_disk.c (revision 326407) +++ head/sys/kern/subr_disk.c (revision 326408) @@ -1,267 +1,269 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * The bioq_disksort() (and the specification of the bioq API) * have been written by Luigi Rizzo and Fabio Checconi under the same * license as above. */ #include __FBSDID("$FreeBSD$"); #include "opt_geom.h" #include #include #include #include #include #include /*- * Disk error is the preface to plaintive error messages * about failing disk transfers. It prints messages of the form * "hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347" * blkdone should be -1 if the position of the error is unknown. * The message is printed with printf. */ void disk_err(struct bio *bp, const char *what, int blkdone, int nl) { daddr_t sn; if (bp->bio_dev != NULL) printf("%s: %s ", devtoname(bp->bio_dev), what); else if (bp->bio_disk != NULL) printf("%s%d: %s ", bp->bio_disk->d_name, bp->bio_disk->d_unit, what); else printf("disk??: %s ", what); switch(bp->bio_cmd) { case BIO_READ: printf("cmd=read "); break; case BIO_WRITE: printf("cmd=write "); break; case BIO_DELETE: printf("cmd=delete "); break; case BIO_GETATTR: printf("cmd=getattr "); break; case BIO_FLUSH: printf("cmd=flush "); break; default: printf("cmd=%x ", bp->bio_cmd); break; } sn = bp->bio_pblkno; if (bp->bio_bcount <= DEV_BSIZE) { printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : ""); return; } if (blkdone >= 0) { sn += blkdone; printf("fsbn %jd of ", (intmax_t)sn); } printf("%jd-%jd", (intmax_t)bp->bio_pblkno, (intmax_t)(bp->bio_pblkno + (bp->bio_bcount - 1) / DEV_BSIZE)); if (nl) printf("\n"); } /* * BIO queue implementation * * Please read carefully the description below before making any change * to the code, or you might change the behaviour of the data structure * in undesirable ways. * * A bioq stores disk I/O request (bio), normally sorted according to * the distance of the requested position (bio->bio_offset) from the * current head position (bioq->last_offset) in the scan direction, i.e. * * (uoff_t)(bio_offset - last_offset) * * Note that the cast to unsigned (uoff_t) is fundamental to insure * that the distance is computed in the scan direction. * * The main methods for manipulating the bioq are: * * bioq_disksort() performs an ordered insertion; * * bioq_first() return the head of the queue, without removing; * * bioq_takefirst() return and remove the head of the queue, * updating the 'current head position' as * bioq->last_offset = bio->bio_offset + bio->bio_length; * * When updating the 'current head position', we assume that the result of * bioq_takefirst() is dispatched to the device, so bioq->last_offset * represents the head position once the request is complete. * * If the bioq is manipulated using only the above calls, it starts * with a sorted sequence of requests with bio_offset >= last_offset, * possibly followed by another sorted sequence of requests with * 0 <= bio_offset < bioq->last_offset * * NOTE: historical behaviour was to ignore bio->bio_length in the * update, but its use tracks the head position in a better way. * Historical behaviour was also to update the head position when * the request under service is complete, rather than when the * request is extracted from the queue. However, the current API * has no method to update the head position; secondly, once * a request has been submitted to the disk, we have no idea of * the actual head position, so the final one is our best guess. * * --- Direct queue manipulation --- * * A bioq uses an underlying TAILQ to store requests, so we also * export methods to manipulate the TAILQ, in particular: * * bioq_insert_tail() insert an entry at the end. * It also creates a 'barrier' so all subsequent * insertions through bioq_disksort() will end up * after this entry; * * bioq_insert_head() insert an entry at the head, update * bioq->last_offset = bio->bio_offset so that * all subsequent insertions through bioq_disksort() * will end up after this entry; * * bioq_remove() remove a generic element from the queue, act as * bioq_takefirst() if invoked on the head of the queue. * * The semantic of these methods is the same as the operations * on the underlying TAILQ, but with additional guarantees on * subsequent bioq_disksort() calls. E.g. bioq_insert_tail() * can be useful for making sure that all previous ops are flushed * to disk before continuing. * * Updating bioq->last_offset on a bioq_insert_head() guarantees * that the bio inserted with the last bioq_insert_head() will stay * at the head of the queue even after subsequent bioq_disksort(). * * Note that when the direct queue manipulation functions are used, * the queue may contain multiple inversion points (i.e. more than * two sorted sequences of requests). * */ void bioq_init(struct bio_queue_head *head) { TAILQ_INIT(&head->queue); head->last_offset = 0; head->insert_point = NULL; } void bioq_remove(struct bio_queue_head *head, struct bio *bp) { if (head->insert_point == NULL) { if (bp == TAILQ_FIRST(&head->queue)) head->last_offset = bp->bio_offset + bp->bio_length; } else if (bp == head->insert_point) head->insert_point = NULL; TAILQ_REMOVE(&head->queue, bp, bio_queue); } void bioq_flush(struct bio_queue_head *head, struct devstat *stp, int error) { struct bio *bp; while ((bp = bioq_takefirst(head)) != NULL) biofinish(bp, stp, error); } void bioq_insert_head(struct bio_queue_head *head, struct bio *bp) { if (head->insert_point == NULL) head->last_offset = bp->bio_offset; TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue); } void bioq_insert_tail(struct bio_queue_head *head, struct bio *bp) { TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue); head->insert_point = bp; head->last_offset = bp->bio_offset; } struct bio * bioq_first(struct bio_queue_head *head) { return (TAILQ_FIRST(&head->queue)); } struct bio * bioq_takefirst(struct bio_queue_head *head) { struct bio *bp; bp = TAILQ_FIRST(&head->queue); if (bp != NULL) bioq_remove(head, bp); return (bp); } /* * Compute the sorting key. The cast to unsigned is * fundamental for correctness, see the description * near the beginning of the file. */ static inline uoff_t bioq_bio_key(struct bio_queue_head *head, struct bio *bp) { return ((uoff_t)(bp->bio_offset - head->last_offset)); } /* * Seek sort for disks. * * Sort all requests in a single queue while keeping * track of the current position of the disk with last_offset. * See above for details. */ void bioq_disksort(struct bio_queue_head *head, struct bio *bp) { struct bio *cur, *prev; uoff_t key; if ((bp->bio_flags & BIO_ORDERED) != 0) { /* * Ordered transactions can only be dispatched * after any currently queued transactions. They * also have barrier semantics - no transactions * queued in the future can pass them. */ bioq_insert_tail(head, bp); return; } prev = NULL; key = bioq_bio_key(head, bp); cur = TAILQ_FIRST(&head->queue); if (head->insert_point) { prev = head->insert_point; cur = TAILQ_NEXT(head->insert_point, bio_queue); } while (cur != NULL && key >= bioq_bio_key(head, cur)) { prev = cur; cur = TAILQ_NEXT(cur, bio_queue); } if (prev == NULL) TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue); else TAILQ_INSERT_AFTER(&head->queue, prev, bp, bio_queue); } Index: head/sys/sys/disk.h =================================================================== --- head/sys/sys/disk.h (revision 326407) +++ head/sys/sys/disk.h (revision 326408) @@ -1,157 +1,159 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #ifndef _SYS_DISK_H_ #define _SYS_DISK_H_ #include #include #include #include #ifdef _KERNEL #ifndef _SYS_CONF_H_ #include /* XXX: temporary to avoid breakage */ #endif void disk_err(struct bio *bp, const char *what, int blkdone, int nl); #endif #define DIOCGSECTORSIZE _IOR('d', 128, u_int) /* * Get the sector size of the device in bytes. The sector size is the * smallest unit of data which can be transferred from this device. * Usually this is a power of 2 but it might not be (i.e. CDROM audio). */ #define DIOCGMEDIASIZE _IOR('d', 129, off_t) /* Get media size in bytes */ /* * Get the size of the entire device in bytes. This should be a * multiple of the sector size. */ #define DIOCGFWSECTORS _IOR('d', 130, u_int) /* Get firmware's sectorcount */ /* * Get the firmware's notion of number of sectors per track. This * value is mostly used for compatibility with various ill designed * disk label formats. Don't use it unless you have to. */ #define DIOCGFWHEADS _IOR('d', 131, u_int) /* Get firmware's headcount */ /* * Get the firmwares notion of number of heads per cylinder. This * value is mostly used for compatibility with various ill designed * disk label formats. Don't use it unless you have to. */ #define DIOCSKERNELDUMP_FREEBSD11 _IOW('d', 133, u_int) /* * Enable/Disable (the argument is boolean) the device for kernel * core dumps. */ #define DIOCGFRONTSTUFF _IOR('d', 134, off_t) /* * Many disk formats have some amount of space reserved at the * start of the disk to hold bootblocks, various disklabels and * similar stuff. This ioctl returns the number of such bytes * which may apply to the device. */ #define DIOCGFLUSH _IO('d', 135) /* Flush write cache */ /* * Flush write cache of the device. */ #define DIOCGDELETE _IOW('d', 136, off_t[2]) /* Delete data */ /* * Mark data on the device as unused. */ #define DISK_IDENT_SIZE 256 #define DIOCGIDENT _IOR('d', 137, char[DISK_IDENT_SIZE]) /*- * Get the ident of the given provider. Ident is (most of the time) * a uniqe and fixed provider's identifier. Ident's properties are as * follow: * - ident value is preserved between reboots, * - provider can be detached/attached and ident is preserved, * - provider's name can change - ident can't, * - ident value should not be based on on-disk metadata; in other * words copying whole data from one disk to another should not * yield the same ident for the other disk, * - there could be more than one provider with the same ident, but * only if they point at exactly the same physical storage, this is * the case for multipathing for example, * - GEOM classes that consumes single providers and provide single * providers, like geli, gbde, should just attach class name to the * ident of the underlying provider, * - ident is an ASCII string (is printable), * - ident is optional and applications can't relay on its presence. */ #define DIOCGPROVIDERNAME _IOR('d', 138, char[MAXPATHLEN]) /* * Store the provider name, given a device path, in a buffer. The buffer * must be at least MAXPATHLEN bytes long. */ #define DIOCGSTRIPESIZE _IOR('d', 139, off_t) /* Get stripe size in bytes */ /* * Get the size of the device's optimal access block in bytes. * This should be a multiple of the sector size. */ #define DIOCGSTRIPEOFFSET _IOR('d', 140, off_t) /* Get stripe offset in bytes */ /* * Get the offset of the first device's optimal access block in bytes. * This should be a multiple of the sector size. */ #define DIOCGPHYSPATH _IOR('d', 141, char[MAXPATHLEN]) /* * Get a string defining the physical path for a given provider. * This has similar rules to ident, but is intended to uniquely * identify the physical location of the device, not the current * occupant of that location. */ struct diocgattr_arg { char name[64]; int len; union { char str[DISK_IDENT_SIZE]; off_t off; int i; uint16_t u16; } value; }; #define DIOCGATTR _IOWR('d', 142, struct diocgattr_arg) #define DIOCZONECMD _IOWR('d', 143, struct disk_zone_args) struct diocskerneldump_arg { uint8_t kda_enable; uint8_t kda_compression; uint8_t kda_encryption; uint8_t kda_key[KERNELDUMP_KEY_MAX_SIZE]; uint32_t kda_encryptedkeysize; uint8_t *kda_encryptedkey; }; #define DIOCSKERNELDUMP _IOW('d', 144, struct diocskerneldump_arg) /* * Enable/Disable the device for kernel core dumps. */ #endif /* _SYS_DISK_H_ */ Index: head/sys/sys/smp.h =================================================================== --- head/sys/sys/smp.h (revision 326407) +++ head/sys/sys/smp.h (revision 326408) @@ -1,272 +1,274 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ */ #ifndef _SYS_SMP_H_ #define _SYS_SMP_H_ #ifdef _KERNEL #ifndef LOCORE #include #include /* * Types of nodes in the topological tree. */ typedef enum { /* No node has this type; can be used in topo API calls. */ TOPO_TYPE_DUMMY, /* Processing unit aka computing unit aka logical CPU. */ TOPO_TYPE_PU, /* Physical subdivision of a package. */ TOPO_TYPE_CORE, /* CPU L1/L2/L3 cache. */ TOPO_TYPE_CACHE, /* Package aka chip, equivalent to socket. */ TOPO_TYPE_PKG, /* NUMA node. */ TOPO_TYPE_NODE, /* Other logical or physical grouping of PUs. */ /* E.g. PUs on the same dye, or PUs sharing an FPU. */ TOPO_TYPE_GROUP, /* The whole system. */ TOPO_TYPE_SYSTEM } topo_node_type; /* Hardware indenitifier of a topology component. */ typedef unsigned int hwid_t; /* Logical CPU idenitifier. */ typedef int cpuid_t; /* A node in the topology. */ struct topo_node { struct topo_node *parent; TAILQ_HEAD(topo_children, topo_node) children; TAILQ_ENTRY(topo_node) siblings; cpuset_t cpuset; topo_node_type type; uintptr_t subtype; hwid_t hwid; cpuid_t id; int nchildren; int cpu_count; }; /* * Scheduling topology of a NUMA or SMP system. * * The top level topology is an array of pointers to groups. Each group * contains a bitmask of cpus in its group or subgroups. It may also * contain a pointer to an array of child groups. * * The bitmasks at non leaf groups may be used by consumers who support * a smaller depth than the hardware provides. * * The topology may be omitted by systems where all CPUs are equal. */ struct cpu_group { struct cpu_group *cg_parent; /* Our parent group. */ struct cpu_group *cg_child; /* Optional children groups. */ cpuset_t cg_mask; /* Mask of cpus in this group. */ int32_t cg_count; /* Count of cpus in this group. */ int16_t cg_children; /* Number of children groups. */ int8_t cg_level; /* Shared cache level. */ int8_t cg_flags; /* Traversal modifiers. */ }; typedef struct cpu_group *cpu_group_t; /* * Defines common resources for CPUs in the group. The highest level * resource should be used when multiple are shared. */ #define CG_SHARE_NONE 0 #define CG_SHARE_L1 1 #define CG_SHARE_L2 2 #define CG_SHARE_L3 3 #define MAX_CACHE_LEVELS CG_SHARE_L3 /* * Behavior modifiers for load balancing and affinity. */ #define CG_FLAG_HTT 0x01 /* Schedule the alternate core last. */ #define CG_FLAG_SMT 0x02 /* New age htt, less crippled. */ #define CG_FLAG_THREAD (CG_FLAG_HTT | CG_FLAG_SMT) /* Any threading. */ /* * Convenience routines for building and traversing topologies. */ #ifdef SMP void topo_init_node(struct topo_node *node); void topo_init_root(struct topo_node *root); struct topo_node * topo_add_node_by_hwid(struct topo_node *parent, int hwid, topo_node_type type, uintptr_t subtype); struct topo_node * topo_find_node_by_hwid(struct topo_node *parent, int hwid, topo_node_type type, uintptr_t subtype); void topo_promote_child(struct topo_node *child); struct topo_node * topo_next_node(struct topo_node *top, struct topo_node *node); struct topo_node * topo_next_nonchild_node(struct topo_node *top, struct topo_node *node); void topo_set_pu_id(struct topo_node *node, cpuid_t id); enum topo_level { TOPO_LEVEL_PKG = 0, /* * Some systems have useful sub-package core organizations. On these, * a package has one or more subgroups. Each subgroup contains one or * more cache groups (cores that share a last level cache). */ TOPO_LEVEL_GROUP, TOPO_LEVEL_CACHEGROUP, TOPO_LEVEL_CORE, TOPO_LEVEL_THREAD, TOPO_LEVEL_COUNT /* Must be last */ }; struct topo_analysis { int entities[TOPO_LEVEL_COUNT]; }; int topo_analyze(struct topo_node *topo_root, int all, struct topo_analysis *results); #define TOPO_FOREACH(i, root) \ for (i = root; i != NULL; i = topo_next_node(root, i)) struct cpu_group *smp_topo(void); struct cpu_group *smp_topo_alloc(u_int count); struct cpu_group *smp_topo_none(void); struct cpu_group *smp_topo_1level(int l1share, int l1count, int l1flags); struct cpu_group *smp_topo_2level(int l2share, int l2count, int l1share, int l1count, int l1flags); struct cpu_group *smp_topo_find(struct cpu_group *top, int cpu); extern void (*cpustop_restartfunc)(void); extern int smp_cpus; extern volatile cpuset_t started_cpus; extern volatile cpuset_t stopped_cpus; extern volatile cpuset_t suspended_cpus; extern cpuset_t hlt_cpus_mask; extern cpuset_t logical_cpus_mask; #endif /* SMP */ extern u_int mp_maxid; extern int mp_maxcpus; extern int mp_ncpus; extern volatile int smp_started; extern cpuset_t all_cpus; extern cpuset_t cpuset_domain[MAXMEMDOM]; /* CPUs in each NUMA domain. */ /* * Macro allowing us to determine whether a CPU is absent at any given * time, thus permitting us to configure sparse maps of cpuid-dependent * (per-CPU) structures. */ #define CPU_ABSENT(x_cpu) (!CPU_ISSET(x_cpu, &all_cpus)) /* * Macros to iterate over non-absent CPUs. CPU_FOREACH() takes an * integer iterator and iterates over the available set of CPUs. * CPU_FIRST() returns the id of the first non-absent CPU. CPU_NEXT() * returns the id of the next non-absent CPU. It will wrap back to * CPU_FIRST() once the end of the list is reached. The iterators are * currently implemented via inline functions. */ #define CPU_FOREACH(i) \ for ((i) = 0; (i) <= mp_maxid; (i)++) \ if (!CPU_ABSENT((i))) static __inline int cpu_first(void) { int i; for (i = 0;; i++) if (!CPU_ABSENT(i)) return (i); } static __inline int cpu_next(int i) { for (;;) { i++; if (i > mp_maxid) i = 0; if (!CPU_ABSENT(i)) return (i); } } #define CPU_FIRST() cpu_first() #define CPU_NEXT(i) cpu_next((i)) #ifdef SMP /* * Machine dependent functions used to initialize MP support. * * The cpu_mp_probe() should check to see if MP support is present and return * zero if it is not or non-zero if it is. If MP support is present, then * cpu_mp_start() will be called so that MP can be enabled. This function * should do things such as startup secondary processors. It should also * setup mp_ncpus, all_cpus, and smp_cpus. It should also ensure that * smp_started is initialized at the appropriate time. * Once cpu_mp_start() returns, machine independent MP startup code will be * executed and a simple message will be output to the console. Finally, * cpu_mp_announce() will be called so that machine dependent messages about * the MP support may be output to the console if desired. * * The cpu_setmaxid() function is called very early during the boot process * so that the MD code may set mp_maxid to provide an upper bound on CPU IDs * that other subsystems may use. If a platform is not able to determine * the exact maximum ID that early, then it may set mp_maxid to MAXCPU - 1. */ struct thread; struct cpu_group *cpu_topo(void); void cpu_mp_announce(void); int cpu_mp_probe(void); void cpu_mp_setmaxid(void); void cpu_mp_start(void); void forward_signal(struct thread *); int restart_cpus(cpuset_t); int stop_cpus(cpuset_t); int stop_cpus_hard(cpuset_t); #if defined(__amd64__) || defined(__i386__) int suspend_cpus(cpuset_t); int resume_cpus(cpuset_t); #endif void smp_rendezvous_action(void); extern struct mtx smp_ipi_mtx; #endif /* SMP */ int quiesce_all_cpus(const char *, int); int quiesce_cpus(cpuset_t, const char *, int); void smp_no_rendezvous_barrier(void *); void smp_rendezvous(void (*)(void *), void (*)(void *), void (*)(void *), void *arg); void smp_rendezvous_cpus(cpuset_t, void (*)(void *), void (*)(void *), void (*)(void *), void *arg); #endif /* !LOCORE */ #endif /* _KERNEL */ #endif /* _SYS_SMP_H_ */ Index: head/sys/sys/timepps.h =================================================================== --- head/sys/sys/timepps.h (revision 326407) +++ head/sys/sys/timepps.h (revision 326408) @@ -1,266 +1,268 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Julien Ridoux at the University * of Melbourne under sponsorship from the FreeBSD Foundation. * * $FreeBSD$ * * The is a FreeBSD version of the RFC 2783 API for Pulse Per Second * timing interfaces. */ #ifndef _SYS_TIMEPPS_H_ #define _SYS_TIMEPPS_H_ #include #include #include #define PPS_API_VERS_1 1 typedef int pps_handle_t; typedef unsigned pps_seq_t; typedef struct ntp_fp { unsigned int integral; unsigned int fractional; } ntp_fp_t; typedef union pps_timeu { struct timespec tspec; ntp_fp_t ntpfp; unsigned long longpad[3]; } pps_timeu_t; typedef struct { pps_seq_t assert_sequence; /* assert event seq # */ pps_seq_t clear_sequence; /* clear event seq # */ pps_timeu_t assert_tu; pps_timeu_t clear_tu; int current_mode; /* current mode bits */ } pps_info_t; typedef struct { pps_seq_t assert_sequence; /* assert event seq # */ pps_seq_t clear_sequence; /* clear event seq # */ pps_timeu_t assert_tu; pps_timeu_t clear_tu; ffcounter assert_ffcount; /* ffcounter on assert event */ ffcounter clear_ffcount; /* ffcounter on clear event */ int current_mode; /* current mode bits */ } pps_info_ffc_t; #define assert_timestamp assert_tu.tspec #define clear_timestamp clear_tu.tspec #define assert_timestamp_ntpfp assert_tu.ntpfp #define clear_timestamp_ntpfp clear_tu.ntpfp typedef struct { int api_version; /* API version # */ int mode; /* mode bits */ pps_timeu_t assert_off_tu; pps_timeu_t clear_off_tu; } pps_params_t; #define assert_offset assert_off_tu.tspec #define clear_offset clear_off_tu.tspec #define assert_offset_ntpfp assert_off_tu.ntpfp #define clear_offset_ntpfp clear_off_tu.ntpfp #define PPS_CAPTUREASSERT 0x01 #define PPS_CAPTURECLEAR 0x02 #define PPS_CAPTUREBOTH 0x03 #define PPS_OFFSETASSERT 0x10 #define PPS_OFFSETCLEAR 0x20 #define PPS_ECHOASSERT 0x40 #define PPS_ECHOCLEAR 0x80 #define PPS_CANWAIT 0x100 #define PPS_CANPOLL 0x200 #define PPS_TSFMT_TSPEC 0x1000 #define PPS_TSFMT_NTPFP 0x2000 #define PPS_TSCLK_FBCK 0x10000 #define PPS_TSCLK_FFWD 0x20000 #define PPS_TSCLK_MASK 0x30000 #define PPS_KC_HARDPPS 0 #define PPS_KC_HARDPPS_PLL 1 #define PPS_KC_HARDPPS_FLL 2 struct pps_fetch_args { int tsformat; pps_info_t pps_info_buf; struct timespec timeout; }; struct pps_fetch_ffc_args { int tsformat; pps_info_ffc_t pps_info_buf_ffc; struct timespec timeout; }; struct pps_kcbind_args { int kernel_consumer; int edge; int tsformat; }; #define PPS_IOC_CREATE _IO('1', 1) #define PPS_IOC_DESTROY _IO('1', 2) #define PPS_IOC_SETPARAMS _IOW('1', 3, pps_params_t) #define PPS_IOC_GETPARAMS _IOR('1', 4, pps_params_t) #define PPS_IOC_GETCAP _IOR('1', 5, int) #define PPS_IOC_FETCH _IOWR('1', 6, struct pps_fetch_args) #define PPS_IOC_KCBIND _IOW('1', 7, struct pps_kcbind_args) #define PPS_IOC_FETCH_FFCOUNTER _IOWR('1', 8, struct pps_fetch_ffc_args) #ifdef _KERNEL struct mtx; #define KCMODE_EDGEMASK 0x03 #define KCMODE_ABIFLAG 0x80000000 /* Internal use: abi-aware driver. */ #define PPS_ABI_VERSION 1 #define PPSFLAG_MTX_SPIN 0x01 /* Driver mtx is MTX_SPIN type. */ struct pps_state { /* Capture information. */ struct timehands *capth; struct fftimehands *capffth; unsigned capgen; unsigned capcount; /* State information. */ pps_params_t ppsparam; pps_info_t ppsinfo; pps_info_ffc_t ppsinfo_ffc; int kcmode; int ppscap; struct timecounter *ppstc; unsigned ppscount[3]; /* * The following fields are valid if the driver calls pps_init_abi(). */ uint16_t driver_abi; /* Driver sets before pps_init_abi(). */ uint16_t kernel_abi; /* Kernel sets during pps_init_abi(). */ struct mtx *driver_mtx; /* Optional, valid if non-NULL. */ uint32_t flags; }; void pps_capture(struct pps_state *pps); void pps_event(struct pps_state *pps, int event); void pps_init(struct pps_state *pps); void pps_init_abi(struct pps_state *pps); int pps_ioctl(unsigned long cmd, caddr_t data, struct pps_state *pps); void hardpps(struct timespec *tsp, long nsec); #else /* !_KERNEL */ static __inline int time_pps_create(int filedes, pps_handle_t *handle) { int error; *handle = -1; error = ioctl(filedes, PPS_IOC_CREATE, 0); if (error < 0) return (-1); *handle = filedes; return (0); } static __inline int time_pps_destroy(pps_handle_t handle) { return (ioctl(handle, PPS_IOC_DESTROY, 0)); } static __inline int time_pps_setparams(pps_handle_t handle, const pps_params_t *ppsparams) { return (ioctl(handle, PPS_IOC_SETPARAMS, ppsparams)); } static __inline int time_pps_getparams(pps_handle_t handle, pps_params_t *ppsparams) { return (ioctl(handle, PPS_IOC_GETPARAMS, ppsparams)); } static __inline int time_pps_getcap(pps_handle_t handle, int *mode) { return (ioctl(handle, PPS_IOC_GETCAP, mode)); } static __inline int time_pps_fetch(pps_handle_t handle, const int tsformat, pps_info_t *ppsinfobuf, const struct timespec *timeout) { int error; struct pps_fetch_args arg; arg.tsformat = tsformat; if (timeout == NULL) { arg.timeout.tv_sec = -1; arg.timeout.tv_nsec = -1; } else arg.timeout = *timeout; error = ioctl(handle, PPS_IOC_FETCH, &arg); *ppsinfobuf = arg.pps_info_buf; return (error); } static __inline int time_pps_fetch_ffc(pps_handle_t handle, const int tsformat, pps_info_ffc_t *ppsinfobuf, const struct timespec *timeout) { struct pps_fetch_ffc_args arg; int error; arg.tsformat = tsformat; if (timeout == NULL) { arg.timeout.tv_sec = -1; arg.timeout.tv_nsec = -1; } else { arg.timeout = *timeout; } error = ioctl(handle, PPS_IOC_FETCH_FFCOUNTER, &arg); *ppsinfobuf = arg.pps_info_buf_ffc; return (error); } static __inline int time_pps_kcbind(pps_handle_t handle, const int kernel_consumer, const int edge, const int tsformat) { struct pps_kcbind_args arg; arg.kernel_consumer = kernel_consumer; arg.edge = edge; arg.tsformat = tsformat; return (ioctl(handle, PPS_IOC_KCBIND, &arg)); } #endif /* KERNEL */ #endif /* !_SYS_TIMEPPS_H_ */ Index: head/sys/x86/include/x86_smp.h =================================================================== --- head/sys/x86/include/x86_smp.h (revision 326407) +++ head/sys/x86/include/x86_smp.h (revision 326408) @@ -1,106 +1,108 @@ /*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #ifndef _X86_X86_SMP_H_ #define _X86_X86_SMP_H_ #include #include #include #include #include struct pmap; /* global data in mp_x86.c */ extern int mp_naps; extern int boot_cpu_id; extern struct pcb stoppcbs[]; extern int cpu_apic_ids[]; extern int bootAP; extern void *dpcpu; extern char *bootSTK; extern void *bootstacks[]; extern volatile u_int cpu_ipi_pending[]; extern volatile int aps_ready; extern struct mtx ap_boot_mtx; extern int cpu_logical; extern int cpu_cores; extern volatile uint32_t smp_tlb_generation; extern struct pmap *smp_tlb_pmap; extern u_int xhits_gbl[]; extern u_int xhits_pg[]; extern u_int xhits_rng[]; extern u_int ipi_global; extern u_int ipi_page; extern u_int ipi_range; extern u_int ipi_range_size; extern int nmi_kdb_lock; extern int nmi_is_broadcast; struct cpu_info { int cpu_present:1; int cpu_bsp:1; int cpu_disabled:1; int cpu_hyperthread:1; }; extern struct cpu_info *cpu_info; #ifdef COUNT_IPIS extern u_long *ipi_invltlb_counts[MAXCPU]; extern u_long *ipi_invlrng_counts[MAXCPU]; extern u_long *ipi_invlpg_counts[MAXCPU]; extern u_long *ipi_invlcache_counts[MAXCPU]; extern u_long *ipi_rendezvous_counts[MAXCPU]; #endif /* IPI handlers */ inthand_t IDTVEC(invltlb), /* TLB shootdowns - global */ IDTVEC(invlpg), /* TLB shootdowns - 1 page */ IDTVEC(invlrng), /* TLB shootdowns - page range */ IDTVEC(invlcache), /* Write back and invalidate cache */ IDTVEC(ipi_intr_bitmap_handler), /* Bitmap based IPIs */ IDTVEC(cpustop), /* CPU stops & waits to be restarted */ IDTVEC(cpususpend), /* CPU suspends & waits to be resumed */ IDTVEC(rendezvous); /* handle CPU rendezvous */ /* functions in x86_mp.c */ void assign_cpu_ids(void); void cpu_add(u_int apic_id, char boot_cpu); void cpustop_handler(void); void cpususpend_handler(void); void init_secondary_tail(void); void invltlb_handler(void); void invlpg_handler(void); void invlrng_handler(void); void invlcache_handler(void); void init_secondary(void); void ipi_startup(int apic_id, int vector); void ipi_all_but_self(u_int ipi); void ipi_bitmap_handler(struct trapframe frame); void ipi_cpu(int cpu, u_int ipi); int ipi_nmi_handler(void); void ipi_selected(cpuset_t cpus, u_int ipi); u_int mp_bootaddress(u_int); void set_interrupt_apic_ids(void); void smp_cache_flush(void); void smp_masked_invlpg(cpuset_t mask, vm_offset_t addr); void smp_masked_invlpg_range(cpuset_t mask, vm_offset_t startva, vm_offset_t endva); void smp_masked_invltlb(cpuset_t mask, struct pmap *pmap); void mem_range_AP_init(void); void topo_probe(void); void ipi_send_cpu(int cpu, u_int ipi); #endif Index: head/tools/tools/kernxref/kernxref.sh =================================================================== --- head/tools/tools/kernxref/kernxref.sh (revision 326407) +++ head/tools/tools/kernxref/kernxref.sh (revision 326408) @@ -1,158 +1,160 @@ : # +# SPDX-License-Identifier: Beerware +# # ---------------------------------------------------------------------------- # "THE BEER-WARE LICENSE" (Revision 42): # wrote this file. As long as you retain this notice you # can do whatever you want with this stuff. If we meet some day, and you think # this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp # ---------------------------------------------------------------------------- # # Sort options by "Matthew Emmerton" # # $FreeBSD$ # # This shell script will make a cross reference of the symbols of a kernel. # COMPILEDIR=/sys/`uname -m`/compile KERNELNAME=LINT SORTORDER=-k1 args=`getopt h?k:s: $*`; if [ $? != 0 ] then args="-h"; fi set -- $args; for i do case "$i" in -h|-\?) echo "Usage: $0 [ -k ] [ -s [ 'symbol' | 'filename' ] ]"; exit 0; ;; -k) KERNELNAME=$2 if [ -d ${COMPILEDIR}/${KERNELNAME} ]; then shift; shift; continue; fi echo "Kernel '$KERNELNAME' does not exist in ${COMPILEDIR}!"; exit 1; ;; -s) if [ "x$2" = "xsymbol" ] then SORTORDER=-k1 shift; shift; continue; fi if [ "x$2" = "xfilename" ] then SORTORDER=-k2 shift; shift; continue; fi echo "Invalid selection for -s: $2"; exit 1; ;; --) shift; break; ;; esac done cd ${COMPILEDIR}/${KERNELNAME} MOD_OBJS=`find modules -name \*.o` for i in *.o $MOD_OBJS do nm -gon $i done | sed ' /aicasm.*:/d /genassym.*:/d s/.*\/// s/:/ / ' | awk ' NF > 1 { if ($2 == "t") next if ($2 == "F") next if ($2 == "U") { ref[$3]=ref[$3]" "$1 nm[$3]++ } else if ($3 == "D" || $3 == "T" || $3 == "B" || $3 == "R" || $3 == "A") { if (def[$4] != "") def[$4]=def[$4]" "$1 else def[$4]=$1 nm[$4]++ } else if ($2 == "?") { if (def[$3] == "S") i++ else if (def[$3] != "") def[$3]=def[$3]",S" else def[$3]="S" ref[$3]=ref[$3]" "$1 nm[$3]++ } else if ($2 == "C") { if (def[$3] == $2) i++ else if (def[$3] == "") def[$3]=$1 else ref[$3]=ref[$3]" "$1 nm[$3]++ } else { print ">>>",$0 } } END { for (i in nm) { printf "%s {%s} %s\n",i,def[i],ref[i] } } ' | sort $SORTORDER | awk ' { if ($2 == "{S}") $2 = "" if (length($3) == 0) { printf "%-31s %d %s\tUNREF\n",$1,0, $2 N1++ } else if ($2 == "{}") { printf "%-31s %d {UNDEF}\n",$1, NF-2 N2++ } else { printf "%-31s %d %s",$1,NF-2,$2 p = 80; for (i = 3 ; i <= NF; i++) { if (p+length ($i)+1 > 48) { printf "\n\t\t\t\t\t%s", $i p = 7; } else { printf " %s", $i } p += 1 + length ($i) } printf "\n" N3++ if (NF-2 == 1) N4++ if (NF-2 == 2) N5++ } } END { printf "Total symbols: %5d\n",N1+N2+N3 printf "unref symbols: %5d\n",N1 printf "undef symbols: %5d\n",N2 printf "1 ref symbols: %5d\n",N4 printf "2 ref symbols: %5d\n",N5 } ' Index: head/usr.bin/ministat/ministat.c =================================================================== --- head/usr.bin/ministat/ministat.c (revision 326407) +++ head/usr.bin/ministat/ministat.c (revision 326408) @@ -1,656 +1,658 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #define NSTUDENT 100 #define NCONF 6 static double const studentpct[] = { 80, 90, 95, 98, 99, 99.5 }; static double student[NSTUDENT + 1][NCONF] = { /* inf */ { 1.282, 1.645, 1.960, 2.326, 2.576, 3.090 }, /* 1. */ { 3.078, 6.314, 12.706, 31.821, 63.657, 318.313 }, /* 2. */ { 1.886, 2.920, 4.303, 6.965, 9.925, 22.327 }, /* 3. */ { 1.638, 2.353, 3.182, 4.541, 5.841, 10.215 }, /* 4. */ { 1.533, 2.132, 2.776, 3.747, 4.604, 7.173 }, /* 5. */ { 1.476, 2.015, 2.571, 3.365, 4.032, 5.893 }, /* 6. */ { 1.440, 1.943, 2.447, 3.143, 3.707, 5.208 }, /* 7. */ { 1.415, 1.895, 2.365, 2.998, 3.499, 4.782 }, /* 8. */ { 1.397, 1.860, 2.306, 2.896, 3.355, 4.499 }, /* 9. */ { 1.383, 1.833, 2.262, 2.821, 3.250, 4.296 }, /* 10. */ { 1.372, 1.812, 2.228, 2.764, 3.169, 4.143 }, /* 11. */ { 1.363, 1.796, 2.201, 2.718, 3.106, 4.024 }, /* 12. */ { 1.356, 1.782, 2.179, 2.681, 3.055, 3.929 }, /* 13. */ { 1.350, 1.771, 2.160, 2.650, 3.012, 3.852 }, /* 14. */ { 1.345, 1.761, 2.145, 2.624, 2.977, 3.787 }, /* 15. */ { 1.341, 1.753, 2.131, 2.602, 2.947, 3.733 }, /* 16. */ { 1.337, 1.746, 2.120, 2.583, 2.921, 3.686 }, /* 17. */ { 1.333, 1.740, 2.110, 2.567, 2.898, 3.646 }, /* 18. */ { 1.330, 1.734, 2.101, 2.552, 2.878, 3.610 }, /* 19. */ { 1.328, 1.729, 2.093, 2.539, 2.861, 3.579 }, /* 20. */ { 1.325, 1.725, 2.086, 2.528, 2.845, 3.552 }, /* 21. */ { 1.323, 1.721, 2.080, 2.518, 2.831, 3.527 }, /* 22. */ { 1.321, 1.717, 2.074, 2.508, 2.819, 3.505 }, /* 23. */ { 1.319, 1.714, 2.069, 2.500, 2.807, 3.485 }, /* 24. */ { 1.318, 1.711, 2.064, 2.492, 2.797, 3.467 }, /* 25. */ { 1.316, 1.708, 2.060, 2.485, 2.787, 3.450 }, /* 26. */ { 1.315, 1.706, 2.056, 2.479, 2.779, 3.435 }, /* 27. */ { 1.314, 1.703, 2.052, 2.473, 2.771, 3.421 }, /* 28. */ { 1.313, 1.701, 2.048, 2.467, 2.763, 3.408 }, /* 29. */ { 1.311, 1.699, 2.045, 2.462, 2.756, 3.396 }, /* 30. */ { 1.310, 1.697, 2.042, 2.457, 2.750, 3.385 }, /* 31. */ { 1.309, 1.696, 2.040, 2.453, 2.744, 3.375 }, /* 32. */ { 1.309, 1.694, 2.037, 2.449, 2.738, 3.365 }, /* 33. */ { 1.308, 1.692, 2.035, 2.445, 2.733, 3.356 }, /* 34. */ { 1.307, 1.691, 2.032, 2.441, 2.728, 3.348 }, /* 35. */ { 1.306, 1.690, 2.030, 2.438, 2.724, 3.340 }, /* 36. */ { 1.306, 1.688, 2.028, 2.434, 2.719, 3.333 }, /* 37. */ { 1.305, 1.687, 2.026, 2.431, 2.715, 3.326 }, /* 38. */ { 1.304, 1.686, 2.024, 2.429, 2.712, 3.319 }, /* 39. */ { 1.304, 1.685, 2.023, 2.426, 2.708, 3.313 }, /* 40. */ { 1.303, 1.684, 2.021, 2.423, 2.704, 3.307 }, /* 41. */ { 1.303, 1.683, 2.020, 2.421, 2.701, 3.301 }, /* 42. */ { 1.302, 1.682, 2.018, 2.418, 2.698, 3.296 }, /* 43. */ { 1.302, 1.681, 2.017, 2.416, 2.695, 3.291 }, /* 44. */ { 1.301, 1.680, 2.015, 2.414, 2.692, 3.286 }, /* 45. */ { 1.301, 1.679, 2.014, 2.412, 2.690, 3.281 }, /* 46. */ { 1.300, 1.679, 2.013, 2.410, 2.687, 3.277 }, /* 47. */ { 1.300, 1.678, 2.012, 2.408, 2.685, 3.273 }, /* 48. */ { 1.299, 1.677, 2.011, 2.407, 2.682, 3.269 }, /* 49. */ { 1.299, 1.677, 2.010, 2.405, 2.680, 3.265 }, /* 50. */ { 1.299, 1.676, 2.009, 2.403, 2.678, 3.261 }, /* 51. */ { 1.298, 1.675, 2.008, 2.402, 2.676, 3.258 }, /* 52. */ { 1.298, 1.675, 2.007, 2.400, 2.674, 3.255 }, /* 53. */ { 1.298, 1.674, 2.006, 2.399, 2.672, 3.251 }, /* 54. */ { 1.297, 1.674, 2.005, 2.397, 2.670, 3.248 }, /* 55. */ { 1.297, 1.673, 2.004, 2.396, 2.668, 3.245 }, /* 56. */ { 1.297, 1.673, 2.003, 2.395, 2.667, 3.242 }, /* 57. */ { 1.297, 1.672, 2.002, 2.394, 2.665, 3.239 }, /* 58. */ { 1.296, 1.672, 2.002, 2.392, 2.663, 3.237 }, /* 59. */ { 1.296, 1.671, 2.001, 2.391, 2.662, 3.234 }, /* 60. */ { 1.296, 1.671, 2.000, 2.390, 2.660, 3.232 }, /* 61. */ { 1.296, 1.670, 2.000, 2.389, 2.659, 3.229 }, /* 62. */ { 1.295, 1.670, 1.999, 2.388, 2.657, 3.227 }, /* 63. */ { 1.295, 1.669, 1.998, 2.387, 2.656, 3.225 }, /* 64. */ { 1.295, 1.669, 1.998, 2.386, 2.655, 3.223 }, /* 65. */ { 1.295, 1.669, 1.997, 2.385, 2.654, 3.220 }, /* 66. */ { 1.295, 1.668, 1.997, 2.384, 2.652, 3.218 }, /* 67. */ { 1.294, 1.668, 1.996, 2.383, 2.651, 3.216 }, /* 68. */ { 1.294, 1.668, 1.995, 2.382, 2.650, 3.214 }, /* 69. */ { 1.294, 1.667, 1.995, 2.382, 2.649, 3.213 }, /* 70. */ { 1.294, 1.667, 1.994, 2.381, 2.648, 3.211 }, /* 71. */ { 1.294, 1.667, 1.994, 2.380, 2.647, 3.209 }, /* 72. */ { 1.293, 1.666, 1.993, 2.379, 2.646, 3.207 }, /* 73. */ { 1.293, 1.666, 1.993, 2.379, 2.645, 3.206 }, /* 74. */ { 1.293, 1.666, 1.993, 2.378, 2.644, 3.204 }, /* 75. */ { 1.293, 1.665, 1.992, 2.377, 2.643, 3.202 }, /* 76. */ { 1.293, 1.665, 1.992, 2.376, 2.642, 3.201 }, /* 77. */ { 1.293, 1.665, 1.991, 2.376, 2.641, 3.199 }, /* 78. */ { 1.292, 1.665, 1.991, 2.375, 2.640, 3.198 }, /* 79. */ { 1.292, 1.664, 1.990, 2.374, 2.640, 3.197 }, /* 80. */ { 1.292, 1.664, 1.990, 2.374, 2.639, 3.195 }, /* 81. */ { 1.292, 1.664, 1.990, 2.373, 2.638, 3.194 }, /* 82. */ { 1.292, 1.664, 1.989, 2.373, 2.637, 3.193 }, /* 83. */ { 1.292, 1.663, 1.989, 2.372, 2.636, 3.191 }, /* 84. */ { 1.292, 1.663, 1.989, 2.372, 2.636, 3.190 }, /* 85. */ { 1.292, 1.663, 1.988, 2.371, 2.635, 3.189 }, /* 86. */ { 1.291, 1.663, 1.988, 2.370, 2.634, 3.188 }, /* 87. */ { 1.291, 1.663, 1.988, 2.370, 2.634, 3.187 }, /* 88. */ { 1.291, 1.662, 1.987, 2.369, 2.633, 3.185 }, /* 89. */ { 1.291, 1.662, 1.987, 2.369, 2.632, 3.184 }, /* 90. */ { 1.291, 1.662, 1.987, 2.368, 2.632, 3.183 }, /* 91. */ { 1.291, 1.662, 1.986, 2.368, 2.631, 3.182 }, /* 92. */ { 1.291, 1.662, 1.986, 2.368, 2.630, 3.181 }, /* 93. */ { 1.291, 1.661, 1.986, 2.367, 2.630, 3.180 }, /* 94. */ { 1.291, 1.661, 1.986, 2.367, 2.629, 3.179 }, /* 95. */ { 1.291, 1.661, 1.985, 2.366, 2.629, 3.178 }, /* 96. */ { 1.290, 1.661, 1.985, 2.366, 2.628, 3.177 }, /* 97. */ { 1.290, 1.661, 1.985, 2.365, 2.627, 3.176 }, /* 98. */ { 1.290, 1.661, 1.984, 2.365, 2.627, 3.175 }, /* 99. */ { 1.290, 1.660, 1.984, 2.365, 2.626, 3.175 }, /* 100. */ { 1.290, 1.660, 1.984, 2.364, 2.626, 3.174 } }; #define MAX_DS 8 static char symbol[MAX_DS] = { ' ', 'x', '+', '*', '%', '#', '@', 'O' }; struct dataset { char *name; double *points; unsigned lpoints; double sy, syy; unsigned n; }; static struct dataset * NewSet(void) { struct dataset *ds; ds = calloc(1, sizeof *ds); ds->lpoints = 100000; ds->points = calloc(sizeof *ds->points, ds->lpoints); return(ds); } static void AddPoint(struct dataset *ds, double a) { double *dp; if (ds->n >= ds->lpoints) { dp = ds->points; ds->lpoints *= 4; ds->points = calloc(sizeof *ds->points, ds->lpoints); memcpy(ds->points, dp, sizeof *dp * ds->n); free(dp); } ds->points[ds->n++] = a; ds->sy += a; ds->syy += a * a; } static double Min(struct dataset *ds) { return (ds->points[0]); } static double Max(struct dataset *ds) { return (ds->points[ds->n -1]); } static double Avg(struct dataset *ds) { return(ds->sy / ds->n); } static double Median(struct dataset *ds) { if ((ds->n % 2) == 0) return ((ds->points[ds->n / 2] + (ds->points[(ds->n / 2) - 1])) / 2); else return (ds->points[ds->n / 2]); } static double Var(struct dataset *ds) { return (ds->syy - ds->sy * ds->sy / ds->n) / (ds->n - 1.0); } static double Stddev(struct dataset *ds) { return sqrt(Var(ds)); } static void VitalsHead(void) { printf(" N Min Max Median Avg Stddev\n"); } static void Vitals(struct dataset *ds, int flag) { printf("%c %3d %13.8g %13.8g %13.8g %13.8g %13.8g", symbol[flag], ds->n, Min(ds), Max(ds), Median(ds), Avg(ds), Stddev(ds)); printf("\n"); } static void Relative(struct dataset *ds, struct dataset *rs, int confidx) { double spool, s, d, e, t; double re; int i; i = ds->n + rs->n - 2; if (i > NSTUDENT) t = student[0][confidx]; else t = student[i][confidx]; spool = (ds->n - 1) * Var(ds) + (rs->n - 1) * Var(rs); spool /= ds->n + rs->n - 2; spool = sqrt(spool); s = spool * sqrt(1.0 / ds->n + 1.0 / rs->n); d = Avg(ds) - Avg(rs); e = t * s; re = (ds->n - 1) * Var(ds) + (rs->n - 1) * Var(rs) * (Avg(ds) * Avg(ds)) / (Avg(rs) * Avg(rs)); re *= (ds->n + rs->n) / (ds->n * rs->n * (ds->n + rs->n - 2.0)); re = t * sqrt(re); if (fabs(d) > e) { printf("Difference at %.1f%% confidence\n", studentpct[confidx]); printf(" %g +/- %g\n", d, e); printf(" %g%% +/- %g%%\n", d * 100 / Avg(rs), re * 100 / Avg(rs)); printf(" (Student's t, pooled s = %g)\n", spool); } else { printf("No difference proven at %.1f%% confidence\n", studentpct[confidx]); } } struct plot { double min; double max; double span; int width; double x0, dx; int height; char *data; char **bar; int separate_bars; int num_datasets; }; static struct plot plot; static void SetupPlot(int width, int separate, int num_datasets) { struct plot *pl; pl = &plot; pl->width = width; pl->height = 0; pl->data = NULL; pl->bar = NULL; pl->separate_bars = separate; pl->num_datasets = num_datasets; pl->min = 999e99; pl->max = -999e99; } static void AdjPlot(double a) { struct plot *pl; pl = &plot; if (a < pl->min) pl->min = a; if (a > pl->max) pl->max = a; pl->span = pl->max - pl->min; pl->dx = pl->span / (pl->width - 1.0); pl->x0 = pl->min - .5 * pl->dx; } static void DimPlot(struct dataset *ds) { AdjPlot(Min(ds)); AdjPlot(Max(ds)); AdjPlot(Avg(ds) - Stddev(ds)); AdjPlot(Avg(ds) + Stddev(ds)); } static void PlotSet(struct dataset *ds, int val) { struct plot *pl; int i, j, m, x; unsigned n; int bar; pl = &plot; if (pl->span == 0) return; if (pl->separate_bars) bar = val-1; else bar = 0; if (pl->bar == NULL) pl->bar = calloc(sizeof(char *), pl->num_datasets); if (pl->bar[bar] == NULL) { pl->bar[bar] = malloc(pl->width); memset(pl->bar[bar], 0, pl->width); } m = 1; i = -1; j = 0; for (n = 0; n < ds->n; n++) { x = (ds->points[n] - pl->x0) / pl->dx; if (x == i) { j++; if (j > m) m = j; } else { j = 1; i = x; } } m += 1; if (m > pl->height) { pl->data = realloc(pl->data, pl->width * m); memset(pl->data + pl->height * pl->width, 0, (m - pl->height) * pl->width); } pl->height = m; i = -1; for (n = 0; n < ds->n; n++) { x = (ds->points[n] - pl->x0) / pl->dx; if (x == i) { j++; } else { j = 1; i = x; } pl->data[j * pl->width + x] |= val; } if (!isnan(Stddev(ds))) { x = ((Avg(ds) - Stddev(ds)) - pl->x0) / pl->dx; m = ((Avg(ds) + Stddev(ds)) - pl->x0) / pl->dx; pl->bar[bar][m] = '|'; pl->bar[bar][x] = '|'; for (i = x + 1; i < m; i++) if (pl->bar[bar][i] == 0) pl->bar[bar][i] = '_'; } x = (Median(ds) - pl->x0) / pl->dx; pl->bar[bar][x] = 'M'; x = (Avg(ds) - pl->x0) / pl->dx; pl->bar[bar][x] = 'A'; } static void DumpPlot(void) { struct plot *pl; int i, j, k; pl = &plot; if (pl->span == 0) { printf("[no plot, span is zero width]\n"); return; } putchar('+'); for (i = 0; i < pl->width; i++) putchar('-'); putchar('+'); putchar('\n'); for (i = 1; i < pl->height; i++) { putchar('|'); for (j = 0; j < pl->width; j++) { k = pl->data[(pl->height - i) * pl->width + j]; if (k >= 0 && k < MAX_DS) putchar(symbol[k]); else printf("[%02x]", k); } putchar('|'); putchar('\n'); } for (i = 0; i < pl->num_datasets; i++) { if (pl->bar[i] == NULL) continue; putchar('|'); for (j = 0; j < pl->width; j++) { k = pl->bar[i][j]; if (k == 0) k = ' '; putchar(k); } putchar('|'); putchar('\n'); } putchar('+'); for (i = 0; i < pl->width; i++) putchar('-'); putchar('+'); putchar('\n'); } static int dbl_cmp(const void *a, const void *b) { const double *aa = a; const double *bb = b; if (*aa < *bb) return (-1); else if (*aa > *bb) return (1); else return (0); } static struct dataset * ReadSet(FILE *f, const char *n, int column, const char *delim) { char buf[BUFSIZ], *p, *t; struct dataset *s; double d; int line; int i; s = NewSet(); s->name = strdup(n); line = 0; while (fgets(buf, sizeof buf, f) != NULL) { line++; i = strlen(buf); while (i > 0 && isspace(buf[i - 1])) buf[--i] = '\0'; for (i = 1, t = strtok(buf, delim); t != NULL && *t != '#'; i++, t = strtok(NULL, delim)) { if (i == column) break; } if (t == NULL || *t == '#') continue; d = strtod(t, &p); if (p != NULL && *p != '\0') errx(2, "Invalid data on line %d in %s", line, n); if (*buf != '\0') AddPoint(s, d); } if (s->n < 3) { fprintf(stderr, "Dataset %s must contain at least 3 data points\n", n); exit (2); } qsort(s->points, s->n, sizeof *s->points, dbl_cmp); return (s); } static void usage(char const *whine) { int i; fprintf(stderr, "%s\n", whine); fprintf(stderr, "Usage: ministat [-C column] [-c confidence] [-d delimiter(s)] [-Ans] [-w width] [file [file ...]]\n"); fprintf(stderr, "\tconfidence = {"); for (i = 0; i < NCONF; i++) { fprintf(stderr, "%s%g%%", i ? ", " : "", studentpct[i]); } fprintf(stderr, "}\n"); fprintf(stderr, "\t-A : print statistics only. suppress the graph.\n"); fprintf(stderr, "\t-C : column number to extract (starts and defaults to 1)\n"); fprintf(stderr, "\t-d : delimiter(s) string, default to \" \\t\"\n"); fprintf(stderr, "\t-n : print summary statistics only, no graph/test\n"); fprintf(stderr, "\t-s : print avg/median/stddev bars on separate lines\n"); fprintf(stderr, "\t-w : width of graph/test output (default 74 or terminal width)\n"); exit (2); } int main(int argc, char **argv) { const char *setfilenames[MAX_DS - 1]; struct dataset *ds[MAX_DS - 1]; FILE *setfiles[MAX_DS - 1]; int nds; double a; const char *delim = " \t"; char *p; int c, i, ci; int column = 1; int flag_s = 0; int flag_n = 0; int termwidth = 74; int suppress_plot = 0; if (isatty(STDOUT_FILENO)) { struct winsize wsz; if ((p = getenv("COLUMNS")) != NULL && *p != '\0') termwidth = atoi(p); else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsz) != -1 && wsz.ws_col > 0) termwidth = wsz.ws_col - 2; } ci = -1; while ((c = getopt(argc, argv, "AC:c:d:snw:")) != -1) switch (c) { case 'A': suppress_plot = 1; break; case 'C': column = strtol(optarg, &p, 10); if (p != NULL && *p != '\0') usage("Invalid column number."); if (column <= 0) usage("Column number should be positive."); break; case 'c': a = strtod(optarg, &p); if (p != NULL && *p != '\0') usage("Not a floating point number"); for (i = 0; i < NCONF; i++) if (a == studentpct[i]) ci = i; if (ci == -1) usage("No support for confidence level"); break; case 'd': if (*optarg == '\0') usage("Can't use empty delimiter string"); delim = optarg; break; case 'n': flag_n = 1; break; case 's': flag_s = 1; break; case 'w': termwidth = strtol(optarg, &p, 10); if (p != NULL && *p != '\0') usage("Invalid width, not a number."); if (termwidth < 0) usage("Unable to move beyond left margin."); break; default: usage("Unknown option"); break; } if (ci == -1) ci = 2; argc -= optind; argv += optind; if (argc == 0) { setfilenames[0] = ""; setfiles[0] = stdin; nds = 1; } else { if (argc > (MAX_DS - 1)) usage("Too many datasets."); nds = argc; for (i = 0; i < nds; i++) { setfilenames[i] = argv[i]; setfiles[i] = fopen(argv[i], "r"); if (setfiles[i] == NULL) err(2, "Cannot open %s", argv[i]); } } if (caph_limit_stdio() < 0) err(2, "capsicum"); for (i = 0; i < nds; i++) if (caph_limit_stream(fileno(setfiles[i]), CAPH_READ) < 0) err(2, "unable to limit rights for %s", setfilenames[i]); /* Enter Capsicum sandbox. */ if (cap_enter() < 0 && errno != ENOSYS) err(2, "unable to enter capability mode"); for (i = 0; i < nds; i++) { ds[i] = ReadSet(setfiles[i], setfilenames[i], column, delim); fclose(setfiles[i]); } for (i = 0; i < nds; i++) printf("%c %s\n", symbol[i+1], ds[i]->name); if (!flag_n && !suppress_plot) { SetupPlot(termwidth, flag_s, nds); for (i = 0; i < nds; i++) DimPlot(ds[i]); for (i = 0; i < nds; i++) PlotSet(ds[i], i + 1); DumpPlot(); } VitalsHead(); Vitals(ds[0], 1); for (i = 1; i < nds; i++) { Vitals(ds[i], i + 1); if (!flag_n) Relative(ds[i], ds[0], ci); } exit(0); } Index: head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c =================================================================== --- head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c (revision 326407) +++ head/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c (revision 326408) @@ -1,208 +1,210 @@ /*- + * SPDX-License-Identifier: (BSD-2-Clause AND Beerware) + * * Copyright (c) 2005-2006 The FreeBSD Project * All rights reserved. * * Author: Victor Cruceru * * Redistribution of this software and documentation 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 or documentation 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. * * This C file contains code developed by Poul-Henning Kamp under the * following license: * * FreeBSD: src/sbin/mdconfig/mdconfig.c,v 1.33.2.1 2004/09/14 03:32:21 jmg Exp * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ */ /* * Host Resources MIB implementation for bsnmpd. */ #include #include #include #include #include #include "hostres_snmp.h" #include "hostres_oid.h" #include "hostres_tree.h" /* Internal id got after we'll register this module with the agent */ static u_int host_registration_id = 0; /* This our hostres module */ static struct lmodule *hostres_module; /* See the generated file hostres_oid.h */ static const struct asn_oid oid_host = OIDX_host; /* descriptor to access kernel memory */ kvm_t *hr_kd; /* * HOST RESOURCES mib module finalization hook. * Returns 0 on success, < 0 on error */ static int hostres_fini(void) { if (hr_kd != NULL) (void)kvm_close(hr_kd); fini_storage_tbl(); fini_fs_tbl(); fini_processor_tbl(); fini_disk_storage_tbl(); fini_device_tbl(); fini_partition_tbl(); fini_network_tbl(); fini_printer_tbl(); fini_swrun_tbl(); fini_swins_tbl(); fini_scalars(); if (host_registration_id > 0) or_unregister(host_registration_id); HRDBG("done."); return (0); } /* * HOST RESOURCES mib module initialization hook. * Returns 0 on success, < 0 on error */ static int hostres_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) { hostres_module = mod; /* * NOTE: order of these calls is important here! */ if ((hr_kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open")) == NULL) { syslog(LOG_ERR, "kvm_open failed: %m "); return (-1); } /* * The order is relevant here, because some table depend on each other. */ init_device_tbl(); /* populates partition table too */ if (init_disk_storage_tbl()) { hostres_fini(); return (-1); } init_processor_tbl(); init_printer_tbl(); /* * populate storage and FS tables. Must be done after device * initialisation because the FS refresh code calls into the * partition refresh code. */ init_storage_tbl(); /* also the hrSWRunPerfTable's support is initialized here */ init_swrun_tbl(); init_swins_tbl(); HRDBG("done."); return (0); } /* * HOST RESOURCES mib module start operation * returns nothing */ static void hostres_start(void) { host_registration_id = or_register(&oid_host, "The MIB module for Host Resource MIB (RFC 2790).", hostres_module); start_device_tbl(hostres_module); start_processor_tbl(hostres_module); start_network_tbl(); HRDBG("done."); } /* this identifies the HOST RESOURCES mib module */ const struct snmp_module config = { "This module implements the host resource mib (rfc 2790)", hostres_init, hostres_fini, NULL, /* idle function, do not use it */ NULL, NULL, hostres_start, NULL, /* proxy a PDU */ hostres_ctree, /* see the generated hostres_tree.h */ hostres_CTREE_SIZE, /* see the generated hostres_tree.h */ NULL }; /** * Make an SNMP DateAndTime from a struct tm. This should be in the library. */ int make_date_time(u_char *str, const struct tm *tm, u_int decisecs) { str[0] = (u_char)((tm->tm_year + 1900) >> 8); str[1] = (u_char)(tm->tm_year + 1900); str[2] = tm->tm_mon + 1; str[3] = tm->tm_mday; str[4] = tm->tm_hour; str[5] = tm->tm_min; str[6] = tm->tm_sec; str[7] = decisecs; if (tm->tm_gmtoff < 0) str[8] = '-'; else str[8] = '+'; str[9] = (u_char)(labs(tm->tm_gmtoff) / 3600); str[10] = (u_char)((labs(tm->tm_gmtoff) % 3600) / 60); return (11); } Index: head/usr.sbin/ctm/ctm/ctm.c =================================================================== --- head/usr.sbin/ctm/ctm/ctm.c (revision 326407) +++ head/usr.sbin/ctm/ctm/ctm.c (revision 326408) @@ -1,331 +1,333 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * * This is the client program of 'CTM'. It will apply a CTM-patch to a * collection of files. * * Options we'd like to see: * * -a Attempt best effort. * -d Debug TBD. * -m Email me instead. * -r Reconstruct file. * -R Read list of files to reconstruct. * * Options we have: * -b Base-dir * -B Backup to tar-file. * -t Tar command (default as in TARCMD). * -c Check it out, don't do anything. * -F Force * -q Tell us less. * -T . Temporary files. * -u Set all file modification times to the timestamp * -v Tell us more. * -V Tell us more level = number of -v * -k Keep files and directories that would have been removed. * -l List actions. * * Options we don't actually use: * -p Less paranoid. * -P Paranoid. */ #define EXTERN /* */ #include #include "ctm.h" #define CTM_STATUS ".ctm_status" extern int Proc(char *, unsigned applied); int main(int argc, char **argv) { int stat=0, err=0; int c; unsigned applied = 0; FILE *statfile; struct CTM_Filter *nfilter = NULL; /* new filter */ u_char * basedir; basedir = NULL; Verbose = 1; Paranoid = 1; SetTime = 0; KeepIt = 0; ListIt = 0; BackupFile = NULL; TarCmd = TARCMD; LastFilter = FilterList = NULL; TmpDir = getenv("TMPDIR"); if (TmpDir == NULL) TmpDir = strdup(_PATH_TMP); setbuf(stderr,0); setbuf(stdout,0); while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) { switch (c) { case 'b': basedir = optarg; break; /* Base Directory */ case 'B': BackupFile = optarg; break; case 'c': CheckIt++; break; /* Only check it */ case 'F': Force = 1; break; case 'k': KeepIt++; break; /* Don't do removes */ case 'l': ListIt++; break; /* Only list actions and files */ case 'p': Paranoid--; break; /* Less Paranoid */ case 'P': Paranoid++; break; /* More Paranoid */ case 'q': Verbose--; break; /* Quiet */ case 't': TarCmd = optarg; break; /* archiver command */ case 'T': TmpDir = optarg; break; /* set temporary directory */ case 'u': SetTime++; break; /* Set timestamp on files */ case 'v': Verbose++; break; /* Verbose */ case 'V': sscanf(optarg,"%d", &c); /* Verbose */ Verbose += c; break; case 'e': /* filter expressions */ case 'x': if (NULL == (nfilter = Malloc(sizeof(struct CTM_Filter)))) { warnx("out of memory for expressions: \"%s\"", optarg); stat++; break; } (void) memset(nfilter, 0, sizeof(struct CTM_Filter)); if (0 != (err = regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) { char errmsg[128]; regerror(err, &nfilter->CompiledRegex, errmsg, sizeof(errmsg)); warnx("regular expression: \"%s\"", errmsg); stat++; break; } /* note whether the filter enables or disables on match */ nfilter->Action = (('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE); /* link in the expression into the list */ nfilter->Next = NULL; if (NULL == FilterList) { LastFilter = FilterList = nfilter; /* init head and tail */ } else { /* place at tail */ LastFilter->Next = nfilter; LastFilter = nfilter; } break; case ':': warnx("option '%c' requires an argument",optopt); stat++; break; case '?': warnx("option '%c' not supported",optopt); stat++; break; default: warnx("option '%c' not yet implemented",optopt); break; } } if(stat) { warnx("%d errors during option processing",stat); return Exit_Pilot; } stat = Exit_Done; argc -= optind; argv += optind; if (basedir == NULL) { Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1); CatPtr = Buffer; *Buffer = '\0'; } else { Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1); strcpy(Buffer, basedir); CatPtr = Buffer + strlen(basedir); if (CatPtr[-1] != '/') { strcat(Buffer, "/"); CatPtr++; } } strcat(Buffer, CTM_STATUS); if(ListIt) applied = 0; else if((statfile = fopen(Buffer, "r")) == NULL) { if (Verbose > 0) warnx("warning: %s not found", Buffer); } else { fscanf(statfile, "%*s %u", &applied); fclose(statfile); } if(!argc) stat |= Proc("-", applied); while(argc-- && stat == Exit_Done) { stat |= Proc(*argv++, applied); stat &= ~(Exit_Version | Exit_NoMatch); } if(stat == Exit_Done) stat = Exit_OK; if(Verbose > 0) warnx("exit(%d)",stat); if (FilterList) for (nfilter = FilterList; nfilter; ) { struct CTM_Filter *tmp = nfilter->Next; Free(nfilter); nfilter = tmp; } return stat; } int Proc(char *filename, unsigned applied) { FILE *f; int i; char *p = strrchr(filename,'.'); if(!strcmp(filename,"-")) { p = 0; f = stdin; } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) { p = alloca(20 + strlen(filename)); strcpy(p,"gunzip < "); strcat(p,filename); f = popen(p,"r"); if(!f) { warn("%s", p); return Exit_Garbage; } } else { p = 0; f = fopen(filename,"r"); } if(!f) { warn("%s", filename); return Exit_Garbage; } if(Verbose > 1) fprintf(stderr,"Working on <%s>\n",filename); Delete(FileName); FileName = String(filename); /* If we cannot seek, we're doomed, so copy to a tmp-file in that case */ if(!p && -1 == fseek(f,0,SEEK_END)) { char *fn; FILE *f2; int fd; if (asprintf(&fn, "%s/CTMclient.XXXXXXXXXX", TmpDir) == -1) { fprintf(stderr, "Cannot allocate memory\n"); fclose(f); return Exit_Broke; } if ((fd = mkstemp(fn)) == -1 || (f2 = fdopen(fd, "w+")) == NULL) { warn("%s", fn); free(fn); if (fd != -1) close(fd); fclose(f); return Exit_Broke; } unlink(fn); if (Verbose > 0) fprintf(stderr,"Writing tmp-file \"%s\"\n",fn); free(fn); while(EOF != (i=getc(f))) if(EOF == putc(i,f2)) { fclose(f2); return Exit_Broke; } fclose(f); f = f2; } if(!p) rewind(f); if((i=Pass1(f, applied))) goto exit_and_close; if(ListIt) { i = Exit_Done; goto exit_and_close; } if(!p) { rewind(f); } else { pclose(f); f = popen(p,"r"); if(!f) { warn("%s", p); return Exit_Broke; } } i=Pass2(f); if(!p) { rewind(f); } else { pclose(f); f = popen(p,"r"); if(!f) { warn("%s", p); return Exit_Broke; } } if(i) { if((!Force) || (i & ~Exit_Forcible)) goto exit_and_close; } if(CheckIt) { if (Verbose > 0) fprintf(stderr,"All checks out ok.\n"); i = Exit_Done; goto exit_and_close; } /* backup files if requested */ if(BackupFile) { i = PassB(f); if(!p) { rewind(f); } else { pclose(f); f = popen(p,"r"); if(!f) { warn("%s", p); return Exit_Broke; } } } i=Pass3(f); exit_and_close: if(!p) fclose(f); else pclose(f); if(i) return i; if (Verbose > 0) fprintf(stderr,"All done ok\n"); return Exit_Done; } Index: head/usr.sbin/ctm/ctm/ctm_ed.c =================================================================== --- head/usr.sbin/ctm/ctm/ctm_ed.c (revision 326407) +++ head/usr.sbin/ctm/ctm/ctm_ed.c (revision 326408) @@ -1,114 +1,116 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include "ctm.h" int ctm_edit(u_char *script, int length, char *filein, char *fileout) { u_char *ep, cmd; int ln, ln2, iln, ret=0, c; FILE *fi=0,*fo=0; fi = fopen(filein,"r"); if(!fi) { warn("%s", filein); return 8; } fo = fopen(fileout,"w"); if(!fo) { warn("%s", fileout); fclose(fi); return 4; } iln = 1; for(ep=script;ep < script+length;) { cmd = *ep++; if(cmd != 'a' && cmd != 'd') { ret = 1; goto bye; } ln = 0; while(isdigit(*ep)) { ln *= 10; ln += (*ep++ - '0'); } if(*ep++ != ' ') { ret = 1; goto bye; } ln2 = 0; while(isdigit(*ep)) { ln2 *= 10; ln2 += (*ep++ - '0'); } if(*ep++ != '\n') { ret = 1; goto bye; } if(cmd == 'd') { while(iln < ln) { c = getc(fi); if(c == EOF) { ret = 1; goto bye; } putc(c,fo); if(c == '\n') iln++; } while(ln2) { c = getc(fi); if(c == EOF) { ret = 1; goto bye; } if(c != '\n') continue; ln2--; iln++; } continue; } if(cmd == 'a') { while(iln <= ln) { c = getc(fi); if(c == EOF) { ret = 1; goto bye; } putc(c,fo); if(c == '\n') iln++; } while(ln2) { c = *ep++; putc(c,fo); if(c != '\n') continue; ln2--; } continue; } ret = 1; goto bye; } while(1) { c = getc(fi); if(c == EOF) break; putc(c,fo); } ret = 0; bye: if(fi) { if(fclose(fi) != 0) { warn("%s", filein); ret = 1; } } if(fo) { if(fflush(fo) != 0) { warn("%s", fileout); ret = 1; } if(fclose(fo) != 0) { warn("%s", fileout); ret = 1; } } return ret; } Index: head/usr.sbin/ctm/ctm/ctm_input.c =================================================================== --- head/usr.sbin/ctm/ctm/ctm_input.c (revision 326407) +++ head/usr.sbin/ctm/ctm/ctm_input.c (revision 326408) @@ -1,134 +1,136 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include "ctm.h" /*---------------------------------------------------------------------------*/ void Fatal_(int ln, char *fn, char *kind) { if(Verbose > 2) fprintf(stderr,"Fatal error. (%s:%d)\n",fn,ln); fprintf(stderr,"%s Fatal error: %s\n",FileName, kind); } #define Fatal(foo) Fatal_(__LINE__,__FILE__,foo) #define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.") /*---------------------------------------------------------------------------*/ /* get next field, check that the terminating whitespace is what we expect */ u_char * Ffield(FILE *fd, MD5_CTX *ctx,u_char term) { static u_char buf[BUFSIZ]; int i,l; for(l=0;;) { if((i=getc(fd)) == EOF) { Fatal("Truncated patch."); return 0; } buf[l++] = i; if(isspace(i)) break; if(l >= sizeof buf) { Fatal("Corrupt patch."); printf("Token is too long.\n"); return 0; } } buf[l] = '\0'; MD5Update(ctx,buf,l); if(buf[l-1] != term) { Fatal("Corrupt patch."); fprintf(stderr,"Expected \"%s\" but didn't find it {%02x}.\n", term == '\n' ? "\\n" : " ",buf[l-1]); if(Verbose > 4) fprintf(stderr,"{%s}\n",buf); return 0; } buf[--l] = '\0'; if(Verbose > 4) fprintf(stderr,"<%s>\n",buf); return buf; } int Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term) { u_char *p,*q; int u_chars=0; p = Ffield(fd,ctx,term); if(!p) return -1; for(q=p;*q;q++) { if(!isdigit(*q)) { Fatal("Bytecount contains non-digit."); return -1; } u_chars *= 10; u_chars += (*q - '0'); } return u_chars; } u_char * Fdata(FILE *fd, int u_chars, MD5_CTX *ctx) { u_char *p = Malloc(u_chars+1); if(u_chars+1 != fread(p,1,u_chars+1,fd)) { Fatal("Truncated patch."); return 0; } MD5Update(ctx,p,u_chars+1); if(p[u_chars] != '\n') { if(Verbose > 3) printf("FileData wasn't followed by a newline.\n"); Fatal("Corrupt patch."); return 0; } p[u_chars] = '\0'; return p; } /*---------------------------------------------------------------------------*/ /* get the filename in the next field, prepend BaseDir and give back the result strings. The sustitute filename is return (the one with the suffix SUBSUFF) if it exists and the qualifier contains CTM_Q_Name_Subst NOTA: Buffer is already initialize with BaseDir, CatPtr is the insertion point on this buffer + the length test in Ffield() is enough for Fname() */ u_char * Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose) { u_char * p; struct stat st; if ((p = Ffield(fd,ctx,term)) == NULL) return(NULL); strcpy(CatPtr, p); if (!(qual & CTM_Q_Name_Subst)) return(Buffer); p = Buffer + strlen(Buffer); strcat(Buffer, SUBSUFF); if ( -1 == stat(Buffer, &st) ) { *p = '\0'; } else { if(verbose > 2) fprintf(stderr,"Using %s as substitute file\n", Buffer); } return (Buffer); } Index: head/usr.sbin/ctm/ctm/ctm_pass1.c =================================================================== --- head/usr.sbin/ctm/ctm/ctm_pass1.c (revision 326407) +++ head/usr.sbin/ctm/ctm/ctm_pass1.c (revision 326408) @@ -1,252 +1,254 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include "ctm.h" #define BADREAD 1 /*---------------------------------------------------------------------------*/ /* Pass1 -- Validate the incoming CTM-file. */ int Pass1(FILE *fd, unsigned applied) { u_char *p,*q; MD5_CTX ctx; int i,j,sep,cnt; u_char *md5=0,*name=0,*trash=0; struct CTM_Syntax *sp; int slashwarn=0, match=0, total_matches=0; unsigned current; char md5_1[33]; if(Verbose>3) printf("Pass1 -- Checking integrity of incoming CTM-patch\n"); MD5Init (&ctx); GETFIELD(p,' '); /* CTM_BEGIN */ if(strcmp(p,"CTM_BEGIN")) { Fatal("Probably not a CTM-patch at all."); if(Verbose>3) fprintf(stderr,"Expected \"CTM_BEGIN\" got \"%s\".\n",p); return 1; } GETFIELDCOPY(Version,' '); /* */ if(strcmp(Version,VERSION)) { Fatal("CTM-patch is wrong version."); if(Verbose>3) fprintf(stderr,"Expected \"%s\" got \"%s\".\n",VERSION,p); return 1; } GETFIELDCOPY(Name,' '); /* */ GETFIELDCOPY(Nbr,' '); /* */ GETFIELDCOPY(TimeStamp,' '); /* */ GETFIELDCOPY(Prefix,'\n'); /* */ sscanf(Nbr, "%u", ¤t); if (FilterList || ListIt) current = 0; /* ignore if -l or if filters are present */ if(current && current <= applied) { if(Verbose > 0) fprintf(stderr,"Delta number %u is already applied; ignoring.\n", current); return Exit_Version; } for(;;) { Delete(md5); Delete(name); Delete(trash); cnt = -1; /* if a filter list is defined we assume that all pathnames require an action opposite to that requested by the first filter in the list. If no filter is defined, all pathnames are assumed to match. */ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE); GETFIELD(p,' '); /* CTM_something */ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') { Fatal("Expected CTM keyword."); fprintf(stderr,"Got [%s]\n",p); return 1; } if(!strcmp(p+3,"_END")) break; for(sp=Syntax;sp->Key;sp++) if(!strcmp(p+3,sp->Key)) goto found; Fatal("Expected CTM keyword."); fprintf(stderr,"Got [%s]\n",p); return 1; found: if(Verbose > 5) fprintf(stderr,"%s ",sp->Key); for(i=0;(j = sp->List[i]);i++) { if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes) sep = ' '; else sep = '\n'; if(Verbose > 5) fprintf(stderr," %x(%d)",sp->List[i],sep); switch (j & CTM_F_MASK) { case CTM_F_Name: /* XXX check for garbage and .. */ GETFIELDCOPY(name,sep); j = strlen(name); if(name[j-1] == '/' && !slashwarn) { fprintf(stderr,"Warning: contains trailing slash\n"); slashwarn++; } if (name[0] == '/') { Fatal("Absolute paths are illegal."); Delete(name); return Exit_Mess; } q = name; for (;;) { if (q[0] == '.' && q[1] == '.') if (q[2] == '/' || q[2] == '\0') { Fatal("Paths containing '..' are illegal."); Delete(name); return Exit_Mess; } if ((q = strchr(q, '/')) == NULL) break; q++; } /* if we have been asked to `keep' files then skip removes; i.e. we don't match these entries at all. */ if (KeepIt && (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) { match = CTM_FILTER_DISABLE; break; } /* If filter expression have been defined, match the path name against the expression list. */ if (FilterList) { struct CTM_Filter *filter; for (filter = FilterList; filter; filter = filter->Next) { if (0 == regexec(&filter->CompiledRegex, name, 0, 0, 0)) /* if the name matches, adopt the action */ match = filter->Action; } } /* Add up the total number of matches */ total_matches += match; break; case CTM_F_Uid: GETFIELD(p,sep); while(*p) { if(!isdigit(*p)) { Fatal("Non-digit in uid."); return 32; } p++; } break; case CTM_F_Gid: GETFIELD(p,sep); while(*p) { if(!isdigit(*p)) { Fatal("Non-digit in gid."); return 32; } p++; } break; case CTM_F_Mode: GETFIELD(p,sep); while(*p) { if(!isdigit(*p)) { Fatal("Non-digit in mode."); return 32; } p++; } break; case CTM_F_MD5: if(j & CTM_Q_MD5_Chunk) { GETFIELDCOPY(md5,sep); /* XXX check for garbage */ } else if(j & CTM_Q_MD5_Before) { GETFIELD(p,sep); /* XXX check for garbage */ } else if(j & CTM_Q_MD5_After) { GETFIELD(p,sep); /* XXX check for garbage */ } else { fprintf(stderr,"List = 0x%x\n",j); Fatal("Unqualified MD5."); return 32; } break; case CTM_F_Count: GETBYTECNT(cnt,sep); break; case CTM_F_Bytes: if(cnt < 0) WRONG GETDATA(trash,cnt); p = MD5Data(trash,cnt,md5_1); if(md5 && strcmp(md5,p)) { Fatal("Internal MD5 failed."); return Exit_Garbage; default: fprintf(stderr,"List = 0x%x\n",j); Fatal("List had garbage."); return Exit_Garbage; } } } if(Verbose > 5) putc('\n',stderr); if(ListIt && match) printf("> %s %s\n", sp->Key, name); } Delete(md5); Delete(name); Delete(trash); q = MD5End (&ctx,md5_1); if(Verbose > 2) printf("Expecting Global MD5 <%s>\n",q); GETFIELD(p,'\n'); /* */ if(Verbose > 2) printf("Reference Global MD5 <%s>\n",p); if(strcmp(q,p)) { Fatal("MD5 sum doesn't match."); fprintf(stderr,"\tI have:<%s>\n",q); fprintf(stderr,"\tShould have been:<%s>\n",p); return Exit_Garbage; } if (-1 != getc(fd)) { if(!Force) { Fatal("Trailing junk in CTM-file. Can Force with -F."); return 16; } } if ((Verbose > 1) && (0 == total_matches)) printf("No matches in \"%s\"\n", FileName); return (total_matches ? Exit_OK : Exit_NoMatch); } Index: head/usr.sbin/ctm/ctm/ctm_pass2.c =================================================================== --- head/usr.sbin/ctm/ctm/ctm_pass2.c (revision 326407) +++ head/usr.sbin/ctm/ctm/ctm_pass2.c (revision 326408) @@ -1,301 +1,303 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include "ctm.h" #define BADREAD 32 /*---------------------------------------------------------------------------*/ /* Pass2 -- Validate the incoming CTM-file. */ int Pass2(FILE *fd) { u_char *p,*q,*md5=0; MD5_CTX ctx; int i,j,sep,cnt,fdesc; u_char *trash=0,*name=0; struct CTM_Syntax *sp; struct stat st; int ret = 0; int match = 0; char md5_1[33]; struct CTM_Filter *filter; FILE *ed = NULL; static char *template = NULL; if(Verbose>3) printf("Pass2 -- Checking if CTM-patch will apply\n"); MD5Init (&ctx); GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG GETFIELD(p,' '); if(strcmp(Version,p)) WRONG GETFIELD(p,' '); if(strcmp(Name,p)) WRONG /* XXX Lookup name in /etc/ctm,conf, read stuff */ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG /* XXX Verify that this is the next patch to apply */ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG /* XXX drop or use ? */ for(;;) { Delete(trash); Delete(name); Delete(md5); cnt = -1; /* if a filter list was specified, check file name against the filters specified if no filter was given operate on all files. */ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE); GETFIELD(p,' '); if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG if(!strcmp(p+3,"_END")) break; for(sp=Syntax;sp->Key;sp++) if(!strcmp(p+3,sp->Key)) goto found; WRONG found: for(i=0;(j = sp->List[i]);i++) { if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes) sep = ' '; else sep = '\n'; switch (j & CTM_F_MASK) { case CTM_F_Name: GETNAMECOPY(name,sep,j,0); /* If `keep' was specified, we won't remove any files, so don't check if the file exists */ if (KeepIt && (!strcmp(sp->Key,"FR") || !strcmp(sp->Key,"DR"))) { match = CTM_FILTER_DISABLE; break; } for (filter = FilterList; filter; filter = filter->Next) if (0 == regexec(&filter->CompiledRegex, name, 0, 0, 0)) { match = filter->Action; } if (CTM_FILTER_DISABLE == match) break; /* should ignore this file */ /* XXX Check DR DM rec's for parent-dir */ if(j & CTM_Q_Name_New) { /* XXX Check DR FR rec's for item */ if(-1 != stat(name,&st)) { fprintf(stderr," %s: %s exists.\n", sp->Key,name); ret |= Exit_Forcible; } break; } if(-1 == stat(name,&st)) { fprintf(stderr," %s: %s doesn't exist.\n", sp->Key,name); if (sp->Key[1] == 'R') ret |= Exit_Forcible; else ret |= Exit_NotOK; break; } if (SetTime && getuid() && (getuid() != st.st_uid)) { fprintf(stderr, " %s: %s not mine, cannot set time.\n", sp->Key,name); ret |= Exit_NotOK; } if (j & CTM_Q_Name_Dir) { if((st.st_mode & S_IFMT) != S_IFDIR) { fprintf(stderr, " %s: %s exist, but isn't dir.\n", sp->Key,name); ret |= Exit_NotOK; } break; } if (j & CTM_Q_Name_File) { if((st.st_mode & S_IFMT) != S_IFREG) { fprintf(stderr, " %s: %s exist, but isn't file.\n", sp->Key,name); ret |= Exit_NotOK; } break; } break; case CTM_F_Uid: case CTM_F_Gid: case CTM_F_Mode: GETFIELD(p,sep); break; case CTM_F_MD5: if(!name) WRONG if(j & CTM_Q_MD5_Before) { char *tmp; GETFIELD(p,sep); if(match && (st.st_mode & S_IFMT) == S_IFREG && (tmp = MD5File(name,md5_1)) != NULL && strcmp(tmp,p)) { fprintf(stderr," %s: %s md5 mismatch.\n", sp->Key,name); GETFIELDCOPY(md5,sep); if(md5 != NULL && strcmp(tmp,md5) == 0) { fprintf(stderr," %s: %s already applied.\n", sp->Key,name); match = CTM_FILTER_DISABLE; } else if(j & CTM_Q_MD5_Force) { if(Force) fprintf(stderr," Can and will force.\n"); else fprintf(stderr," Could have forced.\n"); ret |= Exit_Forcible; } else { ret |= Exit_NotOK; } } break; } else if(j & CTM_Q_MD5_After) { if(md5 == NULL) { GETFIELDCOPY(md5,sep); } break; } /* Unqualified MD5 */ WRONG break; case CTM_F_Count: GETBYTECNT(cnt,sep); break; case CTM_F_Bytes: if(cnt < 0) WRONG GETDATA(trash,cnt); if (!match) break; if (!template) { if (asprintf(&template, "%s/CTMclientXXXXXX", TmpDir) == -1) { fprintf(stderr, " %s: malloc failed.\n", sp->Key); ret |= Exit_Mess; return ret; } } if(!strcmp(sp->Key,"FN")) { if ((p = strdup(template)) == NULL) { fprintf(stderr, " %s: malloc failed.\n", sp->Key); ret |= Exit_Mess; return ret; } if ((fdesc = mkstemp(p)) == -1) { fprintf(stderr, " %s: mkstemp failed.\n", sp->Key); ret |= Exit_Mess; Free(p); return ret; } if (close(fdesc) == -1) { fprintf(stderr, " %s: close failed.\n", sp->Key); ret |= Exit_Mess; unlink(p); Free(p); return ret; } j = ctm_edit(trash,cnt,name,p); if(j) { fprintf(stderr," %s: %s edit returned %d.\n", sp->Key,name,j); ret |= j; unlink(p); Free(p); return ret; } else if(strcmp(md5,MD5File(p,md5_1))) { fprintf(stderr," %s: %s edit fails.\n", sp->Key,name); ret |= Exit_Mess; unlink(p); Free(p); return ret; } unlink(p); Free(p); } else if (!strcmp(sp->Key,"FE")) { if ((p = strdup(template)) == NULL) { fprintf(stderr, " %s: malloc failed.\n", sp->Key); ret |= Exit_Mess; return ret; } if ((fdesc = mkstemp(p)) == -1) { fprintf(stderr, " %s: mkstemp failed.\n", sp->Key); ret |= Exit_Mess; Free(p); return ret; } if (close(fdesc) == -1) { fprintf(stderr, " %s: close failed.\n", sp->Key); ret |= Exit_Mess; unlink(p); Free(p); return ret; } ed = popen("ed","w"); if (!ed) { WRONG } fprintf(ed,"e %s\n", name); if (cnt != fwrite(trash,1,cnt,ed)) { warn("%s", name); pclose(ed); WRONG } fprintf(ed,"w %s\n",p); if (pclose(ed)) { warn("%s", p); WRONG } if(strcmp(md5,MD5File(p,md5_1))) { fprintf(stderr,"%s %s MD5 didn't come out right\n", sp->Key, name); WRONG } unlink(p); Free(p); } break; default: WRONG } } } Delete(trash); Delete(name); Delete(md5); q = MD5End (&ctx,md5_1); GETFIELD(p,'\n'); /* */ if(strcmp(q,p)) WRONG if (-1 != getc(fd)) WRONG return ret; } Index: head/usr.sbin/ctm/ctm/ctm_pass3.c =================================================================== --- head/usr.sbin/ctm/ctm/ctm_pass3.c (revision 326407) +++ head/usr.sbin/ctm/ctm/ctm_pass3.c (revision 326408) @@ -1,295 +1,297 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include "ctm.h" #define BADREAD 32 /*---------------------------------------------------------------------------*/ /* Pass3 -- Validate the incoming CTM-file. */ int settime(const char *name, const struct timeval *times) { if (SetTime) if (utimes(name,times)) { warn("utimes(): %s", name); return -1; } return 0; } int Pass3(FILE *fd) { u_char *p,*q,buf[BUFSIZ]; MD5_CTX ctx; int i,j,sep,cnt; u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0; struct CTM_Syntax *sp; FILE *ed=0; struct stat st; char md5_1[33]; int match=0; struct timeval times[2]; struct CTM_Filter *filter = NULL; if(Verbose>3) printf("Pass3 -- Applying the CTM-patch\n"); MD5Init (&ctx); GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG GETFIELD(p,' '); if(strcmp(Version,p)) WRONG GETFIELD(p,' '); if(strcmp(Name,p)) WRONG GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG /* * This would be cleaner if mktime() worked in UTC rather than * local time. */ if (SetTime) { struct tm tm; char *tz; char buf[5]; int i; #define SUBSTR(off,len) strncpy(buf, &TimeStamp[off], len), buf[len] = '\0' #define WRONGDATE { fprintf(stderr, " %s failed date validation\n",\ TimeStamp); WRONG} if (strlen(TimeStamp) != 15 || TimeStamp[14] != 'Z') WRONGDATE for (i = 0; i < 14; i++) if (!isdigit(TimeStamp[i])) WRONGDATE tz = getenv("TZ"); if (setenv("TZ", "UTC", 1) < 0) WRONG tzset(); tm.tm_isdst = tm.tm_gmtoff = 0; SUBSTR(0, 4); tm.tm_year = atoi(buf) - 1900; SUBSTR(4, 2); tm.tm_mon = atoi(buf) - 1; if (tm.tm_mon < 0 || tm.tm_mon > 11) WRONGDATE SUBSTR(6, 2); tm.tm_mday = atoi(buf); if (tm.tm_mday < 1 || tm.tm_mday > 31) WRONG; SUBSTR(8, 2); tm.tm_hour = atoi(buf); if (tm.tm_hour > 24) WRONGDATE SUBSTR(10, 2); tm.tm_min = atoi(buf); if (tm.tm_min > 59) WRONGDATE SUBSTR(12, 2); tm.tm_sec = atoi(buf); if (tm.tm_min > 62) WRONGDATE /* allow leap seconds */ times[0].tv_sec = times[1].tv_sec = mktime(&tm); if (times[0].tv_sec == -1) WRONGDATE times[0].tv_usec = times[1].tv_usec = 0; if (tz) { if (setenv("TZ", tz, 1) < 0) WRONGDATE } else { unsetenv("TZ"); } } for(;;) { Delete(md5); Delete(uid); Delete(gid); Delete(mode); Delete(md5before); Delete(trash); Delete(name); cnt = -1; GETFIELD(p,' '); if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG if(!strcmp(p+3,"_END")) break; for(sp=Syntax;sp->Key;sp++) if(!strcmp(p+3,sp->Key)) goto found; WRONG found: for(i=0;(j = sp->List[i]);i++) { if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes) sep = ' '; else sep = '\n'; switch (j & CTM_F_MASK) { case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break; case CTM_F_Uid: GETFIELDCOPY(uid,sep); break; case CTM_F_Gid: GETFIELDCOPY(gid,sep); break; case CTM_F_Mode: GETFIELDCOPY(mode,sep); break; case CTM_F_MD5: if(j & CTM_Q_MD5_Before) GETFIELDCOPY(md5before,sep); else GETFIELDCOPY(md5,sep); break; case CTM_F_Count: GETBYTECNT(cnt,sep); break; case CTM_F_Bytes: GETDATA(trash,cnt); break; default: WRONG } } /* XXX This should go away. Disallow trailing '/' */ j = strlen(name)-1; if(name[j] == '/') name[j] = '\0'; /* * If a filter list is specified, run thru the filter list and * match `name' against filters. If the name matches, set the * required action to that specified in the filter. * The default action if no filterlist is given is to match * everything. */ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE); for (filter = FilterList; filter; filter = filter->Next) { if (0 == regexec(&filter->CompiledRegex, name, 0, 0, 0)) { match = filter->Action; } } if (CTM_FILTER_DISABLE == match) /* skip file if disabled */ continue; if (Verbose > 0) fprintf(stderr,"> %s %s\n",sp->Key,name); if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) { i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666); if(i < 0) { warn("%s", name); WRONG } if(cnt != write(i,trash,cnt)) { warn("%s", name); WRONG } close(i); if(strcmp(md5,MD5File(name,md5_1))) { fprintf(stderr," %s %s MD5 didn't come out right\n", sp->Key,name); WRONG } if (settime(name,times)) WRONG continue; } if(!strcmp(sp->Key,"FE")) { ed = popen("ed","w"); if(!ed) { WRONG } fprintf(ed,"e %s\n",name); if(cnt != fwrite(trash,1,cnt,ed)) { warn("%s", name); pclose(ed); WRONG } fprintf(ed,"w %s\n",name); if(pclose(ed)) { warn("ed"); WRONG } if(strcmp(md5,MD5File(name,md5_1))) { fprintf(stderr," %s %s MD5 didn't come out right\n", sp->Key,name); WRONG } if (settime(name,times)) WRONG continue; } if(!strcmp(sp->Key,"FN")) { strcpy(buf,name); strcat(buf,TMPSUFF); i = ctm_edit(trash,cnt,name,buf); if(i) { fprintf(stderr," %s %s Edit failed with code %d.\n", sp->Key,name,i); WRONG } if(strcmp(md5,MD5File(buf,md5_1))) { fprintf(stderr," %s %s Edit failed MD5 check.\n", sp->Key,name); WRONG } if (rename(buf,name) == -1) WRONG if (settime(name,times)) WRONG continue; } if(!strcmp(sp->Key,"DM")) { if(0 > mkdir(name,0777)) { sprintf(buf,"mkdir -p %s",name); system(buf); } if(0 > stat(name,&st) || ((st.st_mode & S_IFMT) != S_IFDIR)) { fprintf(stderr,"<%s> mkdir failed\n",name); WRONG } if (settime(name,times)) WRONG continue; } if(!strcmp(sp->Key,"FR")) { if (KeepIt) { if (Verbose > 1) printf("<%s> not removed\n", name); } else if (0 != unlink(name)) { fprintf(stderr,"<%s> unlink failed\n",name); if (!Force) WRONG } continue; } if(!strcmp(sp->Key,"DR")) { /* * We cannot use rmdir() because we do not get the directories * in '-depth' order (cvs-cur.0018.gz for examples) */ if (KeepIt) { if (Verbose > 1) { printf("<%s> not removed\n", name); } } else { sprintf(buf,"rm -rf %s",name); system(buf); } continue; } WRONG } Delete(md5); Delete(uid); Delete(gid); Delete(mode); Delete(md5before); Delete(trash); Delete(name); q = MD5End (&ctx,md5_1); GETFIELD(p,'\n'); if(strcmp(q,p)) WRONG if (-1 != getc(fd)) WRONG return 0; } Index: head/usr.sbin/ctm/ctm/ctm_passb.c =================================================================== --- head/usr.sbin/ctm/ctm/ctm_passb.c (revision 326407) +++ head/usr.sbin/ctm/ctm/ctm_passb.c (revision 326408) @@ -1,142 +1,144 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Joseph Koshy * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include "ctm.h" #define BADREAD 32 /*---------------------------------------------------------------------------*/ /* PassB -- Backup modified files. */ int PassB(FILE *fd) { u_char *p,*q; MD5_CTX ctx; int i,j,sep,cnt; u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0; struct CTM_Syntax *sp; FILE *b = 0; /* backup command */ u_char buf[BUFSIZ]; char md5_1[33]; int ret = 0; int match = 0; struct CTM_Filter *filter = NULL; if(Verbose>3) printf("PassB -- Backing up files which would be changed.\n"); MD5Init (&ctx); snprintf(buf, sizeof(buf), fmtcheck(TarCmd, TARCMD), BackupFile); b=popen(buf, "w"); if(!b) { warn("%s", buf); return Exit_Garbage; } GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG GETFIELD(p,' '); if(strcmp(Version,p)) WRONG GETFIELD(p,' '); if(strcmp(Name,p)) WRONG GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG for(;;) { Delete(md5); Delete(uid); Delete(gid); Delete(mode); Delete(md5before); Delete(trash); Delete(name); cnt = -1; GETFIELD(p,' '); if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG if(!strcmp(p+3,"_END")) break; for(sp=Syntax;sp->Key;sp++) if(!strcmp(p+3,sp->Key)) goto found; WRONG found: for(i=0;(j = sp->List[i]);i++) { if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes) sep = ' '; else sep = '\n'; switch (j & CTM_F_MASK) { case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break; case CTM_F_Uid: GETFIELDCOPY(uid,sep); break; case CTM_F_Gid: GETFIELDCOPY(gid,sep); break; case CTM_F_Mode: GETFIELDCOPY(mode,sep); break; case CTM_F_MD5: if(j & CTM_Q_MD5_Before) GETFIELDCOPY(md5before,sep); else GETFIELDCOPY(md5,sep); break; case CTM_F_Count: GETBYTECNT(cnt,sep); break; case CTM_F_Bytes: GETDATA(trash,cnt); break; default: WRONG } } /* XXX This should go away. Disallow trailing '/' */ j = strlen(name)-1; if(name[j] == '/') name[j] = '\0'; if (KeepIt && (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) continue; /* match the name against the elements of the filter list. The action associated with the last matched filter determines whether this file should be ignored or backed up. */ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE); for (filter = FilterList; filter; filter = filter->Next) { if (0 == regexec(&filter->CompiledRegex, name, 0, 0, 0)) match = filter->Action; } if (CTM_FILTER_DISABLE == match) continue; if (!strcmp(sp->Key,"FS") || !strcmp(sp->Key,"FN") || !strcmp(sp->Key,"AS") || !strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR")) { /* send name to the archiver for a backup */ cnt = strlen(name); if (cnt != fwrite(name,1,cnt,b) || EOF == fputc('\n',b)) { warn("%s", name); pclose(b); WRONG; } } } ret = pclose(b); Delete(md5); Delete(uid); Delete(gid); Delete(mode); Delete(md5before); Delete(trash); Delete(name); q = MD5End (&ctx,md5_1); GETFIELD(p,'\n'); /* */ if(strcmp(q,p)) WRONG if (-1 != getc(fd)) WRONG return ret; } Index: head/usr.sbin/ctm/ctm/ctm_syntax.c =================================================================== --- head/usr.sbin/ctm/ctm/ctm_syntax.c (revision 326407) +++ head/usr.sbin/ctm/ctm/ctm_syntax.c (revision 326408) @@ -1,67 +1,69 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include "ctm.h" /* The fields... */ #define Name CTM_F_Name #define Uid CTM_F_Uid #define Gid CTM_F_Gid #define Mode CTM_F_Mode #define MD5 CTM_F_MD5 #define Count CTM_F_Count #define Bytes CTM_F_Bytes /* The qualifiers... */ #define File CTM_Q_Name_File #define Dir CTM_Q_Name_Dir #define New CTM_Q_Name_New #define Subst CTM_Q_Name_Subst #define After CTM_Q_MD5_After #define Before CTM_Q_MD5_Before #define Chunk CTM_Q_MD5_Chunk #define Force CTM_Q_MD5_Force static int ctmFM[] = /* File Make */ { Name|File|New|Subst, Uid, Gid, Mode, MD5|After|Chunk, Count, Bytes,0 }; static int ctmFS[] = /* File Substitute */ { Name|File|Subst, Uid, Gid, Mode, MD5|Before|Force, MD5|After|Chunk, Count, Bytes,0 }; static int ctmFE[] = /* File Edit */ { Name|File|Subst, Uid, Gid, Mode, MD5|Before, MD5|After, Count, Bytes,0 }; static int ctmFR[] = /* File Remove */ { Name|File|Subst, MD5|Before, 0 }; static int ctmAS[] = /* Attribute Substitute */ { Name|Subst, Uid, Gid, Mode, 0 }; static int ctmDM[] = /* Directory Make */ { Name|Dir|New , Uid, Gid, Mode, 0 }; static int ctmDR[] = /* Directory Remove */ { Name|Dir, 0 }; struct CTM_Syntax Syntax[] = { { "FM", ctmFM }, { "FS", ctmFS }, { "FE", ctmFE }, { "FN", ctmFE }, { "FR", ctmFR }, { "AS", ctmAS }, { "DM", ctmDM }, { "DR", ctmDR }, { 0, 0} }; Index: head/usr.sbin/fdwrite/fdwrite.c =================================================================== --- head/usr.sbin/fdwrite/fdwrite.c (revision 326407) +++ head/usr.sbin/fdwrite/fdwrite.c (revision 326408) @@ -1,196 +1,198 @@ -/* +/*- + * SPDX-License-Identifier: Beerware + * * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #include static int format_track(int fd, int cyl, int secs, int head, int rate, int gaplen, int secsize, int fill, int interleave) { struct fd_formb f; register int i,j; int il[100]; memset(il,0,sizeof il); for(j = 0, i = 1; i <= secs; i++) { while(il[(j%secs)+1]) j++; il[(j%secs)+1] = i; j += interleave; } f.format_version = FD_FORMAT_VERSION; f.head = head; f.cyl = cyl; f.transfer_rate = rate; f.fd_formb_secshift = secsize; f.fd_formb_nsecs = secs; f.fd_formb_gaplen = gaplen; f.fd_formb_fillbyte = fill; for(i = 0; i < secs; i++) { f.fd_formb_cylno(i) = cyl; f.fd_formb_headno(i) = head; f.fd_formb_secno(i) = il[i+1]; f.fd_formb_secsize(i) = secsize; } return ioctl(fd, FD_FORM, (caddr_t)&f); } static void usage(void) { fprintf(stderr, "usage: fdwrite [-v] [-y] [-f inputfile] [-d device]\n"); exit(2); } int main(int argc, char **argv) { int inputfd = -1, c, fdn = 0, i,j,fd; int bpt, verbose=1, nbytes=0, track; int interactive = 1; const char *device= "/dev/fd0"; char *trackbuf = 0,*vrfybuf = 0; struct fd_type fdt; FILE *tty; setbuf(stdout,0); while((c = getopt(argc, argv, "d:f:vy")) != -1) switch(c) { case 'd': /* Which drive */ device = optarg; break; case 'f': /* input file */ if (inputfd >= 0) close(inputfd); inputfd = open(optarg,O_RDONLY); if (inputfd < 0) err(1, "%s", optarg); break; case 'v': /* Toggle verbosity */ verbose = !verbose; break; case 'y': /* Don't confirm? */ interactive = 0; break; case '?': default: usage(); } if (inputfd < 0) inputfd = 0; if (!isatty(1)) interactive = 0; if(optind < argc) usage(); tty = fopen(_PATH_TTY,"r+"); if(!tty) err(1, _PATH_TTY); setbuf(tty,0); for(j=1;j > 0;) { fdn++; if (interactive) { fprintf(tty, "Please insert floppy #%d in drive %s and press return >", fdn,device); while(1) { i = getc(tty); if(i == '\n') break; } } if((fd = open(device, O_RDWR)) < 0) err(1, "%s", device); if(ioctl(fd, FD_GTYPE, &fdt) < 0) errx(1, "not a floppy disk: %s", device); bpt = fdt.sectrac * (1<= 0 && j= 0 && j