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