1/* memregion_direct.c 2 * 3 * Copyright (C) 2010 - 2013 UNISYS CORPORATION 4 * All rights reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or (at 9 * your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 14 * NON INFRINGEMENT. See the GNU General Public License for more 15 * details. 16 */ 17 18/* 19 * This is an implementation of memory regions that can be used to read/write 20 * channel memory (in main memory of the host system) from code running in 21 * a virtual partition. 22 */ 23#include "uniklog.h" 24#include "timskmod.h" 25#include "memregion.h" 26 27#define MYDRVNAME "memregion" 28 29struct MEMREGION_Tag { 30 HOSTADDRESS physaddr; 31 ulong nbytes; 32 void __iomem *mapped; 33 BOOL requested; 34 BOOL overlapped; 35}; 36 37static BOOL mapit(MEMREGION *memregion); 38static void unmapit(MEMREGION *memregion); 39 40MEMREGION * 41visor_memregion_create(HOSTADDRESS physaddr, ulong nbytes) 42{ 43 MEMREGION *rc = NULL; 44 MEMREGION *memregion = kzalloc(sizeof(MEMREGION), 45 GFP_KERNEL | __GFP_NORETRY); 46 if (memregion == NULL) { 47 ERRDRV("visor_memregion_create allocation failed"); 48 return NULL; 49 } 50 memregion->physaddr = physaddr; 51 memregion->nbytes = nbytes; 52 memregion->overlapped = FALSE; 53 if (!mapit(memregion)) { 54 rc = NULL; 55 goto Away; 56 } 57 rc = memregion; 58Away: 59 if (rc == NULL) { 60 if (memregion != NULL) { 61 visor_memregion_destroy(memregion); 62 memregion = NULL; 63 } 64 } 65 return rc; 66} 67EXPORT_SYMBOL_GPL(visor_memregion_create); 68 69MEMREGION * 70visor_memregion_create_overlapped(MEMREGION *parent, ulong offset, ulong nbytes) 71{ 72 MEMREGION *memregion = NULL; 73 74 if (parent == NULL) { 75 ERRDRV("%s parent is NULL", __func__); 76 return NULL; 77 } 78 if (parent->mapped == NULL) { 79 ERRDRV("%s parent is not mapped!", __func__); 80 return NULL; 81 } 82 if ((offset >= parent->nbytes) || 83 ((offset + nbytes) >= parent->nbytes)) { 84 ERRDRV("%s range (%lu,%lu) out of parent range", 85 __func__, offset, nbytes); 86 return NULL; 87 } 88 memregion = kzalloc(sizeof(MEMREGION), GFP_KERNEL|__GFP_NORETRY); 89 if (memregion == NULL) { 90 ERRDRV("%s allocation failed", __func__); 91 return NULL; 92 } 93 94 memregion->physaddr = parent->physaddr + offset; 95 memregion->nbytes = nbytes; 96 memregion->mapped = ((u8 __iomem *) (parent->mapped)) + offset; 97 memregion->requested = FALSE; 98 memregion->overlapped = TRUE; 99 return memregion; 100} 101EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped); 102 103 104static BOOL 105mapit(MEMREGION *memregion) 106{ 107 ulong physaddr = (ulong) (memregion->physaddr); 108 ulong nbytes = memregion->nbytes; 109 110 memregion->requested = FALSE; 111 if (!request_mem_region(physaddr, nbytes, MYDRVNAME)) 112 ERRDRV("cannot reserve channel memory @0x%lx for 0x%lx-- no big deal", physaddr, nbytes); 113 else 114 memregion->requested = TRUE; 115 memregion->mapped = ioremap_cache(physaddr, nbytes); 116 if (memregion->mapped == NULL) { 117 ERRDRV("cannot ioremap_cache channel memory @0x%lx for 0x%lx", 118 physaddr, nbytes); 119 return FALSE; 120 } 121 return TRUE; 122} 123 124static void 125unmapit(MEMREGION *memregion) 126{ 127 if (memregion->mapped != NULL) { 128 iounmap(memregion->mapped); 129 memregion->mapped = NULL; 130 } 131 if (memregion->requested) { 132 release_mem_region((ulong) (memregion->physaddr), 133 memregion->nbytes); 134 memregion->requested = FALSE; 135 } 136} 137 138HOSTADDRESS 139visor_memregion_get_physaddr(MEMREGION *memregion) 140{ 141 return memregion->physaddr; 142} 143EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr); 144 145ulong 146visor_memregion_get_nbytes(MEMREGION *memregion) 147{ 148 return memregion->nbytes; 149} 150EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes); 151 152void __iomem * 153visor_memregion_get_pointer(MEMREGION *memregion) 154{ 155 return memregion->mapped; 156} 157EXPORT_SYMBOL_GPL(visor_memregion_get_pointer); 158 159int 160visor_memregion_resize(MEMREGION *memregion, ulong newsize) 161{ 162 if (newsize == memregion->nbytes) 163 return 0; 164 if (memregion->overlapped) 165 /* no error check here - we no longer know the 166 * parent's range! 167 */ 168 memregion->nbytes = newsize; 169 else { 170 unmapit(memregion); 171 memregion->nbytes = newsize; 172 if (!mapit(memregion)) 173 return -1; 174 } 175 return 0; 176} 177EXPORT_SYMBOL_GPL(visor_memregion_resize); 178 179 180static int 181memregion_readwrite(BOOL is_write, 182 MEMREGION *memregion, ulong offset, 183 void *local, ulong nbytes) 184{ 185 if (offset + nbytes > memregion->nbytes) { 186 ERRDRV("memregion_readwrite offset out of range!!"); 187 return -EIO; 188 } 189 if (is_write) 190 memcpy_toio(memregion->mapped + offset, local, nbytes); 191 else 192 memcpy_fromio(local, memregion->mapped + offset, nbytes); 193 194 return 0; 195} 196 197int 198visor_memregion_read(MEMREGION *memregion, ulong offset, void *dest, 199 ulong nbytes) 200{ 201 return memregion_readwrite(FALSE, memregion, offset, dest, nbytes); 202} 203EXPORT_SYMBOL_GPL(visor_memregion_read); 204 205int 206visor_memregion_write(MEMREGION *memregion, ulong offset, void *src, 207 ulong nbytes) 208{ 209 return memregion_readwrite(TRUE, memregion, offset, src, nbytes); 210} 211EXPORT_SYMBOL_GPL(visor_memregion_write); 212 213void 214visor_memregion_destroy(MEMREGION *memregion) 215{ 216 if (memregion == NULL) 217 return; 218 if (!memregion->overlapped) 219 unmapit(memregion); 220 kfree(memregion); 221} 222EXPORT_SYMBOL_GPL(visor_memregion_destroy); 223 224