1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Author(s)......: Carsten Otte <cotte@de.ibm.com>
4  * 		    Rob M van der Heij <rvdheij@nl.ibm.com>
5  * 		    Steven Shultz <shultzss@us.ibm.com>
6  * Bugreports.to..: <Linux390@de.ibm.com>
7  * Copyright IBM Corp. 2002, 2004
8  */
9 
10 #define KMSG_COMPONENT "extmem"
11 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
12 
13 #include <linux/kernel.h>
14 #include <linux/string.h>
15 #include <linux/spinlock.h>
16 #include <linux/list.h>
17 #include <linux/slab.h>
18 #include <linux/export.h>
19 #include <linux/memblock.h>
20 #include <linux/ctype.h>
21 #include <linux/ioport.h>
22 #include <linux/refcount.h>
23 #include <linux/pgtable.h>
24 #include <asm/machine.h>
25 #include <asm/diag.h>
26 #include <asm/page.h>
27 #include <asm/ebcdic.h>
28 #include <asm/errno.h>
29 #include <asm/extmem.h>
30 #include <asm/cpcmd.h>
31 #include <asm/setup.h>
32 #include <asm/asm.h>
33 
34 #define DCSS_PURGESEG   0x08
35 #define DCSS_LOADSHRX	0x20
36 #define DCSS_LOADNSRX	0x24
37 #define DCSS_FINDSEGX	0x2c
38 #define DCSS_SEGEXTX	0x38
39 #define DCSS_FINDSEGA   0x0c
40 
41 struct qrange {
42 	unsigned long  start; /* last byte type */
43 	unsigned long  end;   /* last byte reserved */
44 };
45 
46 struct qout64 {
47 	unsigned long segstart;
48 	unsigned long segend;
49 	int segcnt;
50 	int segrcnt;
51 	struct qrange range[6];
52 };
53 
54 struct qin64 {
55 	char qopcode;
56 	char rsrv1[3];
57 	char qrcode;
58 	char rsrv2[3];
59 	char qname[8];
60 	unsigned int qoutptr;
61 	short int qoutlen;
62 };
63 
64 struct dcss_segment {
65 	struct list_head list;
66 	char dcss_name[8];
67 	char res_name[16];
68 	unsigned long start_addr;
69 	unsigned long end;
70 	refcount_t ref_count;
71 	int do_nonshared;
72 	unsigned int vm_segtype;
73 	struct qrange range[6];
74 	int segcnt;
75 	struct resource *res;
76 };
77 
78 static DEFINE_MUTEX(dcss_lock);
79 static LIST_HEAD(dcss_list);
80 static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
81 					"EW/EN-MIXED" };
82 static int loadshr_scode = DCSS_LOADSHRX;
83 static int loadnsr_scode = DCSS_LOADNSRX;
84 static int purgeseg_scode = DCSS_PURGESEG;
85 static int segext_scode = DCSS_SEGEXTX;
86 
87 /*
88  * Create the 8 bytes, ebcdic VM segment name from
89  * an ascii name.
90  */
91 static void
92 dcss_mkname(char *name, char *dcss_name)
93 {
94 	int i;
95 
96 	for (i = 0; i < 8; i++) {
97 		if (name[i] == '\0')
98 			break;
99 		dcss_name[i] = toupper(name[i]);
100 	}
101 	for (; i < 8; i++)
102 		dcss_name[i] = ' ';
103 	ASCEBC(dcss_name, 8);
104 }
105 
106 
107 /*
108  * search all segments in dcss_list, and return the one
109  * namend *name. If not found, return NULL.
110  */
111 static struct dcss_segment *
112 segment_by_name (char *name)
113 {
114 	char dcss_name[9];
115 	struct list_head *l;
116 	struct dcss_segment *tmp, *retval = NULL;
117 
118 	BUG_ON(!mutex_is_locked(&dcss_lock));
119 	dcss_mkname (name, dcss_name);
120 	list_for_each (l, &dcss_list) {
121 		tmp = list_entry (l, struct dcss_segment, list);
122 		if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) {
123 			retval = tmp;
124 			break;
125 		}
126 	}
127 	return retval;
128 }
129 
130 
131 /*
132  * Perform a function on a dcss segment.
133  */
134 static inline int
135 dcss_diag(int *func, void *parameter,
136            unsigned long *ret1, unsigned long *ret2)
137 {
138 	unsigned long rx, ry;
139 	int cc;
140 
141 	rx = virt_to_phys(parameter);
142 	ry = (unsigned long) *func;
143 
144 	diag_stat_inc(DIAG_STAT_X064);
145 	asm volatile(
146 		"	diag	%[rx],%[ry],0x64\n"
147 		CC_IPM(cc)
148 		: CC_OUT(cc, cc), [rx] "+d" (rx), [ry] "+d" (ry)
149 		:
150 		: CC_CLOBBER);
151 	*ret1 = rx;
152 	*ret2 = ry;
153 	return CC_TRANSFORM(cc);
154 }
155 
156 static inline int
157 dcss_diag_translate_rc (int vm_rc) {
158 	if (vm_rc == 44)
159 		return -ENOENT;
160 	return -EIO;
161 }
162 
163 
164 /* do a diag to get info about a segment.
165  * fills start_address, end and vm_segtype fields
166  */
167 static int
168 query_segment_type (struct dcss_segment *seg)
169 {
170 	unsigned long dummy, vmrc;
171 	int diag_cc, rc, i;
172 	struct qout64 *qout;
173 	struct qin64 *qin;
174 
175 	qin = kmalloc(sizeof(*qin), GFP_KERNEL | GFP_DMA);
176 	qout = kmalloc(sizeof(*qout), GFP_KERNEL | GFP_DMA);
177 	if ((qin == NULL) || (qout == NULL)) {
178 		rc = -ENOMEM;
179 		goto out_free;
180 	}
181 
182 	/* initialize diag input parameters */
183 	qin->qopcode = DCSS_FINDSEGA;
184 	qin->qoutptr = virt_to_phys(qout);
185 	qin->qoutlen = sizeof(struct qout64);
186 	memcpy (qin->qname, seg->dcss_name, 8);
187 
188 	diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
189 
190 	if (diag_cc < 0) {
191 		rc = diag_cc;
192 		goto out_free;
193 	}
194 	if (diag_cc > 1) {
195 		pr_warn("Querying a DCSS type failed with rc=%ld\n", vmrc);
196 		rc = dcss_diag_translate_rc (vmrc);
197 		goto out_free;
198 	}
199 
200 	if (qout->segcnt > 6) {
201 		rc = -EOPNOTSUPP;
202 		goto out_free;
203 	}
204 
205 	if (qout->segcnt == 1) {
206 		seg->vm_segtype = qout->range[0].start & 0xff;
207 	} else {
208 		/* multi-part segment. only one type supported here:
209 		    - all parts are contiguous
210 		    - all parts are either EW or EN type
211 		    - maximum 6 parts allowed */
212 		unsigned long start = qout->segstart >> PAGE_SHIFT;
213 		for (i=0; i<qout->segcnt; i++) {
214 			if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
215 			    ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
216 				rc = -EOPNOTSUPP;
217 				goto out_free;
218 			}
219 			if (start != qout->range[i].start >> PAGE_SHIFT) {
220 				rc = -EOPNOTSUPP;
221 				goto out_free;
222 			}
223 			start = (qout->range[i].end >> PAGE_SHIFT) + 1;
224 		}
225 		seg->vm_segtype = SEG_TYPE_EWEN;
226 	}
227 
228 	/* analyze diag output and update seg */
229 	seg->start_addr = qout->segstart;
230 	seg->end = qout->segend;
231 
232 	memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
233 	seg->segcnt = qout->segcnt;
234 
235 	rc = 0;
236 
237  out_free:
238 	kfree(qin);
239 	kfree(qout);
240 	return rc;
241 }
242 
243 /*
244  * get info about a segment
245  * possible return values:
246  * -ENOSYS  : we are not running on VM
247  * -EIO     : could not perform query diagnose
248  * -ENOENT  : no such segment
249  * -EOPNOTSUPP: multi-part segment cannot be used with linux
250  * -ENOMEM  : out of memory
251  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
252  */
253 int
254 segment_type (char* name)
255 {
256 	int rc;
257 	struct dcss_segment seg;
258 
259 	if (!machine_is_vm())
260 		return -ENOSYS;
261 
262 	dcss_mkname(name, seg.dcss_name);
263 	rc = query_segment_type (&seg);
264 	if (rc < 0)
265 		return rc;
266 	return seg.vm_segtype;
267 }
268 
269 /*
270  * check if segment collides with other segments that are currently loaded
271  * returns 1 if this is the case, 0 if no collision was found
272  */
273 static int
274 segment_overlaps_others (struct dcss_segment *seg)
275 {
276 	struct list_head *l;
277 	struct dcss_segment *tmp;
278 
279 	BUG_ON(!mutex_is_locked(&dcss_lock));
280 	list_for_each(l, &dcss_list) {
281 		tmp = list_entry(l, struct dcss_segment, list);
282 		if ((tmp->start_addr >> 20) > (seg->end >> 20))
283 			continue;
284 		if ((tmp->end >> 20) < (seg->start_addr >> 20))
285 			continue;
286 		if (seg == tmp)
287 			continue;
288 		return 1;
289 	}
290 	return 0;
291 }
292 
293 /*
294  * real segment loading function, called from segment_load
295  * Must return either an error code < 0, or the segment type code >= 0
296  */
297 static int
298 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end)
299 {
300 	unsigned long start_addr, end_addr, dummy;
301 	struct dcss_segment *seg;
302 	int rc, diag_cc, segtype;
303 
304 	start_addr = end_addr = 0;
305 	segtype = -1;
306 	seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA);
307 	if (seg == NULL) {
308 		rc = -ENOMEM;
309 		goto out;
310 	}
311 	dcss_mkname (name, seg->dcss_name);
312 	rc = query_segment_type (seg);
313 	if (rc < 0)
314 		goto out_free;
315 
316 	if (segment_overlaps_others(seg)) {
317 		rc = -EBUSY;
318 		goto out_free;
319 	}
320 
321 	seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL);
322 	if (seg->res == NULL) {
323 		rc = -ENOMEM;
324 		goto out_free;
325 	}
326 	seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
327 	seg->res->start = seg->start_addr;
328 	seg->res->end = seg->end;
329 	memcpy(&seg->res_name, seg->dcss_name, 8);
330 	EBCASC(seg->res_name, 8);
331 	seg->res_name[8] = '\0';
332 	strlcat(seg->res_name, " (DCSS)", sizeof(seg->res_name));
333 	seg->res->name = seg->res_name;
334 	segtype = seg->vm_segtype;
335 	if (segtype == SEG_TYPE_SC ||
336 	    ((segtype == SEG_TYPE_SR || segtype == SEG_TYPE_ER) && !do_nonshared))
337 		seg->res->flags |= IORESOURCE_READONLY;
338 
339 	/* Check for overlapping resources before adding the mapping. */
340 	if (request_resource(&iomem_resource, seg->res)) {
341 		rc = -EBUSY;
342 		goto out_free_resource;
343 	}
344 
345 	rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
346 	if (rc)
347 		goto out_resource;
348 
349 	if (do_nonshared)
350 		diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
351 				&start_addr, &end_addr);
352 	else
353 		diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
354 				&start_addr, &end_addr);
355 	if (diag_cc < 0) {
356 		dcss_diag(&purgeseg_scode, seg->dcss_name,
357 				&dummy, &dummy);
358 		rc = diag_cc;
359 		goto out_mapping;
360 	}
361 	if (diag_cc > 1) {
362 		pr_warn("Loading DCSS %s failed with rc=%ld\n", name, end_addr);
363 		rc = dcss_diag_translate_rc(end_addr);
364 		dcss_diag(&purgeseg_scode, seg->dcss_name,
365 				&dummy, &dummy);
366 		goto out_mapping;
367 	}
368 	seg->start_addr = start_addr;
369 	seg->end = end_addr;
370 	seg->do_nonshared = do_nonshared;
371 	refcount_set(&seg->ref_count, 1);
372 	list_add(&seg->list, &dcss_list);
373 	*addr = seg->start_addr;
374 	*end  = seg->end;
375 	if (do_nonshared)
376 		pr_info("DCSS %s of range %px to %px and type %s loaded as "
377 			"exclusive-writable\n", name, (void*) seg->start_addr,
378 			(void*) seg->end, segtype_string[seg->vm_segtype]);
379 	else {
380 		pr_info("DCSS %s of range %px to %px and type %s loaded in "
381 			"shared access mode\n", name, (void*) seg->start_addr,
382 			(void*) seg->end, segtype_string[seg->vm_segtype]);
383 	}
384 	goto out;
385  out_mapping:
386 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
387  out_resource:
388 	release_resource(seg->res);
389  out_free_resource:
390 	kfree(seg->res);
391  out_free:
392 	kfree(seg);
393  out:
394 	return rc < 0 ? rc : segtype;
395 }
396 
397 /*
398  * this function loads a DCSS segment
399  * name         : name of the DCSS
400  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
401  *                1 indicates that the dcss should be exclusive for this linux image
402  * addr         : will be filled with start address of the segment
403  * end          : will be filled with end address of the segment
404  * return values:
405  * -ENOSYS  : we are not running on VM
406  * -EIO     : could not perform query or load diagnose
407  * -ENOENT  : no such segment
408  * -EOPNOTSUPP: multi-part segment cannot be used with linux
409  * -EBUSY   : segment cannot be used (overlaps with dcss or storage)
410  * -ERANGE  : segment cannot be used (exceeds kernel mapping range)
411  * -EPERM   : segment is currently loaded with incompatible permissions
412  * -ENOMEM  : out of memory
413  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
414  */
415 int
416 segment_load (char *name, int do_nonshared, unsigned long *addr,
417 		unsigned long *end)
418 {
419 	struct dcss_segment *seg;
420 	int rc;
421 
422 	if (!machine_is_vm())
423 		return -ENOSYS;
424 
425 	mutex_lock(&dcss_lock);
426 	seg = segment_by_name (name);
427 	if (seg == NULL)
428 		rc = __segment_load (name, do_nonshared, addr, end);
429 	else {
430 		if (do_nonshared == seg->do_nonshared) {
431 			refcount_inc(&seg->ref_count);
432 			*addr = seg->start_addr;
433 			*end  = seg->end;
434 			rc    = seg->vm_segtype;
435 		} else {
436 			*addr = *end = 0;
437 			rc    = -EPERM;
438 		}
439 	}
440 	mutex_unlock(&dcss_lock);
441 	return rc;
442 }
443 
444 /*
445  * this function modifies the shared state of a DCSS segment. note that
446  * name         : name of the DCSS
447  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
448  *                1 indicates that the dcss should be exclusive for this linux image
449  * return values:
450  * -EIO     : could not perform load diagnose (segment gone!)
451  * -ENOENT  : no such segment (segment gone!)
452  * -EAGAIN  : segment is in use by other exploiters, try later
453  * -EINVAL  : no segment with the given name is currently loaded - name invalid
454  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
455  * 0	    : operation succeeded
456  */
457 int
458 segment_modify_shared (char *name, int do_nonshared)
459 {
460 	struct dcss_segment *seg;
461 	unsigned long start_addr, end_addr, dummy;
462 	int rc, diag_cc;
463 
464 	start_addr = end_addr = 0;
465 	mutex_lock(&dcss_lock);
466 	seg = segment_by_name (name);
467 	if (seg == NULL) {
468 		rc = -EINVAL;
469 		goto out_unlock;
470 	}
471 	if (do_nonshared == seg->do_nonshared) {
472 		pr_info("DCSS %s is already in the requested access "
473 			"mode\n", name);
474 		rc = 0;
475 		goto out_unlock;
476 	}
477 	if (refcount_read(&seg->ref_count) != 1) {
478 		pr_warn("DCSS %s is in use and cannot be reloaded\n", name);
479 		rc = -EAGAIN;
480 		goto out_unlock;
481 	}
482 	release_resource(seg->res);
483 	if (do_nonshared)
484 		seg->res->flags &= ~IORESOURCE_READONLY;
485 	else
486 		if (seg->vm_segtype == SEG_TYPE_SR ||
487 		    seg->vm_segtype == SEG_TYPE_ER)
488 			seg->res->flags |= IORESOURCE_READONLY;
489 
490 	if (request_resource(&iomem_resource, seg->res)) {
491 		pr_warn("DCSS %s overlaps with used memory resources and cannot be reloaded\n",
492 			name);
493 		rc = -EBUSY;
494 		kfree(seg->res);
495 		goto out_del_mem;
496 	}
497 
498 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
499 	if (do_nonshared)
500 		diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
501 				&start_addr, &end_addr);
502 	else
503 		diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
504 				&start_addr, &end_addr);
505 	if (diag_cc < 0) {
506 		rc = diag_cc;
507 		goto out_del_res;
508 	}
509 	if (diag_cc > 1) {
510 		pr_warn("Reloading DCSS %s failed with rc=%ld\n",
511 			name, end_addr);
512 		rc = dcss_diag_translate_rc(end_addr);
513 		goto out_del_res;
514 	}
515 	seg->start_addr = start_addr;
516 	seg->end = end_addr;
517 	seg->do_nonshared = do_nonshared;
518 	rc = 0;
519 	goto out_unlock;
520  out_del_res:
521 	release_resource(seg->res);
522 	kfree(seg->res);
523  out_del_mem:
524 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
525 	list_del(&seg->list);
526 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
527 	kfree(seg);
528  out_unlock:
529 	mutex_unlock(&dcss_lock);
530 	return rc;
531 }
532 
533 static void __dcss_diag_purge_on_cpu_0(void *data)
534 {
535 	struct dcss_segment *seg = (struct dcss_segment *)data;
536 	unsigned long dummy;
537 
538 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
539 }
540 
541 /*
542  * Decrease the use count of a DCSS segment and remove
543  * it from the address space if nobody is using it
544  * any longer.
545  */
546 void
547 segment_unload(char *name)
548 {
549 	struct dcss_segment *seg;
550 
551 	if (!machine_is_vm())
552 		return;
553 
554 	mutex_lock(&dcss_lock);
555 	seg = segment_by_name (name);
556 	if (seg == NULL) {
557 		pr_err("Unloading unknown DCSS %s failed\n", name);
558 		goto out_unlock;
559 	}
560 	if (!refcount_dec_and_test(&seg->ref_count))
561 		goto out_unlock;
562 	release_resource(seg->res);
563 	kfree(seg->res);
564 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
565 	list_del(&seg->list);
566 	/*
567 	 * Workaround for z/VM issue, where calling the DCSS unload diag on
568 	 * a non-IPL CPU would cause bogus sclp maximum memory detection on
569 	 * next IPL.
570 	 * IPL CPU 0 cannot be set offline, so the dcss_diag() call can
571 	 * directly be scheduled to that CPU.
572 	 */
573 	smp_call_function_single(0, __dcss_diag_purge_on_cpu_0, seg, 1);
574 	kfree(seg);
575 out_unlock:
576 	mutex_unlock(&dcss_lock);
577 }
578 
579 /*
580  * save segment content permanently
581  */
582 void
583 segment_save(char *name)
584 {
585 	struct dcss_segment *seg;
586 	char cmd1[160];
587 	char cmd2[80];
588 	int i, response;
589 
590 	if (!machine_is_vm())
591 		return;
592 
593 	mutex_lock(&dcss_lock);
594 	seg = segment_by_name (name);
595 
596 	if (seg == NULL) {
597 		pr_err("Saving unknown DCSS %s failed\n", name);
598 		goto out;
599 	}
600 
601 	sprintf(cmd1, "DEFSEG %s", name);
602 	for (i=0; i<seg->segcnt; i++) {
603 		sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
604 			seg->range[i].start >> PAGE_SHIFT,
605 			seg->range[i].end >> PAGE_SHIFT,
606 			segtype_string[seg->range[i].start & 0xff]);
607 	}
608 	sprintf(cmd2, "SAVESEG %s", name);
609 	response = 0;
610 	cpcmd(cmd1, NULL, 0, &response);
611 	if (response) {
612 		pr_err("Saving a DCSS failed with DEFSEG response code "
613 		       "%i\n", response);
614 		goto out;
615 	}
616 	cpcmd(cmd2, NULL, 0, &response);
617 	if (response) {
618 		pr_err("Saving a DCSS failed with SAVESEG response code "
619 		       "%i\n", response);
620 		goto out;
621 	}
622 out:
623 	mutex_unlock(&dcss_lock);
624 }
625 
626 /*
627  * print appropriate error message for segment_load()/segment_type()
628  * return code
629  */
630 void segment_warning(int rc, char *seg_name)
631 {
632 	switch (rc) {
633 	case -ENOENT:
634 		pr_err("DCSS %s cannot be loaded or queried\n", seg_name);
635 		break;
636 	case -ENOSYS:
637 		pr_err("DCSS %s cannot be loaded or queried without "
638 		       "z/VM\n", seg_name);
639 		break;
640 	case -EIO:
641 		pr_err("Loading or querying DCSS %s resulted in a "
642 		       "hardware error\n", seg_name);
643 		break;
644 	case -EOPNOTSUPP:
645 		pr_err("DCSS %s has multiple page ranges and cannot be "
646 		       "loaded or queried\n", seg_name);
647 		break;
648 	case -EBUSY:
649 		pr_err("%s needs used memory resources and cannot be "
650 		       "loaded or queried\n", seg_name);
651 		break;
652 	case -EPERM:
653 		pr_err("DCSS %s is already loaded in a different access "
654 		       "mode\n", seg_name);
655 		break;
656 	case -ENOMEM:
657 		pr_err("There is not enough memory to load or query "
658 		       "DCSS %s\n", seg_name);
659 		break;
660 	case -ERANGE: {
661 		struct range mhp_range = arch_get_mappable_range();
662 
663 		pr_err("DCSS %s exceeds the kernel mapping range (%llu) "
664 		       "and cannot be loaded\n", seg_name, mhp_range.end + 1);
665 		break;
666 	}
667 	default:
668 		break;
669 	}
670 }
671 
672 EXPORT_SYMBOL(segment_load);
673 EXPORT_SYMBOL(segment_unload);
674 EXPORT_SYMBOL(segment_save);
675 EXPORT_SYMBOL(segment_type);
676 EXPORT_SYMBOL(segment_modify_shared);
677 EXPORT_SYMBOL(segment_warning);
678