dcons_os.c revision 190771
167754Smsmith/*-
267754Smsmith * Copyright (C) 2003,2004
377424Smsmith * 	Hidetoshi Shimokawa. All rights reserved.
4123315Snjl *
567754Smsmith * Redistribution and use in source and binary forms, with or without
667754Smsmith * modification, are permitted provided that the following conditions
767754Smsmith * are met:
867754Smsmith * 1. Redistributions of source code must retain the above copyright
967754Smsmith *    notice, this list of conditions and the following disclaimer.
1067754Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1167754Smsmith *    notice, this list of conditions and the following disclaimer in the
12114237Snjl *    documentation and/or other materials provided with the distribution.
1370243Smsmith * 3. All advertising materials mentioning features or use of this software
1467754Smsmith *    must display the following acknowledgement:
1567754Smsmith *
1667754Smsmith *	This product includes software developed by Hidetoshi Shimokawa.
1767754Smsmith *
1867754Smsmith * 4. Neither the name of the author nor the names of its contributors
1967754Smsmith *    may be used to endorse or promote products derived from this software
2067754Smsmith *    without specific prior written permission.
2167754Smsmith *
2267754Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2367754Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2467754Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2567754Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2667754Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2767754Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2867754Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2967754Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3067754Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3167754Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3267754Smsmith * SUCH DAMAGE.
3367754Smsmith *
3467754Smsmith * $FreeBSD: head/sys/dev/dcons/dcons_os.c 190771 2009-04-06 13:09:02Z ed $
3567754Smsmith */
3667754Smsmith
3767754Smsmith#include <sys/param.h>
3867754Smsmith#include <sys/kdb.h>
3967754Smsmith#include <gdb/gdb.h>
4067754Smsmith#include <sys/kernel.h>
4167754Smsmith#include <sys/module.h>
4267754Smsmith#include <sys/systm.h>
4367754Smsmith#include <sys/types.h>
4467754Smsmith#include <sys/conf.h>
4567754Smsmith#include <sys/cons.h>
4667754Smsmith#include <sys/consio.h>
4767754Smsmith#include <sys/tty.h>
4867754Smsmith#include <sys/malloc.h>
4967754Smsmith#include <sys/priv.h>
5067754Smsmith#include <sys/proc.h>
5167754Smsmith#include <sys/ucred.h>
5267754Smsmith
5367754Smsmith#include <machine/bus.h>
5467754Smsmith
5567754Smsmith#include <dev/dcons/dcons.h>
5667754Smsmith#include <dev/dcons/dcons_os.h>
5767754Smsmith
5867754Smsmith#include <ddb/ddb.h>
5967754Smsmith#include <sys/reboot.h>
6067754Smsmith
6167754Smsmith#include <sys/sysctl.h>
6267754Smsmith
6367754Smsmith#include <vm/vm.h>
6467754Smsmith#include <vm/vm_param.h>
6567754Smsmith#include <vm/pmap.h>
6667754Smsmith
6767754Smsmith#include "opt_comconsole.h"
6867754Smsmith#include "opt_dcons.h"
6967754Smsmith#include "opt_kdb.h"
7067754Smsmith#include "opt_gdb.h"
7167754Smsmith#include "opt_ddb.h"
7267754Smsmith
7367754Smsmith
7467754Smsmith#ifndef DCONS_POLL_HZ
7567754Smsmith#define DCONS_POLL_HZ	25
7667754Smsmith#endif
7767754Smsmith
7867754Smsmith#ifndef DCONS_BUF_SIZE
7967754Smsmith#define DCONS_BUF_SIZE (16*1024)
8067754Smsmith#endif
8167754Smsmith
8267754Smsmith#ifndef DCONS_FORCE_CONSOLE
8367754Smsmith#define DCONS_FORCE_CONSOLE	0	/* Mostly for FreeBSD-4/DragonFly */
8467754Smsmith#endif
8567754Smsmith
8667754Smsmith#ifndef KLD_MODULE
8767754Smsmithstatic char bssbuf[DCONS_BUF_SIZE];	/* buf in bss */
8867754Smsmith#endif
8967754Smsmith
9067754Smsmith/* global data */
9167754Smsmithstatic struct dcons_global dg;
9267754Smsmithstruct dcons_global *dcons_conf;
9367754Smsmithstatic int poll_hz = DCONS_POLL_HZ;
9467754Smsmith
9567754Smsmithstatic struct dcons_softc sc[DCONS_NPORT];
9667754Smsmith
9767754SmsmithSYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console");
9867754SmsmithSYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0,
9967754Smsmith				"dcons polling rate");
10067754Smsmith
10167754Smsmithstatic int drv_init = 0;
10267754Smsmithstatic struct callout dcons_callout;
10367754Smsmithstruct dcons_buf *dcons_buf;		/* for local dconschat */
10467754Smsmith
10567754Smsmithstatic void	dcons_timeout(void *);
10667754Smsmithstatic int	dcons_drv_init(int);
10767754Smsmith
10867754Smsmithstatic cn_probe_t	dcons_cnprobe;
10967754Smsmithstatic cn_init_t	dcons_cninit;
11067754Smsmithstatic cn_term_t	dcons_cnterm;
11167754Smsmithstatic cn_getc_t	dcons_cngetc;
11267754Smsmithstatic cn_putc_t	dcons_cnputc;
11367754Smsmith
11467754SmsmithCONSOLE_DRIVER(dcons);
11567754Smsmith
11667754Smsmith#if defined(GDB)
11777424Smsmithstatic gdb_probe_f	dcons_dbg_probe;
11867754Smsmithstatic gdb_init_f	dcons_dbg_init;
11967754Smsmithstatic gdb_term_f	dcons_dbg_term;
12067754Smsmithstatic gdb_getc_f	dcons_dbg_getc;
12167754Smsmithstatic gdb_putc_f	dcons_dbg_putc;
12267754Smsmith
12377424SmsmithGDB_DBGPORT(dcons, dcons_dbg_probe, dcons_dbg_init, dcons_dbg_term,
12491116Smsmith    dcons_dbg_getc, dcons_dbg_putc);
12567754Smsmith
12667754Smsmithextern struct gdb_dbgport *gdb_cur;
12767754Smsmith#endif
12867754Smsmith
12980062Smsmithstatic tsw_outwakeup_t dcons_outwakeup;
13080062Smsmith
13180062Smsmithstatic struct ttydevsw dcons_ttydevsw = {
13280062Smsmith	.tsw_flags      = TF_NOPREFIX,
13380062Smsmith	.tsw_outwakeup  = dcons_outwakeup,
13480062Smsmith};
13580062Smsmith
13680062Smsmith#if (defined(GDB) || defined(DDB)) && defined(ALT_BREAK_TO_DEBUGGER)
13780062Smsmithstatic int
13880062Smsmithdcons_check_break(struct dcons_softc *dc, int c)
13980062Smsmith{
14080062Smsmith	int kdb_brk;
14180062Smsmith
14280062Smsmith	if (c < 0)
14383174Smsmith		return (c);
14480062Smsmith
14580062Smsmith	if ((kdb_brk = kdb_alt_break(c, &dc->brk_state)) != 0) {
14680062Smsmith		switch (kdb_brk) {
14799146Siwasaki		case KDB_REQ_DEBUGGER:
14899146Siwasaki			if ((dc->flags & DC_GDB) != 0) {
14999146Siwasaki#ifdef GDB
15080062Smsmith				if (gdb_cur == &dcons_gdb_dbgport) {
15180062Smsmith					kdb_dbbe_select("gdb");
15280062Smsmith					kdb_enter(KDB_WHY_BREAK,
15380062Smsmith					    "Break sequence on dcons gdb port");
15480062Smsmith				}
15580062Smsmith#endif
15680062Smsmith			} else
15780062Smsmith				kdb_enter(KDB_WHY_BREAK,
15880062Smsmith				    "Break sequence on dcons console port");
15999146Siwasaki			break;
16080062Smsmith		case KDB_REQ_PANIC:
16199146Siwasaki			kdb_panic("Panic sequence on dcons console port");
16280062Smsmith			break;
16380062Smsmith		case KDB_REQ_REBOOT:
16480062Smsmith			kdb_reboot();
16580062Smsmith			break;
16680062Smsmith		}
16780062Smsmith	}
16899146Siwasaki	return (c);
16980062Smsmith}
17099146Siwasaki#else
17180062Smsmith#define	dcons_check_break(dc, c)	(c)
17280062Smsmith#endif
17380062Smsmith
17480062Smsmithstatic int
17580062Smsmithdcons_os_checkc_nopoll(struct dcons_softc *dc)
17680062Smsmith{
17799146Siwasaki	int c;
17880062Smsmith
17999146Siwasaki	if (dg.dma_tag != NULL)
18080062Smsmith		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTREAD);
18180062Smsmith
18280062Smsmith	c = dcons_check_break(dc, dcons_checkc(dc));
18380062Smsmith
18480062Smsmith	if (dg.dma_tag != NULL)
18580062Smsmith		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREREAD);
18699146Siwasaki
18780062Smsmith	return (c);
18899146Siwasaki}
18980062Smsmith
19080062Smsmithstatic int
19180062Smsmithdcons_os_checkc(struct dcons_softc *dc)
19280062Smsmith{
19380062Smsmith	EVENTHANDLER_INVOKE(dcons_poll, 0);
19480062Smsmith	return (dcons_os_checkc_nopoll(dc));
19599146Siwasaki}
19680062Smsmith
19799146Siwasakistatic void
19880062Smsmithdcons_os_putc(struct dcons_softc *dc, int c)
19980062Smsmith{
20099146Siwasaki	if (dg.dma_tag != NULL)
20180062Smsmith		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_POSTWRITE);
20280062Smsmith
20380062Smsmith	dcons_putc(dc, c);
20480062Smsmith
20599146Siwasaki	if (dg.dma_tag != NULL)
20699146Siwasaki		bus_dmamap_sync(dg.dma_tag, dg.dma_map, BUS_DMASYNC_PREWRITE);
20799146Siwasaki}
20899146Siwasaki
20999146Siwasakistatic void
21080062Smsmithdcons_outwakeup(struct tty *tp)
21180062Smsmith{
21280062Smsmith	struct dcons_softc *dc;
21380062Smsmith	char ch;
21480062Smsmith
21567754Smsmith	dc = tty_softc(tp);
21667754Smsmith
21767754Smsmith	while (ttydisc_getc(tp, &ch, sizeof ch) != 0)
21867754Smsmith		dcons_os_putc(dc, ch);
21967754Smsmith}
22067754Smsmith
22167754Smsmithstatic void
22267754Smsmithdcons_timeout(void *v)
22367754Smsmith{
22467754Smsmith	struct	tty *tp;
22567754Smsmith	struct dcons_softc *dc;
226102550Siwasaki	int i, c, polltime;
227114237Snjl
22867754Smsmith	for (i = 0; i < DCONS_NPORT; i ++) {
229114237Snjl		dc = &sc[i];
23067754Smsmith		tp = dc->tty;
23167754Smsmith
23267754Smsmith		tty_lock(tp);
23367754Smsmith		while ((c = dcons_os_checkc_nopoll(dc)) != -1)
23477424Smsmith			ttydisc_rint(tp, c, 0);
23567754Smsmith		ttydisc_rint_done(tp);
23667754Smsmith		tty_unlock(tp);
23767754Smsmith	}
23867754Smsmith	polltime = hz / poll_hz;
23967754Smsmith	if (polltime < 1)
24067754Smsmith		polltime = 1;
241114237Snjl	callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
24267754Smsmith}
24367754Smsmith
24467754Smsmithstatic void
24567754Smsmithdcons_cnprobe(struct consdev *cp)
24667754Smsmith{
24767754Smsmith	sprintf(cp->cn_name, "dcons");
24887031Smsmith#if DCONS_FORCE_CONSOLE
24983174Smsmith	cp->cn_pri = CN_REMOTE;
25067754Smsmith#else
25167754Smsmith	cp->cn_pri = CN_NORMAL;
25283174Smsmith#endif
25367754Smsmith}
254114237Snjl
255114237Snjlstatic void
256114237Snjldcons_cninit(struct consdev *cp)
257114237Snjl{
258114237Snjl	dcons_drv_init(0);
259114237Snjl	cp->cn_arg = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
260114237Snjl}
26169450Smsmith
26269450Smsmithstatic void
26367754Smsmithdcons_cnterm(struct consdev *cp)
26467754Smsmith{
26567754Smsmith}
26667754Smsmith
26767754Smsmithstatic int
26867754Smsmithdcons_cngetc(struct consdev *cp)
26967754Smsmith{
27067754Smsmith	struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
271104470Siwasaki	return (dcons_os_checkc(dc));
27267754Smsmith}
27367754Smsmith
274104470Siwasakistatic void
27577424Smsmithdcons_cnputc(struct consdev *cp, int c)
27677424Smsmith{
27777424Smsmith	struct dcons_softc *dc = (struct dcons_softc *)cp->cn_arg;
27877424Smsmith	dcons_os_putc(dc, c);
27967754Smsmith}
28067754Smsmith
28191116Smsmithstatic int
28267754Smsmithdcons_drv_init(int stage)
283107325Siwasaki{
284107325Siwasaki#if defined(__i386__) || defined(__amd64__)
28599679Siwasaki	quad_t addr, size;
286107325Siwasaki#endif
287107325Siwasaki
28899146Siwasaki	if (drv_init)
28999146Siwasaki		return(drv_init);
29099146Siwasaki
291102550Siwasaki	drv_init = -1;
292102550Siwasaki
293102550Siwasaki	bzero(&dg, sizeof(dg));
294102550Siwasaki	dcons_conf = &dg;
29599679Siwasaki	dg.cdev = &dcons_consdev;
29667754Smsmith	dg.buf = NULL;
29767754Smsmith	dg.size = DCONS_BUF_SIZE;
29867754Smsmith
29967754Smsmith#if defined(__i386__) || defined(__amd64__)
30067754Smsmith	if (getenv_quad("dcons.addr", &addr) > 0 &&
301104470Siwasaki	    getenv_quad("dcons.size", &size) > 0) {
30267754Smsmith#ifdef __i386__
30367754Smsmith		vm_paddr_t pa;
30491116Smsmith		/*
30567754Smsmith		 * Allow read/write access to dcons buffer.
30691116Smsmith		 */
30791116Smsmith		for (pa = trunc_page(addr); pa < addr + size; pa += PAGE_SIZE)
30891116Smsmith			*vtopte(KERNBASE + pa) |= PG_RW;
30991116Smsmith		invltlb();
31099146Siwasaki#endif
31191116Smsmith		/* XXX P to V */
31299146Siwasaki		dg.buf = (struct dcons_buf *)(vm_offset_t)(KERNBASE + addr);
31399146Siwasaki		dg.size = size;
31499146Siwasaki		if (dcons_load_buffer(dg.buf, dg.size, sc) < 0)
31599146Siwasaki			dg.buf = NULL;
31699146Siwasaki	}
31799146Siwasaki#endif
31899146Siwasaki	if (dg.buf != NULL)
31999146Siwasaki		goto ok;
32091116Smsmith
32191116Smsmith#ifndef KLD_MODULE
32291116Smsmith	if (stage == 0) { /* XXX or cold */
32391116Smsmith		/*
32491116Smsmith		 * DCONS_FORCE_CONSOLE == 1 and statically linked.
32591116Smsmith		 * called from cninit(). can't use contigmalloc yet .
32691116Smsmith		 */
32791116Smsmith		dg.buf = (struct dcons_buf *) bssbuf;
32891116Smsmith		dcons_init(dg.buf, dg.size, sc);
32991116Smsmith	} else
33091116Smsmith#endif
33191116Smsmith	{
332107325Siwasaki		/*
333107325Siwasaki		 * DCONS_FORCE_CONSOLE == 0 or kernel module case.
334107325Siwasaki		 * if the module is loaded after boot,
335107325Siwasaki		 * bssbuf could be non-continuous.
33667754Smsmith		 */
33767754Smsmith		dg.buf = (struct dcons_buf *) contigmalloc(dg.size,
33867754Smsmith			M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
33971867Smsmith		if (dg.buf == NULL)
34071867Smsmith			return (-1);
341114237Snjl		dcons_init(dg.buf, dg.size, sc);
34271867Smsmith	}
34399679Siwasaki
34471867Smsmithok:
34583174Smsmith	dcons_buf = dg.buf;
34683174Smsmith
34783174Smsmith	drv_init = 1;
34883174Smsmith
34983174Smsmith	return 0;
35083174Smsmith}
35183174Smsmith
35283174Smsmith
35383174Smsmithstatic int
35483174Smsmithdcons_attach_port(int port, char *name, int flags)
35583174Smsmith{
35683174Smsmith	struct dcons_softc *dc;
35783174Smsmith	struct tty *tp;
35883174Smsmith
35999679Siwasaki	dc = &sc[port];
36082367Smsmith	tp = tty_alloc(&dcons_ttydevsw, dc, NULL);
36182367Smsmith	dc->flags = flags;
36282367Smsmith	dc->tty   = tp;
36382367Smsmith	tty_init_console(tp, 0);
36471867Smsmith	tty_makedev(tp, NULL, "%s", name);
36582367Smsmith	return(0);
36682367Smsmith}
36782367Smsmith
36883174Smsmithstatic int
36967754Smsmithdcons_attach(void)
37067754Smsmith{
37191116Smsmith	int polltime;
37267754Smsmith
37369450Smsmith	dcons_attach_port(DCONS_CON, "dcons", 0);
37469450Smsmith	dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
37569450Smsmith	callout_init(&dcons_callout, CALLOUT_MPSAFE);
37669450Smsmith	polltime = hz / poll_hz;
37791116Smsmith	if (polltime < 1)
37891116Smsmith		polltime = 1;
37991116Smsmith	callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
38067754Smsmith	return(0);
38167754Smsmith}
38267754Smsmith
383117521Snjlstatic int
38467754Smsmithdcons_detach(int port)
38567754Smsmith{
386117521Snjl	struct	tty *tp;
38767754Smsmith	struct dcons_softc *dc;
388100966Siwasaki
38967754Smsmith	dc = &sc[port];
390100966Siwasaki	tp = dc->tty;
391123315Snjl
392123315Snjl	tty_lock(tp);
393123315Snjl	tty_rel_gone(tp);
394100966Siwasaki
395100966Siwasaki	return(0);
396100966Siwasaki}
39767754Smsmith
39867754Smsmithstatic int
39967754Smsmithdcons_modevent(module_t mode, int type, void *data)
40091116Smsmith{
40191116Smsmith	int err = 0, ret;
40291116Smsmith
40391116Smsmith	switch (type) {
40491116Smsmith	case MOD_LOAD:
40591116Smsmith		ret = dcons_drv_init(1);
40691116Smsmith		if (ret != -1)
40791116Smsmith			dcons_attach();
40891116Smsmith		if (ret == 0) {
40991116Smsmith			dcons_cnprobe(&dcons_consdev);
41091116Smsmith			dcons_cninit(&dcons_consdev);
41191116Smsmith			cnadd(&dcons_consdev);
41291116Smsmith		}
41391116Smsmith		break;
41491116Smsmith	case MOD_UNLOAD:
41591116Smsmith		printf("dcons: unload\n");
41691116Smsmith		if (drv_init == 1) {
41791116Smsmith			callout_stop(&dcons_callout);
41891116Smsmith			cnremove(&dcons_consdev);
41991116Smsmith			dcons_detach(DCONS_CON);
42091116Smsmith			dcons_detach(DCONS_GDB);
42191116Smsmith			dg.buf->magic = 0;
42291116Smsmith
42391116Smsmith			contigfree(dg.buf, DCONS_BUF_SIZE, M_DEVBUF);
42491116Smsmith		}
42591116Smsmith
42691116Smsmith		break;
42791116Smsmith	case MOD_SHUTDOWN:
42891116Smsmith#if 0		/* Keep connection after halt */
42991116Smsmith		dg.buf->magic = 0;
43091116Smsmith#endif
43191116Smsmith		break;
43291116Smsmith	default:
43391116Smsmith		err = EOPNOTSUPP;
43491116Smsmith		break;
43591116Smsmith	}
43691116Smsmith	return(err);
43791116Smsmith}
43891116Smsmith
43991116Smsmith#if defined(GDB)
44091116Smsmith/* Debugger interface */
44191116Smsmith
442123315Snjlstatic int
44391116Smsmithdcons_os_getc(struct dcons_softc *dc)
44491116Smsmith{
44591116Smsmith	int c;
44691116Smsmith
44791116Smsmith	while ((c = dcons_os_checkc(dc)) == -1);
44891116Smsmith
44991116Smsmith	return (c & 0xff);
45091116Smsmith}
45191116Smsmith
45291116Smsmithstatic int
45391116Smsmithdcons_dbg_probe(void)
45491116Smsmith{
45591116Smsmith	int dcons_gdb;
45691116Smsmith
45791116Smsmith	if (getenv_int("dcons_gdb", &dcons_gdb) == 0)
45891116Smsmith		return (-1);
459114237Snjl	return (dcons_gdb);
46091116Smsmith}
461114237Snjl
46291116Smsmithstatic void
46391116Smsmithdcons_dbg_init(void)
464100966Siwasaki{
46591116Smsmith}
46691116Smsmith
46791116Smsmithstatic void
468100966Siwasakidcons_dbg_term(void)
469114237Snjl{
470114237Snjl}
47191116Smsmith
47291116Smsmithstatic void
47391116Smsmithdcons_dbg_putc(int c)
474114237Snjl{
47591116Smsmith	struct dcons_softc *dc = &sc[DCONS_GDB];
47691116Smsmith	dcons_os_putc(dc, c);
47791116Smsmith}
47891116Smsmith
47991116Smsmithstatic int
48091116Smsmithdcons_dbg_getc(void)
48191116Smsmith{
48291116Smsmith	struct dcons_softc *dc = &sc[DCONS_GDB];
48391116Smsmith	return (dcons_os_getc(dc));
48491116Smsmith}
48591116Smsmith#endif
486114237Snjl
48791116SmsmithDEV_MODULE(dcons, dcons_modevent, NULL);
48891116SmsmithMODULE_VERSION(dcons, DCONS_VERSION);
489114237Snjl