1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright IBM Corp. 2007,2012 |
4 | * |
5 | * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> |
6 | */ |
7 | |
8 | #define KMSG_COMPONENT "sclp_cmd" |
9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
10 | |
11 | #include <linux/completion.h> |
12 | #include <linux/init.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/err.h> |
15 | #include <linux/export.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/string.h> |
18 | #include <linux/mm.h> |
19 | #include <linux/mmzone.h> |
20 | #include <linux/memory.h> |
21 | #include <linux/module.h> |
22 | #include <asm/ctlreg.h> |
23 | #include <asm/chpid.h> |
24 | #include <asm/setup.h> |
25 | #include <asm/page.h> |
26 | #include <asm/sclp.h> |
27 | #include <asm/numa.h> |
28 | #include <asm/facility.h> |
29 | |
30 | #include "sclp.h" |
31 | |
32 | static void sclp_sync_callback(struct sclp_req *req, void *data) |
33 | { |
34 | struct completion *completion = data; |
35 | |
36 | complete(completion); |
37 | } |
38 | |
39 | int sclp_sync_request(sclp_cmdw_t cmd, void *sccb) |
40 | { |
41 | return sclp_sync_request_timeout(command: cmd, sccb, timeout: 0); |
42 | } |
43 | |
44 | int sclp_sync_request_timeout(sclp_cmdw_t cmd, void *sccb, int timeout) |
45 | { |
46 | struct completion completion; |
47 | struct sclp_req *request; |
48 | int rc; |
49 | |
50 | request = kzalloc(size: sizeof(*request), GFP_KERNEL); |
51 | if (!request) |
52 | return -ENOMEM; |
53 | if (timeout) |
54 | request->queue_timeout = timeout; |
55 | request->command = cmd; |
56 | request->sccb = sccb; |
57 | request->status = SCLP_REQ_FILLED; |
58 | request->callback = sclp_sync_callback; |
59 | request->callback_data = &completion; |
60 | init_completion(x: &completion); |
61 | |
62 | /* Perform sclp request. */ |
63 | rc = sclp_add_request(req: request); |
64 | if (rc) |
65 | goto out; |
66 | wait_for_completion(&completion); |
67 | |
68 | /* Check response. */ |
69 | if (request->status != SCLP_REQ_DONE) { |
70 | pr_warn("sync request failed (cmd=0x%08x, status=0x%02x)\n" , |
71 | cmd, request->status); |
72 | rc = -EIO; |
73 | } |
74 | out: |
75 | kfree(objp: request); |
76 | return rc; |
77 | } |
78 | |
79 | /* |
80 | * CPU configuration related functions. |
81 | */ |
82 | |
83 | #define SCLP_CMDW_CONFIGURE_CPU 0x00110001 |
84 | #define SCLP_CMDW_DECONFIGURE_CPU 0x00100001 |
85 | |
86 | int _sclp_get_core_info(struct sclp_core_info *info) |
87 | { |
88 | int rc; |
89 | int length = test_facility(140) ? EXT_SCCB_READ_CPU : PAGE_SIZE; |
90 | struct read_cpu_info_sccb *sccb; |
91 | |
92 | if (!SCLP_HAS_CPU_INFO) |
93 | return -EOPNOTSUPP; |
94 | |
95 | sccb = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA | __GFP_ZERO, order: get_order(size: length)); |
96 | if (!sccb) |
97 | return -ENOMEM; |
98 | sccb->header.length = length; |
99 | sccb->header.control_mask[2] = 0x80; |
100 | rc = sclp_sync_request_timeout(SCLP_CMDW_READ_CPU_INFO, sccb, |
101 | SCLP_QUEUE_INTERVAL); |
102 | if (rc) |
103 | goto out; |
104 | if (sccb->header.response_code != 0x0010) { |
105 | pr_warn("readcpuinfo failed (response=0x%04x)\n" , |
106 | sccb->header.response_code); |
107 | rc = -EIO; |
108 | goto out; |
109 | } |
110 | sclp_fill_core_info(info, sccb); |
111 | out: |
112 | free_pages(addr: (unsigned long) sccb, order: get_order(size: length)); |
113 | return rc; |
114 | } |
115 | |
116 | struct cpu_configure_sccb { |
117 | struct sccb_header ; |
118 | } __attribute__((packed, aligned(8))); |
119 | |
120 | static int do_core_configure(sclp_cmdw_t cmd) |
121 | { |
122 | struct cpu_configure_sccb *sccb; |
123 | int rc; |
124 | |
125 | if (!SCLP_HAS_CPU_RECONFIG) |
126 | return -EOPNOTSUPP; |
127 | /* |
128 | * This is not going to cross a page boundary since we force |
129 | * kmalloc to have a minimum alignment of 8 bytes on s390. |
130 | */ |
131 | sccb = kzalloc(size: sizeof(*sccb), GFP_KERNEL | GFP_DMA); |
132 | if (!sccb) |
133 | return -ENOMEM; |
134 | sccb->header.length = sizeof(*sccb); |
135 | rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL); |
136 | if (rc) |
137 | goto out; |
138 | switch (sccb->header.response_code) { |
139 | case 0x0020: |
140 | case 0x0120: |
141 | break; |
142 | default: |
143 | pr_warn("configure cpu failed (cmd=0x%08x, response=0x%04x)\n" , |
144 | cmd, sccb->header.response_code); |
145 | rc = -EIO; |
146 | break; |
147 | } |
148 | out: |
149 | kfree(objp: sccb); |
150 | return rc; |
151 | } |
152 | |
153 | int sclp_core_configure(u8 core) |
154 | { |
155 | return do_core_configure(SCLP_CMDW_CONFIGURE_CPU | core << 8); |
156 | } |
157 | |
158 | int sclp_core_deconfigure(u8 core) |
159 | { |
160 | return do_core_configure(SCLP_CMDW_DECONFIGURE_CPU | core << 8); |
161 | } |
162 | |
163 | #ifdef CONFIG_MEMORY_HOTPLUG |
164 | |
165 | static DEFINE_MUTEX(sclp_mem_mutex); |
166 | static LIST_HEAD(sclp_mem_list); |
167 | static u8 sclp_max_storage_id; |
168 | static DECLARE_BITMAP(sclp_storage_ids, 256); |
169 | |
170 | struct memory_increment { |
171 | struct list_head list; |
172 | u16 rn; |
173 | int standby; |
174 | }; |
175 | |
176 | struct assign_storage_sccb { |
177 | struct sccb_header ; |
178 | u16 rn; |
179 | } __packed; |
180 | |
181 | int arch_get_memory_phys_device(unsigned long start_pfn) |
182 | { |
183 | if (!sclp.rzm) |
184 | return 0; |
185 | return PFN_PHYS(start_pfn) >> ilog2(sclp.rzm); |
186 | } |
187 | |
188 | static unsigned long long rn2addr(u16 rn) |
189 | { |
190 | return (unsigned long long) (rn - 1) * sclp.rzm; |
191 | } |
192 | |
193 | static int do_assign_storage(sclp_cmdw_t cmd, u16 rn) |
194 | { |
195 | struct assign_storage_sccb *sccb; |
196 | int rc; |
197 | |
198 | sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); |
199 | if (!sccb) |
200 | return -ENOMEM; |
201 | sccb->header.length = PAGE_SIZE; |
202 | sccb->rn = rn; |
203 | rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL); |
204 | if (rc) |
205 | goto out; |
206 | switch (sccb->header.response_code) { |
207 | case 0x0020: |
208 | case 0x0120: |
209 | break; |
210 | default: |
211 | pr_warn("assign storage failed (cmd=0x%08x, response=0x%04x, rn=0x%04x)\n" , |
212 | cmd, sccb->header.response_code, rn); |
213 | rc = -EIO; |
214 | break; |
215 | } |
216 | out: |
217 | free_page((unsigned long) sccb); |
218 | return rc; |
219 | } |
220 | |
221 | static int sclp_assign_storage(u16 rn) |
222 | { |
223 | unsigned long long start; |
224 | int rc; |
225 | |
226 | rc = do_assign_storage(cmd: 0x000d0001, rn); |
227 | if (rc) |
228 | return rc; |
229 | start = rn2addr(rn); |
230 | storage_key_init_range(start, start + sclp.rzm); |
231 | return 0; |
232 | } |
233 | |
234 | static int sclp_unassign_storage(u16 rn) |
235 | { |
236 | return do_assign_storage(cmd: 0x000c0001, rn); |
237 | } |
238 | |
239 | struct attach_storage_sccb { |
240 | struct sccb_header ; |
241 | u16 :16; |
242 | u16 assigned; |
243 | u32 :32; |
244 | u32 entries[]; |
245 | } __packed; |
246 | |
247 | static int sclp_attach_storage(u8 id) |
248 | { |
249 | struct attach_storage_sccb *sccb; |
250 | int rc; |
251 | int i; |
252 | |
253 | sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); |
254 | if (!sccb) |
255 | return -ENOMEM; |
256 | sccb->header.length = PAGE_SIZE; |
257 | sccb->header.function_code = 0x40; |
258 | rc = sclp_sync_request_timeout(cmd: 0x00080001 | id << 8, sccb, |
259 | SCLP_QUEUE_INTERVAL); |
260 | if (rc) |
261 | goto out; |
262 | switch (sccb->header.response_code) { |
263 | case 0x0020: |
264 | set_bit(nr: id, addr: sclp_storage_ids); |
265 | for (i = 0; i < sccb->assigned; i++) { |
266 | if (sccb->entries[i]) |
267 | sclp_unassign_storage(rn: sccb->entries[i] >> 16); |
268 | } |
269 | break; |
270 | default: |
271 | rc = -EIO; |
272 | break; |
273 | } |
274 | out: |
275 | free_page((unsigned long) sccb); |
276 | return rc; |
277 | } |
278 | |
279 | static int sclp_mem_change_state(unsigned long start, unsigned long size, |
280 | int online) |
281 | { |
282 | struct memory_increment *incr; |
283 | unsigned long long istart; |
284 | int rc = 0; |
285 | |
286 | list_for_each_entry(incr, &sclp_mem_list, list) { |
287 | istart = rn2addr(rn: incr->rn); |
288 | if (start + size - 1 < istart) |
289 | break; |
290 | if (start > istart + sclp.rzm - 1) |
291 | continue; |
292 | if (online) |
293 | rc |= sclp_assign_storage(rn: incr->rn); |
294 | else |
295 | sclp_unassign_storage(rn: incr->rn); |
296 | if (rc == 0) |
297 | incr->standby = online ? 0 : 1; |
298 | } |
299 | return rc ? -EIO : 0; |
300 | } |
301 | |
302 | static bool contains_standby_increment(unsigned long start, unsigned long end) |
303 | { |
304 | struct memory_increment *incr; |
305 | unsigned long istart; |
306 | |
307 | list_for_each_entry(incr, &sclp_mem_list, list) { |
308 | istart = rn2addr(rn: incr->rn); |
309 | if (end - 1 < istart) |
310 | continue; |
311 | if (start > istart + sclp.rzm - 1) |
312 | continue; |
313 | if (incr->standby) |
314 | return true; |
315 | } |
316 | return false; |
317 | } |
318 | |
319 | static int sclp_mem_notifier(struct notifier_block *nb, |
320 | unsigned long action, void *data) |
321 | { |
322 | unsigned long start, size; |
323 | struct memory_notify *arg; |
324 | unsigned char id; |
325 | int rc = 0; |
326 | |
327 | arg = data; |
328 | start = arg->start_pfn << PAGE_SHIFT; |
329 | size = arg->nr_pages << PAGE_SHIFT; |
330 | mutex_lock(&sclp_mem_mutex); |
331 | for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1) |
332 | sclp_attach_storage(id); |
333 | switch (action) { |
334 | case MEM_GOING_OFFLINE: |
335 | /* |
336 | * We do not allow to set memory blocks offline that contain |
337 | * standby memory. This is done to simplify the "memory online" |
338 | * case. |
339 | */ |
340 | if (contains_standby_increment(start, end: start + size)) |
341 | rc = -EPERM; |
342 | break; |
343 | case MEM_ONLINE: |
344 | case MEM_CANCEL_OFFLINE: |
345 | break; |
346 | case MEM_GOING_ONLINE: |
347 | rc = sclp_mem_change_state(start, size, online: 1); |
348 | break; |
349 | case MEM_CANCEL_ONLINE: |
350 | sclp_mem_change_state(start, size, online: 0); |
351 | break; |
352 | case MEM_OFFLINE: |
353 | sclp_mem_change_state(start, size, online: 0); |
354 | break; |
355 | default: |
356 | break; |
357 | } |
358 | mutex_unlock(lock: &sclp_mem_mutex); |
359 | return rc ? NOTIFY_BAD : NOTIFY_OK; |
360 | } |
361 | |
362 | static struct notifier_block sclp_mem_nb = { |
363 | .notifier_call = sclp_mem_notifier, |
364 | }; |
365 | |
366 | static void __init align_to_block_size(unsigned long long *start, |
367 | unsigned long long *size, |
368 | unsigned long long alignment) |
369 | { |
370 | unsigned long long start_align, size_align; |
371 | |
372 | start_align = roundup(*start, alignment); |
373 | size_align = rounddown(*start + *size, alignment) - start_align; |
374 | |
375 | pr_info("Standby memory at 0x%llx (%lluM of %lluM usable)\n" , |
376 | *start, size_align >> 20, *size >> 20); |
377 | *start = start_align; |
378 | *size = size_align; |
379 | } |
380 | |
381 | static void __init add_memory_merged(u16 rn) |
382 | { |
383 | unsigned long long start, size, addr, block_size; |
384 | static u16 first_rn, num; |
385 | |
386 | if (rn && first_rn && (first_rn + num == rn)) { |
387 | num++; |
388 | return; |
389 | } |
390 | if (!first_rn) |
391 | goto skip_add; |
392 | start = rn2addr(rn: first_rn); |
393 | size = (unsigned long long) num * sclp.rzm; |
394 | if (start >= ident_map_size) |
395 | goto skip_add; |
396 | if (start + size > ident_map_size) |
397 | size = ident_map_size - start; |
398 | block_size = memory_block_size_bytes(); |
399 | align_to_block_size(start: &start, size: &size, alignment: block_size); |
400 | if (!size) |
401 | goto skip_add; |
402 | for (addr = start; addr < start + size; addr += block_size) |
403 | add_memory(nid: 0, start: addr, size: block_size, MHP_NONE); |
404 | skip_add: |
405 | first_rn = rn; |
406 | num = 1; |
407 | } |
408 | |
409 | static void __init sclp_add_standby_memory(void) |
410 | { |
411 | struct memory_increment *incr; |
412 | |
413 | list_for_each_entry(incr, &sclp_mem_list, list) |
414 | if (incr->standby) |
415 | add_memory_merged(rn: incr->rn); |
416 | add_memory_merged(rn: 0); |
417 | } |
418 | |
419 | static void __init insert_increment(u16 rn, int standby, int assigned) |
420 | { |
421 | struct memory_increment *incr, *new_incr; |
422 | struct list_head *prev; |
423 | u16 last_rn; |
424 | |
425 | new_incr = kzalloc(size: sizeof(*new_incr), GFP_KERNEL); |
426 | if (!new_incr) |
427 | return; |
428 | new_incr->rn = rn; |
429 | new_incr->standby = standby; |
430 | last_rn = 0; |
431 | prev = &sclp_mem_list; |
432 | list_for_each_entry(incr, &sclp_mem_list, list) { |
433 | if (assigned && incr->rn > rn) |
434 | break; |
435 | if (!assigned && incr->rn - last_rn > 1) |
436 | break; |
437 | last_rn = incr->rn; |
438 | prev = &incr->list; |
439 | } |
440 | if (!assigned) |
441 | new_incr->rn = last_rn + 1; |
442 | if (new_incr->rn > sclp.rnmax) { |
443 | kfree(objp: new_incr); |
444 | return; |
445 | } |
446 | list_add(new: &new_incr->list, head: prev); |
447 | } |
448 | |
449 | static int __init sclp_detect_standby_memory(void) |
450 | { |
451 | struct read_storage_sccb *sccb; |
452 | int i, id, assigned, rc; |
453 | |
454 | if (oldmem_data.start) /* No standby memory in kdump mode */ |
455 | return 0; |
456 | if ((sclp.facilities & 0xe00000000000ULL) != 0xe00000000000ULL) |
457 | return 0; |
458 | rc = -ENOMEM; |
459 | sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA); |
460 | if (!sccb) |
461 | goto out; |
462 | assigned = 0; |
463 | for (id = 0; id <= sclp_max_storage_id; id++) { |
464 | memset(sccb, 0, PAGE_SIZE); |
465 | sccb->header.length = PAGE_SIZE; |
466 | rc = sclp_sync_request(SCLP_CMDW_READ_STORAGE_INFO | id << 8, sccb); |
467 | if (rc) |
468 | goto out; |
469 | switch (sccb->header.response_code) { |
470 | case 0x0010: |
471 | set_bit(nr: id, addr: sclp_storage_ids); |
472 | for (i = 0; i < sccb->assigned; i++) { |
473 | if (!sccb->entries[i]) |
474 | continue; |
475 | assigned++; |
476 | insert_increment(rn: sccb->entries[i] >> 16, standby: 0, assigned: 1); |
477 | } |
478 | break; |
479 | case 0x0310: |
480 | break; |
481 | case 0x0410: |
482 | for (i = 0; i < sccb->assigned; i++) { |
483 | if (!sccb->entries[i]) |
484 | continue; |
485 | assigned++; |
486 | insert_increment(rn: sccb->entries[i] >> 16, standby: 1, assigned: 1); |
487 | } |
488 | break; |
489 | default: |
490 | rc = -EIO; |
491 | break; |
492 | } |
493 | if (!rc) |
494 | sclp_max_storage_id = sccb->max_id; |
495 | } |
496 | if (rc || list_empty(head: &sclp_mem_list)) |
497 | goto out; |
498 | for (i = 1; i <= sclp.rnmax - assigned; i++) |
499 | insert_increment(rn: 0, standby: 1, assigned: 0); |
500 | rc = register_memory_notifier(nb: &sclp_mem_nb); |
501 | if (rc) |
502 | goto out; |
503 | sclp_add_standby_memory(); |
504 | out: |
505 | free_page((unsigned long) sccb); |
506 | return rc; |
507 | } |
508 | __initcall(sclp_detect_standby_memory); |
509 | |
510 | #endif /* CONFIG_MEMORY_HOTPLUG */ |
511 | |
512 | /* |
513 | * Channel path configuration related functions. |
514 | */ |
515 | |
516 | #define SCLP_CMDW_CONFIGURE_CHPATH 0x000f0001 |
517 | #define SCLP_CMDW_DECONFIGURE_CHPATH 0x000e0001 |
518 | #define SCLP_CMDW_READ_CHPATH_INFORMATION 0x00030001 |
519 | |
520 | struct chp_cfg_sccb { |
521 | struct sccb_header ; |
522 | u8 ccm; |
523 | u8 reserved[6]; |
524 | u8 cssid; |
525 | } __attribute__((packed)); |
526 | |
527 | static int do_chp_configure(sclp_cmdw_t cmd) |
528 | { |
529 | struct chp_cfg_sccb *sccb; |
530 | int rc; |
531 | |
532 | if (!SCLP_HAS_CHP_RECONFIG) |
533 | return -EOPNOTSUPP; |
534 | /* Prepare sccb. */ |
535 | sccb = (struct chp_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); |
536 | if (!sccb) |
537 | return -ENOMEM; |
538 | sccb->header.length = sizeof(*sccb); |
539 | rc = sclp_sync_request(cmd, sccb); |
540 | if (rc) |
541 | goto out; |
542 | switch (sccb->header.response_code) { |
543 | case 0x0020: |
544 | case 0x0120: |
545 | case 0x0440: |
546 | case 0x0450: |
547 | break; |
548 | default: |
549 | pr_warn("configure channel-path failed (cmd=0x%08x, response=0x%04x)\n" , |
550 | cmd, sccb->header.response_code); |
551 | rc = -EIO; |
552 | break; |
553 | } |
554 | out: |
555 | free_page((unsigned long) sccb); |
556 | return rc; |
557 | } |
558 | |
559 | /** |
560 | * sclp_chp_configure - perform configure channel-path sclp command |
561 | * @chpid: channel-path ID |
562 | * |
563 | * Perform configure channel-path command sclp command for specified chpid. |
564 | * Return 0 after command successfully finished, non-zero otherwise. |
565 | */ |
566 | int sclp_chp_configure(struct chp_id chpid) |
567 | { |
568 | return do_chp_configure(SCLP_CMDW_CONFIGURE_CHPATH | chpid.id << 8); |
569 | } |
570 | |
571 | /** |
572 | * sclp_chp_deconfigure - perform deconfigure channel-path sclp command |
573 | * @chpid: channel-path ID |
574 | * |
575 | * Perform deconfigure channel-path command sclp command for specified chpid |
576 | * and wait for completion. On success return 0. Return non-zero otherwise. |
577 | */ |
578 | int sclp_chp_deconfigure(struct chp_id chpid) |
579 | { |
580 | return do_chp_configure(SCLP_CMDW_DECONFIGURE_CHPATH | chpid.id << 8); |
581 | } |
582 | |
583 | struct chp_info_sccb { |
584 | struct sccb_header ; |
585 | u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; |
586 | u8 standby[SCLP_CHP_INFO_MASK_SIZE]; |
587 | u8 configured[SCLP_CHP_INFO_MASK_SIZE]; |
588 | u8 ccm; |
589 | u8 reserved[6]; |
590 | u8 cssid; |
591 | } __attribute__((packed)); |
592 | |
593 | /** |
594 | * sclp_chp_read_info - perform read channel-path information sclp command |
595 | * @info: resulting channel-path information data |
596 | * |
597 | * Perform read channel-path information sclp command and wait for completion. |
598 | * On success, store channel-path information in @info and return 0. Return |
599 | * non-zero otherwise. |
600 | */ |
601 | int sclp_chp_read_info(struct sclp_chp_info *info) |
602 | { |
603 | struct chp_info_sccb *sccb; |
604 | int rc; |
605 | |
606 | if (!SCLP_HAS_CHP_INFO) |
607 | return -EOPNOTSUPP; |
608 | /* Prepare sccb. */ |
609 | sccb = (struct chp_info_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); |
610 | if (!sccb) |
611 | return -ENOMEM; |
612 | sccb->header.length = sizeof(*sccb); |
613 | rc = sclp_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); |
614 | if (rc) |
615 | goto out; |
616 | if (sccb->header.response_code != 0x0010) { |
617 | pr_warn("read channel-path info failed (response=0x%04x)\n" , |
618 | sccb->header.response_code); |
619 | rc = -EIO; |
620 | goto out; |
621 | } |
622 | memcpy(info->recognized, sccb->recognized, SCLP_CHP_INFO_MASK_SIZE); |
623 | memcpy(info->standby, sccb->standby, SCLP_CHP_INFO_MASK_SIZE); |
624 | memcpy(info->configured, sccb->configured, SCLP_CHP_INFO_MASK_SIZE); |
625 | out: |
626 | free_page((unsigned long) sccb); |
627 | return rc; |
628 | } |
629 | |