1 | /* |
2 | * S390x DIAG instruction helper functions |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | */ |
14 | |
15 | #include "qemu/osdep.h" |
16 | #include "cpu.h" |
17 | #include "internal.h" |
18 | #include "exec/address-spaces.h" |
19 | #include "hw/watchdog/wdt_diag288.h" |
20 | #include "sysemu/cpus.h" |
21 | #include "hw/s390x/ipl.h" |
22 | #include "hw/s390x/s390-virtio-ccw.h" |
23 | |
24 | int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) |
25 | { |
26 | uint64_t func = env->regs[r1]; |
27 | uint64_t timeout = env->regs[r1 + 1]; |
28 | uint64_t action = env->regs[r3]; |
29 | Object *obj; |
30 | DIAG288State *diag288; |
31 | DIAG288Class *diag288_class; |
32 | |
33 | if (r1 % 2 || action != 0) { |
34 | return -1; |
35 | } |
36 | |
37 | /* Timeout must be more than 15 seconds except for timer deletion */ |
38 | if (func != WDT_DIAG288_CANCEL && timeout < 15) { |
39 | return -1; |
40 | } |
41 | |
42 | obj = object_resolve_path_type("" , TYPE_WDT_DIAG288, NULL); |
43 | if (!obj) { |
44 | return -1; |
45 | } |
46 | |
47 | diag288 = DIAG288(obj); |
48 | diag288_class = DIAG288_GET_CLASS(diag288); |
49 | return diag288_class->handle_timer(diag288, func, timeout); |
50 | } |
51 | |
52 | #define DIAG_308_RC_OK 0x0001 |
53 | #define DIAG_308_RC_NO_CONF 0x0102 |
54 | #define DIAG_308_RC_INVALID 0x0402 |
55 | |
56 | void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) |
57 | { |
58 | CPUState *cs = env_cpu(env); |
59 | uint64_t addr = env->regs[r1]; |
60 | uint64_t subcode = env->regs[r3]; |
61 | IplParameterBlock *iplb; |
62 | |
63 | if (env->psw.mask & PSW_MASK_PSTATE) { |
64 | s390_program_interrupt(env, PGM_PRIVILEGED, ILEN_AUTO, ra); |
65 | return; |
66 | } |
67 | |
68 | if ((subcode & ~0x0ffffULL) || (subcode > 6)) { |
69 | s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); |
70 | return; |
71 | } |
72 | |
73 | switch (subcode) { |
74 | case 0: |
75 | s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR); |
76 | break; |
77 | case 1: |
78 | s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL); |
79 | break; |
80 | case 3: |
81 | s390_ipl_reset_request(cs, S390_RESET_REIPL); |
82 | break; |
83 | case 5: |
84 | if ((r1 & 1) || (addr & 0x0fffULL)) { |
85 | s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); |
86 | return; |
87 | } |
88 | if (!address_space_access_valid(&address_space_memory, addr, |
89 | sizeof(IplParameterBlock), false, |
90 | MEMTXATTRS_UNSPECIFIED)) { |
91 | s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); |
92 | return; |
93 | } |
94 | iplb = g_new0(IplParameterBlock, 1); |
95 | cpu_physical_memory_read(addr, iplb, sizeof(iplb->len)); |
96 | if (!iplb_valid_len(iplb)) { |
97 | env->regs[r1 + 1] = DIAG_308_RC_INVALID; |
98 | goto out; |
99 | } |
100 | |
101 | cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len)); |
102 | |
103 | if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) { |
104 | env->regs[r1 + 1] = DIAG_308_RC_INVALID; |
105 | goto out; |
106 | } |
107 | |
108 | s390_ipl_update_diag308(iplb); |
109 | env->regs[r1 + 1] = DIAG_308_RC_OK; |
110 | out: |
111 | g_free(iplb); |
112 | return; |
113 | case 6: |
114 | if ((r1 & 1) || (addr & 0x0fffULL)) { |
115 | s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); |
116 | return; |
117 | } |
118 | if (!address_space_access_valid(&address_space_memory, addr, |
119 | sizeof(IplParameterBlock), true, |
120 | MEMTXATTRS_UNSPECIFIED)) { |
121 | s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); |
122 | return; |
123 | } |
124 | iplb = s390_ipl_get_iplb(); |
125 | if (iplb) { |
126 | cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len)); |
127 | env->regs[r1 + 1] = DIAG_308_RC_OK; |
128 | } else { |
129 | env->regs[r1 + 1] = DIAG_308_RC_NO_CONF; |
130 | } |
131 | return; |
132 | default: |
133 | s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); |
134 | break; |
135 | } |
136 | } |
137 | |