1d1c5d0cfSMark Johnston /*- 2d1c5d0cfSMark Johnston * SPDX-License-Identifier: BSD-2-Clause 3d1c5d0cfSMark Johnston * 4d1c5d0cfSMark Johnston * Copyright (c) 2012 NetApp, Inc. 5d1c5d0cfSMark Johnston * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 6d1c5d0cfSMark Johnston * All rights reserved. 7d1c5d0cfSMark Johnston * 8d1c5d0cfSMark Johnston * Redistribution and use in source and binary forms, with or without 9d1c5d0cfSMark Johnston * modification, are permitted provided that the following conditions 10d1c5d0cfSMark Johnston * are met: 11d1c5d0cfSMark Johnston * 1. Redistributions of source code must retain the above copyright 12d1c5d0cfSMark Johnston * notice, this list of conditions and the following disclaimer. 13d1c5d0cfSMark Johnston * 2. Redistributions in binary form must reproduce the above copyright 14d1c5d0cfSMark Johnston * notice, this list of conditions and the following disclaimer in the 15d1c5d0cfSMark Johnston * documentation and/or other materials provided with the distribution. 16d1c5d0cfSMark Johnston * 17d1c5d0cfSMark Johnston * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 18d1c5d0cfSMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19d1c5d0cfSMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20d1c5d0cfSMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 21d1c5d0cfSMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22d1c5d0cfSMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23d1c5d0cfSMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24d1c5d0cfSMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25d1c5d0cfSMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26d1c5d0cfSMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27d1c5d0cfSMark Johnston * SUCH DAMAGE. 28d1c5d0cfSMark Johnston */ 29d1c5d0cfSMark Johnston 30d1c5d0cfSMark Johnston #include <sys/types.h> 31d1c5d0cfSMark Johnston 32d1c5d0cfSMark Johnston #include <machine/vmm.h> 33d1c5d0cfSMark Johnston #include <machine/vmm_snapshot.h> 34d1c5d0cfSMark Johnston 35d1c5d0cfSMark Johnston #include <assert.h> 36d1c5d0cfSMark Johnston #include <capsicum_helpers.h> 37d1c5d0cfSMark Johnston #include <err.h> 38*e10b9d66SSHENG-YI HONG #include <pthread.h> 39d1c5d0cfSMark Johnston #include <stdbool.h> 40d1c5d0cfSMark Johnston #include <stdlib.h> 41d1c5d0cfSMark Johnston #include <string.h> 42d1c5d0cfSMark Johnston #include <sysexits.h> 43d1c5d0cfSMark Johnston #include <termios.h> 44d1c5d0cfSMark Johnston #include <unistd.h> 45d1c5d0cfSMark Johnston 46d1c5d0cfSMark Johnston #include "debug.h" 47d1c5d0cfSMark Johnston #include "mevent.h" 48d1c5d0cfSMark Johnston #include "uart_backend.h" 49d1c5d0cfSMark Johnston 50d1c5d0cfSMark Johnston struct ttyfd { 51d1c5d0cfSMark Johnston bool opened; 52d1c5d0cfSMark Johnston int rfd; /* fd for reading */ 53d1c5d0cfSMark Johnston int wfd; /* fd for writing, may be == rfd */ 54d1c5d0cfSMark Johnston }; 55d1c5d0cfSMark Johnston 56d1c5d0cfSMark Johnston #define FIFOSZ 16 57d1c5d0cfSMark Johnston 58d1c5d0cfSMark Johnston struct fifo { 59d1c5d0cfSMark Johnston uint8_t buf[FIFOSZ]; 60d1c5d0cfSMark Johnston int rindex; /* index to read from */ 61d1c5d0cfSMark Johnston int windex; /* index to write to */ 62d1c5d0cfSMark Johnston int num; /* number of characters in the fifo */ 63d1c5d0cfSMark Johnston int size; /* size of the fifo */ 64d1c5d0cfSMark Johnston }; 65d1c5d0cfSMark Johnston 66d1c5d0cfSMark Johnston struct uart_softc { 67d1c5d0cfSMark Johnston struct ttyfd tty; 68d1c5d0cfSMark Johnston struct fifo rxfifo; 69d1c5d0cfSMark Johnston struct mevent *mev; 70*e10b9d66SSHENG-YI HONG pthread_mutex_t mtx; 71d1c5d0cfSMark Johnston }; 72d1c5d0cfSMark Johnston 73d1c5d0cfSMark Johnston static bool uart_stdio; /* stdio in use for i/o */ 74d1c5d0cfSMark Johnston static struct termios tio_stdio_orig; 75d1c5d0cfSMark Johnston 76d1c5d0cfSMark Johnston static void 77d1c5d0cfSMark Johnston ttyclose(void) 78d1c5d0cfSMark Johnston { 79d1c5d0cfSMark Johnston tcsetattr(STDIN_FILENO, TCSANOW, &tio_stdio_orig); 80d1c5d0cfSMark Johnston } 81d1c5d0cfSMark Johnston 82d1c5d0cfSMark Johnston static void 83d1c5d0cfSMark Johnston ttyopen(struct ttyfd *tf) 84d1c5d0cfSMark Johnston { 85d1c5d0cfSMark Johnston struct termios orig, new; 86d1c5d0cfSMark Johnston 87d1c5d0cfSMark Johnston tcgetattr(tf->rfd, &orig); 88d1c5d0cfSMark Johnston new = orig; 89d1c5d0cfSMark Johnston cfmakeraw(&new); 90d1c5d0cfSMark Johnston new.c_cflag |= CLOCAL; 91d1c5d0cfSMark Johnston tcsetattr(tf->rfd, TCSANOW, &new); 92d1c5d0cfSMark Johnston if (uart_stdio) { 93d1c5d0cfSMark Johnston tio_stdio_orig = orig; 94d1c5d0cfSMark Johnston atexit(ttyclose); 95d1c5d0cfSMark Johnston } 96d1c5d0cfSMark Johnston raw_stdio = 1; 97d1c5d0cfSMark Johnston } 98d1c5d0cfSMark Johnston 99d1c5d0cfSMark Johnston static int 100d1c5d0cfSMark Johnston ttyread(struct ttyfd *tf) 101d1c5d0cfSMark Johnston { 102d1c5d0cfSMark Johnston unsigned char rb; 103d1c5d0cfSMark Johnston 104d1c5d0cfSMark Johnston if (read(tf->rfd, &rb, 1) == 1) 105d1c5d0cfSMark Johnston return (rb); 106d1c5d0cfSMark Johnston else 107d1c5d0cfSMark Johnston return (-1); 108d1c5d0cfSMark Johnston } 109d1c5d0cfSMark Johnston 110d1c5d0cfSMark Johnston static void 111d1c5d0cfSMark Johnston ttywrite(struct ttyfd *tf, unsigned char wb) 112d1c5d0cfSMark Johnston { 113d1c5d0cfSMark Johnston (void)write(tf->wfd, &wb, 1); 114d1c5d0cfSMark Johnston } 115d1c5d0cfSMark Johnston 116d1c5d0cfSMark Johnston static bool 117d1c5d0cfSMark Johnston rxfifo_available(struct uart_softc *sc) 118d1c5d0cfSMark Johnston { 119d1c5d0cfSMark Johnston return (sc->rxfifo.num < sc->rxfifo.size); 120d1c5d0cfSMark Johnston } 121d1c5d0cfSMark Johnston 122d1c5d0cfSMark Johnston int 123d1c5d0cfSMark Johnston uart_rxfifo_getchar(struct uart_softc *sc) 124d1c5d0cfSMark Johnston { 125d1c5d0cfSMark Johnston struct fifo *fifo; 126d1c5d0cfSMark Johnston int c, error, wasfull; 127d1c5d0cfSMark Johnston 128d1c5d0cfSMark Johnston wasfull = 0; 129d1c5d0cfSMark Johnston fifo = &sc->rxfifo; 130d1c5d0cfSMark Johnston if (fifo->num > 0) { 131d1c5d0cfSMark Johnston if (!rxfifo_available(sc)) 132d1c5d0cfSMark Johnston wasfull = 1; 133d1c5d0cfSMark Johnston c = fifo->buf[fifo->rindex]; 134d1c5d0cfSMark Johnston fifo->rindex = (fifo->rindex + 1) % fifo->size; 135d1c5d0cfSMark Johnston fifo->num--; 136d1c5d0cfSMark Johnston if (wasfull) { 137d1c5d0cfSMark Johnston if (sc->tty.opened) { 138d1c5d0cfSMark Johnston error = mevent_enable(sc->mev); 139d1c5d0cfSMark Johnston assert(error == 0); 140d1c5d0cfSMark Johnston } 141d1c5d0cfSMark Johnston } 142d1c5d0cfSMark Johnston return (c); 143d1c5d0cfSMark Johnston } else 144d1c5d0cfSMark Johnston return (-1); 145d1c5d0cfSMark Johnston } 146d1c5d0cfSMark Johnston 147d1c5d0cfSMark Johnston int 148d1c5d0cfSMark Johnston uart_rxfifo_numchars(struct uart_softc *sc) 149d1c5d0cfSMark Johnston { 150d1c5d0cfSMark Johnston return (sc->rxfifo.num); 151d1c5d0cfSMark Johnston } 152d1c5d0cfSMark Johnston 153d1c5d0cfSMark Johnston static int 154d1c5d0cfSMark Johnston rxfifo_putchar(struct uart_softc *sc, uint8_t ch) 155d1c5d0cfSMark Johnston { 156d1c5d0cfSMark Johnston struct fifo *fifo; 157d1c5d0cfSMark Johnston int error; 158d1c5d0cfSMark Johnston 159d1c5d0cfSMark Johnston fifo = &sc->rxfifo; 160d1c5d0cfSMark Johnston 161d1c5d0cfSMark Johnston if (fifo->num < fifo->size) { 162d1c5d0cfSMark Johnston fifo->buf[fifo->windex] = ch; 163d1c5d0cfSMark Johnston fifo->windex = (fifo->windex + 1) % fifo->size; 164d1c5d0cfSMark Johnston fifo->num++; 165d1c5d0cfSMark Johnston if (!rxfifo_available(sc)) { 166d1c5d0cfSMark Johnston if (sc->tty.opened) { 167d1c5d0cfSMark Johnston /* 168d1c5d0cfSMark Johnston * Disable mevent callback if the FIFO is full. 169d1c5d0cfSMark Johnston */ 170d1c5d0cfSMark Johnston error = mevent_disable(sc->mev); 171d1c5d0cfSMark Johnston assert(error == 0); 172d1c5d0cfSMark Johnston } 173d1c5d0cfSMark Johnston } 174d1c5d0cfSMark Johnston return (0); 175d1c5d0cfSMark Johnston } else 176d1c5d0cfSMark Johnston return (-1); 177d1c5d0cfSMark Johnston } 178d1c5d0cfSMark Johnston 179d1c5d0cfSMark Johnston void 180d1c5d0cfSMark Johnston uart_rxfifo_drain(struct uart_softc *sc, bool loopback) 181d1c5d0cfSMark Johnston { 182d1c5d0cfSMark Johnston int ch; 183d1c5d0cfSMark Johnston 184d1c5d0cfSMark Johnston if (loopback) { 185d1c5d0cfSMark Johnston (void)ttyread(&sc->tty); 186d1c5d0cfSMark Johnston } else { 187d1c5d0cfSMark Johnston while (rxfifo_available(sc) && 188d1c5d0cfSMark Johnston ((ch = ttyread(&sc->tty)) != -1)) 189d1c5d0cfSMark Johnston rxfifo_putchar(sc, ch); 190d1c5d0cfSMark Johnston } 191d1c5d0cfSMark Johnston } 192d1c5d0cfSMark Johnston 193d1c5d0cfSMark Johnston int 194d1c5d0cfSMark Johnston uart_rxfifo_putchar(struct uart_softc *sc, uint8_t ch, bool loopback) 195d1c5d0cfSMark Johnston { 196d1c5d0cfSMark Johnston if (loopback) { 197d1c5d0cfSMark Johnston return (rxfifo_putchar(sc, ch)); 198d1c5d0cfSMark Johnston } else if (sc->tty.opened) { 199d1c5d0cfSMark Johnston ttywrite(&sc->tty, ch); 200d1c5d0cfSMark Johnston return (0); 201d1c5d0cfSMark Johnston } else { 202d1c5d0cfSMark Johnston /* Drop on the floor. */ 203d1c5d0cfSMark Johnston return (0); 204d1c5d0cfSMark Johnston } 205d1c5d0cfSMark Johnston } 206d1c5d0cfSMark Johnston 207d1c5d0cfSMark Johnston void 208d1c5d0cfSMark Johnston uart_rxfifo_reset(struct uart_softc *sc, int size) 209d1c5d0cfSMark Johnston { 210d1c5d0cfSMark Johnston char flushbuf[32]; 211d1c5d0cfSMark Johnston struct fifo *fifo; 212d1c5d0cfSMark Johnston ssize_t nread; 213d1c5d0cfSMark Johnston int error; 214d1c5d0cfSMark Johnston 215d1c5d0cfSMark Johnston fifo = &sc->rxfifo; 216d1c5d0cfSMark Johnston bzero(fifo, sizeof(struct fifo)); 217d1c5d0cfSMark Johnston fifo->size = size; 218d1c5d0cfSMark Johnston 219d1c5d0cfSMark Johnston if (sc->tty.opened) { 220d1c5d0cfSMark Johnston /* 221d1c5d0cfSMark Johnston * Flush any unread input from the tty buffer. 222d1c5d0cfSMark Johnston */ 223d1c5d0cfSMark Johnston while (1) { 224d1c5d0cfSMark Johnston nread = read(sc->tty.rfd, flushbuf, sizeof(flushbuf)); 225d1c5d0cfSMark Johnston if (nread != sizeof(flushbuf)) 226d1c5d0cfSMark Johnston break; 227d1c5d0cfSMark Johnston } 228d1c5d0cfSMark Johnston 229d1c5d0cfSMark Johnston /* 230d1c5d0cfSMark Johnston * Enable mevent to trigger when new characters are available 231d1c5d0cfSMark Johnston * on the tty fd. 232d1c5d0cfSMark Johnston */ 233d1c5d0cfSMark Johnston error = mevent_enable(sc->mev); 234d1c5d0cfSMark Johnston assert(error == 0); 235d1c5d0cfSMark Johnston } 236d1c5d0cfSMark Johnston } 237d1c5d0cfSMark Johnston 238d1c5d0cfSMark Johnston int 239d1c5d0cfSMark Johnston uart_rxfifo_size(struct uart_softc *sc __unused) 240d1c5d0cfSMark Johnston { 241d1c5d0cfSMark Johnston return (FIFOSZ); 242d1c5d0cfSMark Johnston } 243d1c5d0cfSMark Johnston 244d1c5d0cfSMark Johnston #ifdef BHYVE_SNAPSHOT 245d1c5d0cfSMark Johnston int 246d1c5d0cfSMark Johnston uart_rxfifo_snapshot(struct uart_softc *sc, struct vm_snapshot_meta *meta) 247d1c5d0cfSMark Johnston { 248d1c5d0cfSMark Johnston int ret; 249d1c5d0cfSMark Johnston 250d1c5d0cfSMark Johnston SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.rindex, meta, ret, done); 251d1c5d0cfSMark Johnston SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.windex, meta, ret, done); 252d1c5d0cfSMark Johnston SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.num, meta, ret, done); 253d1c5d0cfSMark Johnston SNAPSHOT_VAR_OR_LEAVE(sc->rxfifo.size, meta, ret, done); 254d1c5d0cfSMark Johnston SNAPSHOT_BUF_OR_LEAVE(sc->rxfifo.buf, sizeof(sc->rxfifo.buf), 255d1c5d0cfSMark Johnston meta, ret, done); 256d1c5d0cfSMark Johnston 257d1c5d0cfSMark Johnston done: 258d1c5d0cfSMark Johnston return (ret); 259d1c5d0cfSMark Johnston } 260d1c5d0cfSMark Johnston #endif 261d1c5d0cfSMark Johnston 262d1c5d0cfSMark Johnston static int 263d1c5d0cfSMark Johnston uart_stdio_backend(struct uart_softc *sc) 264d1c5d0cfSMark Johnston { 265d1c5d0cfSMark Johnston #ifndef WITHOUT_CAPSICUM 266d1c5d0cfSMark Johnston cap_rights_t rights; 267d1c5d0cfSMark Johnston cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 268d1c5d0cfSMark Johnston #endif 269d1c5d0cfSMark Johnston 270d1c5d0cfSMark Johnston if (uart_stdio) 271d1c5d0cfSMark Johnston return (-1); 272d1c5d0cfSMark Johnston 273d1c5d0cfSMark Johnston sc->tty.rfd = STDIN_FILENO; 274d1c5d0cfSMark Johnston sc->tty.wfd = STDOUT_FILENO; 275d1c5d0cfSMark Johnston sc->tty.opened = true; 276d1c5d0cfSMark Johnston 277d1c5d0cfSMark Johnston if (fcntl(sc->tty.rfd, F_SETFL, O_NONBLOCK) != 0) 278d1c5d0cfSMark Johnston return (-1); 279d1c5d0cfSMark Johnston if (fcntl(sc->tty.wfd, F_SETFL, O_NONBLOCK) != 0) 280d1c5d0cfSMark Johnston return (-1); 281d1c5d0cfSMark Johnston 282d1c5d0cfSMark Johnston #ifndef WITHOUT_CAPSICUM 283d1c5d0cfSMark Johnston cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ); 284d1c5d0cfSMark Johnston if (caph_rights_limit(sc->tty.rfd, &rights) == -1) 285d1c5d0cfSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 286d1c5d0cfSMark Johnston if (caph_ioctls_limit(sc->tty.rfd, cmds, nitems(cmds)) == -1) 287d1c5d0cfSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 288d1c5d0cfSMark Johnston #endif 289d1c5d0cfSMark Johnston 290d1c5d0cfSMark Johnston uart_stdio = true; 291d1c5d0cfSMark Johnston 292d1c5d0cfSMark Johnston return (0); 293d1c5d0cfSMark Johnston } 294d1c5d0cfSMark Johnston 295d1c5d0cfSMark Johnston static int 296d1c5d0cfSMark Johnston uart_tty_backend(struct uart_softc *sc, const char *path) 297d1c5d0cfSMark Johnston { 298d1c5d0cfSMark Johnston #ifndef WITHOUT_CAPSICUM 299d1c5d0cfSMark Johnston cap_rights_t rights; 300d1c5d0cfSMark Johnston cap_ioctl_t cmds[] = { TIOCGETA, TIOCSETA, TIOCGWINSZ }; 301d1c5d0cfSMark Johnston #endif 302d1c5d0cfSMark Johnston int fd; 303d1c5d0cfSMark Johnston 304d1c5d0cfSMark Johnston fd = open(path, O_RDWR | O_NONBLOCK); 305d1c5d0cfSMark Johnston if (fd < 0) 306d1c5d0cfSMark Johnston return (-1); 307d1c5d0cfSMark Johnston 308d1c5d0cfSMark Johnston if (!isatty(fd)) { 309d1c5d0cfSMark Johnston close(fd); 310d1c5d0cfSMark Johnston return (-1); 311d1c5d0cfSMark Johnston } 312d1c5d0cfSMark Johnston 313d1c5d0cfSMark Johnston sc->tty.rfd = sc->tty.wfd = fd; 314d1c5d0cfSMark Johnston sc->tty.opened = true; 315d1c5d0cfSMark Johnston 316d1c5d0cfSMark Johnston #ifndef WITHOUT_CAPSICUM 317d1c5d0cfSMark Johnston cap_rights_init(&rights, CAP_EVENT, CAP_IOCTL, CAP_READ, CAP_WRITE); 318d1c5d0cfSMark Johnston if (caph_rights_limit(fd, &rights) == -1) 319d1c5d0cfSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 320d1c5d0cfSMark Johnston if (caph_ioctls_limit(fd, cmds, nitems(cmds)) == -1) 321d1c5d0cfSMark Johnston errx(EX_OSERR, "Unable to apply rights for sandbox"); 322d1c5d0cfSMark Johnston #endif 323d1c5d0cfSMark Johnston 324d1c5d0cfSMark Johnston return (0); 325d1c5d0cfSMark Johnston } 326d1c5d0cfSMark Johnston 327d1c5d0cfSMark Johnston struct uart_softc * 328d1c5d0cfSMark Johnston uart_init(void) 329d1c5d0cfSMark Johnston { 330*e10b9d66SSHENG-YI HONG struct uart_softc *sc = calloc(1, sizeof(struct uart_softc)); 331*e10b9d66SSHENG-YI HONG if (sc == NULL) 332*e10b9d66SSHENG-YI HONG return (NULL); 333*e10b9d66SSHENG-YI HONG 334*e10b9d66SSHENG-YI HONG pthread_mutex_init(&sc->mtx, NULL); 335*e10b9d66SSHENG-YI HONG 336*e10b9d66SSHENG-YI HONG return (sc); 337d1c5d0cfSMark Johnston } 338d1c5d0cfSMark Johnston 339d1c5d0cfSMark Johnston int 340d1c5d0cfSMark Johnston uart_tty_open(struct uart_softc *sc, const char *path, 341d1c5d0cfSMark Johnston void (*drain)(int, enum ev_type, void *), void *arg) 342d1c5d0cfSMark Johnston { 343d1c5d0cfSMark Johnston int retval; 344d1c5d0cfSMark Johnston 345d1c5d0cfSMark Johnston if (strcmp("stdio", path) == 0) 346d1c5d0cfSMark Johnston retval = uart_stdio_backend(sc); 347d1c5d0cfSMark Johnston else 348d1c5d0cfSMark Johnston retval = uart_tty_backend(sc, path); 349d1c5d0cfSMark Johnston if (retval == 0) { 350d1c5d0cfSMark Johnston ttyopen(&sc->tty); 351d1c5d0cfSMark Johnston sc->mev = mevent_add(sc->tty.rfd, EVF_READ, drain, arg); 352d1c5d0cfSMark Johnston assert(sc->mev != NULL); 353d1c5d0cfSMark Johnston } 354d1c5d0cfSMark Johnston 355d1c5d0cfSMark Johnston return (retval); 356d1c5d0cfSMark Johnston } 357*e10b9d66SSHENG-YI HONG 358*e10b9d66SSHENG-YI HONG void 359*e10b9d66SSHENG-YI HONG uart_softc_lock(struct uart_softc *sc) 360*e10b9d66SSHENG-YI HONG { 361*e10b9d66SSHENG-YI HONG pthread_mutex_lock(&sc->mtx); 362*e10b9d66SSHENG-YI HONG } 363*e10b9d66SSHENG-YI HONG 364*e10b9d66SSHENG-YI HONG void 365*e10b9d66SSHENG-YI HONG uart_softc_unlock(struct uart_softc *sc) 366*e10b9d66SSHENG-YI HONG { 367*e10b9d66SSHENG-YI HONG pthread_mutex_unlock(&sc->mtx); 368*e10b9d66SSHENG-YI HONG } 369