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
dcss_mkname(char * name,char * dcss_name)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 *
segment_by_name(char * name)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
dcss_diag(int * func,void * parameter,unsigned long * ret1,unsigned long * ret2)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
dcss_diag_translate_rc(int vm_rc)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
query_segment_type(struct dcss_segment * seg)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
segment_type(char * name)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
segment_overlaps_others(struct dcss_segment * seg)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
__segment_load(char * name,int do_nonshared,unsigned long * addr,unsigned long * end)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
segment_load(char * name,int do_nonshared,unsigned long * addr,unsigned long * end)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
segment_modify_shared(char * name,int do_nonshared)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 /*
534  * Decrease the use count of a DCSS segment and remove
535  * it from the address space if nobody is using it
536  * any longer.
537  */
538 void
segment_unload(char * name)539 segment_unload(char *name)
540 {
541 	unsigned long dummy;
542 	struct dcss_segment *seg;
543 
544 	if (!machine_is_vm())
545 		return;
546 
547 	mutex_lock(&dcss_lock);
548 	seg = segment_by_name (name);
549 	if (seg == NULL) {
550 		pr_err("Unloading unknown DCSS %s failed\n", name);
551 		goto out_unlock;
552 	}
553 	if (!refcount_dec_and_test(&seg->ref_count))
554 		goto out_unlock;
555 	release_resource(seg->res);
556 	kfree(seg->res);
557 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
558 	list_del(&seg->list);
559 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
560 	kfree(seg);
561 out_unlock:
562 	mutex_unlock(&dcss_lock);
563 }
564 
565 /*
566  * save segment content permanently
567  */
568 void
segment_save(char * name)569 segment_save(char *name)
570 {
571 	struct dcss_segment *seg;
572 	char cmd1[160];
573 	char cmd2[80];
574 	int i, response;
575 
576 	if (!machine_is_vm())
577 		return;
578 
579 	mutex_lock(&dcss_lock);
580 	seg = segment_by_name (name);
581 
582 	if (seg == NULL) {
583 		pr_err("Saving unknown DCSS %s failed\n", name);
584 		goto out;
585 	}
586 
587 	sprintf(cmd1, "DEFSEG %s", name);
588 	for (i=0; i<seg->segcnt; i++) {
589 		sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
590 			seg->range[i].start >> PAGE_SHIFT,
591 			seg->range[i].end >> PAGE_SHIFT,
592 			segtype_string[seg->range[i].start & 0xff]);
593 	}
594 	sprintf(cmd2, "SAVESEG %s", name);
595 	response = 0;
596 	cpcmd(cmd1, NULL, 0, &response);
597 	if (response) {
598 		pr_err("Saving a DCSS failed with DEFSEG response code "
599 		       "%i\n", response);
600 		goto out;
601 	}
602 	cpcmd(cmd2, NULL, 0, &response);
603 	if (response) {
604 		pr_err("Saving a DCSS failed with SAVESEG response code "
605 		       "%i\n", response);
606 		goto out;
607 	}
608 out:
609 	mutex_unlock(&dcss_lock);
610 }
611 
612 /*
613  * print appropriate error message for segment_load()/segment_type()
614  * return code
615  */
segment_warning(int rc,char * seg_name)616 void segment_warning(int rc, char *seg_name)
617 {
618 	switch (rc) {
619 	case -ENOENT:
620 		pr_err("DCSS %s cannot be loaded or queried\n", seg_name);
621 		break;
622 	case -ENOSYS:
623 		pr_err("DCSS %s cannot be loaded or queried without "
624 		       "z/VM\n", seg_name);
625 		break;
626 	case -EIO:
627 		pr_err("Loading or querying DCSS %s resulted in a "
628 		       "hardware error\n", seg_name);
629 		break;
630 	case -EOPNOTSUPP:
631 		pr_err("DCSS %s has multiple page ranges and cannot be "
632 		       "loaded or queried\n", seg_name);
633 		break;
634 	case -EBUSY:
635 		pr_err("%s needs used memory resources and cannot be "
636 		       "loaded or queried\n", seg_name);
637 		break;
638 	case -EPERM:
639 		pr_err("DCSS %s is already loaded in a different access "
640 		       "mode\n", seg_name);
641 		break;
642 	case -ENOMEM:
643 		pr_err("There is not enough memory to load or query "
644 		       "DCSS %s\n", seg_name);
645 		break;
646 	case -ERANGE: {
647 		struct range mhp_range = arch_get_mappable_range();
648 
649 		pr_err("DCSS %s exceeds the kernel mapping range (%llu) "
650 		       "and cannot be loaded\n", seg_name, mhp_range.end + 1);
651 		break;
652 	}
653 	default:
654 		break;
655 	}
656 }
657 
658 EXPORT_SYMBOL(segment_load);
659 EXPORT_SYMBOL(segment_unload);
660 EXPORT_SYMBOL(segment_save);
661 EXPORT_SYMBOL(segment_type);
662 EXPORT_SYMBOL(segment_modify_shared);
663 EXPORT_SYMBOL(segment_warning);
664