1295041Sbr/*-
2295041Sbr * Copyright (c) 2014 Andrew Turner
3295041Sbr * All rights reserved.
4295041Sbr *
5295041Sbr * Redistribution and use in source and binary forms, with or without
6295041Sbr * modification, are permitted provided that the following conditions
7295041Sbr * are met:
8295041Sbr * 1. Redistributions of source code must retain the above copyright
9295041Sbr *    notice, this list of conditions and the following disclaimer.
10295041Sbr * 2. Redistributions in binary form must reproduce the above copyright
11295041Sbr *    notice, this list of conditions and the following disclaimer in the
12295041Sbr *    documentation and/or other materials provided with the distribution.
13295041Sbr *
14295041Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15295041Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16295041Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17295041Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18295041Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19295041Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20295041Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21295041Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22295041Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23295041Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24295041Sbr * SUCH DAMAGE.
25295041Sbr *
26295041Sbr */
27295041Sbr
28295041Sbr#include <sys/cdefs.h>
29295041Sbr__FBSDID("$FreeBSD$");
30295041Sbr
31295041Sbr#include <sys/param.h>
32295041Sbr#include <sys/systm.h>
33295041Sbr#include <sys/conf.h>
34295041Sbr#include <sys/malloc.h>
35295041Sbr#include <sys/memrange.h>
36295041Sbr#include <sys/uio.h>
37295041Sbr
38295041Sbr#include <machine/memdev.h>
39295041Sbr#include <machine/vmparam.h>
40295041Sbr
41295041Sbr#include <vm/vm.h>
42295041Sbr#include <vm/pmap.h>
43295041Sbr#include <vm/vm_extern.h>
44295041Sbr#include <vm/vm_page.h>
45295041Sbr
46295041Sbrstruct mem_range_softc mem_range_softc;
47295041Sbr
48295041Sbrint
49295041Sbrmemrw(struct cdev *dev, struct uio *uio, int flags)
50295041Sbr{
51295041Sbr	ssize_t orig_resid;
52295041Sbr	vm_offset_t off, v;
53295041Sbr	struct iovec *iov;
54295041Sbr	struct vm_page m;
55295041Sbr	vm_page_t marr;
56295041Sbr	u_int cnt;
57295041Sbr	int error;
58295041Sbr
59295041Sbr	error = 0;
60295041Sbr	orig_resid = uio->uio_resid;
61295041Sbr	while (uio->uio_resid > 0 && error == 0) {
62295041Sbr		iov = uio->uio_iov;
63295041Sbr		if (iov->iov_len == 0) {
64295041Sbr			uio->uio_iov++;
65295041Sbr			uio->uio_iovcnt--;
66295041Sbr			if (uio->uio_iovcnt < 0)
67295041Sbr				panic("memrw");
68295041Sbr			continue;
69295041Sbr		}
70295041Sbr
71295041Sbr		v = uio->uio_offset;
72295041Sbr		off = v & PAGE_MASK;
73295041Sbr		cnt = ulmin(iov->iov_len, PAGE_SIZE - (u_int)off);
74295041Sbr		if (cnt == 0)
75295041Sbr			continue;
76295041Sbr
77295041Sbr		switch(dev2unit(dev)) {
78295041Sbr		case CDEV_MINOR_KMEM:
79295041Sbr			/* If the address is in the DMAP just copy it */
80295041Sbr			if (VIRT_IN_DMAP(v)) {
81295041Sbr				error = uiomove((void *)v, cnt, uio);
82295041Sbr				break;
83295041Sbr			}
84295041Sbr
85295041Sbr			if (!kernacc((void *)v, cnt, uio->uio_rw == UIO_READ ?
86295041Sbr			    VM_PROT_READ : VM_PROT_WRITE)) {
87295041Sbr				error = EFAULT;
88295041Sbr				break;
89295041Sbr			}
90295041Sbr
91295041Sbr			/* Get the physical address to read */
92295041Sbr			v = pmap_extract(kernel_pmap, v);
93295041Sbr			if (v == 0) {
94295041Sbr				error = EFAULT;
95295041Sbr				break;
96295041Sbr			}
97295041Sbr
98295041Sbr			/* FALLTHROUGH */
99295041Sbr		case CDEV_MINOR_MEM:
100295041Sbr			/* If within the DMAP use this to copy from */
101295041Sbr			if (PHYS_IN_DMAP(v)) {
102295041Sbr				v = PHYS_TO_DMAP(v);
103295041Sbr				error = uiomove((void *)v, cnt, uio);
104295041Sbr				break;
105295041Sbr			}
106295041Sbr
107295041Sbr			/* Have uiomove_fromphys handle the data */
108295041Sbr			m.phys_addr = trunc_page(v);
109295041Sbr			marr = &m;
110295041Sbr			uiomove_fromphys(&marr, off, cnt, uio);
111295041Sbr			break;
112295041Sbr		}
113295041Sbr	}
114295041Sbr
115295041Sbr	/*
116295041Sbr	 * Don't return error if any byte was written.  Read and write
117295041Sbr	 * can return error only if no i/o was performed.
118295041Sbr	 */
119295041Sbr	if (uio->uio_resid != orig_resid)
120295041Sbr		error = 0;
121295041Sbr
122295041Sbr	return (error);
123295041Sbr}
124295041Sbr
125