1 /*
2 * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6 #include <stdlib.h>
7 #include <KernelExport.h>
8
9 #include <kernel.h>
10
11 #include "AreaSupport.h"
12 #include "Compatibility.h"
13 #include "Debug.h"
14 #include "Port.h"
15 #include "RequestAllocator.h"
16
17
18 // constructor
RequestAllocator(Port * port)19 RequestAllocator::RequestAllocator(Port* port)
20 :
21 fError(B_NO_INIT),
22 fPort(NULL),
23 fRequest(NULL),
24 fRequestSize(0),
25 fPortReservedOffset(0),
26 fAllocatedAreaCount(0),
27 fDeferredInitInfoCount(0),
28 fRequestInPortBuffer(false)
29 {
30 Init(port);
31 }
32
33 // destructor
~RequestAllocator()34 RequestAllocator::~RequestAllocator()
35 {
36 Uninit();
37 }
38
39 // Init
40 status_t
Init(Port * port)41 RequestAllocator::Init(Port* port)
42 {
43 Uninit();
44 if (port) {
45 fPort = port;
46 fError = fPort->InitCheck();
47 fPortReservedOffset = fPort->ReservedSize();
48 }
49 return fError;
50 }
51
52 // Uninit
53 void
Uninit()54 RequestAllocator::Uninit()
55 {
56 if (fRequestInPortBuffer)
57 fPort->Unreserve(fPortReservedOffset);
58 else
59 free(fRequest);
60
61 for (int32 i = 0; i < fAllocatedAreaCount; i++)
62 delete_area(fAllocatedAreas[i]);
63 fAllocatedAreaCount = 0;
64
65 for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
66 if (fDeferredInitInfos[i].inPortBuffer)
67 free(fDeferredInitInfos[i].data);
68 }
69
70 fDeferredInitInfoCount = 0;
71 fError = B_NO_INIT;
72 fPort = NULL;
73 fRequest = NULL;
74 fRequestSize = 0;
75 fPortReservedOffset = 0;
76 }
77
78 // Error
79 status_t
Error() const80 RequestAllocator::Error() const
81 {
82 return fError;
83 }
84
85 // FinishDeferredInit
86 void
FinishDeferredInit()87 RequestAllocator::FinishDeferredInit()
88 {
89 if (fError != B_OK)
90 return;
91 for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
92 DeferredInitInfo& info = fDeferredInitInfos[i];
93 if (info.inPortBuffer) {
94 if (info.size > 0)
95 memcpy((uint8*)fRequest + info.offset, info.data, info.size);
96 free(info.data);
97 }
98 PRINT(("RequestAllocator::FinishDeferredInit(): area: %" B_PRId32 ", "
99 "offset: %" B_PRId32 ", size: %" B_PRId32 "\n", info.area,
100 info.offset, info.size));
101 info.target->SetTo(info.area, info.offset, info.size);
102 }
103 fDeferredInitInfoCount = 0;
104 }
105
106 // AllocateRequest
107 status_t
AllocateRequest(int32 size)108 RequestAllocator::AllocateRequest(int32 size)
109 {
110 if (fError != B_OK)
111 RETURN_ERROR(fError);
112
113 fRequestOffset = (fPortReservedOffset + 7) / 8 * 8;
114
115 if (size < (int32)sizeof(Request)
116 || fRequestOffset + size > fPort->GetCapacity()) {
117 RETURN_ERROR(fError = B_BAD_VALUE);
118 }
119
120 fRequest = (Request*)((uint8*)fPort->GetBuffer() + fRequestOffset);
121 fRequestSize = size;
122 fRequestInPortBuffer = true;
123 fPort->Reserve(fRequestOffset + fRequestSize);
124 return B_OK;
125 }
126
127 // ReadRequest
128 status_t
ReadRequest(bigtime_t timeout)129 RequestAllocator::ReadRequest(bigtime_t timeout)
130 {
131 if (fError != B_OK)
132 RETURN_ERROR(fError);
133
134 // read the message from the port
135 void* message;
136 size_t messageSize;
137 status_t error = fPort->Receive(&message, &messageSize, timeout);
138 if (error != B_OK) {
139 if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
140 RETURN_ERROR(fError = error);
141 return error;
142 }
143
144 // shouldn't be shorter than the base Request
145 if (messageSize < (int32)sizeof(Request)) {
146 free(message);
147 RETURN_ERROR(fError = B_BAD_DATA);
148 }
149
150 // init the request
151 fRequest = (Request*)message;
152 fRequestOffset = 0;
153 fRequestSize = messageSize;
154 fRequestInPortBuffer = false;
155
156 // relocate the request
157 fError = relocate_request(fRequest, fRequestSize, fAllocatedAreas,
158 &fAllocatedAreaCount);
159 RETURN_ERROR(fError);
160 }
161
162 // GetRequest
163 Request*
GetRequest() const164 RequestAllocator::GetRequest() const
165 {
166 return fRequest;
167 }
168
169 // GetRequestSize
170 int32
GetRequestSize() const171 RequestAllocator::GetRequestSize() const
172 {
173 return fRequestSize;
174 }
175
176 // AllocateAddress
177 status_t
AllocateAddress(Address & address,int32 size,int32 align,void ** data,bool deferredInit)178 RequestAllocator::AllocateAddress(Address& address, int32 size, int32 align,
179 void** data, bool deferredInit)
180 {
181 if (fError != B_OK)
182 return fError;
183 if (!fRequest)
184 RETURN_ERROR(B_NO_INIT);
185 if (size < 0)
186 RETURN_ERROR(B_BAD_VALUE);
187 if (fDeferredInitInfoCount >= MAX_REQUEST_ADDRESS_COUNT)
188 RETURN_ERROR(B_BAD_VALUE);
189 // fix the alignment -- valid is 1, 2, 4, 8
190 if (align <= 0 || size == 0 || (align & 0x1))
191 align = 1;
192 else if (align & 0x2)
193 align = 2;
194 else if (align & 0x4)
195 align = 4;
196 else
197 align = 8;
198 // check address location
199 // Currently we only support relocation of addresses inside the
200 // port buffer.
201 int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
202 if (addressOffset < (int32)sizeof(Request)
203 || addressOffset + (int32)sizeof(Address) > fRequestSize) {
204 RETURN_ERROR(B_BAD_VALUE);
205 }
206 // get the next free aligned offset in the port buffer
207 int32 offset = (fRequestSize + align - 1) / align * align;
208 // allocate the data
209 if (fRequestOffset + offset + size <= fPort->GetCapacity()) {
210 // there's enough free space in the port buffer
211 fRequestSize = offset + size;
212 fPort->Reserve(fRequestOffset + fRequestSize);
213 if (deferredInit) {
214 DeferredInitInfo& info
215 = fDeferredInitInfos[fDeferredInitInfoCount];
216 if (size > 0) {
217 info.data = (uint8*)malloc(size);
218 if (!info.data)
219 RETURN_ERROR(B_NO_MEMORY);
220 } else
221 info.data = NULL;
222 info.area = -1;
223 info.offset = offset;
224 info.size = size;
225 info.inPortBuffer = true;
226 info.target = &address;
227 *data = info.data;
228 fDeferredInitInfoCount++;
229 } else {
230 *data = (uint8*)fRequest + offset;
231 address.SetTo(-1, offset, size);
232 }
233 } else {
234 // not enough room in the port's buffer: we need to allocate an area
235 if (fAllocatedAreaCount >= MAX_REQUEST_ADDRESS_COUNT)
236 RETURN_ERROR(B_ERROR);
237 int32 areaSize = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
238 area_id area = create_area("request data", data,
239 #ifdef _KERNEL_MODE
240 B_ANY_KERNEL_ADDRESS,
241 #else
242 B_ANY_ADDRESS,
243 #endif
244 areaSize, B_NO_LOCK,
245 #ifdef _KERNEL_MODE
246 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA
247 #else
248 B_READ_AREA | B_WRITE_AREA
249 #endif
250 );
251 if (area < 0)
252 RETURN_ERROR(area);
253 fAllocatedAreas[fAllocatedAreaCount++] = area;
254 if (deferredInit) {
255 DeferredInitInfo& info
256 = fDeferredInitInfos[fDeferredInitInfoCount];
257 info.data = NULL;
258 info.area = area;
259 info.offset = 0;
260 info.size = size;
261 info.inPortBuffer = false;
262 info.target = &address;
263 fDeferredInitInfoCount++;
264 PRINT((" RequestAllocator::AllocateAddress(): deferred allocated "
265 "area: %" B_PRId32 ", size: %" B_PRId32 " (%" B_PRId32
266 "), data: %p\n", area, size, areaSize, *data));
267 } else
268 address.SetTo(area, 0, size);
269 }
270 return B_OK;
271 }
272
273 // AllocateData
274 status_t
AllocateData(Address & address,const void * data,int32 size,int32 align,bool deferredInit)275 RequestAllocator::AllocateData(Address& address, const void* data, int32 size,
276 int32 align, bool deferredInit)
277 {
278 status_t error = B_OK;
279 if (data != NULL) {
280 void* destination;
281 error = AllocateAddress(address, size, align, &destination,
282 deferredInit);
283 if (error != B_OK)
284 return error;
285 if (size > 0) {
286 #ifndef _KERNEL_MODE
287 memcpy(destination, data, size);
288 #else
289 if (user_memcpy(destination, data, size) < B_OK)
290 return B_BAD_ADDRESS;
291 #endif
292 }
293 } else
294 address.SetTo(-1, 0, 0);
295 return error;
296 }
297
298 // AllocateString
299 status_t
AllocateString(Address & address,const char * data,bool deferredInit)300 RequestAllocator::AllocateString(Address& address, const char* data,
301 bool deferredInit)
302 {
303 int32 size = (data ? strlen(data) + 1 : 0);
304 return AllocateData(address, data, size, 1, deferredInit);
305 }
306
307 // SetAddress
308 /*status_t
309 RequestAllocator::SetAddress(Address& address, void* data, int32 size)
310 {
311 if (fError != B_OK)
312 return fError;
313 if (!fRequest)
314 return (fError = B_NO_INIT);
315 // check address location
316 // Currently we only support relocation of addresses inside the
317 // port buffer.
318 int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
319 if (addressOffset < (int32)sizeof(Request)
320 || addressOffset + (int32)sizeof(Address) > fRequestSize) {
321 return (fError = B_BAD_VALUE);
322 }
323 // if data does itself lie within the port buffer, we store only the
324 // request relative offset
325 int32 inRequestOffset = (uint8*)data - (uint8*)fRequest;
326 if (!data) {
327 address.SetTo(-1, 0, 0);
328 } else if (inRequestOffset >= (int32)sizeof(Request)
329 && inRequestOffset <= fRequestSize) {
330 if (inRequestOffset + size > fRequestSize)
331 return (fError = B_BAD_VALUE);
332 address.SetTo(-1, inRequestOffset, size);
333 } else {
334 // get the area and in-area offset for the address
335 area_id area;
336 int32 offset;
337 fError = get_area_for_address(data, size, &area, &offset);
338 if (fError != B_OK)
339 return fError;
340 // set the address
341 address.SetTo(area, offset, size);
342 }
343 return fError;
344 }*/
345
346