unicorn/qemu/util/mmap-alloc.c
Cao jin f2a5ddf5dc
util/mmap-alloc: refactor a little bit for readability
1st mmap returns *ptr* which aligns to host page size,

| size + align |
------------------------------------------
ptr

input param *align* could be 1M, or 2M, or host page size. After
QEMU_ALIGN_UP, offset will >= 0

2nd mmap use flag MAP_FIXED, then it return ptr+offset, or else fail.
If it success, then we will have something like:

| offset | size |
--------------------------------------
ptr ptr1

*ptr1* is what we really want to return, it equals ptr+offset.

Backports commit 6e4c890e15b23f078650499fbde11760b8eccf10 from qemu
2018-03-01 23:55:15 -05:00

85 lines
2.6 KiB
C

/*
* Support for RAM backed by mmaped host memory.
*
* Copyright (c) 2015 Red Hat, Inc.
*
* Authors:
* Michael S. Tsirkin <mst@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* later. See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/mmap-alloc.h"
#include "qemu/host-utils.h"
#include <sys/mman.h>
void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared)
{
/*
* Note: this always allocates at least one extra page of virtual address
* space, even if size is already aligned.
*/
size_t total = size + align;
#if defined(__powerpc64__) && defined(__linux__)
/* On ppc64 mappings in the same segment (aka slice) must share the same
* page size. Since we will be re-allocating part of this segment
* from the supplied fd, we should make sure to use the same page size, to
* this end we mmap the supplied fd. In this case, set MAP_NORESERVE to
* avoid allocating backing store memory.
* We do this unless we are using the system page size, in which case
* anonymous memory is OK.
*/
int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd;
int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE;
void *ptr = mmap(0, total, PROT_NONE, flags | MAP_PRIVATE, anonfd, 0);
#else
void *ptr = mmap(0, total, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
#endif
size_t offset;
void *ptr1;
if (ptr == MAP_FAILED) {
return MAP_FAILED;
}
/* Make sure align is a power of 2 */
assert(is_power_of_2(align));
/* Always align to host page size */
assert(align >= getpagesize());
offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr;
ptr1 = mmap(ptr + offset, size, PROT_READ | PROT_WRITE,
MAP_FIXED |
(fd == -1 ? MAP_ANONYMOUS : 0) |
(shared ? MAP_SHARED : MAP_PRIVATE),
fd, 0);
if (ptr1 == MAP_FAILED) {
munmap(ptr, total);
return MAP_FAILED;
}
if (offset > 0) {
munmap(ptr, offset);
}
/*
* Leave a single PROT_NONE page allocated after the RAM block, to serve as
* a guard page guarding against potential buffer overflows.
*/
total -= offset;
if (total > size + getpagesize()) {
munmap(ptr1 + size + getpagesize(), total - size - getpagesize());
}
return ptr1;
}
void qemu_ram_munmap(void *ptr, size_t size)
{
if (ptr) {
/* Unmap both the RAM block and the guard page */
munmap(ptr, size + getpagesize());
}
}