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 "timskmod.h"
24 #include "memregion.h"
25 
26 #define MYDRVNAME "memregion"
27 
28 struct memregion {
29 	HOSTADDRESS physaddr;
30 	ulong nbytes;
31 	void __iomem *mapped;
32 	BOOL requested;
33 	BOOL overlapped;
34 };
35 
36 static BOOL mapit(struct memregion *memregion);
37 static void unmapit(struct memregion *memregion);
38 
39 struct memregion *
visor_memregion_create(HOSTADDRESS physaddr,ulong nbytes)40 visor_memregion_create(HOSTADDRESS physaddr, ulong nbytes)
41 {
42 	struct memregion *rc = NULL;
43 	struct memregion *memregion;
44 
45 	memregion = kzalloc(sizeof(*memregion), GFP_KERNEL | __GFP_NORETRY);
46 	if (memregion == NULL)
47 		return NULL;
48 
49 	memregion->physaddr = physaddr;
50 	memregion->nbytes = nbytes;
51 	memregion->overlapped = FALSE;
52 	if (!mapit(memregion)) {
53 		rc = NULL;
54 		goto cleanup;
55 	}
56 	rc = memregion;
57 cleanup:
58 	if (rc == NULL) {
59 		visor_memregion_destroy(memregion);
60 		memregion = NULL;
61 	}
62 	return rc;
63 }
64 EXPORT_SYMBOL_GPL(visor_memregion_create);
65 
66 struct memregion *
visor_memregion_create_overlapped(struct memregion * parent,ulong offset,ulong nbytes)67 visor_memregion_create_overlapped(struct memregion *parent, ulong offset,
68 				  ulong nbytes)
69 {
70 	struct memregion *memregion = NULL;
71 
72 	if (parent == NULL)
73 		return NULL;
74 
75 	if (parent->mapped == NULL)
76 		return NULL;
77 
78 	if ((offset >= parent->nbytes) ||
79 	    ((offset + nbytes) >= parent->nbytes))
80 		return NULL;
81 
82 	memregion = kzalloc(sizeof(*memregion), GFP_KERNEL|__GFP_NORETRY);
83 	if (memregion == NULL)
84 		return NULL;
85 
86 	memregion->physaddr = parent->physaddr + offset;
87 	memregion->nbytes = nbytes;
88 	memregion->mapped = ((u8 __iomem *)(parent->mapped)) + offset;
89 	memregion->requested = FALSE;
90 	memregion->overlapped = TRUE;
91 	return memregion;
92 }
93 EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped);
94 
95 static BOOL
mapit(struct memregion * memregion)96 mapit(struct memregion *memregion)
97 {
98 	ulong physaddr = (ulong)(memregion->physaddr);
99 	ulong nbytes = memregion->nbytes;
100 
101 	memregion->requested = FALSE;
102 	if (request_mem_region(physaddr, nbytes, MYDRVNAME))
103 		memregion->requested = TRUE;
104 	memregion->mapped = ioremap_cache(physaddr, nbytes);
105 	if (!memregion->mapped)
106 		return FALSE;
107 	return TRUE;
108 }
109 
110 static void
unmapit(struct memregion * memregion)111 unmapit(struct memregion *memregion)
112 {
113 	if (memregion->mapped != NULL) {
114 		iounmap(memregion->mapped);
115 		memregion->mapped = NULL;
116 	}
117 	if (memregion->requested) {
118 		release_mem_region((ulong)(memregion->physaddr),
119 				   memregion->nbytes);
120 		memregion->requested = FALSE;
121 	}
122 }
123 
124 HOSTADDRESS
visor_memregion_get_physaddr(struct memregion * memregion)125 visor_memregion_get_physaddr(struct memregion *memregion)
126 {
127 	return memregion->physaddr;
128 }
129 EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr);
130 
131 ulong
visor_memregion_get_nbytes(struct memregion * memregion)132 visor_memregion_get_nbytes(struct memregion *memregion)
133 {
134 	return memregion->nbytes;
135 }
136 EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes);
137 
138 void __iomem *
visor_memregion_get_pointer(struct memregion * memregion)139 visor_memregion_get_pointer(struct memregion *memregion)
140 {
141 	return memregion->mapped;
142 }
143 EXPORT_SYMBOL_GPL(visor_memregion_get_pointer);
144 
145 int
visor_memregion_resize(struct memregion * memregion,ulong newsize)146 visor_memregion_resize(struct memregion *memregion, ulong newsize)
147 {
148 	if (newsize == memregion->nbytes)
149 		return 0;
150 	if (memregion->overlapped)
151 		/* no error check here - we no longer know the
152 		 * parent's range!
153 		 */
154 		memregion->nbytes = newsize;
155 	else {
156 		unmapit(memregion);
157 		memregion->nbytes = newsize;
158 		if (!mapit(memregion))
159 			return -1;
160 	}
161 	return 0;
162 }
163 EXPORT_SYMBOL_GPL(visor_memregion_resize);
164 
165 static int
memregion_readwrite(BOOL is_write,struct memregion * memregion,ulong offset,void * local,ulong nbytes)166 memregion_readwrite(BOOL is_write,
167 		    struct memregion *memregion, ulong offset,
168 		    void *local, ulong nbytes)
169 {
170 	if (offset + nbytes > memregion->nbytes)
171 		return -EIO;
172 
173 	if (is_write)
174 		memcpy_toio(memregion->mapped + offset, local, nbytes);
175 	else
176 		memcpy_fromio(local, memregion->mapped + offset, nbytes);
177 
178 	return 0;
179 }
180 
181 int
visor_memregion_read(struct memregion * memregion,ulong offset,void * dest,ulong nbytes)182 visor_memregion_read(struct memregion *memregion, ulong offset, void *dest,
183 		     ulong nbytes)
184 {
185 	return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
186 }
187 EXPORT_SYMBOL_GPL(visor_memregion_read);
188 
189 int
visor_memregion_write(struct memregion * memregion,ulong offset,void * src,ulong nbytes)190 visor_memregion_write(struct memregion *memregion, ulong offset, void *src,
191 		      ulong nbytes)
192 {
193 	return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
194 }
195 EXPORT_SYMBOL_GPL(visor_memregion_write);
196 
197 void
visor_memregion_destroy(struct memregion * memregion)198 visor_memregion_destroy(struct memregion *memregion)
199 {
200 	if (memregion == NULL)
201 		return;
202 	if (!memregion->overlapped)
203 		unmapit(memregion);
204 	kfree(memregion);
205 }
206 EXPORT_SYMBOL_GPL(visor_memregion_destroy);
207 
208