Statistics
| Branch: | Revision:

root / hw / spapr_rtas.c @ 3204db98

History | View | Annotate | Download (8.7 kB)

1
/*
2
 * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
3
 *
4
 * Hypercall based emulated RTAS
5
 *
6
 * Copyright (c) 2010-2011 David Gibson, IBM Corporation.
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to deal
10
 * in the Software without restriction, including without limitation the rights
11
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
 * copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
 * THE SOFTWARE.
25
 *
26
 */
27
#include "cpu.h"
28
#include "sysemu.h"
29
#include "qemu-char.h"
30
#include "hw/qdev.h"
31
#include "device_tree.h"
32

    
33
#include "hw/spapr.h"
34
#include "hw/spapr_vio.h"
35

    
36
#include <libfdt.h>
37

    
38
#define TOKEN_BASE      0x2000
39
#define TOKEN_MAX       0x100
40

    
41
static void rtas_display_character(sPAPREnvironment *spapr,
42
                                   uint32_t token, uint32_t nargs,
43
                                   target_ulong args,
44
                                   uint32_t nret, target_ulong rets)
45
{
46
    uint8_t c = rtas_ld(args, 0);
47
    VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus,
48
                                                 SPAPR_VTY_BASE_ADDRESS);
49

    
50
    if (!sdev) {
51
        rtas_st(rets, 0, -1);
52
    } else {
53
        vty_putchars(sdev, &c, sizeof(c));
54
        rtas_st(rets, 0, 0);
55
    }
56
}
57

    
58
static void rtas_get_time_of_day(sPAPREnvironment *spapr,
59
                                 uint32_t token, uint32_t nargs,
60
                                 target_ulong args,
61
                                 uint32_t nret, target_ulong rets)
62
{
63
    struct tm tm;
64

    
65
    if (nret != 8) {
66
        rtas_st(rets, 0, -3);
67
        return;
68
    }
69

    
70
    qemu_get_timedate(&tm, spapr->rtc_offset);
71

    
72
    rtas_st(rets, 0, 0); /* Success */
73
    rtas_st(rets, 1, tm.tm_year + 1900);
74
    rtas_st(rets, 2, tm.tm_mon + 1);
75
    rtas_st(rets, 3, tm.tm_mday);
76
    rtas_st(rets, 4, tm.tm_hour);
77
    rtas_st(rets, 5, tm.tm_min);
78
    rtas_st(rets, 6, tm.tm_sec);
79
    rtas_st(rets, 7, 0); /* we don't do nanoseconds */
80
}
81

    
82
static void rtas_set_time_of_day(sPAPREnvironment *spapr,
83
                                 uint32_t token, uint32_t nargs,
84
                                 target_ulong args,
85
                                 uint32_t nret, target_ulong rets)
86
{
87
    struct tm tm;
88

    
89
    tm.tm_year = rtas_ld(args, 0) - 1900;
90
    tm.tm_mon = rtas_ld(args, 1) - 1;
91
    tm.tm_mday = rtas_ld(args, 2);
92
    tm.tm_hour = rtas_ld(args, 3);
93
    tm.tm_min = rtas_ld(args, 4);
94
    tm.tm_sec = rtas_ld(args, 5);
95

    
96
    /* Just generate a monitor event for the change */
97
    rtc_change_mon_event(&tm);
98
    spapr->rtc_offset = qemu_timedate_diff(&tm);
99

    
100
    rtas_st(rets, 0, 0); /* Success */
101
}
102

    
103
static void rtas_power_off(sPAPREnvironment *spapr,
104
                           uint32_t token, uint32_t nargs, target_ulong args,
105
                           uint32_t nret, target_ulong rets)
106
{
107
    if (nargs != 2 || nret != 1) {
108
        rtas_st(rets, 0, -3);
109
        return;
110
    }
111
    qemu_system_shutdown_request();
112
    rtas_st(rets, 0, 0);
113
}
114

    
115
static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
116
                                         uint32_t token, uint32_t nargs,
117
                                         target_ulong args,
118
                                         uint32_t nret, target_ulong rets)
119
{
120
    target_ulong id;
121
    CPUState *env;
122

    
123
    if (nargs != 1 || nret != 2) {
124
        rtas_st(rets, 0, -3);
125
        return;
126
    }
127

    
128
    id = rtas_ld(args, 0);
129
    for (env = first_cpu; env; env = env->next_cpu) {
130
        if (env->cpu_index != id) {
131
            continue;
132
        }
133

    
134
        if (env->halted) {
135
            rtas_st(rets, 1, 0);
136
        } else {
137
            rtas_st(rets, 1, 2);
138
        }
139

    
140
        rtas_st(rets, 0, 0);
141
        return;
142
    }
143

    
144
    /* Didn't find a matching cpu */
145
    rtas_st(rets, 0, -3);
146
}
147

    
148
static void rtas_start_cpu(sPAPREnvironment *spapr,
149
                           uint32_t token, uint32_t nargs,
150
                           target_ulong args,
151
                           uint32_t nret, target_ulong rets)
152
{
153
    target_ulong id, start, r3;
154
    CPUState *env;
155

    
156
    if (nargs != 3 || nret != 1) {
157
        rtas_st(rets, 0, -3);
158
        return;
159
    }
160

    
161
    id = rtas_ld(args, 0);
162
    start = rtas_ld(args, 1);
163
    r3 = rtas_ld(args, 2);
164

    
165
    for (env = first_cpu; env; env = env->next_cpu) {
166
        if (env->cpu_index != id) {
167
            continue;
168
        }
169

    
170
        if (!env->halted) {
171
            rtas_st(rets, 0, -1);
172
            return;
173
        }
174

    
175
        env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
176
        env->nip = start;
177
        env->gpr[3] = r3;
178
        env->halted = 0;
179

    
180
        qemu_cpu_kick(env);
181

    
182
        rtas_st(rets, 0, 0);
183
        return;
184
    }
185

    
186
    /* Didn't find a matching cpu */
187
    rtas_st(rets, 0, -3);
188
}
189

    
190
static struct rtas_call {
191
    const char *name;
192
    spapr_rtas_fn fn;
193
} rtas_table[TOKEN_MAX];
194

    
195
struct rtas_call *rtas_next = rtas_table;
196

    
197
target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
198
                             uint32_t token, uint32_t nargs, target_ulong args,
199
                             uint32_t nret, target_ulong rets)
200
{
201
    if ((token >= TOKEN_BASE)
202
        && ((token - TOKEN_BASE) < TOKEN_MAX)) {
203
        struct rtas_call *call = rtas_table + (token - TOKEN_BASE);
204

    
205
        if (call->fn) {
206
            call->fn(spapr, token, nargs, args, nret, rets);
207
            return H_SUCCESS;
208
        }
209
    }
210

    
211
    /* HACK: Some Linux early debug code uses RTAS display-character,
212
     * but assumes the token value is 0xa (which it is on some real
213
     * machines) without looking it up in the device tree.  This
214
     * special case makes this work */
215
    if (token == 0xa) {
216
        rtas_display_character(spapr, 0xa, nargs, args, nret, rets);
217
        return H_SUCCESS;
218
    }
219

    
220
    hcall_dprintf("Unknown RTAS token 0x%x\n", token);
221
    rtas_st(rets, 0, -3);
222
    return H_PARAMETER;
223
}
224

    
225
void spapr_rtas_register(const char *name, spapr_rtas_fn fn)
226
{
227
    assert(rtas_next < (rtas_table + TOKEN_MAX));
228

    
229
    rtas_next->name = name;
230
    rtas_next->fn = fn;
231

    
232
    rtas_next++;
233
}
234

    
235
int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
236
                                 target_phys_addr_t rtas_size)
237
{
238
    int ret;
239
    int i;
240

    
241
    ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
242
    if (ret < 0) {
243
        fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n",
244
                fdt_strerror(ret));
245
        return ret;
246
    }
247

    
248
    ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base",
249
                                    rtas_addr);
250
    if (ret < 0) {
251
        fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n",
252
                fdt_strerror(ret));
253
        return ret;
254
    }
255

    
256
    ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
257
                                    rtas_addr);
258
    if (ret < 0) {
259
        fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n",
260
                fdt_strerror(ret));
261
        return ret;
262
    }
263

    
264
    ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size",
265
                                    rtas_size);
266
    if (ret < 0) {
267
        fprintf(stderr, "Couldn't add rtas-size property: %s\n",
268
                fdt_strerror(ret));
269
        return ret;
270
    }
271

    
272
    for (i = 0; i < TOKEN_MAX; i++) {
273
        struct rtas_call *call = &rtas_table[i];
274

    
275
        if (!call->fn) {
276
            continue;
277
        }
278

    
279
        ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name,
280
                                        i + TOKEN_BASE);
281
        if (ret < 0) {
282
            fprintf(stderr, "Couldn't add rtas token for %s: %s\n",
283
                    call->name, fdt_strerror(ret));
284
            return ret;
285
        }
286

    
287
    }
288
    return 0;
289
}
290

    
291
static void register_core_rtas(void)
292
{
293
    spapr_rtas_register("display-character", rtas_display_character);
294
    spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
295
    spapr_rtas_register("set-time-of-day", rtas_set_time_of_day);
296
    spapr_rtas_register("power-off", rtas_power_off);
297
    spapr_rtas_register("query-cpu-stopped-state",
298
                        rtas_query_cpu_stopped_state);
299
    spapr_rtas_register("start-cpu", rtas_start_cpu);
300
}
301
device_init(register_core_rtas);