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