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
19RequestAllocator::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
34RequestAllocator::~RequestAllocator()
35{
36	Uninit();
37}
38
39// Init
40status_t
41RequestAllocator::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
53void
54RequestAllocator::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
79status_t
80RequestAllocator::Error() const
81{
82	return fError;
83}
84
85// FinishDeferredInit
86void
87RequestAllocator::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
107status_t
108RequestAllocator::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
128status_t
129RequestAllocator::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
163Request*
164RequestAllocator::GetRequest() const
165{
166	return fRequest;
167}
168
169// GetRequestSize
170int32
171RequestAllocator::GetRequestSize() const
172{
173	return fRequestSize;
174}
175
176// AllocateAddress
177status_t
178RequestAllocator::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
274status_t
275RequestAllocator::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
299status_t
300RequestAllocator::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
309RequestAllocator::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