dcons_os.c revision 225203
143902Sbrian/*-
243902Sbrian * Copyright (C) 2003,2004
343948Sbrian * 	Hidetoshi Shimokawa. All rights reserved.
443902Sbrian *
543948Sbrian * Redistribution and use in source and binary forms, with or without
643902Sbrian * modification, are permitted provided that the following conditions
749977Sbrian * are met:
843902Sbrian * 1. Redistributions of source code must retain the above copyright
943902Sbrian *    notice, this list of conditions and the following disclaimer.
1043902Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1143902Sbrian *    notice, this list of conditions and the following disclaimer in the
1243902Sbrian *    documentation and/or other materials provided with the distribution.
1343902Sbrian * 3. All advertising materials mentioning features or use of this software
1443902Sbrian *    must display the following acknowledgement:
1543902Sbrian *
1643902Sbrian *	This product includes software developed by Hidetoshi Shimokawa.
1743902Sbrian *
1843902Sbrian * 4. Neither the name of the author nor the names of its contributors
1943902Sbrian *    may be used to endorse or promote products derived from this software
2043902Sbrian *    without specific prior written permission.
2143948Sbrian *
2243902Sbrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2343902Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2443948Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2543948Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2643902Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2743902Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2843902Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2943902Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3043902Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3143948Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3243948Sbrian * SUCH DAMAGE.
3343902Sbrian *
3443902Sbrian * $FreeBSD: head/sys/dev/dcons/dcons_os.c 225203 2011-08-26 21:46:36Z rwatson $
3543902Sbrian */
3643902Sbrian
3743948Sbrian#include <sys/param.h>
3843948Sbrian#include <sys/kdb.h>
3947634Sbillf#include <gdb/gdb.h>
4043902Sbrian#include <sys/kernel.h>
4143902Sbrian#include <sys/module.h>
4243902Sbrian#include <sys/systm.h>
4343948Sbrian#include <sys/types.h>
4443948Sbrian#include <sys/conf.h>
4543948Sbrian#include <sys/cons.h>
4643902Sbrian#include <sys/consio.h>
4743902Sbrian#include <sys/tty.h>
4843948Sbrian#include <sys/malloc.h>
4943948Sbrian#include <sys/priv.h>
5043902Sbrian#include <sys/proc.h>
5143902Sbrian#include <sys/ucred.h>
5243902Sbrian
5343948Sbrian#include <machine/bus.h>
5443902Sbrian
5543902Sbrian#include <dev/dcons/dcons.h>
5643948Sbrian#include <dev/dcons/dcons_os.h>
5743948Sbrian
5843902Sbrian#include <ddb/ddb.h>
5943902Sbrian#include <sys/reboot.h>
6043902Sbrian
6143902Sbrian#include <sys/sysctl.h>
6243902Sbrian
6343902Sbrian#include <vm/vm.h>
6443948Sbrian#include <vm/vm_param.h>
6543902Sbrian#include <vm/pmap.h>
6643948Sbrian
6743948Sbrian#include "opt_comconsole.h"
6843902Sbrian#include "opt_dcons.h"
6943948Sbrian#include "opt_kdb.h"
7043948Sbrian#include "opt_gdb.h"
7143948Sbrian#include "opt_ddb.h"
7243948Sbrian
7343948Sbrian
7443948Sbrian#ifndef DCONS_POLL_HZ
7543902Sbrian#define DCONS_POLL_HZ	25
7643902Sbrian#endif
7743902Sbrian
7843902Sbrian#ifndef DCONS_BUF_SIZE
7943902Sbrian#define DCONS_BUF_SIZE (16*1024)
8043902Sbrian#endif
8143902Sbrian
8243902Sbrian#ifndef DCONS_FORCE_CONSOLE
8343948Sbrian#define DCONS_FORCE_CONSOLE	0	/* Mostly for FreeBSD-4/DragonFly */
8447634Sbillf#endif
8543902Sbrian
8643902Sbrian#ifndef KLD_MODULE
8743948Sbrianstatic char bssbuf[DCONS_BUF_SIZE];	/* buf in bss */
8843948Sbrian#endif
8943902Sbrian
9043902Sbrian/* global data */
9143902Sbrianstatic struct dcons_global dg;
9243902Sbrianstruct dcons_global *dcons_conf;
9343902Sbrianstatic int poll_hz = DCONS_POLL_HZ;
9443902Sbrian
9543902Sbrianstatic struct dcons_softc sc[DCONS_NPORT];
9643902Sbrian
9743902SbrianSYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console");
9843902SbrianSYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0,
9943902Sbrian				"dcons polling rate");
10043902Sbrian
10143902Sbrianstatic int drv_init = 0;
10243902Sbrianstatic struct callout dcons_callout;
10343902Sbrianstruct dcons_buf *dcons_buf;		/* for local dconschat */
10443902Sbrian
10543902Sbrianstatic void	dcons_timeout(void *);
10643902Sbrianstatic int	dcons_drv_init(int);
10743902Sbrian
10843902Sbrianstatic cn_probe_t	dcons_cnprobe;
10943902Sbrianstatic cn_init_t	dcons_cninit;
11043902Sbrianstatic cn_term_t	dcons_cnterm;
11143902Sbrianstatic cn_getc_t	dcons_cngetc;
11243902Sbrianstatic cn_putc_t	dcons_cnputc;
11343902Sbrian
11443902SbrianCONSOLE_DRIVER(dcons);
11543902Sbrian
11643902Sbrian#if defined(GDB)
11743902Sbrianstatic gdb_probe_f	dcons_dbg_probe;
11843902Sbrianstatic gdb_init_f	dcons_dbg_init;
11943902Sbrianstatic gdb_term_f	dcons_dbg_term;
12043902Sbrianstatic gdb_getc_f	dcons_dbg_getc;
12143902Sbrianstatic gdb_putc_f	dcons_dbg_putc;
12243902Sbrian
12343902SbrianGDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term,
12443902Sbrian    dcons_dbg_getc, dcons_dbg_putc);
12543902Sbrian
12643902Sbrianextern struct gdb_dbgport *gdb_cur;
12743902Sbrian#endif
12843902Sbrian
12943902Sbrianstatic tsw_outwakeup_t dcons_outwakeup;
13043902Sbrian
13143902Sbrianstatic struct ttydevsw dcons_ttydevsw = {
13243902Sbrian	.tsw_flags      = TF_NOPREFIX,
13343902Sbrian	.tsw_outwakeup  = dcons_outwakeup,
13443902Sbrian};
13543902Sbrian
13643902Sbrian#if (defined(GDB) || defined(DDB))
13743902Sbrianstatic int
13843902Sbriandcons_check_break(struct dcons_softc *dc, int c)
13943902Sbrian{
14043902Sbrian
14143902Sbrian	if (c < 0)
14243902Sbrian		return (c);
14343902Sbrian
14443902Sbrian#ifdef GDB
14543902Sbrian	if ((dc->flags & DC_GDB) != 0 && gdb_cur == &dcons_gdb_dbgport)
14643902Sbrian		kdb_alt_break_gdb(c, &dc->brk_state);
14743902Sbrian	else
14843902Sbrian#endif
14943902Sbrian		kdb_alt_break(c, &dc->brk_state);
15043902Sbrian
15143902Sbrian	return (c);
15243902Sbrian}
15343902Sbrian#else
15443902Sbrian#define	dcons_check_break(dc, c)	(c)
15543902Sbrian#endif
15643902Sbrian
15743902Sbrianstatic int
15843902Sbriandcons_os_checkc_nopoll(struct dcons_softc *dc)
15943902Sbrian{
16043902Sbrian	int c;
16143902Sbrian
16243902Sbrian	if (dg.dma_tag != NULL)
16343902Sbrian		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD);
16443902Sbrian
16543902Sbrian	c = dcons_check_break(dc, dcons_checkc(dc));
16643902Sbrian
16743902Sbrian	if (dg.dma_tag != NULL)
16843902Sbrian		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREREAD);
16943902Sbrian
17043902Sbrian	return (c);
17143902Sbrian}
17243902Sbrian
17343902Sbrianstatic int
17443902Sbriandcons_os_checkc(struct dcons_softc *dc)
17543902Sbrian{
17643902Sbrian	EVENTHANDLER_INVOKE(dcons_poll, 0);
17743902Sbrian	return (dcons_os_checkc_nopoll(dc));
17843902Sbrian}
17943902Sbrian
18043902Sbrianstatic void
18143902Sbriandcons_os_putc(struct dcons_softc *dc, int c)
18243902Sbrian{
18343902Sbrian	if (dg.dma_tag != NULL)
18443902Sbrian		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTWRITE);
18543902Sbrian
18643902Sbrian	dcons_putc(dc, c);
18743902Sbrian
18843902Sbrian	if (dg.dma_tag != NULL)
18943902Sbrian		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE);
19043902Sbrian}
19143902Sbrian
19243902Sbrianstatic void
19343902Sbriandcons_outwakeup(struct tty *tp)
19443902Sbrian{
19543902Sbrian	struct dcons_softc *dc;
19643902Sbrian	char ch;
19743902Sbrian
19843902Sbrian	dc = tty_softc(tp);
19943902Sbrian
20043902Sbrian	while (ttydisc_getc(tp, &ch, sizeof ch) != 0)
20143902Sbrian		dcons_os_putc(dc, ch);
20243902Sbrian}
20343902Sbrian
20443902Sbrianstatic void
20543902Sbriandcons_timeout(void *v)
20643902Sbrian{
20743902Sbrian	struct	tty *tp;
20843902Sbrian	struct dcons_softc *dc;
20943902Sbrian	int i, c, polltime;
21043902Sbrian
21143902Sbrian	for (i = 0; i < DCONS_NPORT; i ++) {
21243902Sbrian		dc = &sc[i];
21343902Sbrian		tp = dc->tty;
21443902Sbrian
21543902Sbrian		tty_lock(tp);
21643902Sbrian		while ((c = dcons_os_checkc_nopoll(dc)) != -1)
21743902Sbrian			ttydisc_rint(tp, c, 0);
21843902Sbrian		ttydisc_rint_done(tp);
21943902Sbrian		tty_unlock(tp);
22043902Sbrian	}
22143902Sbrian	polltime = hz / poll_hz;
22243902Sbrian	if (polltime < 1)
22343902Sbrian		polltime = 1;
22443902Sbrian	callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
22543902Sbrian}
22643902Sbrian
22743902Sbrianstatic void
22843902Sbriandcons_cnprobe(struct consdev *cp)
22943902Sbrian{
23043902Sbrian	sprintf(cp->cn_name, "dcons");
23143902Sbrian#if DCONS_FORCE_CONSOLE
23243902Sbrian	cp->cn_pri = CN_REMOTE;
23343902Sbrian#else
23443902Sbrian	cp->cn_pri = CN_NORMAL;
23543902Sbrian#endif
23643902Sbrian}
23743902Sbrian
23843902Sbrianstatic void
23943902Sbriandcons_cninit(struct consdev *cp)
24043902Sbrian{
24143902Sbrian	dcons_drv_init(0);
24243902Sbrian	cp->cn_arg = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
24343902Sbrian}
24443902Sbrian
24543902Sbrianstatic void
24643902Sbriandcons_cnterm(struct consdev *cp)
24743902Sbrian{
24843902Sbrian}
24943902Sbrian
25049141Sbrianstatic int
25149141Sbriandcons_cngetc(struct consdev *cp)
25249141Sbrian{
25349141Sbrian	struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
25449141Sbrian	return (dcons_os_checkc(dc));
25549141Sbrian}
25649141Sbrian
25749141Sbrianstatic void
25849141Sbriandcons_cnputc(struct consdev *cp, int c)
25949141Sbrian{
26049141Sbrian	struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
26149141Sbrian	dcons_os_putc(dc, c);
26249141Sbrian}
26349141Sbrian
26449141Sbrianstatic int
26549141Sbriandcons_drv_init(int stage)
26649141Sbrian{
26749141Sbrian#if defined(__i386__) || defined(__amd64__)
26849141Sbrian	quad_t addr, size;
26949141Sbrian#endif
27049141Sbrian
27149141Sbrian	if (drv_init)
27249141Sbrian		return(drv_init);
27349141Sbrian
27449141Sbrian	drv_init = -1;
27549141Sbrian
27649141Sbrian	bzero(&dg, sizeof(dg));
27749141Sbrian	dcons_conf = &dg;
27849141Sbrian	dg.cdev = &dcons_consdev;
27943902Sbrian	dg.buf = NULL;
28049141Sbrian	dg.size = DCONS_BUF_SIZE;
28149141Sbrian
28249141Sbrian#if defined(__i386__) || defined(__amd64__)
28349141Sbrian	if (getenv_quad("dcons.addr", &addr) > 0 &&
28449141Sbrian	    getenv_quad("dcons.size", &size) > 0) {
28549141Sbrian#ifdef __i386__
28649141Sbrian		vm_paddr_t pa;
28749141Sbrian		/*
28849141Sbrian		 * Allow read/write access to dcons buffer.
28949141Sbrian		 */
29049141Sbrian		for (pa = trunc_page(addr); pa < addr + size; pa += PAGE_SIZE)
29149141Sbrian			*vtopte(KERNBASE + pa) |= PG_RW;
29249141Sbrian		invltlb();
29349141Sbrian#endif
29449141Sbrian		/* XXX P to V */
29549141Sbrian		dg.buf = (struct dcons_buf *)(vm_offset_t)(KERNBASE + addr);
29649141Sbrian		dg.size = size;
29749141Sbrian		if (dcons_load_buffer(dg.buf, dg.size, sc) < 0)
29849141Sbrian			dg.buf = NULL;
29949141Sbrian	}
30049141Sbrian#endif
30149141Sbrian	if (dg.buf != NULL)
30249141Sbrian		goto ok;
30349141Sbrian
30449141Sbrian#ifndef KLD_MODULE
30549141Sbrian	if (stage == 0) { /* XXX or cold */
30649141Sbrian		/*
30749141Sbrian		 * DCONS_FORCE_CONSOLE == 1 and statically linked.
30849141Sbrian		 * called from cninit(). can't use contigmalloc yet .
30949141Sbrian		 */
31049141Sbrian		dg.buf = (struct dcons_buf *) bssbuf;
31149141Sbrian		dcons_init(dg.buf, dg.size, sc);
31249141Sbrian	} else
31349141Sbrian#endif
31449141Sbrian	{
31549141Sbrian		/*
31649141Sbrian		 * DCONS_FORCE_CONSOLE == 0 or kernel module case.
31749141Sbrian		 * if the module is loaded after boot,
31849141Sbrian		 * bssbuf could be non-continuous.
31949141Sbrian		 */
32049141Sbrian		dg.buf = (struct dcons_buf *) contigmalloc(dg.size,
32149141Sbrian			M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
32249141Sbrian		if (dg.buf == NULL)
32349141Sbrian			return (-1);
32449141Sbrian		dcons_init(dg.buf, dg.size, sc);
32549141Sbrian	}
32649141Sbrian
32749141Sbrianok:
32849141Sbrian	dcons_buf = dg.buf;
32949141Sbrian
33049141Sbrian	drv_init = 1;
33149141Sbrian
33249141Sbrian	return 0;
33349141Sbrian}
33449141Sbrian
33549141Sbrian
33649141Sbrianstatic int
33749141Sbriandcons_attach_port(int port, char *name, int flags)
33849141Sbrian{
33943902Sbrian	struct dcons_softc *dc;
34043902Sbrian	struct tty *tp;
34143948Sbrian
34243948Sbrian	dc = &sc[port];
34343948Sbrian	tp = tty_alloc(&dcons_ttydevsw, dc);
34443948Sbrian	dc->flags = flags;
34543948Sbrian	dc->tty   = tp;
34643948Sbrian	tty_init_console(tp, 0);
34743948Sbrian	tty_makedev(tp, NULL, "%s", name);
34843948Sbrian	return(0);
34943948Sbrian}
35043948Sbrian
35143948Sbrianstatic int
35243948Sbriandcons_attach(void)
35343948Sbrian{
35443948Sbrian	int polltime;
35543948Sbrian
35643948Sbrian	dcons_attach_port(DCONS_CON, "dcons", 0);
35743948Sbrian	dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
35843948Sbrian	callout_init(&dcons_callout, CALLOUT_MPSAFE);
35943948Sbrian	polltime = hz / poll_hz;
36043948Sbrian	if (polltime < 1)
36143948Sbrian		polltime = 1;
36243948Sbrian	callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
36343948Sbrian	return(0);
36443948Sbrian}
36543948Sbrian
36643948Sbrianstatic int
36743948Sbriandcons_detach(int port)
36843948Sbrian{
36943948Sbrian	struct	tty *tp;
37043902Sbrian	struct dcons_softc *dc;
37143948Sbrian
37243902Sbrian	dc = &sc[port];
37343948Sbrian	tp = dc->tty;
37443902Sbrian
37543902Sbrian	tty_lock(tp);
37643902Sbrian	tty_rel_gone(tp);
37743902Sbrian
37843948Sbrian	return(0);
37943902Sbrian}
38043902Sbrian
38143948Sbrianstatic int
38243948Sbriandcons_modevent(module_t mode, int type, void *data)
38343948Sbrian{
38443948Sbrian	int err = 0, ret;
38543948Sbrian
38643948Sbrian	switch (type) {
38743948Sbrian	case MOD_LOAD:
38843902Sbrian		ret = dcons_drv_init(1);
38943948Sbrian		if (ret != -1)
39043948Sbrian			dcons_attach();
39143948Sbrian		if (ret == 0) {
39243948Sbrian			dcons_cnprobe(&dcons_consdev);
39343948Sbrian			dcons_cninit(&dcons_consdev);
39443902Sbrian			cnadd(&dcons_consdev);
39543902Sbrian		}
39643902Sbrian		break;
39743902Sbrian	case MOD_UNLOAD:
39843902Sbrian		printf("dcons: unload\n");
39943902Sbrian		if (drv_init == 1) {
40043902Sbrian			callout_stop(&dcons_callout);
40143902Sbrian			cnremove(&dcons_consdev);
40243902Sbrian			dcons_detach(DCONS_CON);
40343902Sbrian			dcons_detach(DCONS_GDB);
40443902Sbrian			dg.buf->magic = 0;
40543902Sbrian
40643902Sbrian			contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
40746103Sbrian		}
40843902Sbrian
40943902Sbrian		break;
41043902Sbrian	case MOD_SHUTDOWN:
41143902Sbrian#if 0		/* Keep connection after halt */
41243902Sbrian		dg.buf->magic = 0;
41343902Sbrian#endif
41443902Sbrian		break;
41543902Sbrian	default:
41643902Sbrian		err = EOPNOTSUPP;
41743902Sbrian		break;
41843902Sbrian	}
41943902Sbrian	return(err);
42043902Sbrian}
42143902Sbrian
42243902Sbrian#if defined(GDB)
42343902Sbrian/* Debugger interface */
42443902Sbrian
42543902Sbrianstatic int
42643948Sbriandcons_os_getc(struct dcons_softc *dc)
42743948Sbrian{
42843948Sbrian	int c;
42943902Sbrian
43043902Sbrian	while ((c = dcons_os_checkc(dc)) == -1);
43143902Sbrian
43243902Sbrian	return (c & 0xff);
43345070Sbrian}
43445070Sbrian
43543902Sbrianstatic int
43643902Sbriandcons_dbg_probe(void)
43743902Sbrian{
43843902Sbrian	int dcons_gdb;
43943902Sbrian
44043902Sbrian	if (getenv_int("dcons_gdb", &dcons_gdb) == 0)
44143902Sbrian		return (-1);
44243902Sbrian	return (dcons_gdb);
44343902Sbrian}
44443902Sbrian
44543902Sbrianstatic void
44643902Sbriandcons_dbg_init(void)
44743902Sbrian{
44843902Sbrian}
44943902Sbrian
45043902Sbrianstatic void
45143902Sbriandcons_dbg_term(void)
45243902Sbrian{
45343902Sbrian}
45443902Sbrian
45543902Sbrianstatic void
45643902Sbriandcons_dbg_putc(int c)
45743902Sbrian{
45843948Sbrian	struct dcons_softc *dc = &sc[DCONS_GDB];
45943948Sbrian	dcons_os_putc(dc, c);
46043902Sbrian}
46143948Sbrian
46243948Sbrianstatic int
46343902Sbriandcons_dbg_getc(void)
46443948Sbrian{
46543948Sbrian	struct dcons_softc *dc = &sc[DCONS_GDB];
46643902Sbrian	return (dcons_os_getc(dc));
46743902Sbrian}
46843902Sbrian#endif
46943902Sbrian
47043902SbrianDEV_MODULE(dcons, dcons_modevent, NULL);
47143902SbrianMODULE_VERSION(dcons, DCONS_VERSION);
47243902Sbrian