Statistics
| Branch: | Revision:

root / hw / spapr_rtas.c @ ba7cb5a8

History | View | Annotate | Download (8 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, 0);
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_power_off(sPAPREnvironment *spapr,
83
                           uint32_t token, uint32_t nargs, target_ulong args,
84
                           uint32_t nret, target_ulong rets)
85
{
86
    if (nargs != 2 || nret != 1) {
87
        rtas_st(rets, 0, -3);
88
        return;
89
    }
90
    qemu_system_shutdown_request();
91
    rtas_st(rets, 0, 0);
92
}
93

    
94
static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
95
                                         uint32_t token, uint32_t nargs,
96
                                         target_ulong args,
97
                                         uint32_t nret, target_ulong rets)
98
{
99
    target_ulong id;
100
    CPUState *env;
101

    
102
    if (nargs != 1 || nret != 2) {
103
        rtas_st(rets, 0, -3);
104
        return;
105
    }
106

    
107
    id = rtas_ld(args, 0);
108
    for (env = first_cpu; env; env = env->next_cpu) {
109
        if (env->cpu_index != id) {
110
            continue;
111
        }
112

    
113
        if (env->halted) {
114
            rtas_st(rets, 1, 0);
115
        } else {
116
            rtas_st(rets, 1, 2);
117
        }
118

    
119
        rtas_st(rets, 0, 0);
120
        return;
121
    }
122

    
123
    /* Didn't find a matching cpu */
124
    rtas_st(rets, 0, -3);
125
}
126

    
127
static void rtas_start_cpu(sPAPREnvironment *spapr,
128
                           uint32_t token, uint32_t nargs,
129
                           target_ulong args,
130
                           uint32_t nret, target_ulong rets)
131
{
132
    target_ulong id, start, r3;
133
    CPUState *env;
134

    
135
    if (nargs != 3 || nret != 1) {
136
        rtas_st(rets, 0, -3);
137
        return;
138
    }
139

    
140
    id = rtas_ld(args, 0);
141
    start = rtas_ld(args, 1);
142
    r3 = rtas_ld(args, 2);
143

    
144
    for (env = first_cpu; env; env = env->next_cpu) {
145
        if (env->cpu_index != id) {
146
            continue;
147
        }
148

    
149
        if (!env->halted) {
150
            rtas_st(rets, 0, -1);
151
            return;
152
        }
153

    
154
        env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
155
        env->nip = start;
156
        env->gpr[3] = r3;
157
        env->halted = 0;
158

    
159
        qemu_cpu_kick(env);
160

    
161
        rtas_st(rets, 0, 0);
162
        return;
163
    }
164

    
165
    /* Didn't find a matching cpu */
166
    rtas_st(rets, 0, -3);
167
}
168

    
169
static struct rtas_call {
170
    const char *name;
171
    spapr_rtas_fn fn;
172
} rtas_table[TOKEN_MAX];
173

    
174
struct rtas_call *rtas_next = rtas_table;
175

    
176
target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
177
                             uint32_t token, uint32_t nargs, target_ulong args,
178
                             uint32_t nret, target_ulong rets)
179
{
180
    if ((token >= TOKEN_BASE)
181
        && ((token - TOKEN_BASE) < TOKEN_MAX)) {
182
        struct rtas_call *call = rtas_table + (token - TOKEN_BASE);
183

    
184
        if (call->fn) {
185
            call->fn(spapr, token, nargs, args, nret, rets);
186
            return H_SUCCESS;
187
        }
188
    }
189

    
190
    /* HACK: Some Linux early debug code uses RTAS display-character,
191
     * but assumes the token value is 0xa (which it is on some real
192
     * machines) without looking it up in the device tree.  This
193
     * special case makes this work */
194
    if (token == 0xa) {
195
        rtas_display_character(spapr, 0xa, nargs, args, nret, rets);
196
        return H_SUCCESS;
197
    }
198

    
199
    hcall_dprintf("Unknown RTAS token 0x%x\n", token);
200
    rtas_st(rets, 0, -3);
201
    return H_PARAMETER;
202
}
203

    
204
void spapr_rtas_register(const char *name, spapr_rtas_fn fn)
205
{
206
    assert(rtas_next < (rtas_table + TOKEN_MAX));
207

    
208
    rtas_next->name = name;
209
    rtas_next->fn = fn;
210

    
211
    rtas_next++;
212
}
213

    
214
int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
215
                                 target_phys_addr_t rtas_size)
216
{
217
    int ret;
218
    int i;
219

    
220
    ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
221
    if (ret < 0) {
222
        fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n",
223
                fdt_strerror(ret));
224
        return ret;
225
    }
226

    
227
    ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base",
228
                                    rtas_addr);
229
    if (ret < 0) {
230
        fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n",
231
                fdt_strerror(ret));
232
        return ret;
233
    }
234

    
235
    ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
236
                                    rtas_addr);
237
    if (ret < 0) {
238
        fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n",
239
                fdt_strerror(ret));
240
        return ret;
241
    }
242

    
243
    ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size",
244
                                    rtas_size);
245
    if (ret < 0) {
246
        fprintf(stderr, "Couldn't add rtas-size property: %s\n",
247
                fdt_strerror(ret));
248
        return ret;
249
    }
250

    
251
    for (i = 0; i < TOKEN_MAX; i++) {
252
        struct rtas_call *call = &rtas_table[i];
253

    
254
        if (!call->fn) {
255
            continue;
256
        }
257

    
258
        ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name,
259
                                        i + TOKEN_BASE);
260
        if (ret < 0) {
261
            fprintf(stderr, "Couldn't add rtas token for %s: %s\n",
262
                    call->name, fdt_strerror(ret));
263
            return ret;
264
        }
265

    
266
    }
267
    return 0;
268
}
269

    
270
static void register_core_rtas(void)
271
{
272
    spapr_rtas_register("display-character", rtas_display_character);
273
    spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
274
    spapr_rtas_register("power-off", rtas_power_off);
275
    spapr_rtas_register("query-cpu-stopped-state",
276
                        rtas_query_cpu_stopped_state);
277
    spapr_rtas_register("start-cpu", rtas_start_cpu);
278
}
279
device_init(register_core_rtas);