]>
Commit | Line | Data |
---|---|---|
f6c98f92 HG |
1 | /* |
2 | * SCLP Support | |
3 | * | |
4 | * Copyright IBM, Corp. 2012 | |
5 | * | |
6 | * Authors: | |
7 | * Christian Borntraeger <[email protected]> | |
8 | * Heinz Graalfs <[email protected]> | |
9 | * | |
10 | * This work is licensed under the terms of the GNU GPL, version 2 or (at your | |
11 | * option) any later version. See the COPYING file in the top-level directory. | |
12 | * | |
13 | */ | |
14 | ||
9615495a | 15 | #include "qemu/osdep.h" |
393fc4c7 | 16 | #include "qemu/units.h" |
da34e65c | 17 | #include "qapi/error.h" |
f6c98f92 | 18 | #include "cpu.h" |
8cc3aecf | 19 | #include "sysemu/sysemu.h" |
311467f7 | 20 | #include "hw/boards.h" |
83c9f4ca | 21 | #include "hw/s390x/sclp.h" |
477a72a1 | 22 | #include "hw/s390x/event-facility.h" |
8cba80c3 | 23 | #include "hw/s390x/s390-pci-bus.h" |
b038411d | 24 | #include "hw/s390x/ipl.h" |
f6c98f92 | 25 | |
25a3c5af DH |
26 | static inline SCLPDevice *get_sclp_device(void) |
27 | { | |
989fd865 CB |
28 | static SCLPDevice *sclp; |
29 | ||
30 | if (!sclp) { | |
31 | sclp = SCLP(object_resolve_path_type("", TYPE_SCLP, NULL)); | |
32 | } | |
33 | return sclp; | |
25a3c5af DH |
34 | } |
35 | ||
0f73c5b3 JF |
36 | static inline bool sclp_command_code_valid(uint32_t code) |
37 | { | |
38 | switch (code & SCLP_CMD_CODE_MASK) { | |
39 | case SCLP_CMDW_READ_SCP_INFO: | |
40 | case SCLP_CMDW_READ_SCP_INFO_FORCED: | |
41 | case SCLP_CMDW_READ_CPU_INFO: | |
42 | case SCLP_CMDW_CONFIGURE_IOA: | |
43 | case SCLP_CMDW_DECONFIGURE_IOA: | |
44 | case SCLP_CMD_READ_EVENT_DATA: | |
45 | case SCLP_CMD_WRITE_EVENT_DATA: | |
46 | case SCLP_CMD_WRITE_EVENT_MASK: | |
47 | return true; | |
48 | } | |
49 | return false; | |
50 | } | |
51 | ||
1ecd6078 CW |
52 | static bool sccb_verify_boundary(uint64_t sccb_addr, uint16_t sccb_len, |
53 | uint32_t code) | |
db13387c CW |
54 | { |
55 | uint64_t sccb_max_addr = sccb_addr + sccb_len - 1; | |
56 | uint64_t sccb_boundary = (sccb_addr & PAGE_MASK) + PAGE_SIZE; | |
57 | ||
1ecd6078 CW |
58 | switch (code & SCLP_CMD_CODE_MASK) { |
59 | case SCLP_CMDW_READ_SCP_INFO: | |
60 | case SCLP_CMDW_READ_SCP_INFO_FORCED: | |
61 | case SCLP_CMDW_READ_CPU_INFO: | |
62 | /* | |
63 | * An extended-length SCCB is only allowed for Read SCP/CPU Info and | |
64 | * is allowed to exceed the 4k boundary. The respective commands will | |
65 | * set the length field to the required length if an insufficient | |
66 | * SCCB length is provided. | |
67 | */ | |
68 | if (s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB)) { | |
69 | return true; | |
70 | } | |
71 | /* fallthrough */ | |
72 | default: | |
73 | if (sccb_max_addr < sccb_boundary) { | |
74 | return true; | |
75 | } | |
db13387c CW |
76 | } |
77 | ||
78 | return false; | |
79 | } | |
80 | ||
912d70d2 | 81 | static void prepare_cpu_entries(MachineState *ms, CPUEntry *entry, int *count) |
026546e6 | 82 | { |
4dd4200e | 83 | uint8_t features[SCCB_CPU_FEATURE_LEN] = { 0 }; |
026546e6 DH |
84 | int i; |
85 | ||
4dd4200e | 86 | s390_get_feat_block(S390_FEAT_TYPE_SCLP_CPU, features); |
bb535bb6 DH |
87 | for (i = 0, *count = 0; i < ms->possible_cpus->len; i++) { |
88 | if (!ms->possible_cpus->cpus[i].cpu) { | |
89 | continue; | |
90 | } | |
91 | entry[*count].address = ms->possible_cpus->cpus[i].arch_id; | |
92 | entry[*count].type = 0; | |
93 | memcpy(entry[*count].features, features, sizeof(features)); | |
94 | (*count)++; | |
026546e6 DH |
95 | } |
96 | } | |
97 | ||
0260b978 CW |
98 | #define SCCB_REQ_LEN(s, max_cpus) (sizeof(s) + max_cpus * sizeof(CPUEntry)) |
99 | ||
1ecd6078 CW |
100 | static inline bool ext_len_sccb_supported(SCCBHeader header) |
101 | { | |
102 | return s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) && | |
103 | header.control_mask[2] & SCLP_VARIABLE_LENGTH_RESPONSE; | |
104 | } | |
105 | ||
f6c98f92 | 106 | /* Provide information about the configuration, CPUs and storage */ |
25a3c5af | 107 | static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) |
f6c98f92 HG |
108 | { |
109 | ReadInfo *read_info = (ReadInfo *) sccb; | |
311467f7 | 110 | MachineState *machine = MACHINE(qdev_get_machine()); |
bb535bb6 | 111 | int cpu_count; |
1def6656 | 112 | int rnsize, rnmax; |
b038411d | 113 | IplParameterBlock *ipib = s390_ipl_get_iplb(); |
0260b978 | 114 | int required_len = SCCB_REQ_LEN(ReadInfo, machine->possible_cpus->len); |
1ecd6078 CW |
115 | int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ? |
116 | offsetof(ReadInfo, entries) : | |
117 | SCLP_READ_SCP_INFO_FIXED_CPU_OFFSET; | |
1a7a5688 | 118 | CPUEntry *entries_start = (void *)sccb + offset_cpu; |
0260b978 CW |
119 | |
120 | if (be16_to_cpu(sccb->h.length) < required_len) { | |
1ecd6078 CW |
121 | if (ext_len_sccb_supported(sccb->h)) { |
122 | sccb->h.length = cpu_to_be16(required_len); | |
123 | } | |
0260b978 CW |
124 | sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); |
125 | return; | |
126 | } | |
8cc3aecf | 127 | |
8cc3aecf | 128 | /* CPU information */ |
1a7a5688 | 129 | prepare_cpu_entries(machine, entries_start, &cpu_count); |
8cc3aecf | 130 | read_info->entries_cpu = cpu_to_be16(cpu_count); |
1a7a5688 | 131 | read_info->offset_cpu = cpu_to_be16(offset_cpu); |
ae71ed86 | 132 | read_info->highest_cpu = cpu_to_be16(machine->smp.max_cpus - 1); |
8cc3aecf | 133 | |
059be520 DH |
134 | read_info->ibc_val = cpu_to_be32(s390_get_ibc_val()); |
135 | ||
4dd4200e DH |
136 | /* Configuration Characteristic (Extension) */ |
137 | s390_get_feat_block(S390_FEAT_TYPE_SCLP_CONF_CHAR, | |
138 | read_info->conf_char); | |
139 | s390_get_feat_block(S390_FEAT_TYPE_SCLP_CONF_CHAR_EXT, | |
140 | read_info->conf_char_ext); | |
141 | ||
fabdada9 CW |
142 | if (s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB)) { |
143 | s390_get_feat_block(S390_FEAT_TYPE_SCLP_FAC134, | |
144 | &read_info->fac134); | |
145 | } | |
146 | ||
8cba80c3 | 147 | read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO | |
80b7a265 | 148 | SCLP_HAS_IOA_RECONFIG); |
f6c98f92 | 149 | |
3fad3252 | 150 | read_info->mha_pow = s390_get_mha_pow(); |
a3669307 | 151 | read_info->hmfai = cpu_to_be32(s390_get_hmfai()); |
1def6656 | 152 | |
71a2fd35 | 153 | rnsize = 1 << (sclp->increment_size - 20); |
1def6656 MR |
154 | if (rnsize <= 128) { |
155 | read_info->rnsize = rnsize; | |
156 | } else { | |
157 | read_info->rnsize = 0; | |
158 | read_info->rnsize2 = cpu_to_be32(rnsize); | |
159 | } | |
160 | ||
82fab5c5 DH |
161 | /* we don't support standby memory, maxram_size is never exposed */ |
162 | rnmax = machine->ram_size >> sclp->increment_size; | |
1def6656 MR |
163 | if (rnmax < 0x10000) { |
164 | read_info->rnmax = cpu_to_be16(rnmax); | |
165 | } else { | |
166 | read_info->rnmax = cpu_to_be16(0); | |
167 | read_info->rnmax2 = cpu_to_be64(rnmax); | |
168 | } | |
169 | ||
b038411d FA |
170 | if (ipib && ipib->flags & DIAG308_FLAGS_LP_VALID) { |
171 | memcpy(&read_info->loadparm, &ipib->loadparm, | |
172 | sizeof(read_info->loadparm)); | |
173 | } else { | |
174 | s390_ipl_set_loadparm(read_info->loadparm); | |
175 | } | |
176 | ||
1def6656 MR |
177 | sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); |
178 | } | |
179 | ||
8cc3aecf | 180 | /* Provide information about the CPU */ |
25a3c5af | 181 | static void sclp_read_cpu_info(SCLPDevice *sclp, SCCB *sccb) |
8cc3aecf | 182 | { |
912d70d2 | 183 | MachineState *machine = MACHINE(qdev_get_machine()); |
8cc3aecf | 184 | ReadCpuInfo *cpu_info = (ReadCpuInfo *) sccb; |
bb535bb6 | 185 | int cpu_count; |
0260b978 CW |
186 | int required_len = SCCB_REQ_LEN(ReadCpuInfo, machine->possible_cpus->len); |
187 | ||
188 | if (be16_to_cpu(sccb->h.length) < required_len) { | |
1ecd6078 CW |
189 | if (ext_len_sccb_supported(sccb->h)) { |
190 | sccb->h.length = cpu_to_be16(required_len); | |
191 | } | |
0260b978 CW |
192 | sccb->h.response_code = cpu_to_be16(SCLP_RC_INSUFFICIENT_SCCB_LENGTH); |
193 | return; | |
194 | } | |
8cc3aecf | 195 | |
912d70d2 | 196 | prepare_cpu_entries(machine, cpu_info->entries, &cpu_count); |
8cc3aecf JH |
197 | cpu_info->nr_configured = cpu_to_be16(cpu_count); |
198 | cpu_info->offset_configured = cpu_to_be16(offsetof(ReadCpuInfo, entries)); | |
199 | cpu_info->nr_standby = cpu_to_be16(0); | |
200 | ||
201 | /* The standby offset is 16-byte for each CPU */ | |
202 | cpu_info->offset_standby = cpu_to_be16(cpu_info->offset_configured | |
203 | + cpu_info->nr_configured*sizeof(CPUEntry)); | |
204 | ||
8cc3aecf JH |
205 | |
206 | sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); | |
207 | } | |
208 | ||
80b7a265 CH |
209 | static void sclp_configure_io_adapter(SCLPDevice *sclp, SCCB *sccb, |
210 | bool configure) | |
211 | { | |
212 | int rc; | |
213 | ||
214 | if (be16_to_cpu(sccb->h.length) < 16) { | |
215 | rc = SCLP_RC_INSUFFICIENT_SCCB_LENGTH; | |
216 | goto out_err; | |
217 | } | |
218 | ||
219 | switch (((IoaCfgSccb *)sccb)->atype) { | |
220 | case SCLP_RECONFIG_PCI_ATYPE: | |
221 | if (s390_has_feat(S390_FEAT_ZPCI)) { | |
222 | if (configure) { | |
223 | s390_pci_sclp_configure(sccb); | |
224 | } else { | |
225 | s390_pci_sclp_deconfigure(sccb); | |
226 | } | |
227 | return; | |
228 | } | |
229 | /* fallthrough */ | |
230 | default: | |
231 | rc = SCLP_RC_ADAPTER_TYPE_NOT_RECOGNIZED; | |
232 | } | |
233 | ||
234 | out_err: | |
235 | sccb->h.response_code = cpu_to_be16(rc); | |
236 | } | |
237 | ||
25a3c5af | 238 | static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code) |
f6c98f92 | 239 | { |
25a3c5af DH |
240 | SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); |
241 | SCLPEventFacility *ef = sclp->event_facility; | |
477a72a1 | 242 | SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); |
559a17a1 | 243 | |
5f04c14a | 244 | switch (code & SCLP_CMD_CODE_MASK) { |
f6c98f92 HG |
245 | case SCLP_CMDW_READ_SCP_INFO: |
246 | case SCLP_CMDW_READ_SCP_INFO_FORCED: | |
25a3c5af | 247 | sclp_c->read_SCP_info(sclp, sccb); |
f6c98f92 | 248 | break; |
8cc3aecf | 249 | case SCLP_CMDW_READ_CPU_INFO: |
25a3c5af | 250 | sclp_c->read_cpu_info(sclp, sccb); |
8cc3aecf | 251 | break; |
80b7a265 CH |
252 | case SCLP_CMDW_CONFIGURE_IOA: |
253 | sclp_configure_io_adapter(sclp, sccb, true); | |
8cba80c3 | 254 | break; |
80b7a265 CH |
255 | case SCLP_CMDW_DECONFIGURE_IOA: |
256 | sclp_configure_io_adapter(sclp, sccb, false); | |
8cba80c3 | 257 | break; |
f6c98f92 | 258 | default: |
477a72a1 | 259 | efc->command_handler(ef, sccb, code); |
f6c98f92 HG |
260 | break; |
261 | } | |
262 | } | |
263 | ||
0f73c5b3 JF |
264 | /* |
265 | * We only need the address to have something valid for the | |
266 | * service_interrupt call. | |
267 | */ | |
268 | #define SCLP_PV_DUMMY_ADDR 0x4000 | |
269 | int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb, | |
270 | uint32_t code) | |
271 | { | |
272 | SCLPDevice *sclp = get_sclp_device(); | |
273 | SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); | |
c1db53a5 CW |
274 | SCCBHeader header; |
275 | g_autofree SCCB *work_sccb = NULL; | |
0f73c5b3 | 276 | |
c1db53a5 CW |
277 | s390_cpu_pv_mem_read(env_archcpu(env), 0, &header, sizeof(SCCBHeader)); |
278 | ||
279 | work_sccb = g_malloc0(be16_to_cpu(header.length)); | |
280 | s390_cpu_pv_mem_read(env_archcpu(env), 0, work_sccb, | |
281 | be16_to_cpu(header.length)); | |
0f73c5b3 JF |
282 | |
283 | if (!sclp_command_code_valid(code)) { | |
c1db53a5 | 284 | work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); |
0f73c5b3 JF |
285 | goto out_write; |
286 | } | |
287 | ||
c1db53a5 | 288 | sclp_c->execute(sclp, work_sccb, code); |
0f73c5b3 | 289 | out_write: |
c1db53a5 CW |
290 | s390_cpu_pv_mem_write(env_archcpu(env), 0, work_sccb, |
291 | be16_to_cpu(work_sccb->h.length)); | |
0f73c5b3 JF |
292 | sclp_c->service_interrupt(sclp, SCLP_PV_DUMMY_ADDR); |
293 | return 0; | |
294 | } | |
295 | ||
6e252802 | 296 | int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code) |
f6c98f92 | 297 | { |
25a3c5af DH |
298 | SCLPDevice *sclp = get_sclp_device(); |
299 | SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); | |
c1db53a5 CW |
300 | SCCBHeader header; |
301 | g_autofree SCCB *work_sccb = NULL; | |
f6c98f92 HG |
302 | |
303 | /* first some basic checks on program checks */ | |
6e252802 | 304 | if (env->psw.mask & PSW_MASK_PSTATE) { |
e6de76fc | 305 | return -PGM_PRIVILEGED; |
6e252802 | 306 | } |
f6c98f92 | 307 | if (cpu_physical_memory_is_io(sccb)) { |
e6de76fc | 308 | return -PGM_ADDRESSING; |
f6c98f92 | 309 | } |
6e252802 TH |
310 | if ((sccb & ~0x1fffUL) == 0 || (sccb & ~0x1fffUL) == env->psa |
311 | || (sccb & ~0x7ffffff8UL) != 0) { | |
e6de76fc | 312 | return -PGM_SPECIFICATION; |
f6c98f92 HG |
313 | } |
314 | ||
c1db53a5 CW |
315 | /* the header contains the actual length of the sccb */ |
316 | cpu_physical_memory_read(sccb, &header, sizeof(SCCBHeader)); | |
317 | ||
318 | /* Valid sccb sizes */ | |
319 | if (be16_to_cpu(header.length) < sizeof(SCCBHeader)) { | |
320 | return -PGM_SPECIFICATION; | |
321 | } | |
322 | ||
f6c98f92 HG |
323 | /* |
324 | * we want to work on a private copy of the sccb, to prevent guests | |
325 | * from playing dirty tricks by modifying the memory content after | |
326 | * the host has checked the values | |
327 | */ | |
c1db53a5 CW |
328 | work_sccb = g_malloc0(be16_to_cpu(header.length)); |
329 | cpu_physical_memory_read(sccb, work_sccb, be16_to_cpu(header.length)); | |
f6c98f92 | 330 | |
0f73c5b3 | 331 | if (!sclp_command_code_valid(code)) { |
c1db53a5 | 332 | work_sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND); |
679b8447 JF |
333 | goto out_write; |
334 | } | |
f6c98f92 | 335 | |
1ecd6078 | 336 | if (!sccb_verify_boundary(sccb, be16_to_cpu(work_sccb->h.length), code)) { |
c1db53a5 | 337 | work_sccb->h.response_code = cpu_to_be16(SCLP_RC_SCCB_BOUNDARY_VIOLATION); |
6f6c9333 JF |
338 | goto out_write; |
339 | } | |
340 | ||
c1db53a5 | 341 | sclp_c->execute(sclp, work_sccb, code); |
679b8447 | 342 | out_write: |
c1db53a5 CW |
343 | cpu_physical_memory_write(sccb, work_sccb, |
344 | be16_to_cpu(work_sccb->h.length)); | |
f6c98f92 | 345 | |
1723a1b6 | 346 | sclp_c->service_interrupt(sclp, sccb); |
f6c98f92 | 347 | |
e6de76fc | 348 | return 0; |
f6c98f92 HG |
349 | } |
350 | ||
1723a1b6 | 351 | static void service_interrupt(SCLPDevice *sclp, uint32_t sccb) |
f6c98f92 | 352 | { |
1723a1b6 | 353 | SCLPEventFacility *ef = sclp->event_facility; |
477a72a1 HG |
354 | SCLPEventFacilityClass *efc = EVENT_FACILITY_GET_CLASS(ef); |
355 | ||
559a17a1 HG |
356 | uint32_t param = sccb & ~3; |
357 | ||
358 | /* Indicate whether an event is still pending */ | |
477a72a1 | 359 | param |= efc->event_pending(ef) ? 1 : 0; |
559a17a1 HG |
360 | |
361 | if (!param) { | |
362 | /* No need to send an interrupt, there's nothing to be notified about */ | |
363 | return; | |
364 | } | |
365 | s390_sclp_extint(param); | |
f6c98f92 HG |
366 | } |
367 | ||
1723a1b6 DH |
368 | void sclp_service_interrupt(uint32_t sccb) |
369 | { | |
370 | SCLPDevice *sclp = get_sclp_device(); | |
371 | SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp); | |
372 | ||
373 | sclp_c->service_interrupt(sclp, sccb); | |
374 | } | |
375 | ||
f6c98f92 HG |
376 | /* qemu object creation and initialization functions */ |
377 | ||
559a17a1 HG |
378 | void s390_sclp_init(void) |
379 | { | |
515190d9 | 380 | Object *new = object_new(TYPE_SCLP); |
559a17a1 | 381 | |
d2623129 | 382 | object_property_add_child(qdev_get_machine(), TYPE_SCLP, new); |
688ffbb4 | 383 | object_unref(new); |
ce189ab2 | 384 | qdev_realize(DEVICE(new), NULL, &error_fatal); |
559a17a1 | 385 | } |
0844df77 | 386 | |
515190d9 DH |
387 | static void sclp_realize(DeviceState *dev, Error **errp) |
388 | { | |
1cf065fb | 389 | MachineState *machine = MACHINE(qdev_get_machine()); |
515190d9 | 390 | SCLPDevice *sclp = SCLP(dev); |
1cf065fb DH |
391 | uint64_t hw_limit; |
392 | int ret; | |
515190d9 | 393 | |
8b638c43 DH |
394 | /* |
395 | * qdev_device_add searches the sysbus for TYPE_SCLP_EVENTS_BUS. As long | |
396 | * as we can't find a fitting bus via the qom tree, we have to add the | |
397 | * event facility to the sysbus, so e.g. a sclp console can be created. | |
398 | */ | |
992861fb MA |
399 | if (!sysbus_realize(SYS_BUS_DEVICE(sclp->event_facility), errp)) { |
400 | return; | |
68424112 | 401 | } |
1cf065fb DH |
402 | |
403 | ret = s390_set_memory_limit(machine->maxram_size, &hw_limit); | |
404 | if (ret == -E2BIG) { | |
992861fb | 405 | error_setg(errp, "host supports a maximum of %" PRIu64 " GB", |
393fc4c7 | 406 | hw_limit / GiB); |
1cf065fb | 407 | } else if (ret) { |
992861fb | 408 | error_setg(errp, "setting the guest size failed"); |
1cf065fb | 409 | } |
515190d9 DH |
410 | } |
411 | ||
1cf065fb DH |
412 | static void sclp_memory_init(SCLPDevice *sclp) |
413 | { | |
414 | MachineState *machine = MACHINE(qdev_get_machine()); | |
5c30ef93 | 415 | MachineClass *machine_class = MACHINE_GET_CLASS(qdev_get_machine()); |
1cf065fb | 416 | ram_addr_t initial_mem = machine->ram_size; |
1cf065fb DH |
417 | int increment_size = 20; |
418 | ||
419 | /* The storage increment size is a multiple of 1M and is a power of 2. | |
5c30ef93 CB |
420 | * For some machine types, the number of storage increments must be |
421 | * MAX_STORAGE_INCREMENTS or fewer. | |
1cf065fb DH |
422 | * The variable 'increment_size' is an exponent of 2 that can be |
423 | * used to calculate the size (in bytes) of an increment. */ | |
5c30ef93 CB |
424 | while (machine_class->fixup_ram_size != NULL && |
425 | (initial_mem >> increment_size) > MAX_STORAGE_INCREMENTS) { | |
1cf065fb DH |
426 | increment_size++; |
427 | } | |
71a2fd35 | 428 | sclp->increment_size = increment_size; |
1cf065fb DH |
429 | } |
430 | ||
515190d9 DH |
431 | static void sclp_init(Object *obj) |
432 | { | |
433 | SCLPDevice *sclp = SCLP(obj); | |
434 | Object *new; | |
435 | ||
436 | new = object_new(TYPE_SCLP_EVENT_FACILITY); | |
d2623129 | 437 | object_property_add_child(obj, TYPE_SCLP_EVENT_FACILITY, new); |
515190d9 DH |
438 | object_unref(new); |
439 | sclp->event_facility = EVENT_FACILITY(new); | |
1cf065fb DH |
440 | |
441 | sclp_memory_init(sclp); | |
515190d9 DH |
442 | } |
443 | ||
444 | static void sclp_class_init(ObjectClass *oc, void *data) | |
445 | { | |
25a3c5af | 446 | SCLPDeviceClass *sc = SCLP_CLASS(oc); |
515190d9 DH |
447 | DeviceClass *dc = DEVICE_CLASS(oc); |
448 | ||
449 | dc->desc = "SCLP (Service-Call Logical Processor)"; | |
450 | dc->realize = sclp_realize; | |
451 | dc->hotpluggable = false; | |
452 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); | |
e6cb60bf TH |
453 | /* |
454 | * Reason: Creates TYPE_SCLP_EVENT_FACILITY in sclp_init | |
455 | * which is a non-pluggable sysbus device | |
456 | */ | |
457 | dc->user_creatable = false; | |
25a3c5af DH |
458 | |
459 | sc->read_SCP_info = read_SCP_info; | |
25a3c5af DH |
460 | sc->read_cpu_info = sclp_read_cpu_info; |
461 | sc->execute = sclp_execute; | |
1723a1b6 | 462 | sc->service_interrupt = service_interrupt; |
515190d9 DH |
463 | } |
464 | ||
465 | static TypeInfo sclp_info = { | |
466 | .name = TYPE_SCLP, | |
467 | .parent = TYPE_DEVICE, | |
468 | .instance_init = sclp_init, | |
469 | .instance_size = sizeof(SCLPDevice), | |
470 | .class_init = sclp_class_init, | |
471 | .class_size = sizeof(SCLPDeviceClass), | |
472 | }; | |
473 | ||
0844df77 MR |
474 | static void register_types(void) |
475 | { | |
515190d9 | 476 | type_register_static(&sclp_info); |
0844df77 MR |
477 | } |
478 | type_init(register_types); |