Statistics
| Branch: | Revision:

root / hw / spapr_vty.c @ 57c83dac

History | View | Annotate | Download (5.8 kB)

1
#include "qdev.h"
2
#include "qemu-char.h"
3
#include "hw/spapr.h"
4
#include "hw/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(dev->sdev.qirq);
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 VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg);
74

    
75
static target_ulong h_put_term_char(CPUState *env, sPAPREnvironment *spapr,
76
                                    target_ulong opcode, target_ulong *args)
77
{
78
    target_ulong reg = args[0];
79
    target_ulong len = args[1];
80
    target_ulong char0_7 = args[2];
81
    target_ulong char8_15 = args[3];
82
    VIOsPAPRDevice *sdev;
83
    uint8_t buf[16];
84

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

    
90
    if (len > 16) {
91
        return H_PARAMETER;
92
    }
93

    
94
    *((uint64_t *)buf) = cpu_to_be64(char0_7);
95
    *((uint64_t *)buf + 1) = cpu_to_be64(char8_15);
96

    
97
    vty_putchars(sdev, buf, len);
98

    
99
    return H_SUCCESS;
100
}
101

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

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

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

    
122
    *char0_7 = be64_to_cpu(*((uint64_t *)buf));
123
    *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1));
124

    
125
    return H_SUCCESS;
126
}
127

    
128
void spapr_vty_create(VIOsPAPRBus *bus, uint32_t reg, CharDriverState *chardev)
129
{
130
    DeviceState *dev;
131

    
132
    dev = qdev_create(&bus->bus, "spapr-vty");
133
    qdev_prop_set_uint32(dev, "reg", reg);
134
    qdev_prop_set_chr(dev, "chardev", chardev);
135
    qdev_init_nofail(dev);
136
}
137

    
138
static Property spapr_vty_properties[] = {
139
    DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev, SPAPR_VTY_BASE_ADDRESS, 0),
140
    DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev),
141
    DEFINE_PROP_END_OF_LIST(),
142
};
143

    
144
static void spapr_vty_class_init(ObjectClass *klass, void *data)
145
{
146
    DeviceClass *dc = DEVICE_CLASS(klass);
147
    VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass);
148

    
149
    k->init = spapr_vty_init;
150
    k->dt_name = "vty";
151
    k->dt_type = "serial";
152
    k->dt_compatible = "hvterm1";
153
    dc->props = spapr_vty_properties;
154
}
155

    
156
static TypeInfo spapr_vty_info = {
157
    .name          = "spapr-vty",
158
    .parent        = TYPE_VIO_SPAPR_DEVICE,
159
    .instance_size = sizeof(VIOsPAPRVTYDevice),
160
    .class_init    = spapr_vty_class_init,
161
};
162

    
163
VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus)
164
{
165
    VIOsPAPRDevice *sdev, *selected;
166
    DeviceState *iter;
167

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

    
174
    selected = NULL;
175
    QTAILQ_FOREACH(iter, &bus->bus.children, sibling) {
176
        /* Only look at VTY devices */
177
        if (!object_dynamic_cast(OBJECT(iter), "spapr-vty")) {
178
            continue;
179
        }
180

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

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

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

    
195
    return selected;
196
}
197

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

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

    
212
    return sdev;
213
}
214

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