summaryrefslogtreecommitdiffstats
path: root/drivers/char/hvc_lguest.c
blob: feeccbaec4382c55c1583fcad49cf73264a407ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*D:300
 * The Guest console driver
 *
 * This is a trivial console driver: we use lguest's DMA mechanism to send
 * bytes out, and register a DMA buffer to receive bytes in.  It is assumed to
 * be present and available from the very beginning of boot.
 *
 * Writing console drivers is one of the few remaining Dark Arts in Linux.
 * Fortunately for us, the path of virtual consoles has been well-trodden by
 * the PowerPC folks, who wrote "hvc_console.c" to generically support any
 * virtual console.  We use that infrastructure which only requires us to write
 * the basic put_chars and get_chars functions and call the right register
 * functions.
 :*/

/*M:002 The console can be flooded: while the Guest is processing input the
 * Host can send more.  Buffering in the Host could alleviate this, but it is a
 * difficult problem in general. :*/
/* Copyright (C) 2006 Rusty Russell, IBM Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <linux/err.h>
#include <linux/init.h>
#include <linux/lguest_bus.h>
#include "hvc_console.h"

/*D:340 This is our single console input buffer, with associated "struct
 * lguest_dma" referring to it.  Note the 0-terminated length array, and the
 * use of physical address for the buffer itself. */
static char inbuf[256];
static struct lguest_dma cons_input = { .used_len = 0,
					.addr[0] = __pa(inbuf),
					.len[0] = sizeof(inbuf),
					.len[1] = 0 };

/*D:310 The put_chars() callback is pretty straightforward.
 *
 * First we put the pointer and length in a "struct lguest_dma": we only have
 * one pointer, so we set the second length to 0.  Then we use SEND_DMA to send
 * the data to (Host) buffers attached to the console key.  Usually a device's
 * key is a physical address within the device's memory, but because the
 * console device doesn't have any associated physical memory, we use the
 * LGUEST_CONSOLE_DMA_KEY constant (aka 0). */
static int put_chars(u32 vtermno, const char *buf, int count)
{
	struct lguest_dma dma;

	/* FIXME: DMA buffers in a "struct lguest_dma" are not allowed
	 * to go over page boundaries.  This never seems to happen,
	 * but if it did we'd need to fix this code. */
	dma.len[0] = count;
	dma.len[1] = 0;
	dma.addr[0] = __pa(buf);

	lguest_send_dma(LGUEST_CONSOLE_DMA_KEY, &dma);
	/* We're expected to return the amount of data we wrote: all of it. */
	return count;
}

/*D:350 get_chars() is the callback from the hvc_console infrastructure when
 * an interrupt is received.
 *
 * Firstly we see if our buffer has been filled: if not, we return.  The rest
 * of the code deals with the fact that the hvc_console() infrastructure only
 * asks us for 16 bytes at a time.  We keep a "cons_offset" variable for
 * partially-read buffers. */
static int get_chars(u32 vtermno, char *buf, int count)
{
	static int cons_offset;

	/* Nothing left to see here... */
	if (!cons_input.used_len)
		return 0;

	/* You want more than we have to give?  Well, try wanting less! */
	if (cons_input.used_len - cons_offset < count)
		count = cons_input.used_len - cons_offset;

	/* Copy across to their buffer and increment offset. */
	memcpy(buf, inbuf + cons_offset, count);
	cons_offset += count;

	/* Finished?  Zero offset, and reset cons_input so Host will use it
	 * again. */
	if (cons_offset == cons_input.used_len) {
		cons_offset = 0;
		cons_input.used_len = 0;
	}
	return count;
}
/*:*/

static struct hv_ops lguest_cons = {
	.get_chars = get_chars,
	.put_chars = put_chars,
};

/*D:320 Console drivers are initialized very early so boot messages can go
 * out.  At this stage, the console is output-only.  Our driver checks we're a
 * Guest, and if so hands hvc_instantiate() the console number (0), priority
 * (0), and the struct hv_ops containing the put_chars() function. */
static int __init cons_init(void)
{
	if (strcmp(paravirt_ops.name, "lguest") != 0)
		return 0;

	return hvc_instantiate(0, 0, &lguest_cons);
}
console_initcall(cons_init);

/*D:370 To set up and manage our virtual console, we call hvc_alloc() and
 * stash the result in the private pointer of the "struct lguest_device".
 * Since we never remove the console device we never need this pointer again,
 * but using ->private is considered good form, and you never know who's going
 * to copy your driver.
 *
 * Once the console is set up, we bind our input buffer ready for input. */
static int lguestcons_probe(struct lguest_device *lgdev)
{
	int err;

	/* The first argument of hvc_alloc() is the virtual console number, so
	 * we use zero.  The second argument is the interrupt number.
	 *
	 * The third argument is a "struct hv_ops" containing the put_chars()
	 * and get_chars() pointers.  The final argument is the output buffer
	 * size: we use 256 and expect the Host to have room for us to send
	 * that much. */
	lgdev->private = hvc_alloc(0, lgdev_irq(lgdev), &lguest_cons, 256);
	if (IS_ERR(lgdev->private))
		return PTR_ERR(lgdev->private);

	/* We bind a single DMA buffer at key LGUEST_CONSOLE_DMA_KEY.
	 * "cons_input" is that statically-initialized global DMA buffer we saw
	 * above, and we also give the interrupt we want. */
	err = lguest_bind_dma(LGUEST_CONSOLE_DMA_KEY, &cons_input, 1,
			      lgdev_irq(lgdev));
	if (err)
		printk("lguest console: failed to bind buffer.\n");
	return err;
}
/* Note the use of lgdev_irq() for the interrupt number.  We tell hvc_alloc()
 * to expect input when this interrupt is triggered, and then tell
 * lguest_bind_dma() that is the interrupt to send us when input comes in. */

/*D:360 From now on the console driver follows standard Guest driver form:
 * register_lguest_driver() registers the device type and probe function, and
 * the probe function sets up the device.
 *
 * The standard "struct lguest_driver": */
static struct lguest_driver lguestcons_drv = {
	.name = "lguestcons",
	.owner = THIS_MODULE,
	.device_type = LGUEST_DEVICE_T_CONSOLE,
	.probe = lguestcons_probe,
};

/* The standard init function */
static int __init hvc_lguest_init(void)
{
	return register_lguest_driver(&lguestcons_drv);
}
module_init(hvc_lguest_init);
OpenPOWER on IntegriCloud