]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * s390 IPL device | |
3 | * | |
4 | * Copyright 2015, 2020 IBM Corp. | |
5 | * Author(s): Zhang Fan <[email protected]> | |
6 | * Janosch Frank <[email protected]> | |
7 | * | |
8 | * This work is licensed under the terms of the GNU GPL, version 2 or (at | |
9 | * your option) any later version. See the COPYING file in the top-level | |
10 | * directory. | |
11 | */ | |
12 | ||
13 | #ifndef HW_S390_IPL_H | |
14 | #define HW_S390_IPL_H | |
15 | ||
16 | #include "cpu.h" | |
17 | #include "exec/address-spaces.h" | |
18 | #include "hw/qdev-core.h" | |
19 | #include "qom/object.h" | |
20 | ||
21 | struct IPLBlockPVComp { | |
22 | uint64_t tweak_pref; | |
23 | uint64_t addr; | |
24 | uint64_t size; | |
25 | } QEMU_PACKED; | |
26 | typedef struct IPLBlockPVComp IPLBlockPVComp; | |
27 | ||
28 | struct IPLBlockPV { | |
29 | uint8_t reserved18[87]; /* 0x18 */ | |
30 | uint8_t version; /* 0x6f */ | |
31 | uint32_t reserved70; /* 0x70 */ | |
32 | uint32_t num_comp; /* 0x74 */ | |
33 | uint64_t pv_header_addr; /* 0x78 */ | |
34 | uint64_t pv_header_len; /* 0x80 */ | |
35 | struct IPLBlockPVComp components[0]; | |
36 | } QEMU_PACKED; | |
37 | typedef struct IPLBlockPV IPLBlockPV; | |
38 | ||
39 | struct IplBlockCcw { | |
40 | uint8_t reserved0[85]; | |
41 | uint8_t ssid; | |
42 | uint16_t devno; | |
43 | uint8_t vm_flags; | |
44 | uint8_t reserved3[3]; | |
45 | uint32_t vm_parm_len; | |
46 | uint8_t nss_name[8]; | |
47 | uint8_t vm_parm[64]; | |
48 | uint8_t reserved4[8]; | |
49 | } QEMU_PACKED; | |
50 | typedef struct IplBlockCcw IplBlockCcw; | |
51 | ||
52 | struct IplBlockFcp { | |
53 | uint8_t reserved1[305 - 1]; | |
54 | uint8_t opt; | |
55 | uint8_t reserved2[3]; | |
56 | uint16_t reserved3; | |
57 | uint16_t devno; | |
58 | uint8_t reserved4[4]; | |
59 | uint64_t wwpn; | |
60 | uint64_t lun; | |
61 | uint32_t bootprog; | |
62 | uint8_t reserved5[12]; | |
63 | uint64_t br_lba; | |
64 | uint32_t scp_data_len; | |
65 | uint8_t reserved6[260]; | |
66 | uint8_t scp_data[0]; | |
67 | } QEMU_PACKED; | |
68 | typedef struct IplBlockFcp IplBlockFcp; | |
69 | ||
70 | struct IplBlockQemuScsi { | |
71 | uint32_t lun; | |
72 | uint16_t target; | |
73 | uint16_t channel; | |
74 | uint8_t reserved0[77]; | |
75 | uint8_t ssid; | |
76 | uint16_t devno; | |
77 | } QEMU_PACKED; | |
78 | typedef struct IplBlockQemuScsi IplBlockQemuScsi; | |
79 | ||
80 | #define DIAG308_FLAGS_LP_VALID 0x80 | |
81 | ||
82 | union IplParameterBlock { | |
83 | struct { | |
84 | uint32_t len; | |
85 | uint8_t reserved0[3]; | |
86 | uint8_t version; | |
87 | uint32_t blk0_len; | |
88 | uint8_t pbt; | |
89 | uint8_t flags; | |
90 | uint16_t reserved01; | |
91 | uint8_t loadparm[8]; | |
92 | union { | |
93 | IplBlockCcw ccw; | |
94 | IplBlockFcp fcp; | |
95 | IPLBlockPV pv; | |
96 | IplBlockQemuScsi scsi; | |
97 | }; | |
98 | } QEMU_PACKED; | |
99 | struct { | |
100 | uint8_t reserved1[110]; | |
101 | uint16_t devno; | |
102 | uint8_t reserved2[88]; | |
103 | uint8_t reserved_ext[4096 - 200]; | |
104 | } QEMU_PACKED; | |
105 | } QEMU_PACKED; | |
106 | typedef union IplParameterBlock IplParameterBlock; | |
107 | ||
108 | int s390_ipl_set_loadparm(uint8_t *loadparm); | |
109 | void s390_ipl_update_diag308(IplParameterBlock *iplb); | |
110 | int s390_ipl_prepare_pv_header(void); | |
111 | int s390_ipl_pv_unpack(void); | |
112 | void s390_ipl_prepare_cpu(S390CPU *cpu); | |
113 | IplParameterBlock *s390_ipl_get_iplb(void); | |
114 | IplParameterBlock *s390_ipl_get_iplb_pv(void); | |
115 | ||
116 | enum s390_reset { | |
117 | /* default is a reset not triggered by a CPU e.g. issued by QMP */ | |
118 | S390_RESET_EXTERNAL = 0, | |
119 | S390_RESET_REIPL, | |
120 | S390_RESET_MODIFIED_CLEAR, | |
121 | S390_RESET_LOAD_NORMAL, | |
122 | S390_RESET_PV, | |
123 | }; | |
124 | void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type); | |
125 | void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type); | |
126 | void s390_ipl_clear_reset_request(void); | |
127 | ||
128 | #define QIPL_ADDRESS 0xcc | |
129 | ||
130 | /* Boot Menu flags */ | |
131 | #define QIPL_FLAG_BM_OPTS_CMD 0x80 | |
132 | #define QIPL_FLAG_BM_OPTS_ZIPL 0x40 | |
133 | ||
134 | /* | |
135 | * The QEMU IPL Parameters will be stored at absolute address | |
136 | * 204 (0xcc) which means it is 32-bit word aligned but not | |
137 | * double-word aligned. | |
138 | * Placement of data fields in this area must account for | |
139 | * their alignment needs. E.g., netboot_start_address must | |
140 | * have an offset of 4 + n * 8 bytes within the struct in order | |
141 | * to keep it double-word aligned. | |
142 | * The total size of the struct must never exceed 28 bytes. | |
143 | * This definition must be kept in sync with the definition | |
144 | * in pc-bios/s390-ccw/iplb.h. | |
145 | */ | |
146 | struct QemuIplParameters { | |
147 | uint8_t qipl_flags; | |
148 | uint8_t reserved1[3]; | |
149 | uint64_t netboot_start_addr; | |
150 | uint32_t boot_menu_timeout; | |
151 | uint8_t reserved2[12]; | |
152 | } QEMU_PACKED; | |
153 | typedef struct QemuIplParameters QemuIplParameters; | |
154 | ||
155 | #define TYPE_S390_IPL "s390-ipl" | |
156 | OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL) | |
157 | ||
158 | struct S390IPLState { | |
159 | /*< private >*/ | |
160 | DeviceState parent_obj; | |
161 | IplParameterBlock iplb; | |
162 | IplParameterBlock iplb_pv; | |
163 | QemuIplParameters qipl; | |
164 | uint64_t start_addr; | |
165 | uint64_t compat_start_addr; | |
166 | uint64_t bios_start_addr; | |
167 | uint64_t compat_bios_start_addr; | |
168 | bool enforce_bios; | |
169 | bool iplb_valid; | |
170 | bool iplb_valid_pv; | |
171 | bool netboot; | |
172 | /* reset related properties don't have to be migrated or reset */ | |
173 | enum s390_reset reset_type; | |
174 | int reset_cpu_index; | |
175 | ||
176 | /*< public >*/ | |
177 | char *kernel; | |
178 | char *initrd; | |
179 | char *cmdline; | |
180 | char *firmware; | |
181 | char *netboot_fw; | |
182 | uint8_t cssid; | |
183 | uint8_t ssid; | |
184 | uint16_t devno; | |
185 | bool iplbext_migration; | |
186 | }; | |
187 | QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong"); | |
188 | ||
189 | #define DIAG_308_RC_OK 0x0001 | |
190 | #define DIAG_308_RC_NO_CONF 0x0102 | |
191 | #define DIAG_308_RC_INVALID 0x0402 | |
192 | #define DIAG_308_RC_NO_PV_CONF 0x0902 | |
193 | #define DIAG_308_RC_INVAL_FOR_PV 0x0a02 | |
194 | ||
195 | #define DIAG308_RESET_MOD_CLR 0 | |
196 | #define DIAG308_RESET_LOAD_NORM 1 | |
197 | #define DIAG308_LOAD_CLEAR 3 | |
198 | #define DIAG308_LOAD_NORMAL_DUMP 4 | |
199 | #define DIAG308_SET 5 | |
200 | #define DIAG308_STORE 6 | |
201 | #define DIAG308_PV_SET 8 | |
202 | #define DIAG308_PV_STORE 9 | |
203 | #define DIAG308_PV_START 10 | |
204 | ||
205 | #define S390_IPL_TYPE_FCP 0x00 | |
206 | #define S390_IPL_TYPE_CCW 0x02 | |
207 | #define S390_IPL_TYPE_PV 0x05 | |
208 | #define S390_IPL_TYPE_QEMU_SCSI 0xff | |
209 | ||
210 | #define S390_IPLB_HEADER_LEN 8 | |
211 | #define S390_IPLB_MIN_PV_LEN 148 | |
212 | #define S390_IPLB_MIN_CCW_LEN 200 | |
213 | #define S390_IPLB_MIN_FCP_LEN 384 | |
214 | #define S390_IPLB_MIN_QEMU_SCSI_LEN 200 | |
215 | ||
216 | static inline bool iplb_valid_len(IplParameterBlock *iplb) | |
217 | { | |
218 | return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock); | |
219 | } | |
220 | ||
221 | static inline bool ipl_valid_pv_components(IplParameterBlock *iplb) | |
222 | { | |
223 | IPLBlockPV *ipib_pv = &iplb->pv; | |
224 | int i; | |
225 | ||
226 | if (ipib_pv->num_comp == 0) { | |
227 | return false; | |
228 | } | |
229 | ||
230 | for (i = 0; i < ipib_pv->num_comp; i++) { | |
231 | /* Addr must be 4k aligned */ | |
232 | if (ipib_pv->components[i].addr & ~TARGET_PAGE_MASK) { | |
233 | return false; | |
234 | } | |
235 | ||
236 | /* Tweak prefix is monotonically increasing with each component */ | |
237 | if (i < ipib_pv->num_comp - 1 && | |
238 | ipib_pv->components[i].tweak_pref >= | |
239 | ipib_pv->components[i + 1].tweak_pref) { | |
240 | return false; | |
241 | } | |
242 | } | |
243 | return true; | |
244 | } | |
245 | ||
246 | static inline bool ipl_valid_pv_header(IplParameterBlock *iplb) | |
247 | { | |
248 | IPLBlockPV *ipib_pv = &iplb->pv; | |
249 | ||
250 | if (ipib_pv->pv_header_len > 2 * TARGET_PAGE_SIZE) { | |
251 | return false; | |
252 | } | |
253 | ||
254 | if (!address_space_access_valid(&address_space_memory, | |
255 | ipib_pv->pv_header_addr, | |
256 | ipib_pv->pv_header_len, | |
257 | false, | |
258 | MEMTXATTRS_UNSPECIFIED)) { | |
259 | return false; | |
260 | } | |
261 | ||
262 | return true; | |
263 | } | |
264 | ||
265 | static inline bool iplb_valid_pv(IplParameterBlock *iplb) | |
266 | { | |
267 | if (iplb->pbt != S390_IPL_TYPE_PV || | |
268 | be32_to_cpu(iplb->len) < S390_IPLB_MIN_PV_LEN) { | |
269 | return false; | |
270 | } | |
271 | if (!ipl_valid_pv_header(iplb)) { | |
272 | return false; | |
273 | } | |
274 | return ipl_valid_pv_components(iplb); | |
275 | } | |
276 | ||
277 | static inline bool iplb_valid(IplParameterBlock *iplb) | |
278 | { | |
279 | switch (iplb->pbt) { | |
280 | case S390_IPL_TYPE_FCP: | |
281 | return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN; | |
282 | case S390_IPL_TYPE_CCW: | |
283 | return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN; | |
284 | default: | |
285 | return false; | |
286 | } | |
287 | } | |
288 | ||
289 | #endif |