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