| /* | |
| * Copyright (C) 2010 Piotr JaroszyĆski <p.jaroszynski@gmail.com> | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU General Public License as | |
| * published by the Free Software Foundation; either version 2 of the | |
| * License, or any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, but | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| */ | |
| FILE_LICENCE(GPL2_OR_LATER); | |
| #include <valgrind/memcheck.h> | |
| /** @file | |
| * | |
| * iPXE user memory allocation API for linux | |
| * | |
| */ | |
| #include <assert.h> | |
| #include <ipxe/umalloc.h> | |
| #include <ipxe/linux_api.h> | |
| /** Poison to make the metadata more unique */ | |
| #define POISON 0xa5a5a5a5 | |
| #define min(a,b) (((a)<(b))?(a):(b)) | |
| /** Metadata stored at the beginning of all allocations */ | |
| struct metadata | |
| { | |
| unsigned poison; | |
| size_t size; | |
| }; | |
| #define SIZE_MD (sizeof(struct metadata)) | |
| /** | |
| * Reallocate external memory | |
| * | |
| * @v old_ptr Memory previously allocated by umalloc(), or NULL | |
| * @v new_size Requested size | |
| * @ret new_ptr Allocated memory, or NULL | |
| * | |
| * Calling realloc() with a new size of zero is a valid way to free a | |
| * memory block. | |
| */ | |
| static void * linux_realloc(void *ptr, size_t size) | |
| { | |
| struct metadata md = {0, 0}; | |
| struct metadata * mdptr = NULL; | |
| DBG2("linux_realloc(%p, %zd)\n", ptr, size); | |
| /* Check whether we have a valid pointer */ | |
| if (ptr != NULL && ptr != NOWHERE) { | |
| mdptr = ptr - SIZE_MD; | |
| VALGRIND_MAKE_MEM_DEFINED(mdptr, SIZE_MD); | |
| md = *mdptr; | |
| VALGRIND_MAKE_MEM_NOACCESS(mdptr, SIZE_MD); | |
| /* Check for poison in the metadata */ | |
| if (md.poison != POISON) { | |
| DBG("linux_realloc bad poison: 0x%x (expected 0x%x)\n", md.poison, POISON); | |
| return NULL; | |
| } | |
| } else { | |
| /* Handle NOWHERE as NULL */ | |
| ptr = NULL; | |
| } | |
| /* | |
| * At this point, ptr is either NULL or pointing to a region allocated by us. | |
| * In the latter case mdptr is pointing to a valid metadata, otherwise it is NULL. | |
| */ | |
| /* Handle deallocation or allocation of size 0 */ | |
| if (size == 0) { | |
| if (mdptr) { | |
| if (linux_munmap(mdptr, md.size)) | |
| DBG("linux_realloc munmap failed: %s\n", linux_strerror(linux_errno)); | |
| VALGRIND_FREELIKE_BLOCK(ptr, sizeof(*mdptr)); | |
| } | |
| return NOWHERE; | |
| } | |
| if (ptr) { | |
| char *vbits = NULL; | |
| if (RUNNING_ON_VALGRIND > 0) | |
| vbits = linux_realloc(NULL, min(size, md.size)); | |
| /* prevent an unused variable warning when building w/o valgrind support */ | |
| #ifndef NVALGRIND | |
| VALGRIND_GET_VBITS(ptr, vbits, min(size, md.size)); | |
| #endif | |
| VALGRIND_FREELIKE_BLOCK(ptr, SIZE_MD); | |
| mdptr = linux_mremap(mdptr, md.size + SIZE_MD, size + SIZE_MD, MREMAP_MAYMOVE); | |
| if (mdptr == MAP_FAILED) { | |
| DBG("linux_realloc mremap failed: %s\n", linux_strerror(linux_errno)); | |
| return NULL; | |
| } | |
| ptr = ((void *)mdptr) + SIZE_MD; | |
| VALGRIND_MALLOCLIKE_BLOCK(ptr, size, SIZE_MD, 0); | |
| /* prevent an unused variable warning when building w/o valgrind support */ | |
| #ifndef NVALGRIND | |
| VALGRIND_SET_VBITS(ptr, vbits, min(size, md.size)); | |
| #endif | |
| if (RUNNING_ON_VALGRIND > 0) | |
| linux_realloc(vbits, 0); | |
| } else { | |
| mdptr = linux_mmap(NULL, size + SIZE_MD, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
| if (mdptr == MAP_FAILED) { | |
| DBG("linux_realloc mmap failed: %s\n", linux_strerror(linux_errno)); | |
| return NULL; | |
| } | |
| ptr = ((void *)mdptr) + SIZE_MD; | |
| VALGRIND_MALLOCLIKE_BLOCK(ptr, size, SIZE_MD, 0); | |
| } | |
| /* Update the metadata */ | |
| VALGRIND_MAKE_MEM_DEFINED(mdptr, SIZE_MD); | |
| mdptr->poison = POISON; | |
| mdptr->size = size; | |
| VALGRIND_MAKE_MEM_NOACCESS(mdptr, SIZE_MD); | |
| // VALGRIND_MALLOCLIKE_BLOCK ignores redzones currently, make our own | |
| VALGRIND_MAKE_MEM_NOACCESS(ptr + size, SIZE_MD); | |
| return ptr; | |
| } | |
| PROVIDE_UMALLOC(linux, urealloc, linux_realloc); |