Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144098679
D4245.id10751.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
D4245.id10751.diff
View Options
Index: share/man/man9/Makefile
===================================================================
--- share/man/man9/Makefile
+++ share/man/man9/Makefile
@@ -239,6 +239,7 @@
printf.9 \
prison_check.9 \
priv.9 \
+ proc_rwmem.9 \
pseudofs.9 \
psignal.9 \
random.9 \
@@ -1339,6 +1340,8 @@
printf.9 uprintf.9
MLINKS+=priv.9 priv_check.9 \
priv.9 priv_check_cred.9
+MLINKS+=proc_rwmem.9 proc_readmem.9 \
+ proc_rwmem.9 proc_writemem.9
MLINKS+=psignal.9 gsignal.9 \
psignal.9 pgsignal.9 \
psignal.9 tdsignal.9
Index: share/man/man9/proc_rwmem.9
===================================================================
--- /dev/null
+++ share/man/man9/proc_rwmem.9
@@ -0,0 +1,110 @@
+.\"
+.\" Copyright (c) 2015 Mark Johnston <markj@FreeBSD.org>
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 4, 2015
+.Dt PROC_RWMEM 9
+.Os
+.Sh NAME
+.Nm proc_rwmem ,
+.Nm proc_readmem ,
+.Nm proc_writemem
+.Nd read from or write to a process address space
+.Sh SYNOPSIS
+.In sys/types.h
+.In sys/ptrace.h
+.Ft int
+.Fn proc_rwmem "struct proc *p" "struct uio *uio"
+.Ft ssize_t
+.Fo proc_readmem
+.Fa "struct thread *td" "struct proc *p" "vm_offset_t va" "void *buf"
+.Fa "size_t len"
+.Fc
+.Ft ssize_t
+.Fo proc_writemem
+.Fa "struct thread *td" "struct proc *p" "vm_offset_t va" "void *buf"
+.Fa "size_t len"
+.Fc
+.Sh DESCRIPTION
+These functions are used to read to or write from the address space of the
+process
+.Fa p .
+The
+.Fn proc_rwmem
+function requires the caller to specify the I/O parameters using a
+.Vt "struct uio" ,
+described in
+.Xr uio 9 .
+The
+.Fn proc_readmem
+and
+.Fn proc_writemem
+functions provide a simpler, less general interface which allows the caller to
+read into or write the kernel buffer
+.Fa buf
+of size
+.Fa len
+from or to the memory at offset
+.Fa va
+in the address space of
+.Fa p .
+The operation is performed on behalf of thread
+.Fa td ,
+which will most often be the current thread.
+.Pp
+These functions may sleep and thus may not be called with any non-sleepable
+locks held.
+The process
+.Fa p
+must be held by the caller using
+.Xr PHOLD 9 .
+.Sh RETURN VALUES
+The
+.Fn proc_rwmem
+function returns
+.Dv 0
+on success.
+.Dv EFAULT
+is returned if the specified user address is invalid, and
+.Dv ENOMEM
+is returned if the target pages could not be faulted in due to a resource
+shortage.
+.Pp
+The
+.Fn proc_readmem
+and
+.Fn proc_writemem
+functions return the number of bytes read or written, respectively.
+This may be smaller than the number of bytes requested, for example if the
+request spans multiple pages in the process address space and one of them after
+the first is not mapped.
+Otherwise, -1 is returned.
+.Sh SEE ALSO
+.Xr copyin 9 ,
+.Xr locking 9 ,
+.Xr PHOLD 9 ,
+.Xr uio 9
+.Sh AUTHORS
+This manual page was written by
+.An Mark Johnston Aq Mt markj@FreeBSD.org .
Index: sys/arm/arm/machdep.c
===================================================================
--- sys/arm/arm/machdep.c
+++ sys/arm/arm/machdep.c
@@ -591,41 +591,21 @@
static int
-ptrace_read_int(struct thread *td, vm_offset_t addr, u_int32_t *v)
+ptrace_read_int(struct thread *td, vm_offset_t addr, uint32_t *v)
{
- struct iovec iov;
- struct uio uio;
-
- PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED);
- iov.iov_base = (caddr_t) v;
- iov.iov_len = sizeof(u_int32_t);
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- uio.uio_offset = (off_t)addr;
- uio.uio_resid = sizeof(u_int32_t);
- uio.uio_segflg = UIO_SYSSPACE;
- uio.uio_rw = UIO_READ;
- uio.uio_td = td;
- return proc_rwmem(td->td_proc, &uio);
+
+ if (proc_readmem(td, td->td_proc, addr, v, sizeof(*v)) != sizeof(*v))
+ return (ENOMEM);
+ return (0);
}
static int
-ptrace_write_int(struct thread *td, vm_offset_t addr, u_int32_t v)
+ptrace_write_int(struct thread *td, vm_offset_t addr, uint32_t v)
{
- struct iovec iov;
- struct uio uio;
-
- PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED);
- iov.iov_base = (caddr_t) &v;
- iov.iov_len = sizeof(u_int32_t);
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- uio.uio_offset = (off_t)addr;
- uio.uio_resid = sizeof(u_int32_t);
- uio.uio_segflg = UIO_SYSSPACE;
- uio.uio_rw = UIO_WRITE;
- uio.uio_td = td;
- return proc_rwmem(td->td_proc, &uio);
+
+ if (proc_writemem(td, td->td_proc, addr, &v, sizeof(v)) != sizeof(v))
+ return (ENOMEM);
+ return (0);
}
static u_int
Index: sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c
+++ sys/cddl/contrib/opensolaris/uts/intel/dtrace/fasttrap_isa.c
@@ -60,43 +60,31 @@
#include <sys/ptrace.h>
static int
-proc_ops(int op, proc_t *p, void *kaddr, off_t uaddr, size_t len)
+uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr)
{
- struct iovec iov;
- struct uio uio;
-
- iov.iov_base = kaddr;
- iov.iov_len = len;
- uio.uio_offset = uaddr;
- uio.uio_iov = &iov;
- uio.uio_resid = len;
- uio.uio_iovcnt = 1;
- uio.uio_segflg = UIO_SYSSPACE;
- uio.uio_td = curthread;
- uio.uio_rw = op;
+ ssize_t n;
+
PHOLD(p);
- if (proc_rwmem(p, &uio) != 0) {
- PRELE(p);
- return (-1);
- }
+ n = proc_readmem(curthread, p, uaddr, kaddr, len);
PRELE(p);
-
+ if (n != len)
+ return (ENOMEM);
return (0);
}
static int
-uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr)
-{
-
- return (proc_ops(UIO_READ, p, kaddr, uaddr, len));
-}
-
-static int
uwrite(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr)
{
+ ssize_t n;
- return (proc_ops(UIO_WRITE, p, kaddr, uaddr, len));
+ PHOLD(p);
+ n = proc_writemem(curthread, p, uaddr, kaddr, len);
+ PRELE(p);
+ if (n != len)
+ return (ENOMEM);
+ return (0);
}
+
#endif /* illumos */
#ifdef __i386__
#define r_rax r_eax
Index: sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c
+++ sys/cddl/contrib/opensolaris/uts/powerpc/dtrace/fasttrap_isa.c
@@ -43,44 +43,30 @@
#define OP_RA(x) (((x) & 0x001F0000) >> 16)
#define OP_RB(x) (((x) & 0x0000F100) >> 11)
-
static int
-proc_ops(int op, proc_t *p, void *kaddr, off_t uaddr, size_t len)
+uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr)
{
- struct iovec iov;
- struct uio uio;
-
- iov.iov_base = kaddr;
- iov.iov_len = len;
- uio.uio_offset = uaddr;
- uio.uio_iov = &iov;
- uio.uio_resid = len;
- uio.uio_iovcnt = 1;
- uio.uio_segflg = UIO_SYSSPACE;
- uio.uio_td = curthread;
- uio.uio_rw = op;
+ ssize_t n;
+
PHOLD(p);
- if (proc_rwmem(p, &uio) != 0) {
- PRELE(p);
- return (-1);
- }
+ n = proc_readmem(curthread, p, uaddr, kaddr, len);
PRELE(p);
-
+ if (n <= 0 || n < len)
+ return (ENOMEM);
return (0);
}
static int
-uread(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr)
-{
-
- return (proc_ops(UIO_READ, p, kaddr, uaddr, len));
-}
-
-static int
uwrite(proc_t *p, void *kaddr, size_t len, uintptr_t uaddr)
{
+ ssize_t n;
- return (proc_ops(UIO_WRITE, p, kaddr, uaddr, len));
+ PHOLD(p);
+ n = proc_writemem(curthread, p, uaddr, kaddr, len);
+ PRELE(p);
+ if (n <= 0 || n < len)
+ return (ENOMEM);
+ return (0);
}
int
Index: sys/kern/kern_proc.c
===================================================================
--- sys/kern/kern_proc.c
+++ sys/kern/kern_proc.c
@@ -1526,50 +1526,20 @@
}
static int
-proc_read_mem(struct thread *td, struct proc *p, vm_offset_t offset, void* buf,
- size_t len)
-{
- struct iovec iov;
- struct uio uio;
-
- iov.iov_base = (caddr_t)buf;
- iov.iov_len = len;
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- uio.uio_offset = offset;
- uio.uio_resid = (ssize_t)len;
- uio.uio_segflg = UIO_SYSSPACE;
- uio.uio_rw = UIO_READ;
- uio.uio_td = td;
-
- return (proc_rwmem(p, &uio));
-}
-
-static int
proc_read_string(struct thread *td, struct proc *p, const char *sptr, char *buf,
size_t len)
{
- size_t i;
- int error;
+ ssize_t n;
- error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, len);
/*
- * Reading the chunk may validly return EFAULT if the string is shorter
- * than the chunk and is aligned at the end of the page, assuming the
- * next page is not mapped. So if EFAULT is returned do a fallback to
- * one byte read loop.
+ * This may return a short read if the string is shorter than the chunk
+ * and is aligned at the end of the page, and the following page is not
+ * mapped.
*/
- if (error == EFAULT) {
- for (i = 0; i < len; i++, buf++, sptr++) {
- error = proc_read_mem(td, p, (vm_offset_t)sptr, buf, 1);
- if (error != 0)
- return (error);
- if (*buf == '\0')
- break;
- }
- error = 0;
- }
- return (error);
+ n = proc_readmem(td, p, (vm_offset_t)sptr, buf, len);
+ if (n <= 0)
+ return (ENOMEM);
+ return (0);
}
#define PROC_AUXV_MAX 256 /* Safety limit on auxv size. */
@@ -1593,10 +1563,10 @@
size_t vsize, size;
int i, error;
- error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings),
- &pss, sizeof(pss));
- if (error != 0)
- return (error);
+ error = 0;
+ if (proc_readmem(td, p, (vm_offset_t)p->p_sysent->sv_psstrings, &pss,
+ sizeof(pss)) != sizeof(pss))
+ return (ENOMEM);
switch (type) {
case PROC_ARG:
vptr = (vm_offset_t)PTRIN(pss.ps_argvstr);
@@ -1618,9 +1588,9 @@
if (vptr % 4 != 0)
return (ENOEXEC);
for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) {
- error = proc_read_mem(td, p, ptr, &aux, sizeof(aux));
- if (error != 0)
- return (error);
+ if (proc_readmem(td, p, ptr, &aux, sizeof(aux)) !=
+ sizeof(aux))
+ return (ENOMEM);
if (aux.a_type == AT_NULL)
break;
ptr += sizeof(aux);
@@ -1635,9 +1605,10 @@
return (EINVAL);
}
proc_vector32 = malloc(size, M_TEMP, M_WAITOK);
- error = proc_read_mem(td, p, vptr, proc_vector32, size);
- if (error != 0)
+ if (proc_readmem(td, p, vptr, proc_vector32, size) != size) {
+ error = ENOMEM;
goto done;
+ }
if (type == PROC_AUX) {
*proc_vectorp = (char **)proc_vector32;
*vsizep = vsize;
@@ -1663,16 +1634,15 @@
vm_offset_t vptr, ptr;
char **proc_vector;
size_t vsize, size;
- int error, i;
+ int i;
#ifdef COMPAT_FREEBSD32
if (SV_PROC_FLAG(p, SV_ILP32) != 0)
return (get_proc_vector32(td, p, proc_vectorp, vsizep, type));
#endif
- error = proc_read_mem(td, p, (vm_offset_t)(p->p_sysent->sv_psstrings),
- &pss, sizeof(pss));
- if (error != 0)
- return (error);
+ if (proc_readmem(td, p, (vm_offset_t)p->p_sysent->sv_psstrings, &pss,
+ sizeof(pss)) != sizeof(pss))
+ return (ENOMEM);
switch (type) {
case PROC_ARG:
vptr = (vm_offset_t)pss.ps_argvstr;
@@ -1709,9 +1679,9 @@
* to the allocated proc_vector.
*/
for (ptr = vptr, i = 0; i < PROC_AUXV_MAX; i++) {
- error = proc_read_mem(td, p, ptr, &aux, sizeof(aux));
- if (error != 0)
- return (error);
+ if (proc_readmem(td, p, ptr, &aux, sizeof(aux)) !=
+ sizeof(aux))
+ return (ENOMEM);
if (aux.a_type == AT_NULL)
break;
ptr += sizeof(aux);
@@ -1732,12 +1702,9 @@
return (EINVAL); /* In case we are built without INVARIANTS. */
}
proc_vector = malloc(size, M_TEMP, M_WAITOK);
- if (proc_vector == NULL)
- return (ENOMEM);
- error = proc_read_mem(td, p, vptr, proc_vector, size);
- if (error != 0) {
+ if (proc_readmem(td, p, vptr, proc_vector, size) != size) {
free(proc_vector, M_TEMP);
- return (error);
+ return (ENOMEM);
}
*proc_vectorp = proc_vector;
*vsizep = vsize;
Index: sys/kern/sys_process.c
===================================================================
--- sys/kern/sys_process.c
+++ sys/kern/sys_process.c
@@ -252,6 +252,7 @@
* from exiting out from under us until this operation completes.
*/
PROC_ASSERT_HELD(p);
+ PROC_LOCK_ASSERT(p, MA_NOTOWNED);
/*
* The map we want...
@@ -328,6 +329,49 @@
}
static int
+proc_iop(struct thread *td, struct proc *p, vm_offset_t va, void *buf,
+ size_t len, enum uio_rw rw)
+{
+ struct iovec iov;
+ struct uio uio;
+ ssize_t slen;
+ int error;
+
+ MPASS(len < SSIZE_MAX);
+ slen = (ssize_t)len;
+
+ iov.iov_base = (caddr_t)buf;
+ iov.iov_len = len;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_offset = va;
+ uio.uio_resid = slen;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_rw = rw;
+ uio.uio_td = td;
+ error = proc_rwmem(p, &uio);
+ if (uio.uio_resid == slen)
+ return (-1);
+ return (slen - uio.uio_resid);
+}
+
+int
+proc_readmem(struct thread *td, struct proc *p, vm_offset_t va, void *buf,
+ size_t len)
+{
+
+ return (proc_iop(td, p, va, buf, len, UIO_READ));
+}
+
+int
+proc_writemem(struct thread *td, struct proc *p, vm_offset_t va, void *buf,
+ size_t len)
+{
+
+ return (proc_iop(td, p, va, buf, len, UIO_WRITE));
+}
+
+static int
ptrace_vm_entry(struct thread *td, struct proc *p, struct ptrace_vm_entry *pve)
{
struct vattr vattr;
@@ -644,7 +688,7 @@
struct thread *td2 = NULL, *td3;
struct ptrace_io_desc *piod = NULL;
struct ptrace_lwpinfo *pl;
- int error, write, tmp, num;
+ int error, num, tmp;
int proctree_locked = 0;
lwpid_t tid = 0, *buf;
#ifdef COMPAT_FREEBSD32
@@ -674,7 +718,6 @@
break;
}
- write = 0;
if (req == PT_TRACE_ME) {
p = td->td_proc;
PROC_LOCK(p);
@@ -1033,46 +1076,28 @@
case PT_WRITE_I:
case PT_WRITE_D:
td2->td_dbgflags |= TDB_USERWR;
- write = 1;
- /* FALLTHROUGH */
+ PROC_UNLOCK(p);
+ error = 0;
+ if (proc_writemem(td, p, (off_t)(uintptr_t)addr, &data,
+ sizeof(int)) != sizeof(int))
+ error = ENOMEM;
+ else
+ CTR3(KTR_PTRACE, "PT_WRITE: pid %d: %p <= %#x",
+ p->p_pid, addr, data);
+ PROC_LOCK(p);
+ break;
+
case PT_READ_I:
case PT_READ_D:
PROC_UNLOCK(p);
- tmp = 0;
- /* write = 0 set above */
- iov.iov_base = write ? (caddr_t)&data : (caddr_t)&tmp;
- iov.iov_len = sizeof(int);
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- uio.uio_offset = (off_t)(uintptr_t)addr;
- uio.uio_resid = sizeof(int);
- uio.uio_segflg = UIO_SYSSPACE; /* i.e.: the uap */
- uio.uio_rw = write ? UIO_WRITE : UIO_READ;
- uio.uio_td = td;
- error = proc_rwmem(p, &uio);
- if (uio.uio_resid != 0) {
- /*
- * XXX proc_rwmem() doesn't currently return ENOSPC,
- * so I think write() can bogusly return 0.
- * XXX what happens for short writes? We don't want
- * to write partial data.
- * XXX proc_rwmem() returns EPERM for other invalid
- * addresses. Convert this to EINVAL. Does this
- * clobber returns of EPERM for other reasons?
- */
- if (error == 0 || error == ENOSPC || error == EPERM)
- error = EINVAL; /* EOF */
- }
- if (!write)
- td->td_retval[0] = tmp;
- if (error == 0) {
- if (write)
- CTR3(KTR_PTRACE, "PT_WRITE: pid %d: %p <= %#x",
- p->p_pid, addr, data);
- else
- CTR3(KTR_PTRACE, "PT_READ: pid %d: %p >= %#x",
- p->p_pid, addr, tmp);
- }
+ error = tmp = 0;
+ if (proc_readmem(td, p, (off_t)(uintptr_t)addr, &tmp,
+ sizeof(int)) != sizeof(int))
+ error = ENOMEM;
+ else
+ CTR3(KTR_PTRACE, "PT_READ: pid %d: %p >= %#x",
+ p->p_pid, addr, tmp);
+ td->td_retval[0] = tmp;
PROC_LOCK(p);
break;
Index: sys/mips/mips/pm_machdep.c
===================================================================
--- sys/mips/mips/pm_machdep.c
+++ sys/mips/mips/pm_machdep.c
@@ -214,39 +214,19 @@
static int
ptrace_read_int(struct thread *td, off_t addr, int *v)
{
- struct iovec iov;
- struct uio uio;
-
- PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED);
- iov.iov_base = (caddr_t) v;
- iov.iov_len = sizeof(int);
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- uio.uio_offset = (off_t)addr;
- uio.uio_resid = sizeof(int);
- uio.uio_segflg = UIO_SYSSPACE;
- uio.uio_rw = UIO_READ;
- uio.uio_td = td;
- return proc_rwmem(td->td_proc, &uio);
+
+ if (proc_readmem(td, td->proc, addr, v, sizeof(*v)) != sizeof(*v))
+ return (ENOMEM);
+ return (0);
}
static int
ptrace_write_int(struct thread *td, off_t addr, int v)
{
- struct iovec iov;
- struct uio uio;
-
- PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED);
- iov.iov_base = (caddr_t) &v;
- iov.iov_len = sizeof(int);
- uio.uio_iov = &iov;
- uio.uio_iovcnt = 1;
- uio.uio_offset = (off_t)addr;
- uio.uio_resid = sizeof(int);
- uio.uio_segflg = UIO_SYSSPACE;
- uio.uio_rw = UIO_WRITE;
- uio.uio_td = td;
- return proc_rwmem(td->td_proc, &uio);
+
+ if (proc_writemem(td, td->proc, addr, &v, sizeof(v)) != sizeof(v))
+ return (ENOMEM);
+ return (0);
}
int
Index: sys/sys/ptrace.h
===================================================================
--- sys/sys/ptrace.h
+++ sys/sys/ptrace.h
@@ -166,6 +166,10 @@
int proc_write_dbregs(struct thread *_td, struct dbreg *_dbreg);
int proc_sstep(struct thread *_td);
int proc_rwmem(struct proc *_p, struct uio *_uio);
+int proc_readmem(struct thread *_td, struct proc *_p, vm_offset_t _va,
+ void *_buf, size_t _len);
+int proc_writemem(struct thread *_td, struct proc *_p, vm_offset_t _va,
+ void *_buf, size_t _len);
#ifdef COMPAT_FREEBSD32
struct reg32;
struct fpreg32;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Feb 5, 5:39 PM (21 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28443323
Default Alt Text
D4245.id10751.diff (17 KB)
Attached To
Mode
D4245: add proc_readmem() and proc_writemem() interfaces
Attached
Detach File
Event Timeline
Log In to Comment