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 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 34 RequestAllocator::~RequestAllocator() 35 { 36 Uninit(); 37 } 38 39 // Init 40 status_t 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 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 80 RequestAllocator::Error() const 81 { 82 return fError; 83 } 84 85 // FinishDeferredInit 86 void 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 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 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* 164 RequestAllocator::GetRequest() const 165 { 166 return fRequest; 167 } 168 169 // GetRequestSize 170 int32 171 RequestAllocator::GetRequestSize() const 172 { 173 return fRequestSize; 174 } 175 176 // AllocateAddress 177 status_t 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 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 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