Statistics
| Branch: | Revision:

root / hw / char / spapr_vty.c @ 9944d320

History | View | Annotate | Download (5.7 kB)

1
#include "hw/qdev.h"
2
#include "char/char.h"
3
#include "hw/ppc/spapr.h"
4
#include "hw/ppc/spapr_vio.h"
5

    
6
#define VTERM_BUFSIZE   16
7

    
8
typedef struct VIOsPAPRVTYDevice {
9
    VIOsPAPRDevice sdev;
10
    CharDriverState *chardev;
11
    uint32_t in, out;
12
    uint8_t buf[VTERM_BUFSIZE];
13
} VIOsPAPRVTYDevice;
14

    
15
static int vty_can_receive(void *opaque)
16
{
17
    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque;
18

    
19
    return (dev->in - dev->out) < VTERM_BUFSIZE;
20
}
21

    
22
static void vty_receive(void *opaque, const uint8_t *buf, int size)
23
{
24
    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque;
25
    int i;
26

    
27
    if ((dev->in == dev->out) && size) {
28
        /* toggle line to simulate edge interrupt */
29
        qemu_irq_pulse(spapr_vio_qirq(&dev->sdev));
30
    }
31
    for (i = 0; i < size; i++) {
32
        assert((dev->in - dev->out) < VTERM_BUFSIZE);
33
        dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i];
34
    }
35
}
36

    
37
static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max)
38
{
39
    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
40
    int n = 0;
41

    
42
    while ((n < max) && (dev->out != dev->in)) {
43
        buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE];
44
    }
45

    
46
    return n;
47
}
48

    
49
void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len)
50
{
51
    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
52

    
53
    /* FIXME: should check the qemu_chr_fe_write() return value */
54
    qemu_chr_fe_write(dev->chardev, buf, len);
55
}
56

    
57
static int spapr_vty_init(VIOsPAPRDevice *sdev)
58
{
59
    VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev;
60

    
61
    if (!dev->chardev) {
62
        fprintf(stderr, "spapr-vty: Can't create vty without a chardev!\n");
63
        exit(1);
64
    }
65

    
66
    qemu_chr_add_handlers(dev->chardev, vty_can_receive,
67
                          vty_receive, NULL, dev);
68

    
69
    return 0;
70
}
71

    
72
/* Forward declaration */
73
static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr,
74
                                    target_ulong opcode, target_ulong *args)
75
{
76
    target_ulong reg = args[0];
77
    target_ulong len = args[1];
78
    target_ulong char0_7 = args[2];
79
    target_ulong char8_15 = args[3];
80
    VIOsPAPRDevice *sdev;
81
    uint8_t buf[16];
82

    
83
    sdev = vty_lookup(spapr, reg);
84
    if (!sdev) {
85
        return H_PARAMETER;
86
    }
87

    
88
    if (len > 16) {
89
        return H_PARAMETER;
90
    }
91

    
92
    *((uint64_t *)buf) = cpu_to_be64(char0_7);
93
    *((uint64_t *)buf + 1) = cpu_to_be64(char8_15);
94

    
95
    vty_putchars(sdev, buf, len);
96

    
97
    return H_SUCCESS;
98
}
99

    
100
static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr,
101
                                    target_ulong opcode, target_ulong *args)
102
{
103
    target_ulong reg = args[0];
104
    target_ulong *len = args + 0;
105
    target_ulong *char0_7 = args + 1;
106
    target_ulong *char8_15 = args + 2;
107
    VIOsPAPRDevice *sdev;
108
    uint8_t buf[16];
109

    
110
    sdev = vty_lookup(spapr, reg);
111
    if (!sdev) {
112
        return H_PARAMETER;
113
    }
114

    
115
    *len = vty_getchars(sdev, buf, sizeof(buf));
116
    if (*len < 16) {
117
        memset(buf + *len, 0, 16 - *len);
118
    }
119

    
120
    *char0_7 = be64_to_cpu(*((uint64_t *)buf));
121
    *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1));
122

    
123
    return H_SUCCESS;
124
}
125

    
126
void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev)
127
{
128
    DeviceState *dev;
129

    
130
    dev = qdev_create(&bus->bus, "spapr-vty");
131
    qdev_prop_set_chr(dev, "chardev", chardev);
132
    qdev_init_nofail(dev);
133
}
134

    
135
static Property spapr_vty_properties[] = {
136
    DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev),
137
    DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev),
138
    DEFINE_PROP_END_OF_LIST(),
139
};
140

    
141
static void spapr_vty_class_init(ObjectClass *klass, void *data)
142
{
143
    DeviceClass *dc = DEVICE_CLASS(klass);
144
    VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
145

    
146
    k->init = spapr_vty_init;
147
    k->dt_name = "vty";
148
    k->dt_type = "serial";
149
    k->dt_compatible = "hvterm1";
150
    dc->props = spapr_vty_properties;
151
}
152

    
153
static const TypeInfo spapr_vty_info = {
154
    .name          = "spapr-vty",
155
    .parent        = TYPE_VIO_SPAPR_DEVICE,
156
    .instance_size = sizeof(VIOsPAPRVTYDevice),
157
    .class_init    = spapr_vty_class_init,
158
};
159

    
160
VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus)
161
{
162
    VIOsPAPRDevice *sdev, *selected;
163
    BusChild *kid;
164

    
165
    /*
166
     * To avoid the console bouncing around we want one VTY to be
167
     * the "default". We haven't really got anything to go on, so
168
     * arbitrarily choose the one with the lowest reg value.
169
     */
170

    
171
    selected = NULL;
172
    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
173
        DeviceState *iter = kid->child;
174

    
175
        /* Only look at VTY devices */
176
        if (!object_dynamic_cast(OBJECT(iter), "spapr-vty")) {
177
            continue;
178
        }
179

    
180
        sdev = DO_UPCAST(VIOsPAPRDevice, qdev, iter);
181

    
182
        /* First VTY we've found, so it is selected for now */
183
        if (!selected) {
184
            selected = sdev;
185
            continue;
186
        }
187

    
188
        /* Choose VTY with lowest reg value */
189
        if (sdev->reg < selected->reg) {
190
            selected = sdev;
191
        }
192
    }
193

    
194
    return selected;
195
}
196

    
197
VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg)
198
{
199
    VIOsPAPRDevice *sdev;
200

    
201
    sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
202
    if (!sdev && reg == 0) {
203
        /* Hack for kernel early debug, which always specifies reg==0.
204
         * We search all VIO devices, and grab the vty with the lowest
205
         * reg.  This attempts to mimic existing PowerVM behaviour
206
         * (early debug does work there, despite having no vty with
207
         * reg==0. */
208
        return spapr_vty_get_default(spapr->vio_bus);
209
    }
210

    
211
    return sdev;
212
}
213

    
214
static void spapr_vty_register_types(void)
215
{
216
    spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char);
217
    spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char);
218
    type_register_static(&spapr_vty_info);
219
}
220

    
221
type_init(spapr_vty_register_types)