memregion_direct.c revision a3acc83a4a2b8e1a6f8f3c5bdcfa3bb5f5f9e338
1/* memregion_direct.c
2 *
3 * Copyright � 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 *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		RETPTR(NULL);
55	RETPTR(memregion);
56
57Away:
58	if (rc == NULL) {
59		if (memregion != NULL) {
60			visor_memregion_destroy(memregion);
61			memregion = NULL;
62		}
63	}
64	return rc;
65}
66EXPORT_SYMBOL_GPL(visor_memregion_create);
67
68MEMREGION *
69visor_memregion_create_overlapped(MEMREGION *parent, ulong offset, ulong nbytes)
70{
71	MEMREGION *memregion = NULL;
72
73	if (parent == NULL) {
74		ERRDRV("%s parent is NULL", __func__);
75		return NULL;
76	}
77	if (parent->mapped == NULL) {
78		ERRDRV("%s parent is not mapped!", __func__);
79		return NULL;
80	}
81	if ((offset >= parent->nbytes) ||
82	    ((offset + nbytes) >= parent->nbytes)) {
83		ERRDRV("%s range (%lu,%lu) out of parent range",
84		       __func__, offset, nbytes);
85		return NULL;
86	}
87	memregion = kzalloc(sizeof(MEMREGION), GFP_KERNEL|__GFP_NORETRY);
88	if (memregion == NULL) {
89		ERRDRV("%s allocation failed", __func__);
90		return NULL;
91	}
92
93	memregion->physaddr = parent->physaddr + offset;
94	memregion->nbytes = nbytes;
95	memregion->mapped = ((u8 *) (parent->mapped)) + offset;
96	memregion->requested = FALSE;
97	memregion->overlapped = TRUE;
98	return memregion;
99}
100EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped);
101
102
103static BOOL
104mapit(MEMREGION *memregion)
105{
106	ulong physaddr = (ulong) (memregion->physaddr);
107	ulong nbytes = memregion->nbytes;
108
109	memregion->requested = FALSE;
110	if (!request_mem_region(physaddr, nbytes, MYDRVNAME))
111		ERRDRV("cannot reserve channel memory @0x%lx for 0x%lx-- no big deal", physaddr, nbytes);
112	else
113		memregion->requested = TRUE;
114	memregion->mapped = ioremap_cache(physaddr, nbytes);
115	if (memregion->mapped == NULL) {
116		ERRDRV("cannot ioremap_cache channel memory @0x%lx for 0x%lx",
117		       physaddr, nbytes);
118		return FALSE;
119	}
120	return TRUE;
121}
122
123static void
124unmapit(MEMREGION *memregion)
125{
126	if (memregion->mapped != NULL) {
127		iounmap(memregion->mapped);
128		memregion->mapped = NULL;
129	}
130	if (memregion->requested) {
131		release_mem_region((ulong) (memregion->physaddr),
132				   memregion->nbytes);
133		memregion->requested = FALSE;
134	}
135}
136
137HOSTADDRESS
138visor_memregion_get_physaddr(MEMREGION *memregion)
139{
140	return memregion->physaddr;
141}
142EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr);
143
144ulong
145visor_memregion_get_nbytes(MEMREGION *memregion)
146{
147	return memregion->nbytes;
148}
149EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes);
150
151void *
152visor_memregion_get_pointer(MEMREGION *memregion)
153{
154	return memregion->mapped;
155}
156EXPORT_SYMBOL_GPL(visor_memregion_get_pointer);
157
158int
159visor_memregion_resize(MEMREGION *memregion, ulong newsize)
160{
161	if (newsize == memregion->nbytes)
162		return 0;
163	if (memregion->overlapped)
164		/* no error check here - we no longer know the
165		 * parent's range!
166		 */
167		memregion->nbytes = newsize;
168	else {
169		unmapit(memregion);
170		memregion->nbytes = newsize;
171		if (!mapit(memregion))
172			return -1;
173	}
174	return 0;
175}
176EXPORT_SYMBOL_GPL(visor_memregion_resize);
177
178
179static int
180memregion_readwrite(BOOL is_write,
181		    MEMREGION *memregion, ulong offset,
182		    void *local, ulong nbytes)
183{
184	if (offset + nbytes > memregion->nbytes) {
185		ERRDRV("memregion_readwrite offset out of range!!");
186		return -EFAULT;
187	}
188	if (is_write)
189		memcpy_toio(memregion->mapped + offset, local, nbytes);
190	else
191		memcpy_fromio(local, memregion->mapped + offset, nbytes);
192
193	return 0;
194}
195
196int
197visor_memregion_read(MEMREGION *memregion, ulong offset, void *dest,
198		     ulong nbytes)
199{
200	return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
201}
202EXPORT_SYMBOL_GPL(visor_memregion_read);
203
204int
205visor_memregion_write(MEMREGION *memregion, ulong offset, void *src,
206		      ulong nbytes)
207{
208	return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
209}
210EXPORT_SYMBOL_GPL(visor_memregion_write);
211
212void
213visor_memregion_destroy(MEMREGION *memregion)
214{
215	if (memregion == NULL)
216		return;
217	if (!memregion->overlapped)
218		unmapit(memregion);
219	kfree(memregion);
220}
221EXPORT_SYMBOL_GPL(visor_memregion_destroy);
222
223