-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathbuffer.rs
222 lines (206 loc) · 7.16 KB
/
buffer.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
//! Server wrappers around IPC Buffers
//!
//! IPC Servers may accept different kinds of IPC Buffers, in order to move
//! around large amounts of data efficiently. There exists two kinds of IPC
//! Buffers, the Pointers and the Buffers.
//!
//! Pointers work in a pair of input and output: the Server pushes an InPointer
//! while the Client pushes an OutPointer. The kernel will memcpy the contents of
//! the OutPointer to the appropriate InPointer of the other side.
//!
//! Buffers work by remapping the memory from the sender to the receiver. The in
//! and out simply decide whether the memory is remapped as read-only or write-
//! only (on supported platforms. On platforms that don't have write-only memory,
//! it will be mapped RW instead).
//!
//! Those types are not meant to be used directly. Instead, you should get them
//! as arguments from the [object macro](crate::ipc::macros).
//!
//! The types will auto-deref to their underlying type, allowing the user to
//! manipulate them as if they were normal pointers.
use core::marker::PhantomData;
use crate::ipc::IPCBuffer;
use crate::error::{Error, LibuserError};
use core::mem::{size_of, align_of};
// TODO: Use plain to ensure T is a valid POD type
// BODY: Plain would give us two benefits: it would do the alignment and size
// BODY: checks for us, and it would give a type-system guarantee that our T
// BODY: is valid for any arbitrary type.
/// An incoming Pointer buffer, also known as a Type-X Buffer.
///
/// Note that `T` should be a POD type (in other word, it should be defined for
/// all bit values). This usually means that it should be a repr(C) struct and
/// only contain numeric fields.
pub struct InPointer<'a, T: ?Sized> {
/// Address of the InBuffer in the current address space.
addr: u64,
/// Size of the InPointer. Should match the size of T, or be a multiple of
/// the size of T::Item if T is a slice.
size: u64,
/// Lifetime of the InPointer, should be bound to a [Message](crate::ipc::Message).
phantom: PhantomData<&'a T>
}
impl<'a, T> InPointer<'a, T> {
/// Creates a new InPointer from an underlying [IPCBuffer].
///
/// # Panics
///
/// Panics if the passed buffer is not a Type-X buffer.
///
/// # Errors
///
/// Returns an InvalidIpcBuffer error if the size does not match what was
/// expected.
///
/// Returns an InvalidIpcBuffer error if the address is not properly aligned.
pub fn new(buf: IPCBuffer) -> Result<InPointer<'_, T>, Error> {
assert!(buf.buftype().is_type_x());
if buf.size != size_of::<T>() as u64 ||
buf.addr % (align_of::<T>() as u64) != 0 {
Err(LibuserError::InvalidIpcBuffer.into())
} else {
Ok(InPointer {
addr: buf.addr,
size: buf.size,
phantom: PhantomData
})
}
}
}
impl<'a, T> core::ops::Deref for InPointer<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe {
(self.addr as usize as *const T).as_ref().unwrap()
}
}
}
impl<'a, T: ?Sized + core::fmt::Debug> core::fmt::Debug for InPointer<'a, T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_tuple("InPointer")
.field(&*self)
.finish()
}
}
impl<'a, T> InPointer<'a, [T]> {
/// Creates a new InPointer from an underlying [IPCBuffer].
///
/// # Panics
///
/// Panics if the passed buffer is not a Type-X buffer.
///
/// # Errors
///
/// Returns a InvalidIpcBuffer error if the size does not match what was
/// expected
pub fn new(buf: IPCBuffer) -> Result<InPointer<'_, [T]>, Error> {
assert!(buf.buftype().is_type_x());
if buf.size % size_of::<T>() as u64 != 0 || buf.size == 0 ||
buf.addr % (align_of::<T>() as u64) != 0 {
Err(LibuserError::InvalidIpcBuffer.into())
} else {
Ok(InPointer {
addr: buf.addr,
size: buf.size,
phantom: PhantomData
})
}
}
}
impl<'a, T> core::ops::Deref for InPointer<'a, [T]> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe {
core::slice::from_raw_parts(self.addr as usize as *const T,
self.size as usize / size_of::<T>())
}
}
}
/// An incoming Buffer, also known as a Type-A Buffer.
///
/// Note that `T` should be a POD type (in other word, it should be defined for
/// all bit values). This usually means that it should be a repr(C) struct and
/// only contain numeric fields.
pub struct InBuffer<'a, T: ?Sized> {
/// Address of the InBuffer in the current address space.
addr: u64,
/// Size of the InBuffer. Should match the size of T, or be a multiple of
/// the size of T::Item if T is a slice.
size: u64,
/// Lifetime of the InBuffer, should be bound to a [Message](crate::ipc::Message).
phantom: PhantomData<&'a T>
}
impl<'a, T> InBuffer<'a, T> {
/// Creates a new InBuffer from an underlying [IPCBuffer].
///
/// # Panics
///
/// Panics if the passed buffer is not a Type-A buffer.
///
/// # Errors
///
/// Returns a InvalidIpcBuffer error if the size does not match what was
/// expected
pub fn new(buf: IPCBuffer) -> Result<InBuffer<'_, T>, Error> {
assert!(buf.buftype().is_type_a());
if buf.size != size_of::<T>() as u64 ||
buf.addr % (align_of::<T>() as u64) != 0 {
Err(LibuserError::InvalidIpcBuffer.into())
} else {
Ok(InBuffer {
addr: buf.addr,
size: buf.size,
phantom: PhantomData
})
}
}
}
impl<'a, T> core::ops::Deref for InBuffer<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe {
(self.addr as usize as *const T).as_ref().unwrap()
}
}
}
impl<'a, T: ?Sized + core::fmt::Debug> core::fmt::Debug for InBuffer<'a, T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_tuple("InBuffer")
.field(&*self)
.finish()
}
}
impl<'a, T> InBuffer<'a, [T]> {
/// Creates a new InBuffer from an underlying [IPCBuffer].
///
/// # Panics
///
/// Panics if the passed buffer is not a Type-A buffer.
///
/// # Errors
///
/// Returns a InvalidIpcBuffer error if the size does not match what was
/// expected
pub fn new(buf: IPCBuffer) -> Result<InBuffer<'_, [T]>, Error> {
assert!(buf.buftype().is_type_a());
if buf.size % size_of::<T>() as u64 != 0 || buf.size == 0 ||
buf.addr % (align_of::<T>() as u64) != 0 {
Err(LibuserError::InvalidIpcBuffer.into())
} else {
Ok(InBuffer {
addr: buf.addr,
size: buf.size,
phantom: PhantomData
})
}
}
}
impl<'a, T> core::ops::Deref for InBuffer<'a, [T]> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe {
core::slice::from_raw_parts(self.addr as usize as *const T,
self.size as usize / size_of::<T>())
}
}
}