blob: eb7422fbe20f4f08db705e8ce6f982450975d750 [file] [log] [blame]
/* memregion_direct.c
*
* Copyright (C) 2010 - 2013 UNISYS CORPORATION
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*/
/*
* This is an implementation of memory regions that can be used to read/write
* channel memory (in main memory of the host system) from code running in
* a virtual partition.
*/
#include "timskmod.h"
#include "memregion.h"
#define MYDRVNAME "memregion"
struct memregion {
HOSTADDRESS physaddr;
ulong nbytes;
void __iomem *mapped;
BOOL requested;
BOOL overlapped;
};
static BOOL mapit(struct memregion *memregion);
static void unmapit(struct memregion *memregion);
struct memregion *
visor_memregion_create(HOSTADDRESS physaddr, ulong nbytes)
{
struct memregion *rc = NULL;
struct memregion *memregion;
memregion = kzalloc(sizeof(*memregion), GFP_KERNEL | __GFP_NORETRY);
if (memregion == NULL)
return NULL;
memregion->physaddr = physaddr;
memregion->nbytes = nbytes;
memregion->overlapped = FALSE;
if (!mapit(memregion)) {
rc = NULL;
goto cleanup;
}
rc = memregion;
cleanup:
if (rc == NULL) {
visor_memregion_destroy(memregion);
memregion = NULL;
}
return rc;
}
EXPORT_SYMBOL_GPL(visor_memregion_create);
struct memregion *
visor_memregion_create_overlapped(struct memregion *parent, ulong offset,
ulong nbytes)
{
struct memregion *memregion = NULL;
if (parent == NULL)
return NULL;
if (parent->mapped == NULL)
return NULL;
if ((offset >= parent->nbytes) ||
((offset + nbytes) >= parent->nbytes))
return NULL;
memregion = kzalloc(sizeof(*memregion), GFP_KERNEL|__GFP_NORETRY);
if (memregion == NULL)
return NULL;
memregion->physaddr = parent->physaddr + offset;
memregion->nbytes = nbytes;
memregion->mapped = ((u8 __iomem *)(parent->mapped)) + offset;
memregion->requested = FALSE;
memregion->overlapped = TRUE;
return memregion;
}
EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped);
static BOOL
mapit(struct memregion *memregion)
{
ulong physaddr = (ulong)(memregion->physaddr);
ulong nbytes = memregion->nbytes;
memregion->requested = FALSE;
if (request_mem_region(physaddr, nbytes, MYDRVNAME))
memregion->requested = TRUE;
memregion->mapped = ioremap_cache(physaddr, nbytes);
if (!memregion->mapped)
return FALSE;
return TRUE;
}
static void
unmapit(struct memregion *memregion)
{
if (memregion->mapped != NULL) {
iounmap(memregion->mapped);
memregion->mapped = NULL;
}
if (memregion->requested) {
release_mem_region((ulong)(memregion->physaddr),
memregion->nbytes);
memregion->requested = FALSE;
}
}
HOSTADDRESS
visor_memregion_get_physaddr(struct memregion *memregion)
{
return memregion->physaddr;
}
EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr);
ulong
visor_memregion_get_nbytes(struct memregion *memregion)
{
return memregion->nbytes;
}
EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes);
void __iomem *
visor_memregion_get_pointer(struct memregion *memregion)
{
return memregion->mapped;
}
EXPORT_SYMBOL_GPL(visor_memregion_get_pointer);
int
visor_memregion_resize(struct memregion *memregion, ulong newsize)
{
if (newsize == memregion->nbytes)
return 0;
if (memregion->overlapped)
/* no error check here - we no longer know the
* parent's range!
*/
memregion->nbytes = newsize;
else {
unmapit(memregion);
memregion->nbytes = newsize;
if (!mapit(memregion))
return -1;
}
return 0;
}
EXPORT_SYMBOL_GPL(visor_memregion_resize);
static int
memregion_readwrite(BOOL is_write,
struct memregion *memregion, ulong offset,
void *local, ulong nbytes)
{
if (offset + nbytes > memregion->nbytes)
return -EIO;
if (is_write)
memcpy_toio(memregion->mapped + offset, local, nbytes);
else
memcpy_fromio(local, memregion->mapped + offset, nbytes);
return 0;
}
int
visor_memregion_read(struct memregion *memregion, ulong offset, void *dest,
ulong nbytes)
{
return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
}
EXPORT_SYMBOL_GPL(visor_memregion_read);
int
visor_memregion_write(struct memregion *memregion, ulong offset, void *src,
ulong nbytes)
{
return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
}
EXPORT_SYMBOL_GPL(visor_memregion_write);
void
visor_memregion_destroy(struct memregion *memregion)
{
if (memregion == NULL)
return;
if (!memregion->overlapped)
unmapit(memregion);
kfree(memregion);
}
EXPORT_SYMBOL_GPL(visor_memregion_destroy);