0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #define KMSG_COMPONENT "extmem"
0011 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0012
0013 #include <linux/kernel.h>
0014 #include <linux/string.h>
0015 #include <linux/spinlock.h>
0016 #include <linux/list.h>
0017 #include <linux/slab.h>
0018 #include <linux/export.h>
0019 #include <linux/memblock.h>
0020 #include <linux/ctype.h>
0021 #include <linux/ioport.h>
0022 #include <linux/refcount.h>
0023 #include <linux/pgtable.h>
0024 #include <asm/diag.h>
0025 #include <asm/page.h>
0026 #include <asm/ebcdic.h>
0027 #include <asm/errno.h>
0028 #include <asm/extmem.h>
0029 #include <asm/cpcmd.h>
0030 #include <asm/setup.h>
0031
0032 #define DCSS_PURGESEG 0x08
0033 #define DCSS_LOADSHRX 0x20
0034 #define DCSS_LOADNSRX 0x24
0035 #define DCSS_FINDSEGX 0x2c
0036 #define DCSS_SEGEXTX 0x38
0037 #define DCSS_FINDSEGA 0x0c
0038
0039 struct qrange {
0040 unsigned long start;
0041 unsigned long end;
0042 };
0043
0044 struct qout64 {
0045 unsigned long segstart;
0046 unsigned long segend;
0047 int segcnt;
0048 int segrcnt;
0049 struct qrange range[6];
0050 };
0051
0052 struct qin64 {
0053 char qopcode;
0054 char rsrv1[3];
0055 char qrcode;
0056 char rsrv2[3];
0057 char qname[8];
0058 unsigned int qoutptr;
0059 short int qoutlen;
0060 };
0061
0062 struct dcss_segment {
0063 struct list_head list;
0064 char dcss_name[8];
0065 char res_name[16];
0066 unsigned long start_addr;
0067 unsigned long end;
0068 refcount_t ref_count;
0069 int do_nonshared;
0070 unsigned int vm_segtype;
0071 struct qrange range[6];
0072 int segcnt;
0073 struct resource *res;
0074 };
0075
0076 static DEFINE_MUTEX(dcss_lock);
0077 static LIST_HEAD(dcss_list);
0078 static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
0079 "EW/EN-MIXED" };
0080 static int loadshr_scode = DCSS_LOADSHRX;
0081 static int loadnsr_scode = DCSS_LOADNSRX;
0082 static int purgeseg_scode = DCSS_PURGESEG;
0083 static int segext_scode = DCSS_SEGEXTX;
0084
0085
0086
0087
0088
0089 static void
0090 dcss_mkname(char *name, char *dcss_name)
0091 {
0092 int i;
0093
0094 for (i = 0; i < 8; i++) {
0095 if (name[i] == '\0')
0096 break;
0097 dcss_name[i] = toupper(name[i]);
0098 }
0099 for (; i < 8; i++)
0100 dcss_name[i] = ' ';
0101 ASCEBC(dcss_name, 8);
0102 }
0103
0104
0105
0106
0107
0108
0109 static struct dcss_segment *
0110 segment_by_name (char *name)
0111 {
0112 char dcss_name[9];
0113 struct list_head *l;
0114 struct dcss_segment *tmp, *retval = NULL;
0115
0116 BUG_ON(!mutex_is_locked(&dcss_lock));
0117 dcss_mkname (name, dcss_name);
0118 list_for_each (l, &dcss_list) {
0119 tmp = list_entry (l, struct dcss_segment, list);
0120 if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) {
0121 retval = tmp;
0122 break;
0123 }
0124 }
0125 return retval;
0126 }
0127
0128
0129
0130
0131
0132 static inline int
0133 dcss_diag(int *func, void *parameter,
0134 unsigned long *ret1, unsigned long *ret2)
0135 {
0136 unsigned long rx, ry;
0137 int rc;
0138
0139 rx = (unsigned long) parameter;
0140 ry = (unsigned long) *func;
0141
0142 diag_stat_inc(DIAG_STAT_X064);
0143 asm volatile(
0144 " diag %0,%1,0x64\n"
0145 " ipm %2\n"
0146 " srl %2,28\n"
0147 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
0148 *ret1 = rx;
0149 *ret2 = ry;
0150 return rc;
0151 }
0152
0153 static inline int
0154 dcss_diag_translate_rc (int vm_rc) {
0155 if (vm_rc == 44)
0156 return -ENOENT;
0157 return -EIO;
0158 }
0159
0160
0161
0162
0163
0164 static int
0165 query_segment_type (struct dcss_segment *seg)
0166 {
0167 unsigned long dummy, vmrc;
0168 int diag_cc, rc, i;
0169 struct qout64 *qout;
0170 struct qin64 *qin;
0171
0172 qin = kmalloc(sizeof(*qin), GFP_KERNEL | GFP_DMA);
0173 qout = kmalloc(sizeof(*qout), GFP_KERNEL | GFP_DMA);
0174 if ((qin == NULL) || (qout == NULL)) {
0175 rc = -ENOMEM;
0176 goto out_free;
0177 }
0178
0179
0180 qin->qopcode = DCSS_FINDSEGA;
0181 qin->qoutptr = (unsigned long) qout;
0182 qin->qoutlen = sizeof(struct qout64);
0183 memcpy (qin->qname, seg->dcss_name, 8);
0184
0185 diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
0186
0187 if (diag_cc < 0) {
0188 rc = diag_cc;
0189 goto out_free;
0190 }
0191 if (diag_cc > 1) {
0192 pr_warn("Querying a DCSS type failed with rc=%ld\n", vmrc);
0193 rc = dcss_diag_translate_rc (vmrc);
0194 goto out_free;
0195 }
0196
0197 if (qout->segcnt > 6) {
0198 rc = -EOPNOTSUPP;
0199 goto out_free;
0200 }
0201
0202 if (qout->segcnt == 1) {
0203 seg->vm_segtype = qout->range[0].start & 0xff;
0204 } else {
0205
0206
0207
0208
0209 unsigned long start = qout->segstart >> PAGE_SHIFT;
0210 for (i=0; i<qout->segcnt; i++) {
0211 if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
0212 ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
0213 rc = -EOPNOTSUPP;
0214 goto out_free;
0215 }
0216 if (start != qout->range[i].start >> PAGE_SHIFT) {
0217 rc = -EOPNOTSUPP;
0218 goto out_free;
0219 }
0220 start = (qout->range[i].end >> PAGE_SHIFT) + 1;
0221 }
0222 seg->vm_segtype = SEG_TYPE_EWEN;
0223 }
0224
0225
0226 seg->start_addr = qout->segstart;
0227 seg->end = qout->segend;
0228
0229 memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
0230 seg->segcnt = qout->segcnt;
0231
0232 rc = 0;
0233
0234 out_free:
0235 kfree(qin);
0236 kfree(qout);
0237 return rc;
0238 }
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250 int
0251 segment_type (char* name)
0252 {
0253 int rc;
0254 struct dcss_segment seg;
0255
0256 if (!MACHINE_IS_VM)
0257 return -ENOSYS;
0258
0259 dcss_mkname(name, seg.dcss_name);
0260 rc = query_segment_type (&seg);
0261 if (rc < 0)
0262 return rc;
0263 return seg.vm_segtype;
0264 }
0265
0266
0267
0268
0269
0270 static int
0271 segment_overlaps_others (struct dcss_segment *seg)
0272 {
0273 struct list_head *l;
0274 struct dcss_segment *tmp;
0275
0276 BUG_ON(!mutex_is_locked(&dcss_lock));
0277 list_for_each(l, &dcss_list) {
0278 tmp = list_entry(l, struct dcss_segment, list);
0279 if ((tmp->start_addr >> 20) > (seg->end >> 20))
0280 continue;
0281 if ((tmp->end >> 20) < (seg->start_addr >> 20))
0282 continue;
0283 if (seg == tmp)
0284 continue;
0285 return 1;
0286 }
0287 return 0;
0288 }
0289
0290
0291
0292
0293 static int
0294 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end)
0295 {
0296 unsigned long start_addr, end_addr, dummy;
0297 struct dcss_segment *seg;
0298 int rc, diag_cc;
0299
0300 start_addr = end_addr = 0;
0301 seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA);
0302 if (seg == NULL) {
0303 rc = -ENOMEM;
0304 goto out;
0305 }
0306 dcss_mkname (name, seg->dcss_name);
0307 rc = query_segment_type (seg);
0308 if (rc < 0)
0309 goto out_free;
0310
0311 if (segment_overlaps_others(seg)) {
0312 rc = -EBUSY;
0313 goto out_free;
0314 }
0315
0316 seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL);
0317 if (seg->res == NULL) {
0318 rc = -ENOMEM;
0319 goto out_free;
0320 }
0321 seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
0322 seg->res->start = seg->start_addr;
0323 seg->res->end = seg->end;
0324 memcpy(&seg->res_name, seg->dcss_name, 8);
0325 EBCASC(seg->res_name, 8);
0326 seg->res_name[8] = '\0';
0327 strlcat(seg->res_name, " (DCSS)", sizeof(seg->res_name));
0328 seg->res->name = seg->res_name;
0329 rc = seg->vm_segtype;
0330 if (rc == SEG_TYPE_SC ||
0331 ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared))
0332 seg->res->flags |= IORESOURCE_READONLY;
0333
0334
0335 if (request_resource(&iomem_resource, seg->res)) {
0336 rc = -EBUSY;
0337 goto out_free_resource;
0338 }
0339
0340 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
0341 if (rc)
0342 goto out_resource;
0343
0344 if (do_nonshared)
0345 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
0346 &start_addr, &end_addr);
0347 else
0348 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
0349 &start_addr, &end_addr);
0350 if (diag_cc < 0) {
0351 dcss_diag(&purgeseg_scode, seg->dcss_name,
0352 &dummy, &dummy);
0353 rc = diag_cc;
0354 goto out_mapping;
0355 }
0356 if (diag_cc > 1) {
0357 pr_warn("Loading DCSS %s failed with rc=%ld\n", name, end_addr);
0358 rc = dcss_diag_translate_rc(end_addr);
0359 dcss_diag(&purgeseg_scode, seg->dcss_name,
0360 &dummy, &dummy);
0361 goto out_mapping;
0362 }
0363 seg->start_addr = start_addr;
0364 seg->end = end_addr;
0365 seg->do_nonshared = do_nonshared;
0366 refcount_set(&seg->ref_count, 1);
0367 list_add(&seg->list, &dcss_list);
0368 *addr = seg->start_addr;
0369 *end = seg->end;
0370 if (do_nonshared)
0371 pr_info("DCSS %s of range %px to %px and type %s loaded as "
0372 "exclusive-writable\n", name, (void*) seg->start_addr,
0373 (void*) seg->end, segtype_string[seg->vm_segtype]);
0374 else {
0375 pr_info("DCSS %s of range %px to %px and type %s loaded in "
0376 "shared access mode\n", name, (void*) seg->start_addr,
0377 (void*) seg->end, segtype_string[seg->vm_segtype]);
0378 }
0379 goto out;
0380 out_mapping:
0381 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
0382 out_resource:
0383 release_resource(seg->res);
0384 out_free_resource:
0385 kfree(seg->res);
0386 out_free:
0387 kfree(seg);
0388 out:
0389 return rc;
0390 }
0391
0392
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403
0404
0405
0406
0407
0408
0409
0410 int
0411 segment_load (char *name, int do_nonshared, unsigned long *addr,
0412 unsigned long *end)
0413 {
0414 struct dcss_segment *seg;
0415 int rc;
0416
0417 if (!MACHINE_IS_VM)
0418 return -ENOSYS;
0419
0420 mutex_lock(&dcss_lock);
0421 seg = segment_by_name (name);
0422 if (seg == NULL)
0423 rc = __segment_load (name, do_nonshared, addr, end);
0424 else {
0425 if (do_nonshared == seg->do_nonshared) {
0426 refcount_inc(&seg->ref_count);
0427 *addr = seg->start_addr;
0428 *end = seg->end;
0429 rc = seg->vm_segtype;
0430 } else {
0431 *addr = *end = 0;
0432 rc = -EPERM;
0433 }
0434 }
0435 mutex_unlock(&dcss_lock);
0436 return rc;
0437 }
0438
0439
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451
0452 int
0453 segment_modify_shared (char *name, int do_nonshared)
0454 {
0455 struct dcss_segment *seg;
0456 unsigned long start_addr, end_addr, dummy;
0457 int rc, diag_cc;
0458
0459 start_addr = end_addr = 0;
0460 mutex_lock(&dcss_lock);
0461 seg = segment_by_name (name);
0462 if (seg == NULL) {
0463 rc = -EINVAL;
0464 goto out_unlock;
0465 }
0466 if (do_nonshared == seg->do_nonshared) {
0467 pr_info("DCSS %s is already in the requested access "
0468 "mode\n", name);
0469 rc = 0;
0470 goto out_unlock;
0471 }
0472 if (refcount_read(&seg->ref_count) != 1) {
0473 pr_warn("DCSS %s is in use and cannot be reloaded\n", name);
0474 rc = -EAGAIN;
0475 goto out_unlock;
0476 }
0477 release_resource(seg->res);
0478 if (do_nonshared)
0479 seg->res->flags &= ~IORESOURCE_READONLY;
0480 else
0481 if (seg->vm_segtype == SEG_TYPE_SR ||
0482 seg->vm_segtype == SEG_TYPE_ER)
0483 seg->res->flags |= IORESOURCE_READONLY;
0484
0485 if (request_resource(&iomem_resource, seg->res)) {
0486 pr_warn("DCSS %s overlaps with used memory resources and cannot be reloaded\n",
0487 name);
0488 rc = -EBUSY;
0489 kfree(seg->res);
0490 goto out_del_mem;
0491 }
0492
0493 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
0494 if (do_nonshared)
0495 diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
0496 &start_addr, &end_addr);
0497 else
0498 diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
0499 &start_addr, &end_addr);
0500 if (diag_cc < 0) {
0501 rc = diag_cc;
0502 goto out_del_res;
0503 }
0504 if (diag_cc > 1) {
0505 pr_warn("Reloading DCSS %s failed with rc=%ld\n",
0506 name, end_addr);
0507 rc = dcss_diag_translate_rc(end_addr);
0508 goto out_del_res;
0509 }
0510 seg->start_addr = start_addr;
0511 seg->end = end_addr;
0512 seg->do_nonshared = do_nonshared;
0513 rc = 0;
0514 goto out_unlock;
0515 out_del_res:
0516 release_resource(seg->res);
0517 kfree(seg->res);
0518 out_del_mem:
0519 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
0520 list_del(&seg->list);
0521 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
0522 kfree(seg);
0523 out_unlock:
0524 mutex_unlock(&dcss_lock);
0525 return rc;
0526 }
0527
0528
0529
0530
0531
0532
0533 void
0534 segment_unload(char *name)
0535 {
0536 unsigned long dummy;
0537 struct dcss_segment *seg;
0538
0539 if (!MACHINE_IS_VM)
0540 return;
0541
0542 mutex_lock(&dcss_lock);
0543 seg = segment_by_name (name);
0544 if (seg == NULL) {
0545 pr_err("Unloading unknown DCSS %s failed\n", name);
0546 goto out_unlock;
0547 }
0548 if (!refcount_dec_and_test(&seg->ref_count))
0549 goto out_unlock;
0550 release_resource(seg->res);
0551 kfree(seg->res);
0552 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
0553 list_del(&seg->list);
0554 dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
0555 kfree(seg);
0556 out_unlock:
0557 mutex_unlock(&dcss_lock);
0558 }
0559
0560
0561
0562
0563 void
0564 segment_save(char *name)
0565 {
0566 struct dcss_segment *seg;
0567 char cmd1[160];
0568 char cmd2[80];
0569 int i, response;
0570
0571 if (!MACHINE_IS_VM)
0572 return;
0573
0574 mutex_lock(&dcss_lock);
0575 seg = segment_by_name (name);
0576
0577 if (seg == NULL) {
0578 pr_err("Saving unknown DCSS %s failed\n", name);
0579 goto out;
0580 }
0581
0582 sprintf(cmd1, "DEFSEG %s", name);
0583 for (i=0; i<seg->segcnt; i++) {
0584 sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
0585 seg->range[i].start >> PAGE_SHIFT,
0586 seg->range[i].end >> PAGE_SHIFT,
0587 segtype_string[seg->range[i].start & 0xff]);
0588 }
0589 sprintf(cmd2, "SAVESEG %s", name);
0590 response = 0;
0591 cpcmd(cmd1, NULL, 0, &response);
0592 if (response) {
0593 pr_err("Saving a DCSS failed with DEFSEG response code "
0594 "%i\n", response);
0595 goto out;
0596 }
0597 cpcmd(cmd2, NULL, 0, &response);
0598 if (response) {
0599 pr_err("Saving a DCSS failed with SAVESEG response code "
0600 "%i\n", response);
0601 goto out;
0602 }
0603 out:
0604 mutex_unlock(&dcss_lock);
0605 }
0606
0607
0608
0609
0610
0611 void segment_warning(int rc, char *seg_name)
0612 {
0613 switch (rc) {
0614 case -ENOENT:
0615 pr_err("DCSS %s cannot be loaded or queried\n", seg_name);
0616 break;
0617 case -ENOSYS:
0618 pr_err("DCSS %s cannot be loaded or queried without "
0619 "z/VM\n", seg_name);
0620 break;
0621 case -EIO:
0622 pr_err("Loading or querying DCSS %s resulted in a "
0623 "hardware error\n", seg_name);
0624 break;
0625 case -EOPNOTSUPP:
0626 pr_err("DCSS %s has multiple page ranges and cannot be "
0627 "loaded or queried\n", seg_name);
0628 break;
0629 case -EBUSY:
0630 pr_err("%s needs used memory resources and cannot be "
0631 "loaded or queried\n", seg_name);
0632 break;
0633 case -EPERM:
0634 pr_err("DCSS %s is already loaded in a different access "
0635 "mode\n", seg_name);
0636 break;
0637 case -ENOMEM:
0638 pr_err("There is not enough memory to load or query "
0639 "DCSS %s\n", seg_name);
0640 break;
0641 case -ERANGE:
0642 pr_err("DCSS %s exceeds the kernel mapping range (%lu) "
0643 "and cannot be loaded\n", seg_name, VMEM_MAX_PHYS);
0644 break;
0645 default:
0646 break;
0647 }
0648 }
0649
0650 EXPORT_SYMBOL(segment_load);
0651 EXPORT_SYMBOL(segment_unload);
0652 EXPORT_SYMBOL(segment_save);
0653 EXPORT_SYMBOL(segment_type);
0654 EXPORT_SYMBOL(segment_modify_shared);
0655 EXPORT_SYMBOL(segment_warning);