]>
Commit | Line | Data |
---|---|---|
3d672205 TH |
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" | |
4e58b838 | 17 | #include "internal.h" |
3d672205 | 18 | #include "exec/address-spaces.h" |
3d672205 TH |
19 | #include "hw/watchdog/wdt_diag288.h" |
20 | #include "sysemu/cpus.h" | |
21 | #include "hw/s390x/ipl.h" | |
19c69829 | 22 | #include "hw/s390x/s390-virtio-ccw.h" |
3d672205 | 23 | |
3d672205 TH |
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 | ||
0b7fd817 JF |
56 | #define DIAG308_RESET_MOD_CLR 0 |
57 | #define DIAG308_RESET_LOAD_NORM 1 | |
58 | #define DIAG308_LOAD_CLEAR 3 | |
59 | #define DIAG308_LOAD_NORMAL_DUMP 4 | |
60 | #define DIAG308_SET 5 | |
61 | #define DIAG308_STORE 6 | |
62 | ||
63 | static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr, | |
64 | uintptr_t ra, bool write) | |
65 | { | |
66 | if ((r1 & 1) || (addr & ~TARGET_PAGE_MASK)) { | |
67 | s390_program_interrupt(env, PGM_SPECIFICATION, ra); | |
68 | return -1; | |
69 | } | |
70 | if (!address_space_access_valid(&address_space_memory, addr, | |
71 | sizeof(IplParameterBlock), write, | |
72 | MEMTXATTRS_UNSPECIFIED)) { | |
73 | s390_program_interrupt(env, PGM_ADDRESSING, ra); | |
74 | return -1; | |
75 | } | |
76 | return 0; | |
77 | } | |
78 | ||
968db419 | 79 | void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) |
3d672205 | 80 | { |
dc79e928 | 81 | CPUState *cs = env_cpu(env); |
3d672205 TH |
82 | uint64_t addr = env->regs[r1]; |
83 | uint64_t subcode = env->regs[r3]; | |
84 | IplParameterBlock *iplb; | |
85 | ||
86 | if (env->psw.mask & PSW_MASK_PSTATE) { | |
77b703f8 | 87 | s390_program_interrupt(env, PGM_PRIVILEGED, ra); |
3d672205 TH |
88 | return; |
89 | } | |
90 | ||
0b7fd817 | 91 | if (subcode & ~0x0ffffULL) { |
77b703f8 | 92 | s390_program_interrupt(env, PGM_SPECIFICATION, ra); |
3d672205 TH |
93 | return; |
94 | } | |
95 | ||
96 | switch (subcode) { | |
0b7fd817 | 97 | case DIAG308_RESET_MOD_CLR: |
a30fb811 | 98 | s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR); |
3d672205 | 99 | break; |
0b7fd817 | 100 | case DIAG308_RESET_LOAD_NORM: |
a30fb811 | 101 | s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL); |
3d672205 | 102 | break; |
0b7fd817 JF |
103 | case DIAG308_LOAD_CLEAR: |
104 | /* Well we still lack the clearing bit... */ | |
a30fb811 | 105 | s390_ipl_reset_request(cs, S390_RESET_REIPL); |
3d672205 | 106 | break; |
0b7fd817 JF |
107 | case DIAG308_SET: |
108 | if (diag308_parm_check(env, r1, addr, ra, false)) { | |
3d672205 TH |
109 | return; |
110 | } | |
96f64aa8 | 111 | iplb = g_new0(IplParameterBlock, 1); |
3d672205 TH |
112 | cpu_physical_memory_read(addr, iplb, sizeof(iplb->len)); |
113 | if (!iplb_valid_len(iplb)) { | |
114 | env->regs[r1 + 1] = DIAG_308_RC_INVALID; | |
115 | goto out; | |
116 | } | |
117 | ||
118 | cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len)); | |
119 | ||
120 | if (!iplb_valid_ccw(iplb) && !iplb_valid_fcp(iplb)) { | |
121 | env->regs[r1 + 1] = DIAG_308_RC_INVALID; | |
122 | goto out; | |
123 | } | |
124 | ||
125 | s390_ipl_update_diag308(iplb); | |
126 | env->regs[r1 + 1] = DIAG_308_RC_OK; | |
127 | out: | |
128 | g_free(iplb); | |
129 | return; | |
0b7fd817 JF |
130 | case DIAG308_STORE: |
131 | if (diag308_parm_check(env, r1, addr, ra, true)) { | |
3d672205 TH |
132 | return; |
133 | } | |
134 | iplb = s390_ipl_get_iplb(); | |
135 | if (iplb) { | |
136 | cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len)); | |
137 | env->regs[r1 + 1] = DIAG_308_RC_OK; | |
138 | } else { | |
139 | env->regs[r1 + 1] = DIAG_308_RC_NO_CONF; | |
140 | } | |
141 | return; | |
142 | default: | |
77b703f8 | 143 | s390_program_interrupt(env, PGM_SPECIFICATION, ra); |
3d672205 TH |
144 | break; |
145 | } | |
146 | } |