// SPDX-License-Identifier: GPL-2.0 /* * Author(s)......: Carsten Otte <cotte@de.ibm.com> * Rob M van der Heij <rvdheij@nl.ibm.com> * Steven Shultz <shultzss@us.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * Copyright IBM Corp. 2002, 2004
*/
/* * Perform a function on a dcss segment.
*/ staticinlineint
dcss_diag(int *func, void *parameter, unsignedlong *ret1, unsignedlong *ret2)
{ unsignedlong rx, ry; int cc;
rx = virt_to_phys(parameter);
ry = (unsignedlong) *func;
/* do a diag to get info about a segment. * fills start_address, end and vm_segtype fields
*/ staticint
query_segment_type (struct dcss_segment *seg)
{ unsignedlong dummy, vmrc; int diag_cc, rc, i; struct qout64 *qout; struct qin64 *qin;
if (qout->segcnt == 1) {
seg->vm_segtype = qout->range[0].start & 0xff;
} else { /* multi-part segment. only one type supported here: - all parts are contiguous - all parts are either EW or EN type
- maximum 6 parts allowed */ unsignedlong start = qout->segstart >> PAGE_SHIFT; for (i=0; i<qout->segcnt; i++) { if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
rc = -EOPNOTSUPP; goto out_free;
} if (start != qout->range[i].start >> PAGE_SHIFT) {
rc = -EOPNOTSUPP; goto out_free;
}
start = (qout->range[i].end >> PAGE_SHIFT) + 1;
}
seg->vm_segtype = SEG_TYPE_EWEN;
}
/* analyze diag output and update seg */
seg->start_addr = qout->segstart;
seg->end = qout->segend;
/* * get info about a segment * possible return values: * -ENOSYS : we are not running on VM * -EIO : could not perform query diagnose * -ENOENT : no such segment * -EOPNOTSUPP: multi-part segment cannot be used with linux * -ENOMEM : out of memory * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h
*/ int
segment_type (char* name)
{ int rc; struct dcss_segment seg;
/* * check if segment collides with other segments that are currently loaded * returns 1 if this is the case, 0 if no collision was found
*/ staticint
segment_overlaps_others (struct dcss_segment *seg)
{ struct list_head *l; struct dcss_segment *tmp;
/* * real segment loading function, called from segment_load * Must return either an error code < 0, or the segment type code >= 0
*/ staticint
__segment_load (char *name, int do_nonshared, unsignedlong *addr, unsignedlong *end)
{ unsignedlong start_addr, end_addr, dummy; struct dcss_segment *seg; int rc, diag_cc, segtype;
/* Check for overlapping resources before adding the mapping. */ if (request_resource(&iomem_resource, seg->res)) {
rc = -EBUSY; goto out_free_resource;
}
if (do_nonshared)
diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
&start_addr, &end_addr); else
diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
&start_addr, &end_addr); if (diag_cc < 0) {
dcss_diag(&purgeseg_scode, seg->dcss_name,
&dummy, &dummy);
rc = diag_cc; goto out_mapping;
} if (diag_cc > 1) {
pr_warn("Loading DCSS %s failed with rc=%ld\n", name, end_addr);
rc = dcss_diag_translate_rc(end_addr);
dcss_diag(&purgeseg_scode, seg->dcss_name,
&dummy, &dummy); goto out_mapping;
}
seg->start_addr = start_addr;
seg->end = end_addr;
seg->do_nonshared = do_nonshared;
refcount_set(&seg->ref_count, 1);
list_add(&seg->list, &dcss_list);
*addr = seg->start_addr;
*end = seg->end; if (do_nonshared)
pr_info("DCSS %s of range %px to %px and type %s loaded as " "exclusive-writable\n", name, (void*) seg->start_addr,
(void*) seg->end, segtype_string[seg->vm_segtype]); else {
pr_info("DCSS %s of range %px to %px and type %s loaded in " "shared access mode\n", name, (void*) seg->start_addr,
(void*) seg->end, segtype_string[seg->vm_segtype]);
} goto out;
out_mapping:
vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
out_resource:
release_resource(seg->res);
out_free_resource:
kfree(seg->res);
out_free:
kfree(seg);
out: return rc < 0 ? rc : segtype;
}
/* * this function loads a DCSS segment * name : name of the DCSS * do_nonshared : 0 indicates that the dcss should be shared with other linux images * 1 indicates that the dcss should be exclusive for this linux image * addr : will be filled with start address of the segment * end : will be filled with end address of the segment * return values: * -ENOSYS : we are not running on VM * -EIO : could not perform query or load diagnose * -ENOENT : no such segment * -EOPNOTSUPP: multi-part segment cannot be used with linux * -EBUSY : segment cannot be used (overlaps with dcss or storage) * -ERANGE : segment cannot be used (exceeds kernel mapping range) * -EPERM : segment is currently loaded with incompatible permissions * -ENOMEM : out of memory * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h
*/ int
segment_load (char *name, int do_nonshared, unsignedlong *addr, unsignedlong *end)
{ struct dcss_segment *seg; int rc;
/* * this function modifies the shared state of a DCSS segment. note that * name : name of the DCSS * do_nonshared : 0 indicates that the dcss should be shared with other linux images * 1 indicates that the dcss should be exclusive for this linux image * return values: * -EIO : could not perform load diagnose (segment gone!) * -ENOENT : no such segment (segment gone!) * -EAGAIN : segment is in use by other exploiters, try later * -EINVAL : no segment with the given name is currently loaded - name invalid * -EBUSY : segment can temporarily not be used (overlaps with dcss) * 0 : operation succeeded
*/ int
segment_modify_shared (char *name, int do_nonshared)
{ struct dcss_segment *seg; unsignedlong start_addr, end_addr, dummy; int rc, diag_cc;
start_addr = end_addr = 0;
mutex_lock(&dcss_lock);
seg = segment_by_name (name); if (seg == NULL) {
rc = -EINVAL; goto out_unlock;
} if (do_nonshared == seg->do_nonshared) {
pr_info("DCSS %s is already in the requested access " "mode\n", name);
rc = 0; goto out_unlock;
} if (refcount_read(&seg->ref_count) != 1) {
pr_warn("DCSS %s is in use and cannot be reloaded\n", name);
rc = -EAGAIN; goto out_unlock;
}
release_resource(seg->res); if (do_nonshared)
seg->res->flags &= ~IORESOURCE_READONLY; else if (seg->vm_segtype == SEG_TYPE_SR ||
seg->vm_segtype == SEG_TYPE_ER)
seg->res->flags |= IORESOURCE_READONLY;
if (request_resource(&iomem_resource, seg->res)) {
pr_warn("DCSS %s overlaps with used memory resources and cannot be reloaded\n",
name);
rc = -EBUSY;
kfree(seg->res); goto out_del_mem;
}
/* * Decrease the use count of a DCSS segment and remove * it from the address space if nobody is using it * any longer.
*/ void
segment_unload(char *name)
{ struct dcss_segment *seg;
if (!machine_is_vm()) return;
mutex_lock(&dcss_lock);
seg = segment_by_name (name); if (seg == NULL) {
pr_err("Unloading unknown DCSS %s failed\n", name); goto out_unlock;
} if (!refcount_dec_and_test(&seg->ref_count)) goto out_unlock;
release_resource(seg->res);
kfree(seg->res);
vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
list_del(&seg->list); /* * Workaround for z/VM issue, where calling the DCSS unload diag on * a non-IPL CPU would cause bogus sclp maximum memory detection on * next IPL. * IPL CPU 0 cannot be set offline, so the dcss_diag() call can * directly be scheduled to that CPU.
*/
smp_call_function_single(0, __dcss_diag_purge_on_cpu_0, seg, 1);
kfree(seg);
out_unlock:
mutex_unlock(&dcss_lock);
}
/* * save segment content permanently
*/ void
segment_save(char *name)
{ struct dcss_segment *seg; char cmd1[160]; char cmd2[80]; int i, response;
if (!machine_is_vm()) return;
mutex_lock(&dcss_lock);
seg = segment_by_name (name);
/* * print appropriate error message for segment_load()/segment_type() * return code
*/ void segment_warning(int rc, char *seg_name)
{ switch (rc) { case -ENOENT:
pr_err("DCSS %s cannot be loaded or queried\n", seg_name); break; case -ENOSYS:
pr_err("DCSS %s cannot be loaded or queried without " "z/VM\n", seg_name); break; case -EIO:
pr_err("Loading or querying DCSS %s resulted in a " "hardware error\n", seg_name); break; case -EOPNOTSUPP:
pr_err("DCSS %s has multiple page ranges and cannot be " "loaded or queried\n", seg_name); break; case -EBUSY:
pr_err("%s needs used memory resources and cannot be " "loaded or queried\n", seg_name); break; case -EPERM:
pr_err("DCSS %s is already loaded in a different access " "mode\n", seg_name); break; case -ENOMEM:
pr_err("There is not enough memory to load or query " "DCSS %s\n", seg_name); break; case -ERANGE: { struct range mhp_range = arch_get_mappable_range();
pr_err("DCSS %s exceeds the kernel mapping range (%llu) " "and cannot be loaded\n", seg_name, mhp_range.end + 1); break;
} default: break;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.