1// Copyright (C) 2019 The Android Open Source Project 2// Copyright (C) 2019 Google Inc. 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15 16#include <linux/types.h> 17#include <linux/ioctl.h> 18#include <sys/types.h> 19#include <sys/stat.h> 20#include <sys/mman.h> 21#include <sys/ioctl.h> 22#include <fcntl.h> 23#include <unistd.h> 24#include <cstdlib> 25#include <errno.h> 26#include <memory> 27 28#include <log/log.h> 29 30#include "goldfish_address_space.h" 31 32namespace { 33 34struct goldfish_address_space_allocate_block { 35 __u64 size; 36 __u64 offset; 37 __u64 phys_addr; 38}; 39 40struct goldfish_address_space_claim_shared { 41 __u64 offset; 42 __u64 size; 43}; 44 45#define GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC 'G' 46#define GOLDFISH_ADDRESS_SPACE_IOCTL_OP(OP, T) _IOWR(GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC, OP, T) 47#define GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK GOLDFISH_ADDRESS_SPACE_IOCTL_OP(10, struct goldfish_address_space_allocate_block) 48#define GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK GOLDFISH_ADDRESS_SPACE_IOCTL_OP(11, __u64) 49#define GOLDFISH_ADDRESS_SPACE_IOCTL_PING GOLDFISH_ADDRESS_SPACE_IOCTL_OP(12, struct goldfish_address_space_ping) 50#define GOLDFISH_ADDRESS_SPACE_IOCTL_CLAIM_SHARED GOLDFISH_ADDRESS_SPACE_IOCTL_OP(13, struct goldfish_address_space_claim_shared) 51#define GOLDFISH_ADDRESS_SPACE_IOCTL_UNCLAIM_SHARED GOLDFISH_ADDRESS_SPACE_IOCTL_OP(14, __u64) 52 53const char GOLDFISH_ADDRESS_SPACE_DEVICE_NAME[] = "/dev/goldfish_address_space"; 54 55const int HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID = 1; 56const int HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID = 2; 57 58int create_address_space_fd() 59{ 60 return ::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR); 61} 62 63long ioctl_allocate(int fd, struct goldfish_address_space_allocate_block *request) 64{ 65 return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK, request); 66} 67 68long ioctl_deallocate(int fd, uint64_t offset) 69{ 70 return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK, &offset); 71} 72 73long ioctl_ping(int fd, struct goldfish_address_space_ping *request) 74{ 75 return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_PING, request); 76} 77 78long set_address_space_subdevice_type(int fd, uint64_t type) 79{ 80 struct goldfish_address_space_ping request; 81 ::memset(&request, 0, sizeof(request)); 82 request.version = sizeof(request); 83 request.metadata = type; 84 85 long ret = ioctl_ping(fd, &request); 86 if (ret) { 87 return ret; 88 } 89 90 return request.metadata; 91} 92 93long ioctl_claim_shared(int fd, struct goldfish_address_space_claim_shared *request) 94{ 95 return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_CLAIM_SHARED, request); 96} 97 98long ioctl_unclaim_shared(int fd, uint64_t offset) 99{ 100 return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_UNCLAIM_SHARED, &offset); 101} 102 103} // namespace 104 105GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType subdevice) 106 : m_handle(create_address_space_fd()) 107{ 108 if ((subdevice != GoldfishAddressSpaceSubdeviceType::NoSubdevice) && is_opened()) { 109 const long ret = set_address_space_subdevice_type(m_handle, subdevice); 110 if (ret != 0 && ret != subdevice) { // TODO: retire the 'ret != subdevice' check 111 ALOGE("%s: set_address_space_subdevice_type failed for device_type=%lu, ret=%ld", 112 __func__, static_cast<unsigned long>(subdevice), ret); 113 close(); 114 } 115 } 116} 117 118GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider() 119{ 120 if (is_opened()) { 121 ::close(m_handle); 122 } 123} 124 125bool GoldfishAddressSpaceBlockProvider::is_opened() const 126{ 127 return m_handle >= 0; 128} 129 130void GoldfishAddressSpaceBlockProvider::close() 131{ 132 if (is_opened()) { 133 ::close(m_handle); 134 m_handle = -1; 135 } 136} 137 138address_space_handle_t GoldfishAddressSpaceBlockProvider::release() 139{ 140 address_space_handle_t handle = m_handle; 141 m_handle = -1; 142 return handle; 143} 144 145void GoldfishAddressSpaceBlockProvider::closeHandle(address_space_handle_t handle) 146{ 147 ::close(handle); 148} 149 150GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock() 151 : m_handle(-1) 152 , m_mmaped_ptr(NULL) 153 , m_phys_addr(0) 154 , m_host_addr(0) 155 , m_offset(0) 156 , m_size(0) {} 157 158GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock() 159{ 160 destroy(); 161} 162 163GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs) 164{ 165 m_mmaped_ptr = rhs.m_mmaped_ptr; 166 m_phys_addr = rhs.m_phys_addr; 167 m_host_addr = rhs.m_host_addr; 168 m_offset = rhs.m_offset; 169 m_size = rhs.m_size; 170 m_handle = rhs.m_handle; 171 172 return *this; 173} 174 175bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size) 176{ 177 ALOGD("%s: Ask for block of size 0x%llx\n", __func__, 178 (unsigned long long)size); 179 180 destroy(); 181 182 if (!provider->is_opened()) { 183 return false; 184 } 185 186 struct goldfish_address_space_allocate_block request; 187 ::memset(&request, 0, sizeof(request)); 188 request.size = size; 189 190 long res = ioctl_allocate(provider->m_handle, &request); 191 if (res) { 192 return false; 193 } else { 194 m_phys_addr = request.phys_addr; 195 m_offset = request.offset; 196 m_size = request.size; 197 m_handle = provider->m_handle; 198 m_is_shared_mapping = false; 199 200 ALOGD("%s: ioctl allocate returned offset 0x%llx size 0x%llx\n", __func__, 201 (unsigned long long)m_offset, 202 (unsigned long long)m_size); 203 204 return true; 205 } 206} 207 208bool GoldfishAddressSpaceBlock::claimShared(GoldfishAddressSpaceBlockProvider *provider, uint64_t offset, uint64_t size) 209{ 210 ALOGD("%s: Ask to claim region [0x%llx 0x%llx]\n", __func__, 211 (unsigned long long)offset, 212 (unsigned long long)offset + size); 213 214 destroy(); 215 216 if (!provider->is_opened()) { 217 return false; 218 } 219 220 struct goldfish_address_space_claim_shared request; 221 request.offset = offset; 222 request.size = size; 223 long res = ioctl_claim_shared(provider->m_handle, &request); 224 225 if (res) { 226 return false; 227 } 228 229 m_offset = offset; 230 m_size = size; 231 m_handle = provider->m_handle; 232 m_is_shared_mapping = true; 233 234 return true; 235} 236 237uint64_t GoldfishAddressSpaceBlock::physAddr() const 238{ 239 return m_phys_addr; 240} 241 242uint64_t GoldfishAddressSpaceBlock::hostAddr() const 243{ 244 return m_host_addr; 245} 246 247void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr) 248{ 249 if (m_size == 0) { 250 ALOGE("%s: called with zero size\n", __func__); 251 return NULL; 252 } 253 if (m_mmaped_ptr) { 254 ALOGE("'mmap' called for an already mmaped address block"); 255 ::abort(); 256 } 257 258 void *result; 259 const int res = memoryMap(NULL, m_size, m_handle, m_offset, &result); 260 if (res) { 261 ALOGE("%s: host memory map failed with size 0x%llx " 262 "off 0x%llx errno %d\n", 263 __func__, 264 (unsigned long long)m_size, 265 (unsigned long long)m_offset, res); 266 return NULL; 267 } else { 268 m_mmaped_ptr = result; 269 m_host_addr = host_addr; 270 return guestPtr(); 271 } 272} 273 274void *GoldfishAddressSpaceBlock::guestPtr() const 275{ 276 return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1)); 277} 278 279void GoldfishAddressSpaceBlock::destroy() 280{ 281 if (m_mmaped_ptr && m_size) { 282 memoryUnmap(m_mmaped_ptr, m_size); 283 m_mmaped_ptr = NULL; 284 } 285 286 if (m_size) { 287 long res = -EINVAL; 288 289 if (m_is_shared_mapping) { 290 res = ioctl_unclaim_shared(m_handle, m_offset); 291 if (res) { 292 ALOGE("ioctl_unclaim_shared failed, res=%ld", res); 293 ::abort(); 294 } 295 } else { 296 res = ioctl_deallocate(m_handle, m_offset); 297 if (res) { 298 ALOGE("ioctl_deallocate failed, res=%ld", res); 299 ::abort(); 300 } 301 } 302 303 m_is_shared_mapping = false; 304 305 m_phys_addr = 0; 306 m_host_addr = 0; 307 m_offset = 0; 308 m_size = 0; 309 } 310} 311 312void GoldfishAddressSpaceBlock::release() 313{ 314 m_handle = -1; 315 m_mmaped_ptr = NULL; 316 m_phys_addr = 0; 317 m_host_addr = 0; 318 m_offset = 0; 319 m_size = 0; 320} 321 322int GoldfishAddressSpaceBlock::memoryMap(void *addr, 323 size_t len, 324 address_space_handle_t fd, 325 uint64_t off, 326 void** dst) { 327 void* ptr = ::mmap64(addr, len, PROT_WRITE, MAP_SHARED, fd, off); 328 if (MAP_FAILED == ptr) { 329 return errno; 330 } else { 331 *dst = ptr; 332 return 0; 333 } 334} 335 336void GoldfishAddressSpaceBlock::memoryUnmap(void *ptr, size_t size) 337{ 338 ::munmap(ptr, size); 339} 340 341GoldfishAddressSpaceHostMemoryAllocator::GoldfishAddressSpaceHostMemoryAllocator(bool useSharedSlots) 342 : m_provider(useSharedSlots 343 ? GoldfishAddressSpaceSubdeviceType::SharedSlotsHostMemoryAllocator 344 : GoldfishAddressSpaceSubdeviceType::HostMemoryAllocator), 345 m_useSharedSlots(useSharedSlots) 346{} 347 348bool GoldfishAddressSpaceHostMemoryAllocator::is_opened() const { return m_provider.is_opened(); } 349 350long GoldfishAddressSpaceHostMemoryAllocator::hostMalloc(GoldfishAddressSpaceBlock *block, size_t size) 351{ 352 if (size == 0) { 353 return -EINVAL; 354 } 355 if (block->size() > 0) { 356 return -EINVAL; 357 } 358 if (!m_provider.is_opened()) { 359 return -ENODEV; 360 } 361 362 struct goldfish_address_space_ping request; 363 if (m_useSharedSlots) { 364 // shared memory slots are supported 365 ::memset(&request, 0, sizeof(request)); 366 request.version = sizeof(request); 367 request.size = size; 368 request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID; 369 370 long ret = ioctl_ping(m_provider.m_handle, &request); 371 if (ret) { 372 return ret; 373 } 374 ret = static_cast<long>(request.metadata); 375 if (ret) { 376 return ret; 377 } 378 379 block->claimShared(&m_provider, request.offset, request.size); 380 } else { 381 // shared memory slots are not supported 382 if (!block->allocate(&m_provider, size)) { 383 return -ENOMEM; 384 } 385 386 ::memset(&request, 0, sizeof(request)); 387 request.version = sizeof(request); 388 request.offset = block->offset(); 389 request.size = block->size(); 390 request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID; 391 392 long ret = ioctl_ping(m_provider.m_handle, &request); 393 if (ret) { 394 return ret; 395 } 396 ret = static_cast<long>(request.metadata); 397 if (ret) { 398 return ret; 399 } 400 } 401 402 block->mmap(0); 403 return 0; 404} 405 406void GoldfishAddressSpaceHostMemoryAllocator::hostFree(GoldfishAddressSpaceBlock *block) 407{ 408 if (block->size() == 0) { 409 return; 410 } 411 412 if (!m_provider.is_opened()) { 413 ALOGE("%s: device is not available", __func__); 414 ::abort(); 415 } 416 417 if (block->guestPtr()) { 418 struct goldfish_address_space_ping request; 419 ::memset(&request, 0, sizeof(request)); 420 request.version = sizeof(request); 421 request.offset = block->offset(); 422 request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID; 423 424 const long ret = ioctl_ping(m_provider.m_handle, &request); 425 if (ret) { 426 ALOGE("%s: ioctl_ping failed, ret=%ld", __func__, ret); 427 ::abort(); 428 } 429 } 430 431 block->replace(NULL); 432} 433 434address_space_handle_t goldfish_address_space_open() { 435 return ::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR); 436} 437 438void goldfish_address_space_close(address_space_handle_t handle) { 439 ::close(handle); 440} 441 442bool goldfish_address_space_allocate( 443 address_space_handle_t handle, 444 size_t size, uint64_t* phys_addr, uint64_t* offset) { 445 446 struct goldfish_address_space_allocate_block request; 447 ::memset(&request, 0, sizeof(request)); 448 request.size = size; 449 450 long res = ioctl_allocate(handle, &request); 451 452 if (res) return false; 453 454 *phys_addr = request.phys_addr; 455 *offset = request.offset; 456 return true; 457} 458 459bool goldfish_address_space_free( 460 address_space_handle_t handle, uint64_t offset) { 461 462 long res = ioctl_deallocate(handle, offset); 463 464 if (res) { 465 ALOGE("ioctl_deallocate failed, res=%ld", res); 466 ::abort(); 467 } 468 469 return true; 470} 471 472bool goldfish_address_space_claim_shared( 473 address_space_handle_t handle, uint64_t offset, uint64_t size) { 474 475 struct goldfish_address_space_claim_shared request; 476 request.offset = offset; 477 request.size = size; 478 long res = ioctl_claim_shared(handle, &request); 479 480 if (res) return false; 481 482 return true; 483} 484 485bool goldfish_address_space_unclaim_shared( 486 address_space_handle_t handle, uint64_t offset) { 487 long res = ioctl_unclaim_shared(handle, offset); 488 if (res) { 489 ALOGE("ioctl_unclaim_shared failed, res=%ld", res); 490 ::abort(); 491 } 492 493 return true; 494} 495 496// pgoff is the offset into the page to return in the result 497void* goldfish_address_space_map( 498 address_space_handle_t handle, 499 uint64_t offset, uint64_t size, 500 uint64_t pgoff) { 501 502 void* res = ::mmap64(0, size, PROT_WRITE, MAP_SHARED, handle, offset); 503 504 if (res == MAP_FAILED) { 505 ALOGE("%s: failed to map. errno: %d\n", __func__, errno); 506 return 0; 507 } 508 509 return (void*)(((char*)res) + (uintptr_t)(pgoff & (PAGE_SIZE - 1))); 510} 511 512void goldfish_address_space_unmap(void* ptr, uint64_t size) { 513 void* pagePtr = (void*)(((uintptr_t)ptr) & ~(PAGE_SIZE - 1)); 514 ::munmap(pagePtr, size); 515} 516 517bool goldfish_address_space_set_subdevice_type( 518 address_space_handle_t handle, GoldfishAddressSpaceSubdeviceType type, 519 address_space_handle_t* handle_out) { 520 struct goldfish_address_space_ping request; 521 request.metadata = (uint64_t)type; 522 *handle_out = handle; 523 return goldfish_address_space_ping(handle, &request); 524} 525 526bool goldfish_address_space_ping( 527 address_space_handle_t handle, 528 struct goldfish_address_space_ping* ping) { 529 long res = ioctl_ping(handle, ping); 530 531 if (res) { 532 ALOGE("%s: ping failed: errno: %d\n", __func__, errno); 533 return false; 534 } 535 536 return true; 537} 538