mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
virtio,vhost,pci,pc: features, cleanups SRAT tables for DIMM devices new virtio net flags for speed/duplex post-copy migration support in vhost cleanups in pci Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Tue 20 Mar 2018 14:40:43 GMT # gpg: using RSA key 281F0DB8D28D5469 # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * remotes/mst/tags/for_upstream: (51 commits) postcopy shared docs libvhost-user: Claim support for postcopy postcopy: Allow shared memory vhost: Huge page align and merge vhost+postcopy: Wire up POSTCOPY_END notify vhost-user: Add VHOST_USER_POSTCOPY_END message libvhost-user: mprotect & madvises for postcopy vhost+postcopy: Call wakeups vhost+postcopy: Add vhost waker postcopy: postcopy_notify_shared_wake postcopy: helper for waking shared vhost+postcopy: Resolve client address postcopy-ram: add a stub for postcopy_request_shared_page vhost+postcopy: Helper to send requests to source for shared pages vhost+postcopy: Stash RAMBlock and offset vhost+postcopy: Send address back to qemu libvhost-user+postcopy: Register new regions with the ufd migration/ram: ramblock_recv_bitmap_test_byte_offset postcopy+vhost-user: Split set_mem_table for postcopy vhost+postcopy: Transmit 'listen' to slave ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org> # Conflicts: # scripts/update-linux-headers.sh
This commit is contained in:
@@ -777,7 +777,6 @@ bepo cz
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
acpi-dsdt.aml \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
@@ -1048,6 +1047,9 @@ endif
|
||||
include $(SRC_PATH)/tests/docker/Makefile.include
|
||||
include $(SRC_PATH)/tests/vm/Makefile.include
|
||||
|
||||
printgen:
|
||||
@echo $(GENERATED_FILES)
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo 'Generic targets:'
|
||||
|
||||
@@ -26,9 +26,20 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/mman.h>
|
||||
#include "qemu/compiler.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/vhost.h>
|
||||
|
||||
#include "qemu/compiler.h"
|
||||
#ifdef __NR_userfaultfd
|
||||
#include <linux/userfaultfd.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#include "qemu/atomic.h"
|
||||
|
||||
#include "libvhost-user.h"
|
||||
@@ -86,6 +97,9 @@ vu_request_to_string(unsigned int req)
|
||||
REQ(VHOST_USER_SET_VRING_ENDIAN),
|
||||
REQ(VHOST_USER_GET_CONFIG),
|
||||
REQ(VHOST_USER_SET_CONFIG),
|
||||
REQ(VHOST_USER_POSTCOPY_ADVISE),
|
||||
REQ(VHOST_USER_POSTCOPY_LISTEN),
|
||||
REQ(VHOST_USER_POSTCOPY_END),
|
||||
REQ(VHOST_USER_MAX),
|
||||
};
|
||||
#undef REQ
|
||||
@@ -171,6 +185,35 @@ vmsg_close_fds(VhostUserMsg *vmsg)
|
||||
}
|
||||
}
|
||||
|
||||
/* A test to see if we have userfault available */
|
||||
static bool
|
||||
have_userfault(void)
|
||||
{
|
||||
#if defined(__linux__) && defined(__NR_userfaultfd) &&\
|
||||
defined(UFFD_FEATURE_MISSING_SHMEM) &&\
|
||||
defined(UFFD_FEATURE_MISSING_HUGETLBFS)
|
||||
/* Now test the kernel we're running on really has the features */
|
||||
int ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
||||
struct uffdio_api api_struct;
|
||||
if (ufd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
api_struct.api = UFFD_API;
|
||||
api_struct.features = UFFD_FEATURE_MISSING_SHMEM |
|
||||
UFFD_FEATURE_MISSING_HUGETLBFS;
|
||||
if (ioctl(ufd, UFFDIO_API, &api_struct)) {
|
||||
close(ufd);
|
||||
return false;
|
||||
}
|
||||
close(ufd);
|
||||
return true;
|
||||
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_message_read(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -245,6 +288,31 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *p = (uint8_t *)vmsg;
|
||||
char control[CMSG_SPACE(VHOST_MEMORY_MAX_NREGIONS * sizeof(int))] = { };
|
||||
struct iovec iov = {
|
||||
.iov_base = (char *)vmsg,
|
||||
.iov_len = VHOST_USER_HDR_SIZE,
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = control,
|
||||
};
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
memset(control, 0, sizeof(control));
|
||||
assert(vmsg->fd_num <= VHOST_MEMORY_MAX_NREGIONS);
|
||||
if (vmsg->fd_num > 0) {
|
||||
size_t fdsize = vmsg->fd_num * sizeof(int);
|
||||
msg.msg_controllen = CMSG_SPACE(fdsize);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(fdsize);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
memcpy(CMSG_DATA(cmsg), vmsg->fds, fdsize);
|
||||
} else {
|
||||
msg.msg_controllen = 0;
|
||||
}
|
||||
|
||||
/* Set the version in the flags when sending the reply */
|
||||
vmsg->flags &= ~VHOST_USER_VERSION_MASK;
|
||||
@@ -252,7 +320,7 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
||||
vmsg->flags |= VHOST_USER_REPLY_MASK;
|
||||
|
||||
do {
|
||||
rc = write(conn_fd, p, VHOST_USER_HDR_SIZE);
|
||||
rc = sendmsg(conn_fd, &msg, 0);
|
||||
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
do {
|
||||
@@ -345,6 +413,7 @@ vu_get_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
}
|
||||
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
DPRINT("Sending back to guest u64: 0x%016"PRIx64"\n", vmsg->payload.u64);
|
||||
|
||||
@@ -409,6 +478,148 @@ vu_reset_device_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
int i;
|
||||
VhostUserMemory *memory = &vmsg->payload.memory;
|
||||
dev->nregions = memory->nregions;
|
||||
|
||||
DPRINT("Nregions: %d\n", memory->nregions);
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
void *mmap_addr;
|
||||
VhostUserMemoryRegion *msg_region = &memory->regions[i];
|
||||
VuDevRegion *dev_region = &dev->regions[i];
|
||||
|
||||
DPRINT("Region %d\n", i);
|
||||
DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n",
|
||||
msg_region->guest_phys_addr);
|
||||
DPRINT(" memory_size: 0x%016"PRIx64"\n",
|
||||
msg_region->memory_size);
|
||||
DPRINT(" userspace_addr 0x%016"PRIx64"\n",
|
||||
msg_region->userspace_addr);
|
||||
DPRINT(" mmap_offset 0x%016"PRIx64"\n",
|
||||
msg_region->mmap_offset);
|
||||
|
||||
dev_region->gpa = msg_region->guest_phys_addr;
|
||||
dev_region->size = msg_region->memory_size;
|
||||
dev_region->qva = msg_region->userspace_addr;
|
||||
dev_region->mmap_offset = msg_region->mmap_offset;
|
||||
|
||||
/* We don't use offset argument of mmap() since the
|
||||
* mapped address has to be page aligned, and we use huge
|
||||
* pages.
|
||||
* In postcopy we're using PROT_NONE here to catch anyone
|
||||
* accessing it before we userfault
|
||||
*/
|
||||
mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset,
|
||||
PROT_NONE, MAP_SHARED,
|
||||
vmsg->fds[i], 0);
|
||||
|
||||
if (mmap_addr == MAP_FAILED) {
|
||||
vu_panic(dev, "region mmap error: %s", strerror(errno));
|
||||
} else {
|
||||
dev_region->mmap_addr = (uint64_t)(uintptr_t)mmap_addr;
|
||||
DPRINT(" mmap_addr: 0x%016"PRIx64"\n",
|
||||
dev_region->mmap_addr);
|
||||
}
|
||||
|
||||
/* Return the address to QEMU so that it can translate the ufd
|
||||
* fault addresses back.
|
||||
*/
|
||||
msg_region->userspace_addr = (uintptr_t)(mmap_addr +
|
||||
dev_region->mmap_offset);
|
||||
close(vmsg->fds[i]);
|
||||
}
|
||||
|
||||
/* Send the message back to qemu with the addresses filled in */
|
||||
vmsg->fd_num = 0;
|
||||
if (!vu_message_write(dev, dev->sock, vmsg)) {
|
||||
vu_panic(dev, "failed to respond to set-mem-table for postcopy");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wait for QEMU to confirm that it's registered the handler for the
|
||||
* faults.
|
||||
*/
|
||||
if (!vu_message_read(dev, dev->sock, vmsg) ||
|
||||
vmsg->size != sizeof(vmsg->payload.u64) ||
|
||||
vmsg->payload.u64 != 0) {
|
||||
vu_panic(dev, "failed to receive valid ack for postcopy set-mem-table");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* OK, now we can go and register the memory and generate faults */
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
VuDevRegion *dev_region = &dev->regions[i];
|
||||
int ret;
|
||||
#ifdef UFFDIO_REGISTER
|
||||
/* We should already have an open ufd. Mark each memory
|
||||
* range as ufd.
|
||||
* Discard any mapping we have here; note I can't use MADV_REMOVE
|
||||
* or fallocate to make the hole since I don't want to lose
|
||||
* data that's already arrived in the shared process.
|
||||
* TODO: How to do hugepage
|
||||
*/
|
||||
ret = madvise((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
MADV_DONTNEED);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"%s: Failed to madvise(DONTNEED) region %d: %s\n",
|
||||
__func__, i, strerror(errno));
|
||||
}
|
||||
/* Turn off transparent hugepages so we dont get lose wakeups
|
||||
* in neighbouring pages.
|
||||
* TODO: Turn this backon later.
|
||||
*/
|
||||
ret = madvise((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
MADV_NOHUGEPAGE);
|
||||
if (ret) {
|
||||
/* Note: This can happen legally on kernels that are configured
|
||||
* without madvise'able hugepages
|
||||
*/
|
||||
fprintf(stderr,
|
||||
"%s: Failed to madvise(NOHUGEPAGE) region %d: %s\n",
|
||||
__func__, i, strerror(errno));
|
||||
}
|
||||
struct uffdio_register reg_struct;
|
||||
reg_struct.range.start = (uintptr_t)dev_region->mmap_addr;
|
||||
reg_struct.range.len = dev_region->size + dev_region->mmap_offset;
|
||||
reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING;
|
||||
|
||||
if (ioctl(dev->postcopy_ufd, UFFDIO_REGISTER, ®_struct)) {
|
||||
vu_panic(dev, "%s: Failed to userfault region %d "
|
||||
"@%p + size:%zx offset: %zx: (ufd=%d)%s\n",
|
||||
__func__, i,
|
||||
dev_region->mmap_addr,
|
||||
dev_region->size, dev_region->mmap_offset,
|
||||
dev->postcopy_ufd, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (!(reg_struct.ioctls & ((__u64)1 << _UFFDIO_COPY))) {
|
||||
vu_panic(dev, "%s Region (%d) doesn't support COPY",
|
||||
__func__, i);
|
||||
return false;
|
||||
}
|
||||
DPRINT("%s: region %d: Registered userfault for %llx + %llx\n",
|
||||
__func__, i, reg_struct.range.start, reg_struct.range.len);
|
||||
/* Now it's registered we can let the client at it */
|
||||
if (mprotect((void *)dev_region->mmap_addr,
|
||||
dev_region->size + dev_region->mmap_offset,
|
||||
PROT_READ | PROT_WRITE)) {
|
||||
vu_panic(dev, "failed to mprotect region %d for postcopy (%s)",
|
||||
i, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
/* TODO: Stash 'zero' support flags somewhere */
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -425,6 +636,10 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
}
|
||||
dev->nregions = memory->nregions;
|
||||
|
||||
if (dev->postcopy_listening) {
|
||||
return vu_set_mem_table_exec_postcopy(dev, vmsg);
|
||||
}
|
||||
|
||||
DPRINT("Nregions: %d\n", memory->nregions);
|
||||
for (i = 0; i < dev->nregions; i++) {
|
||||
void *mmap_addr;
|
||||
@@ -500,6 +715,7 @@ vu_set_log_base_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
dev->log_size = log_mmap_size;
|
||||
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -752,12 +968,17 @@ vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD |
|
||||
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ;
|
||||
|
||||
if (have_userfault()) {
|
||||
features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT;
|
||||
}
|
||||
|
||||
if (dev->iface->get_protocol_features) {
|
||||
features |= dev->iface->get_protocol_features(dev);
|
||||
}
|
||||
|
||||
vmsg->payload.u64 = features;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->fd_num = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -856,6 +1077,77 @@ vu_set_config(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_advise(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
dev->postcopy_ufd = -1;
|
||||
#ifdef UFFDIO_API
|
||||
struct uffdio_api api_struct;
|
||||
|
||||
dev->postcopy_ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
|
||||
vmsg->size = 0;
|
||||
#endif
|
||||
|
||||
if (dev->postcopy_ufd == -1) {
|
||||
vu_panic(dev, "Userfaultfd not available: %s", strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef UFFDIO_API
|
||||
api_struct.api = UFFD_API;
|
||||
api_struct.features = 0;
|
||||
if (ioctl(dev->postcopy_ufd, UFFDIO_API, &api_struct)) {
|
||||
vu_panic(dev, "Failed UFFDIO_API: %s", strerror(errno));
|
||||
close(dev->postcopy_ufd);
|
||||
dev->postcopy_ufd = -1;
|
||||
goto out;
|
||||
}
|
||||
/* TODO: Stash feature flags somewhere */
|
||||
#endif
|
||||
|
||||
out:
|
||||
/* Return a ufd to the QEMU */
|
||||
vmsg->fd_num = 1;
|
||||
vmsg->fds[0] = dev->postcopy_ufd;
|
||||
return true; /* = send a reply */
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_listen(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
vmsg->payload.u64 = -1;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
|
||||
if (dev->nregions) {
|
||||
vu_panic(dev, "Regions already registered at postcopy-listen");
|
||||
return true;
|
||||
}
|
||||
dev->postcopy_listening = true;
|
||||
|
||||
vmsg->flags = VHOST_USER_VERSION | VHOST_USER_REPLY_MASK;
|
||||
vmsg->payload.u64 = 0; /* Success */
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_set_postcopy_end(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
DPRINT("%s: Entry\n", __func__);
|
||||
dev->postcopy_listening = false;
|
||||
if (dev->postcopy_ufd > 0) {
|
||||
close(dev->postcopy_ufd);
|
||||
dev->postcopy_ufd = -1;
|
||||
DPRINT("%s: Done close\n", __func__);
|
||||
}
|
||||
|
||||
vmsg->fd_num = 0;
|
||||
vmsg->payload.u64 = 0;
|
||||
vmsg->size = sizeof(vmsg->payload.u64);
|
||||
vmsg->flags = VHOST_USER_VERSION | VHOST_USER_REPLY_MASK;
|
||||
DPRINT("%s: exit\n", __func__);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
@@ -927,6 +1219,12 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg)
|
||||
return vu_set_config(dev, vmsg);
|
||||
case VHOST_USER_NONE:
|
||||
break;
|
||||
case VHOST_USER_POSTCOPY_ADVISE:
|
||||
return vu_set_postcopy_advise(dev, vmsg);
|
||||
case VHOST_USER_POSTCOPY_LISTEN:
|
||||
return vu_set_postcopy_listen(dev, vmsg);
|
||||
case VHOST_USER_POSTCOPY_END:
|
||||
return vu_set_postcopy_end(dev, vmsg);
|
||||
default:
|
||||
vmsg_close_fds(vmsg);
|
||||
vu_panic(dev, "Unhandled request: %d", vmsg->request);
|
||||
|
||||
@@ -48,6 +48,8 @@ enum VhostUserProtocolFeature {
|
||||
VHOST_USER_PROTOCOL_F_NET_MTU = 4,
|
||||
VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5,
|
||||
VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6,
|
||||
VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7,
|
||||
VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
|
||||
|
||||
VHOST_USER_PROTOCOL_F_MAX
|
||||
};
|
||||
@@ -81,6 +83,11 @@ typedef enum VhostUserRequest {
|
||||
VHOST_USER_SET_VRING_ENDIAN = 23,
|
||||
VHOST_USER_GET_CONFIG = 24,
|
||||
VHOST_USER_SET_CONFIG = 25,
|
||||
VHOST_USER_CREATE_CRYPTO_SESSION = 26,
|
||||
VHOST_USER_CLOSE_CRYPTO_SESSION = 27,
|
||||
VHOST_USER_POSTCOPY_ADVISE = 28,
|
||||
VHOST_USER_POSTCOPY_LISTEN = 29,
|
||||
VHOST_USER_POSTCOPY_END = 30,
|
||||
VHOST_USER_MAX
|
||||
} VhostUserRequest;
|
||||
|
||||
@@ -277,6 +284,10 @@ struct VuDev {
|
||||
* re-initialize */
|
||||
vu_panic_cb panic;
|
||||
const VuDevIface *iface;
|
||||
|
||||
/* Postcopy data */
|
||||
int postcopy_ufd;
|
||||
bool postcopy_listening;
|
||||
};
|
||||
|
||||
typedef struct VuVirtqElement {
|
||||
|
||||
@@ -577,3 +577,44 @@ Postcopy now works with hugetlbfs backed memory:
|
||||
hugepages works well, however 1GB hugepages are likely to be problematic
|
||||
since it takes ~1 second to transfer a 1GB hugepage across a 10Gbps link,
|
||||
and until the full page is transferred the destination thread is blocked.
|
||||
|
||||
Postcopy with shared memory
|
||||
---------------------------
|
||||
|
||||
Postcopy migration with shared memory needs explicit support from the other
|
||||
processes that share memory and from QEMU. There are restrictions on the type of
|
||||
memory that userfault can support shared.
|
||||
|
||||
The Linux kernel userfault support works on `/dev/shm` memory and on `hugetlbfs`
|
||||
(although the kernel doesn't provide an equivalent to `madvise(MADV_DONTNEED)`
|
||||
for hugetlbfs which may be a problem in some configurations).
|
||||
|
||||
The vhost-user code in QEMU supports clients that have Postcopy support,
|
||||
and the `vhost-user-bridge` (in `tests/`) and the DPDK package have changes
|
||||
to support postcopy.
|
||||
|
||||
The client needs to open a userfaultfd and register the areas
|
||||
of memory that it maps with userfault. The client must then pass the
|
||||
userfaultfd back to QEMU together with a mapping table that allows
|
||||
fault addresses in the clients address space to be converted back to
|
||||
RAMBlock/offsets. The client's userfaultfd is added to the postcopy
|
||||
fault-thread and page requests are made on behalf of the client by QEMU.
|
||||
QEMU performs 'wake' operations on the client's userfaultfd to allow it
|
||||
to continue after a page has arrived.
|
||||
|
||||
.. note::
|
||||
There are two future improvements that would be nice:
|
||||
a) Some way to make QEMU ignorant of the addresses in the clients
|
||||
address space
|
||||
b) Avoiding the need for QEMU to perform ufd-wake calls after the
|
||||
pages have arrived
|
||||
|
||||
Retro-fitting postcopy to existing clients is possible:
|
||||
a) A mechanism is needed for the registration with userfault as above,
|
||||
and the registration needs to be coordinated with the phases of
|
||||
postcopy. In vhost-user extra messages are added to the existing
|
||||
control channel.
|
||||
b) Any thread that can block due to guest memory accesses must be
|
||||
identified and the implication understood; for example if the
|
||||
guest memory access is made while holding a lock then all other
|
||||
threads waiting for that lock will also be blocked.
|
||||
|
||||
@@ -290,6 +290,15 @@ Once the source has finished migration, rings will be stopped by
|
||||
the source. No further update must be done before rings are
|
||||
restarted.
|
||||
|
||||
In postcopy migration the slave is started before all the memory has been
|
||||
received from the source host, and care must be taken to avoid accessing pages
|
||||
that have yet to be received. The slave opens a 'userfault'-fd and registers
|
||||
the memory with it; this fd is then passed back over to the master.
|
||||
The master services requests on the userfaultfd for pages that are accessed
|
||||
and when the page is available it performs WAKE ioctl's on the userfaultfd
|
||||
to wake the stalled slave. The client indicates support for this via the
|
||||
VHOST_USER_PROTOCOL_F_PAGEFAULT feature.
|
||||
|
||||
Memory access
|
||||
-------------
|
||||
|
||||
@@ -369,6 +378,7 @@ Protocol features
|
||||
#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5
|
||||
#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6
|
||||
#define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7
|
||||
#define VHOST_USER_PROTOCOL_F_PAGEFAULT 8
|
||||
|
||||
Master message types
|
||||
--------------------
|
||||
@@ -445,12 +455,21 @@ Master message types
|
||||
Id: 5
|
||||
Equivalent ioctl: VHOST_SET_MEM_TABLE
|
||||
Master payload: memory regions description
|
||||
Slave payload: (postcopy only) memory regions description
|
||||
|
||||
Sets the memory map regions on the slave so it can translate the vring
|
||||
addresses. In the ancillary data there is an array of file descriptors
|
||||
for each memory mapped region. The size and ordering of the fds matches
|
||||
the number and ordering of memory regions.
|
||||
|
||||
When VHOST_USER_POSTCOPY_LISTEN has been received, SET_MEM_TABLE replies with
|
||||
the bases of the memory mapped regions to the master. The slave must
|
||||
have mmap'd the regions but not yet accessed them and should not yet generate
|
||||
a userfault event. Note NEED_REPLY_MASK is not set in this case.
|
||||
QEMU will then reply back to the list of mappings with an empty
|
||||
VHOST_USER_SET_MEM_TABLE as an acknowledgment; only upon reception of this
|
||||
message may the guest start accessing the memory and generating faults.
|
||||
|
||||
* VHOST_USER_SET_LOG_BASE
|
||||
|
||||
Id: 6
|
||||
@@ -689,6 +708,39 @@ Master message types
|
||||
feature has been successfully negotiated.
|
||||
It's a required feature for crypto devices.
|
||||
|
||||
* VHOST_USER_POSTCOPY_ADVISE
|
||||
Id: 28
|
||||
Master payload: N/A
|
||||
Slave payload: userfault fd
|
||||
|
||||
When VHOST_USER_PROTOCOL_F_PAGEFAULT is supported, the
|
||||
master advises slave that a migration with postcopy enabled is underway,
|
||||
the slave must open a userfaultfd for later use.
|
||||
Note that at this stage the migration is still in precopy mode.
|
||||
|
||||
* VHOST_USER_POSTCOPY_LISTEN
|
||||
Id: 29
|
||||
Master payload: N/A
|
||||
|
||||
Master advises slave that a transition to postcopy mode has happened.
|
||||
The slave must ensure that shared memory is registered with userfaultfd
|
||||
to cause faulting of non-present pages.
|
||||
|
||||
This is always sent sometime after a VHOST_USER_POSTCOPY_ADVISE, and
|
||||
thus only when VHOST_USER_PROTOCOL_F_PAGEFAULT is supported.
|
||||
|
||||
* VHOST_USER_POSTCOPY_END
|
||||
Id: 30
|
||||
Slave payload: u64
|
||||
|
||||
Master advises that postcopy migration has now completed. The
|
||||
slave must disable the userfaultfd. The response is an acknowledgement
|
||||
only.
|
||||
When VHOST_USER_PROTOCOL_F_PAGEFAULT is supported, this message
|
||||
is sent at the end of the migration, after VHOST_USER_POSTCOPY_LISTEN
|
||||
was previously sent.
|
||||
The value returned is an error indication; 0 is success.
|
||||
|
||||
Slave message types
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -99,6 +99,11 @@ static MemoryRegion io_mem_unassigned;
|
||||
*/
|
||||
#define RAM_RESIZEABLE (1 << 2)
|
||||
|
||||
/* UFFDIO_ZEROPAGE is available on this RAMBlock to atomically
|
||||
* zero the page and wake waiting processes.
|
||||
* (Set during postcopy)
|
||||
*/
|
||||
#define RAM_UF_ZEROPAGE (1 << 3)
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_PAGE_BITS_VARY
|
||||
@@ -1790,6 +1795,17 @@ bool qemu_ram_is_shared(RAMBlock *rb)
|
||||
return rb->flags & RAM_SHARED;
|
||||
}
|
||||
|
||||
/* Note: Only set at the start of postcopy */
|
||||
bool qemu_ram_is_uf_zeroable(RAMBlock *rb)
|
||||
{
|
||||
return rb->flags & RAM_UF_ZEROPAGE;
|
||||
}
|
||||
|
||||
void qemu_ram_set_uf_zeroable(RAMBlock *rb)
|
||||
{
|
||||
rb->flags |= RAM_UF_ZEROPAGE;
|
||||
}
|
||||
|
||||
/* Called with iothread lock held. */
|
||||
void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev)
|
||||
{
|
||||
@@ -2320,6 +2336,16 @@ static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
|
||||
return ramblock_ptr(block, addr);
|
||||
}
|
||||
|
||||
/* Return the offset of a hostpointer within a ramblock */
|
||||
ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host)
|
||||
{
|
||||
ram_addr_t res = (uint8_t *)host - (uint8_t *)rb->host;
|
||||
assert((uintptr_t)host >= (uintptr_t)rb->host);
|
||||
assert(res < rb->max_length);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translates a host ptr back to a RAMBlock, a ram_addr and an offset
|
||||
* in that RAMBlock.
|
||||
@@ -3744,6 +3770,7 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length)
|
||||
}
|
||||
|
||||
if ((start + length) <= rb->used_length) {
|
||||
bool need_madvise, need_fallocate;
|
||||
uint8_t *host_endaddr = host_startaddr + length;
|
||||
if ((uintptr_t)host_endaddr & (rb->page_size - 1)) {
|
||||
error_report("ram_block_discard_range: Unaligned end address: %p",
|
||||
@@ -3753,29 +3780,60 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length)
|
||||
|
||||
errno = ENOTSUP; /* If we are missing MADVISE etc */
|
||||
|
||||
if (rb->page_size == qemu_host_page_size) {
|
||||
#if defined(CONFIG_MADVISE)
|
||||
/* Note: We need the madvise MADV_DONTNEED behaviour of definitely
|
||||
* freeing the page.
|
||||
*/
|
||||
ret = madvise(host_startaddr, length, MADV_DONTNEED);
|
||||
#endif
|
||||
} else {
|
||||
/* Huge page case - unfortunately it can't do DONTNEED, but
|
||||
* it can do the equivalent by FALLOC_FL_PUNCH_HOLE in the
|
||||
* huge page file.
|
||||
/* The logic here is messy;
|
||||
* madvise DONTNEED fails for hugepages
|
||||
* fallocate works on hugepages and shmem
|
||||
*/
|
||||
need_madvise = (rb->page_size == qemu_host_page_size);
|
||||
need_fallocate = rb->fd != -1;
|
||||
if (need_fallocate) {
|
||||
/* For a file, this causes the area of the file to be zero'd
|
||||
* if read, and for hugetlbfs also causes it to be unmapped
|
||||
* so a userfault will trigger.
|
||||
*/
|
||||
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
|
||||
ret = fallocate(rb->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
start, length);
|
||||
#endif
|
||||
}
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
error_report("ram_block_discard_range: Failed to discard range "
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
error_report("ram_block_discard_range: Failed to fallocate "
|
||||
"%s:%" PRIx64 " +%zx (%d)",
|
||||
rb->idstr, start, length, ret);
|
||||
goto err;
|
||||
}
|
||||
#else
|
||||
ret = -ENOSYS;
|
||||
error_report("ram_block_discard_range: fallocate not available/file"
|
||||
"%s:%" PRIx64 " +%zx (%d)",
|
||||
rb->idstr, start, length, ret);
|
||||
goto err;
|
||||
#endif
|
||||
}
|
||||
if (need_madvise) {
|
||||
/* For normal RAM this causes it to be unmapped,
|
||||
* for shared memory it causes the local mapping to disappear
|
||||
* and to fall back on the file contents (which we just
|
||||
* fallocate'd away).
|
||||
*/
|
||||
#if defined(CONFIG_MADVISE)
|
||||
ret = madvise(host_startaddr, length, MADV_DONTNEED);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
error_report("ram_block_discard_range: Failed to discard range "
|
||||
"%s:%" PRIx64 " +%zx (%d)",
|
||||
rb->idstr, start, length, ret);
|
||||
goto err;
|
||||
}
|
||||
#else
|
||||
ret = -ENOSYS;
|
||||
error_report("ram_block_discard_range: MADVISE not available"
|
||||
"%s:%" PRIx64 " +%zx (%d)",
|
||||
rb->idstr, start, length, ret);
|
||||
goto err;
|
||||
#endif
|
||||
}
|
||||
trace_ram_block_discard_range(rb->idstr, host_startaddr, length,
|
||||
need_madvise, need_fallocate, ret);
|
||||
} else {
|
||||
error_report("ram_block_discard_range: Overrun block '%s' (%" PRIu64
|
||||
"/%zx/" RAM_ADDR_FMT")",
|
||||
|
||||
@@ -2423,7 +2423,18 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
|
||||
switch (value->type) {
|
||||
case MEMORY_DEVICE_INFO_KIND_DIMM:
|
||||
di = value->u.dimm.data;
|
||||
break;
|
||||
|
||||
case MEMORY_DEVICE_INFO_KIND_NVDIMM:
|
||||
di = value->u.nvdimm.data;
|
||||
break;
|
||||
|
||||
default:
|
||||
di = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (di) {
|
||||
monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
|
||||
MemoryDeviceInfoKind_str(value->type),
|
||||
di->id ? di->id : "");
|
||||
@@ -2436,9 +2447,6 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
|
||||
di->hotplugged ? "true" : "false");
|
||||
monitor_printf(mon, " hotpluggable: %s\n",
|
||||
di->hotpluggable ? "true" : "false");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,6 +258,22 @@ static void build_append_int(GArray *table, uint64_t value)
|
||||
}
|
||||
}
|
||||
|
||||
/* Generic Address Structure (GAS)
|
||||
* ACPI 2.0/3.0: 5.2.3.1 Generic Address Structure
|
||||
* 2.0 compat note:
|
||||
* @access_width must be 0, see ACPI 2.0:Table 5-1
|
||||
*/
|
||||
void build_append_gas(GArray *table, AmlAddressSpace as,
|
||||
uint8_t bit_width, uint8_t bit_offset,
|
||||
uint8_t access_width, uint64_t address)
|
||||
{
|
||||
build_append_int_noprefix(table, as, 1);
|
||||
build_append_int_noprefix(table, bit_width, 1);
|
||||
build_append_int_noprefix(table, bit_offset, 1);
|
||||
build_append_int_noprefix(table, access_width, 1);
|
||||
build_append_int_noprefix(table, address, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build NAME(XXXX, 0x00000000) where 0x00000000 is encoded as a dword,
|
||||
* and return the offset to 0x00000000 for runtime patching.
|
||||
@@ -1662,3 +1678,127 @@ void build_slit(GArray *table_data, BIOSLinker *linker)
|
||||
"SLIT",
|
||||
table_data->len - slit_start, 1, NULL, NULL);
|
||||
}
|
||||
|
||||
/* build rev1/rev3/rev5.1 FADT */
|
||||
void build_fadt(GArray *tbl, BIOSLinker *linker, const AcpiFadtData *f,
|
||||
const char *oem_id, const char *oem_table_id)
|
||||
{
|
||||
int off;
|
||||
int fadt_start = tbl->len;
|
||||
|
||||
acpi_data_push(tbl, sizeof(AcpiTableHeader));
|
||||
|
||||
/* FACS address to be filled by Guest linker at runtime */
|
||||
off = tbl->len;
|
||||
build_append_int_noprefix(tbl, 0, 4); /* FIRMWARE_CTRL */
|
||||
if (f->facs_tbl_offset) { /* don't patch if not supported by platform */
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, off, 4,
|
||||
ACPI_BUILD_TABLE_FILE, *f->facs_tbl_offset);
|
||||
}
|
||||
|
||||
/* DSDT address to be filled by Guest linker at runtime */
|
||||
off = tbl->len;
|
||||
build_append_int_noprefix(tbl, 0, 4); /* DSDT */
|
||||
if (f->dsdt_tbl_offset) { /* don't patch if not supported by platform */
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, off, 4,
|
||||
ACPI_BUILD_TABLE_FILE, *f->dsdt_tbl_offset);
|
||||
}
|
||||
|
||||
/* ACPI1.0: INT_MODEL, ACPI2.0+: Reserved */
|
||||
build_append_int_noprefix(tbl, f->int_model /* Multiple APIC */, 1);
|
||||
/* Preferred_PM_Profile */
|
||||
build_append_int_noprefix(tbl, 0 /* Unspecified */, 1);
|
||||
build_append_int_noprefix(tbl, f->sci_int, 2); /* SCI_INT */
|
||||
build_append_int_noprefix(tbl, f->smi_cmd, 4); /* SMI_CMD */
|
||||
build_append_int_noprefix(tbl, f->acpi_enable_cmd, 1); /* ACPI_ENABLE */
|
||||
build_append_int_noprefix(tbl, f->acpi_disable_cmd, 1); /* ACPI_DISABLE */
|
||||
build_append_int_noprefix(tbl, 0 /* not supported */, 1); /* S4BIOS_REQ */
|
||||
/* ACPI1.0: Reserved, ACPI2.0+: PSTATE_CNT */
|
||||
build_append_int_noprefix(tbl, 0, 1);
|
||||
build_append_int_noprefix(tbl, f->pm1a_evt.address, 4); /* PM1a_EVT_BLK */
|
||||
build_append_int_noprefix(tbl, 0, 4); /* PM1b_EVT_BLK */
|
||||
build_append_int_noprefix(tbl, f->pm1a_cnt.address, 4); /* PM1a_CNT_BLK */
|
||||
build_append_int_noprefix(tbl, 0, 4); /* PM1b_CNT_BLK */
|
||||
build_append_int_noprefix(tbl, 0, 4); /* PM2_CNT_BLK */
|
||||
build_append_int_noprefix(tbl, f->pm_tmr.address, 4); /* PM_TMR_BLK */
|
||||
build_append_int_noprefix(tbl, f->gpe0_blk.address, 4); /* GPE0_BLK */
|
||||
build_append_int_noprefix(tbl, 0, 4); /* GPE1_BLK */
|
||||
/* PM1_EVT_LEN */
|
||||
build_append_int_noprefix(tbl, f->pm1a_evt.bit_width / 8, 1);
|
||||
/* PM1_CNT_LEN */
|
||||
build_append_int_noprefix(tbl, f->pm1a_cnt.bit_width / 8, 1);
|
||||
build_append_int_noprefix(tbl, 0, 1); /* PM2_CNT_LEN */
|
||||
build_append_int_noprefix(tbl, f->pm_tmr.bit_width / 8, 1); /* PM_TMR_LEN */
|
||||
/* GPE0_BLK_LEN */
|
||||
build_append_int_noprefix(tbl, f->gpe0_blk.bit_width / 8, 1);
|
||||
build_append_int_noprefix(tbl, 0, 1); /* GPE1_BLK_LEN */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* GPE1_BASE */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* CST_CNT */
|
||||
build_append_int_noprefix(tbl, f->plvl2_lat, 2); /* P_LVL2_LAT */
|
||||
build_append_int_noprefix(tbl, f->plvl3_lat, 2); /* P_LVL3_LAT */
|
||||
build_append_int_noprefix(tbl, 0, 2); /* FLUSH_SIZE */
|
||||
build_append_int_noprefix(tbl, 0, 2); /* FLUSH_STRIDE */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* DUTY_OFFSET */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* DUTY_WIDTH */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* DAY_ALRM */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* MON_ALRM */
|
||||
build_append_int_noprefix(tbl, f->rtc_century, 1); /* CENTURY */
|
||||
build_append_int_noprefix(tbl, 0, 2); /* IAPC_BOOT_ARCH */
|
||||
build_append_int_noprefix(tbl, 0, 1); /* Reserved */
|
||||
build_append_int_noprefix(tbl, f->flags, 4); /* Flags */
|
||||
|
||||
if (f->rev == 1) {
|
||||
goto build_hdr;
|
||||
}
|
||||
|
||||
build_append_gas_from_struct(tbl, &f->reset_reg); /* RESET_REG */
|
||||
build_append_int_noprefix(tbl, f->reset_val, 1); /* RESET_VALUE */
|
||||
/* Since ACPI 5.1 */
|
||||
if ((f->rev >= 6) || ((f->rev == 5) && f->minor_ver > 0)) {
|
||||
build_append_int_noprefix(tbl, f->arm_boot_arch, 2); /* ARM_BOOT_ARCH */
|
||||
/* FADT Minor Version */
|
||||
build_append_int_noprefix(tbl, f->minor_ver, 1);
|
||||
} else {
|
||||
build_append_int_noprefix(tbl, 0, 3); /* Reserved upto ACPI 5.0 */
|
||||
}
|
||||
build_append_int_noprefix(tbl, 0, 8); /* X_FIRMWARE_CTRL */
|
||||
|
||||
/* XDSDT address to be filled by Guest linker at runtime */
|
||||
off = tbl->len;
|
||||
build_append_int_noprefix(tbl, 0, 8); /* X_DSDT */
|
||||
if (f->xdsdt_tbl_offset) {
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, off, 8,
|
||||
ACPI_BUILD_TABLE_FILE, *f->xdsdt_tbl_offset);
|
||||
}
|
||||
|
||||
build_append_gas_from_struct(tbl, &f->pm1a_evt); /* X_PM1a_EVT_BLK */
|
||||
/* X_PM1b_EVT_BLK */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
build_append_gas_from_struct(tbl, &f->pm1a_cnt); /* X_PM1a_CNT_BLK */
|
||||
/* X_PM1b_CNT_BLK */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
/* X_PM2_CNT_BLK */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
build_append_gas_from_struct(tbl, &f->pm_tmr); /* X_PM_TMR_BLK */
|
||||
build_append_gas_from_struct(tbl, &f->gpe0_blk); /* X_GPE0_BLK */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0); /* X_GPE1_BLK */
|
||||
|
||||
if (f->rev <= 4) {
|
||||
goto build_hdr;
|
||||
}
|
||||
|
||||
/* SLEEP_CONTROL_REG */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
/* SLEEP_STATUS_REG */
|
||||
build_append_gas(tbl, AML_AS_SYSTEM_MEMORY, 0 , 0, 0, 0);
|
||||
|
||||
/* TODO: extra fields need to be added to support revisions above rev5 */
|
||||
assert(f->rev == 5);
|
||||
|
||||
build_hdr:
|
||||
build_header(linker, tbl, (void *)(tbl->data + fadt_start),
|
||||
"FACP", tbl->len - fadt_start, f->rev, oem_id, oem_table_id);
|
||||
}
|
||||
|
||||
+15
-24
@@ -651,42 +651,33 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
}
|
||||
|
||||
/* FADT */
|
||||
static void build_fadt(GArray *table_data, BIOSLinker *linker,
|
||||
VirtMachineState *vms, unsigned dsdt_tbl_offset)
|
||||
static void build_fadt_rev5(GArray *table_data, BIOSLinker *linker,
|
||||
VirtMachineState *vms, unsigned dsdt_tbl_offset)
|
||||
{
|
||||
int fadt_start = table_data->len;
|
||||
AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
|
||||
unsigned xdsdt_entry_offset = (char *)&fadt->x_dsdt - table_data->data;
|
||||
uint16_t bootflags;
|
||||
/* ACPI v5.1 */
|
||||
AcpiFadtData fadt = {
|
||||
.rev = 5,
|
||||
.minor_ver = 1,
|
||||
.flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI,
|
||||
.xdsdt_tbl_offset = &dsdt_tbl_offset,
|
||||
};
|
||||
|
||||
switch (vms->psci_conduit) {
|
||||
case QEMU_PSCI_CONDUIT_DISABLED:
|
||||
bootflags = 0;
|
||||
fadt.arm_boot_arch = 0;
|
||||
break;
|
||||
case QEMU_PSCI_CONDUIT_HVC:
|
||||
bootflags = ACPI_FADT_ARM_PSCI_COMPLIANT | ACPI_FADT_ARM_PSCI_USE_HVC;
|
||||
fadt.arm_boot_arch = ACPI_FADT_ARM_PSCI_COMPLIANT |
|
||||
ACPI_FADT_ARM_PSCI_USE_HVC;
|
||||
break;
|
||||
case QEMU_PSCI_CONDUIT_SMC:
|
||||
bootflags = ACPI_FADT_ARM_PSCI_COMPLIANT;
|
||||
fadt.arm_boot_arch = ACPI_FADT_ARM_PSCI_COMPLIANT;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
/* Hardware Reduced = 1 and use PSCI 0.2+ */
|
||||
fadt->flags = cpu_to_le32(1 << ACPI_FADT_F_HW_REDUCED_ACPI);
|
||||
fadt->arm_boot_flags = cpu_to_le16(bootflags);
|
||||
|
||||
/* ACPI v5.1 (fadt->revision.fadt->minor_revision) */
|
||||
fadt->minor_revision = 0x1;
|
||||
|
||||
/* DSDT address to be filled by Guest linker */
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, xdsdt_entry_offset, sizeof(fadt->x_dsdt),
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
||||
|
||||
build_header(linker, table_data, (void *)(table_data->data + fadt_start),
|
||||
"FACP", table_data->len - fadt_start, 5, NULL, NULL);
|
||||
build_fadt(table_data, linker, &fadt, NULL, NULL);
|
||||
}
|
||||
|
||||
/* DSDT */
|
||||
@@ -761,7 +752,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
|
||||
|
||||
/* FADT MADT GTDT MCFG SPCR pointed to by RSDT */
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_fadt(tables_blob, tables->linker, vms, dsdt);
|
||||
build_fadt_rev5(tables_blob, tables->linker, vms, dsdt);
|
||||
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_madt(tables_blob, tables->linker, vms);
|
||||
|
||||
+115
-137
@@ -91,17 +91,11 @@ typedef struct AcpiMcfgInfo {
|
||||
} AcpiMcfgInfo;
|
||||
|
||||
typedef struct AcpiPmInfo {
|
||||
bool force_rev1_fadt;
|
||||
bool s3_disabled;
|
||||
bool s4_disabled;
|
||||
bool pcihp_bridge_en;
|
||||
uint8_t s4_val;
|
||||
uint16_t sci_int;
|
||||
uint8_t acpi_enable_cmd;
|
||||
uint8_t acpi_disable_cmd;
|
||||
uint32_t gpe0_blk;
|
||||
uint32_t gpe0_blk_len;
|
||||
uint32_t io_base;
|
||||
AcpiFadtData fadt;
|
||||
uint16_t cpu_hp_io_base;
|
||||
uint16_t pcihp_io_base;
|
||||
uint16_t pcihp_io_len;
|
||||
@@ -124,21 +118,59 @@ typedef struct AcpiBuildPciBusHotplugState {
|
||||
bool pcihp_bridge_en;
|
||||
} AcpiBuildPciBusHotplugState;
|
||||
|
||||
static void init_common_fadt_data(Object *o, AcpiFadtData *data)
|
||||
{
|
||||
uint32_t io = object_property_get_uint(o, ACPI_PM_PROP_PM_IO_BASE, NULL);
|
||||
AmlAddressSpace as = AML_AS_SYSTEM_IO;
|
||||
AcpiFadtData fadt = {
|
||||
.rev = 3,
|
||||
.flags =
|
||||
(1 << ACPI_FADT_F_WBINVD) |
|
||||
(1 << ACPI_FADT_F_PROC_C1) |
|
||||
(1 << ACPI_FADT_F_SLP_BUTTON) |
|
||||
(1 << ACPI_FADT_F_RTC_S4) |
|
||||
(1 << ACPI_FADT_F_USE_PLATFORM_CLOCK) |
|
||||
/* APIC destination mode ("Flat Logical") has an upper limit of 8
|
||||
* CPUs for more than 8 CPUs, "Clustered Logical" mode has to be
|
||||
* used
|
||||
*/
|
||||
((max_cpus > 8) ? (1 << ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL) : 0),
|
||||
.int_model = 1 /* Multiple APIC */,
|
||||
.rtc_century = RTC_CENTURY,
|
||||
.plvl2_lat = 0xfff /* C2 state not supported */,
|
||||
.plvl3_lat = 0xfff /* C3 state not supported */,
|
||||
.smi_cmd = ACPI_PORT_SMI_CMD,
|
||||
.sci_int = object_property_get_uint(o, ACPI_PM_PROP_SCI_INT, NULL),
|
||||
.acpi_enable_cmd =
|
||||
object_property_get_uint(o, ACPI_PM_PROP_ACPI_ENABLE_CMD, NULL),
|
||||
.acpi_disable_cmd =
|
||||
object_property_get_uint(o, ACPI_PM_PROP_ACPI_DISABLE_CMD, NULL),
|
||||
.pm1a_evt = { .space_id = as, .bit_width = 4 * 8, .address = io },
|
||||
.pm1a_cnt = { .space_id = as, .bit_width = 2 * 8,
|
||||
.address = io + 0x04 },
|
||||
.pm_tmr = { .space_id = as, .bit_width = 4 * 8, .address = io + 0x08 },
|
||||
.gpe0_blk = { .space_id = as, .bit_width =
|
||||
object_property_get_uint(o, ACPI_PM_PROP_GPE0_BLK_LEN, NULL) * 8,
|
||||
.address = object_property_get_uint(o, ACPI_PM_PROP_GPE0_BLK, NULL)
|
||||
},
|
||||
};
|
||||
*data = fadt;
|
||||
}
|
||||
|
||||
static void acpi_get_pm_info(AcpiPmInfo *pm)
|
||||
{
|
||||
Object *piix = piix4_pm_find();
|
||||
Object *lpc = ich9_lpc_find();
|
||||
Object *obj = NULL;
|
||||
Object *obj = piix ? piix : lpc;
|
||||
QObject *o;
|
||||
|
||||
pm->force_rev1_fadt = false;
|
||||
pm->cpu_hp_io_base = 0;
|
||||
pm->pcihp_io_base = 0;
|
||||
pm->pcihp_io_len = 0;
|
||||
|
||||
init_common_fadt_data(obj, &pm->fadt);
|
||||
if (piix) {
|
||||
/* w2k requires FADT(rev1) or it won't boot, keep PC compatible */
|
||||
pm->force_rev1_fadt = true;
|
||||
obj = piix;
|
||||
pm->fadt.rev = 1;
|
||||
pm->cpu_hp_io_base = PIIX4_CPU_HOTPLUG_IO_BASE;
|
||||
pm->pcihp_io_base =
|
||||
object_property_get_uint(obj, ACPI_PCIHP_IO_BASE_PROP, NULL);
|
||||
@@ -146,11 +178,19 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
|
||||
object_property_get_uint(obj, ACPI_PCIHP_IO_LEN_PROP, NULL);
|
||||
}
|
||||
if (lpc) {
|
||||
obj = lpc;
|
||||
struct AcpiGenericAddress r = { .space_id = AML_AS_SYSTEM_IO,
|
||||
.bit_width = 8, .address = ICH9_RST_CNT_IOPORT };
|
||||
pm->fadt.reset_reg = r;
|
||||
pm->fadt.reset_val = 0xf;
|
||||
pm->fadt.flags |= 1 << ACPI_FADT_F_RESET_REG_SUP;
|
||||
pm->cpu_hp_io_base = ICH9_CPU_HOTPLUG_IO_BASE;
|
||||
}
|
||||
assert(obj);
|
||||
|
||||
/* The above need not be conditional on machine type because the reset port
|
||||
* happens to be the same on PIIX (pc) and ICH9 (q35). */
|
||||
QEMU_BUILD_BUG_ON(ICH9_RST_CNT_IOPORT != RCR_IOPORT);
|
||||
|
||||
/* Fill in optional s3/s4 related properties */
|
||||
o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL);
|
||||
if (o) {
|
||||
@@ -174,22 +214,6 @@ static void acpi_get_pm_info(AcpiPmInfo *pm)
|
||||
}
|
||||
qobject_decref(o);
|
||||
|
||||
/* Fill in mandatory properties */
|
||||
pm->sci_int = object_property_get_uint(obj, ACPI_PM_PROP_SCI_INT, NULL);
|
||||
|
||||
pm->acpi_enable_cmd = object_property_get_uint(obj,
|
||||
ACPI_PM_PROP_ACPI_ENABLE_CMD,
|
||||
NULL);
|
||||
pm->acpi_disable_cmd =
|
||||
object_property_get_uint(obj,
|
||||
ACPI_PM_PROP_ACPI_DISABLE_CMD,
|
||||
NULL);
|
||||
pm->io_base = object_property_get_uint(obj, ACPI_PM_PROP_PM_IO_BASE,
|
||||
NULL);
|
||||
pm->gpe0_blk = object_property_get_uint(obj, ACPI_PM_PROP_GPE0_BLK,
|
||||
NULL);
|
||||
pm->gpe0_blk_len = object_property_get_uint(obj, ACPI_PM_PROP_GPE0_BLK_LEN,
|
||||
NULL);
|
||||
pm->pcihp_bridge_en =
|
||||
object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support",
|
||||
NULL);
|
||||
@@ -257,8 +281,6 @@ static void acpi_get_pci_holes(Range *hole, Range *hole64)
|
||||
NULL));
|
||||
}
|
||||
|
||||
#define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */
|
||||
|
||||
static void acpi_align_size(GArray *blob, unsigned align)
|
||||
{
|
||||
/* Align size to multiple of given size. This reduces the chance
|
||||
@@ -276,106 +298,6 @@ build_facs(GArray *table_data, BIOSLinker *linker)
|
||||
facs->length = cpu_to_le32(sizeof(*facs));
|
||||
}
|
||||
|
||||
/* Load chipset information in FADT */
|
||||
static void fadt_setup(AcpiFadtDescriptorRev3 *fadt, AcpiPmInfo *pm)
|
||||
{
|
||||
fadt->model = 1;
|
||||
fadt->reserved1 = 0;
|
||||
fadt->sci_int = cpu_to_le16(pm->sci_int);
|
||||
fadt->smi_cmd = cpu_to_le32(ACPI_PORT_SMI_CMD);
|
||||
fadt->acpi_enable = pm->acpi_enable_cmd;
|
||||
fadt->acpi_disable = pm->acpi_disable_cmd;
|
||||
/* EVT, CNT, TMR offset matches hw/acpi/core.c */
|
||||
fadt->pm1a_evt_blk = cpu_to_le32(pm->io_base);
|
||||
fadt->pm1a_cnt_blk = cpu_to_le32(pm->io_base + 0x04);
|
||||
fadt->pm_tmr_blk = cpu_to_le32(pm->io_base + 0x08);
|
||||
fadt->gpe0_blk = cpu_to_le32(pm->gpe0_blk);
|
||||
/* EVT, CNT, TMR length matches hw/acpi/core.c */
|
||||
fadt->pm1_evt_len = 4;
|
||||
fadt->pm1_cnt_len = 2;
|
||||
fadt->pm_tmr_len = 4;
|
||||
fadt->gpe0_blk_len = pm->gpe0_blk_len;
|
||||
fadt->plvl2_lat = cpu_to_le16(0xfff); /* C2 state not supported */
|
||||
fadt->plvl3_lat = cpu_to_le16(0xfff); /* C3 state not supported */
|
||||
fadt->flags = cpu_to_le32((1 << ACPI_FADT_F_WBINVD) |
|
||||
(1 << ACPI_FADT_F_PROC_C1) |
|
||||
(1 << ACPI_FADT_F_SLP_BUTTON) |
|
||||
(1 << ACPI_FADT_F_RTC_S4));
|
||||
fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_USE_PLATFORM_CLOCK);
|
||||
/* APIC destination mode ("Flat Logical") has an upper limit of 8 CPUs
|
||||
* For more than 8 CPUs, "Clustered Logical" mode has to be used
|
||||
*/
|
||||
if (max_cpus > 8) {
|
||||
fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL);
|
||||
}
|
||||
fadt->century = RTC_CENTURY;
|
||||
if (pm->force_rev1_fadt) {
|
||||
return;
|
||||
}
|
||||
|
||||
fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_RESET_REG_SUP);
|
||||
fadt->reset_value = 0xf;
|
||||
fadt->reset_register.space_id = AML_SYSTEM_IO;
|
||||
fadt->reset_register.bit_width = 8;
|
||||
fadt->reset_register.address = cpu_to_le64(ICH9_RST_CNT_IOPORT);
|
||||
/* The above need not be conditional on machine type because the reset port
|
||||
* happens to be the same on PIIX (pc) and ICH9 (q35). */
|
||||
QEMU_BUILD_BUG_ON(ICH9_RST_CNT_IOPORT != RCR_IOPORT);
|
||||
|
||||
fadt->xpm1a_event_block.space_id = AML_SYSTEM_IO;
|
||||
fadt->xpm1a_event_block.bit_width = fadt->pm1_evt_len * 8;
|
||||
fadt->xpm1a_event_block.address = cpu_to_le64(pm->io_base);
|
||||
|
||||
fadt->xpm1a_control_block.space_id = AML_SYSTEM_IO;
|
||||
fadt->xpm1a_control_block.bit_width = fadt->pm1_cnt_len * 8;
|
||||
fadt->xpm1a_control_block.address = cpu_to_le64(pm->io_base + 0x4);
|
||||
|
||||
fadt->xpm_timer_block.space_id = AML_SYSTEM_IO;
|
||||
fadt->xpm_timer_block.bit_width = fadt->pm_tmr_len * 8;
|
||||
fadt->xpm_timer_block.address = cpu_to_le64(pm->io_base + 0x8);
|
||||
|
||||
fadt->xgpe0_block.space_id = AML_SYSTEM_IO;
|
||||
fadt->xgpe0_block.bit_width = pm->gpe0_blk_len * 8;
|
||||
fadt->xgpe0_block.address = cpu_to_le64(pm->gpe0_blk);
|
||||
}
|
||||
|
||||
|
||||
/* FADT */
|
||||
static void
|
||||
build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm,
|
||||
unsigned facs_tbl_offset, unsigned dsdt_tbl_offset,
|
||||
const char *oem_id, const char *oem_table_id)
|
||||
{
|
||||
AcpiFadtDescriptorRev3 *fadt = acpi_data_push(table_data, sizeof(*fadt));
|
||||
unsigned fw_ctrl_offset = (char *)&fadt->firmware_ctrl - table_data->data;
|
||||
unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data;
|
||||
unsigned xdsdt_entry_offset = (char *)&fadt->x_dsdt - table_data->data;
|
||||
int fadt_size = sizeof(*fadt);
|
||||
int rev = 3;
|
||||
|
||||
/* FACS address to be filled by Guest linker */
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, fw_ctrl_offset, sizeof(fadt->firmware_ctrl),
|
||||
ACPI_BUILD_TABLE_FILE, facs_tbl_offset);
|
||||
|
||||
/* DSDT address to be filled by Guest linker */
|
||||
fadt_setup(fadt, pm);
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt),
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
||||
if (pm->force_rev1_fadt) {
|
||||
rev = 1;
|
||||
fadt_size = offsetof(typeof(*fadt), reset_register);
|
||||
} else {
|
||||
bios_linker_loader_add_pointer(linker,
|
||||
ACPI_BUILD_TABLE_FILE, xdsdt_entry_offset, sizeof(fadt->x_dsdt),
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
||||
}
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)fadt, "FACP", fadt_size, rev, oem_id, oem_table_id);
|
||||
}
|
||||
|
||||
void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
|
||||
const CPUArchIdList *apic_ids, GArray *entry)
|
||||
{
|
||||
@@ -2053,7 +1975,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_io(AML_DECODE16, pm->gpe0_blk, pm->gpe0_blk, 1, pm->gpe0_blk_len)
|
||||
aml_io(
|
||||
AML_DECODE16,
|
||||
pm->fadt.gpe0_blk.address,
|
||||
pm->fadt.gpe0_blk.address,
|
||||
1,
|
||||
pm->fadt.gpe0_blk.bit_width / 8)
|
||||
);
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
aml_append(scope, dev);
|
||||
@@ -2323,6 +2250,55 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog)
|
||||
#define HOLE_640K_START (640 * 1024)
|
||||
#define HOLE_640K_END (1024 * 1024)
|
||||
|
||||
static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base,
|
||||
uint64_t len, int default_node)
|
||||
{
|
||||
MemoryDeviceInfoList *info_list = qmp_pc_dimm_device_list();
|
||||
MemoryDeviceInfoList *info;
|
||||
MemoryDeviceInfo *mi;
|
||||
PCDIMMDeviceInfo *di;
|
||||
uint64_t end = base + len, cur, size;
|
||||
bool is_nvdimm;
|
||||
AcpiSratMemoryAffinity *numamem;
|
||||
MemoryAffinityFlags flags;
|
||||
|
||||
for (cur = base, info = info_list;
|
||||
cur < end;
|
||||
cur += size, info = info->next) {
|
||||
numamem = acpi_data_push(table_data, sizeof *numamem);
|
||||
|
||||
if (!info) {
|
||||
build_srat_memory(numamem, cur, end - cur, default_node,
|
||||
MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
|
||||
break;
|
||||
}
|
||||
|
||||
mi = info->value;
|
||||
is_nvdimm = (mi->type == MEMORY_DEVICE_INFO_KIND_NVDIMM);
|
||||
di = !is_nvdimm ? mi->u.dimm.data : mi->u.nvdimm.data;
|
||||
|
||||
if (cur < di->addr) {
|
||||
build_srat_memory(numamem, cur, di->addr - cur, default_node,
|
||||
MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
|
||||
numamem = acpi_data_push(table_data, sizeof *numamem);
|
||||
}
|
||||
|
||||
size = di->size;
|
||||
|
||||
flags = MEM_AFFINITY_ENABLED;
|
||||
if (di->hotpluggable) {
|
||||
flags |= MEM_AFFINITY_HOTPLUGGABLE;
|
||||
}
|
||||
if (is_nvdimm) {
|
||||
flags |= MEM_AFFINITY_NON_VOLATILE;
|
||||
}
|
||||
|
||||
build_srat_memory(numamem, di->addr, size, di->node, flags);
|
||||
}
|
||||
|
||||
qapi_free_MemoryDeviceInfoList(info_list);
|
||||
}
|
||||
|
||||
static void
|
||||
build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
|
||||
{
|
||||
@@ -2434,10 +2410,9 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
|
||||
* providing _PXM method if necessary.
|
||||
*/
|
||||
if (hotplugabble_address_space_size) {
|
||||
numamem = acpi_data_push(table_data, sizeof *numamem);
|
||||
build_srat_memory(numamem, pcms->hotplug_memory.base,
|
||||
hotplugabble_address_space_size, pcms->numa_nodes - 1,
|
||||
MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
|
||||
build_srat_hotpluggable_memory(table_data, pcms->hotplug_memory.base,
|
||||
hotplugabble_address_space_size,
|
||||
pcms->numa_nodes - 1);
|
||||
}
|
||||
|
||||
build_header(linker, table_data,
|
||||
@@ -2700,7 +2675,10 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||
/* ACPI tables pointed to by RSDT */
|
||||
fadt = tables_blob->len;
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
build_fadt(tables_blob, tables->linker, &pm, facs, dsdt,
|
||||
pm.fadt.facs_tbl_offset = &facs;
|
||||
pm.fadt.dsdt_tbl_offset = &dsdt;
|
||||
pm.fadt.xdsdt_tbl_offset = &dsdt;
|
||||
build_fadt(tables_blob, tables->linker, &pm.fadt,
|
||||
slic_oem.id, slic_oem.table_id);
|
||||
aml_len += tables_blob->len - fadt;
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#endif
|
||||
|
||||
/* fixed I/O location */
|
||||
#define APM_CNT_IOPORT 0xb2
|
||||
#define APM_STS_IOPORT 0xb3
|
||||
|
||||
static void apm_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
|
||||
|
||||
+52
-39
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/mem/pc-dimm.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qapi/visitor.h"
|
||||
@@ -162,45 +163,6 @@ uint64_t get_plugged_memory_size(void)
|
||||
return pc_existing_dimms_capacity(&error_abort);
|
||||
}
|
||||
|
||||
int qmp_pc_dimm_device_list(Object *obj, void *opaque)
|
||||
{
|
||||
MemoryDeviceInfoList ***prev = opaque;
|
||||
|
||||
if (object_dynamic_cast(obj, TYPE_PC_DIMM)) {
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
|
||||
if (dev->realized) {
|
||||
MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1);
|
||||
MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1);
|
||||
PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1);
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(obj);
|
||||
PCDIMMDevice *dimm = PC_DIMM(obj);
|
||||
|
||||
if (dev->id) {
|
||||
di->has_id = true;
|
||||
di->id = g_strdup(dev->id);
|
||||
}
|
||||
di->hotplugged = dev->hotplugged;
|
||||
di->hotpluggable = dc->hotpluggable;
|
||||
di->addr = dimm->addr;
|
||||
di->slot = dimm->slot;
|
||||
di->node = dimm->node;
|
||||
di->size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP,
|
||||
NULL);
|
||||
di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));
|
||||
|
||||
info->u.dimm.data = di;
|
||||
elem->value = info;
|
||||
elem->next = NULL;
|
||||
**prev = elem;
|
||||
*prev = &elem->next;
|
||||
}
|
||||
}
|
||||
|
||||
object_child_foreach(obj, qmp_pc_dimm_device_list, opaque);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
|
||||
{
|
||||
unsigned long *bitmap = opaque;
|
||||
@@ -276,6 +238,57 @@ static int pc_dimm_built_list(Object *obj, void *opaque)
|
||||
return 0;
|
||||
}
|
||||
|
||||
MemoryDeviceInfoList *qmp_pc_dimm_device_list(void)
|
||||
{
|
||||
GSList *dimms = NULL, *item;
|
||||
MemoryDeviceInfoList *list = NULL, *prev = NULL;
|
||||
|
||||
object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &dimms);
|
||||
|
||||
for (item = dimms; item; item = g_slist_next(item)) {
|
||||
PCDIMMDevice *dimm = PC_DIMM(item->data);
|
||||
Object *obj = OBJECT(dimm);
|
||||
MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1);
|
||||
MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1);
|
||||
PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1);
|
||||
bool is_nvdimm = object_dynamic_cast(obj, TYPE_NVDIMM);
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(obj);
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
|
||||
if (dev->id) {
|
||||
di->has_id = true;
|
||||
di->id = g_strdup(dev->id);
|
||||
}
|
||||
di->hotplugged = dev->hotplugged;
|
||||
di->hotpluggable = dc->hotpluggable;
|
||||
di->addr = dimm->addr;
|
||||
di->slot = dimm->slot;
|
||||
di->node = dimm->node;
|
||||
di->size = object_property_get_uint(obj, PC_DIMM_SIZE_PROP, NULL);
|
||||
di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));
|
||||
|
||||
if (!is_nvdimm) {
|
||||
info->u.dimm.data = di;
|
||||
info->type = MEMORY_DEVICE_INFO_KIND_DIMM;
|
||||
} else {
|
||||
info->u.nvdimm.data = di;
|
||||
info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM;
|
||||
}
|
||||
elem->value = info;
|
||||
elem->next = NULL;
|
||||
if (prev) {
|
||||
prev->next = elem;
|
||||
} else {
|
||||
list = elem;
|
||||
}
|
||||
prev = elem;
|
||||
}
|
||||
|
||||
g_slist_free(dimms);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
uint64_t pc_dimm_get_free_addr(uint64_t address_space_start,
|
||||
uint64_t address_space_size,
|
||||
uint64_t *hint, uint64_t align, uint64_t size,
|
||||
|
||||
+54
-27
@@ -26,6 +26,7 @@
|
||||
#include "qapi/qapi-events-net.h"
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "migration/misc.h"
|
||||
#include "standard-headers/linux/ethtool.h"
|
||||
|
||||
#define VIRTIO_NET_VM_VERSION 11
|
||||
|
||||
@@ -48,19 +49,21 @@
|
||||
(offsetof(container, field) + sizeof(((container *)0)->field))
|
||||
|
||||
typedef struct VirtIOFeature {
|
||||
uint32_t flags;
|
||||
uint64_t flags;
|
||||
size_t end;
|
||||
} VirtIOFeature;
|
||||
|
||||
static VirtIOFeature feature_sizes[] = {
|
||||
{.flags = 1 << VIRTIO_NET_F_MAC,
|
||||
{.flags = 1ULL << VIRTIO_NET_F_MAC,
|
||||
.end = endof(struct virtio_net_config, mac)},
|
||||
{.flags = 1 << VIRTIO_NET_F_STATUS,
|
||||
{.flags = 1ULL << VIRTIO_NET_F_STATUS,
|
||||
.end = endof(struct virtio_net_config, status)},
|
||||
{.flags = 1 << VIRTIO_NET_F_MQ,
|
||||
{.flags = 1ULL << VIRTIO_NET_F_MQ,
|
||||
.end = endof(struct virtio_net_config, max_virtqueue_pairs)},
|
||||
{.flags = 1 << VIRTIO_NET_F_MTU,
|
||||
{.flags = 1ULL << VIRTIO_NET_F_MTU,
|
||||
.end = endof(struct virtio_net_config, mtu)},
|
||||
{.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX,
|
||||
.end = endof(struct virtio_net_config, duplex)},
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -89,6 +92,8 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
|
||||
virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues);
|
||||
virtio_stw_p(vdev, &netcfg.mtu, n->net_conf.mtu);
|
||||
memcpy(netcfg.mac, n->mac, ETH_ALEN);
|
||||
virtio_stl_p(vdev, &netcfg.speed, n->net_conf.speed);
|
||||
netcfg.duplex = n->net_conf.duplex;
|
||||
memcpy(config, &netcfg, n->config_size);
|
||||
}
|
||||
|
||||
@@ -1938,7 +1943,26 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
|
||||
int i;
|
||||
|
||||
if (n->net_conf.mtu) {
|
||||
n->host_features |= (0x1 << VIRTIO_NET_F_MTU);
|
||||
n->host_features |= (1ULL << VIRTIO_NET_F_MTU);
|
||||
}
|
||||
|
||||
if (n->net_conf.duplex_str) {
|
||||
if (strncmp(n->net_conf.duplex_str, "half", 5) == 0) {
|
||||
n->net_conf.duplex = DUPLEX_HALF;
|
||||
} else if (strncmp(n->net_conf.duplex_str, "full", 5) == 0) {
|
||||
n->net_conf.duplex = DUPLEX_FULL;
|
||||
} else {
|
||||
error_setg(errp, "'duplex' must be 'half' or 'full'");
|
||||
}
|
||||
n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
|
||||
} else {
|
||||
n->net_conf.duplex = DUPLEX_UNKNOWN;
|
||||
}
|
||||
|
||||
if (n->net_conf.speed < SPEED_UNKNOWN) {
|
||||
error_setg(errp, "'speed' must be between 0 and INT_MAX");
|
||||
} else if (n->net_conf.speed >= 0) {
|
||||
n->host_features |= (1ULL << VIRTIO_NET_F_SPEED_DUPLEX);
|
||||
}
|
||||
|
||||
virtio_net_set_config_size(n, n->host_features);
|
||||
@@ -2109,45 +2133,46 @@ static const VMStateDescription vmstate_virtio_net = {
|
||||
};
|
||||
|
||||
static Property virtio_net_properties[] = {
|
||||
DEFINE_PROP_BIT("csum", VirtIONet, host_features, VIRTIO_NET_F_CSUM, true),
|
||||
DEFINE_PROP_BIT("guest_csum", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("csum", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CSUM, true),
|
||||
DEFINE_PROP_BIT64("guest_csum", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_CSUM, true),
|
||||
DEFINE_PROP_BIT("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
|
||||
DEFINE_PROP_BIT("guest_tso4", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("gso", VirtIONet, host_features, VIRTIO_NET_F_GSO, true),
|
||||
DEFINE_PROP_BIT64("guest_tso4", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_TSO4, true),
|
||||
DEFINE_PROP_BIT("guest_tso6", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("guest_tso6", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_TSO6, true),
|
||||
DEFINE_PROP_BIT("guest_ecn", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("guest_ecn", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_ECN, true),
|
||||
DEFINE_PROP_BIT("guest_ufo", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("guest_ufo", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_UFO, true),
|
||||
DEFINE_PROP_BIT("guest_announce", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("guest_announce", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_GUEST_ANNOUNCE, true),
|
||||
DEFINE_PROP_BIT("host_tso4", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("host_tso4", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_HOST_TSO4, true),
|
||||
DEFINE_PROP_BIT("host_tso6", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("host_tso6", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_HOST_TSO6, true),
|
||||
DEFINE_PROP_BIT("host_ecn", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("host_ecn", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_HOST_ECN, true),
|
||||
DEFINE_PROP_BIT("host_ufo", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("host_ufo", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_HOST_UFO, true),
|
||||
DEFINE_PROP_BIT("mrg_rxbuf", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("mrg_rxbuf", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_MRG_RXBUF, true),
|
||||
DEFINE_PROP_BIT("status", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("status", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_STATUS, true),
|
||||
DEFINE_PROP_BIT("ctrl_vq", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("ctrl_vq", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_VQ, true),
|
||||
DEFINE_PROP_BIT("ctrl_rx", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("ctrl_rx", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_RX, true),
|
||||
DEFINE_PROP_BIT("ctrl_vlan", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("ctrl_vlan", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_VLAN, true),
|
||||
DEFINE_PROP_BIT("ctrl_rx_extra", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("ctrl_rx_extra", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_RX_EXTRA, true),
|
||||
DEFINE_PROP_BIT("ctrl_mac_addr", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("ctrl_mac_addr", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_MAC_ADDR, true),
|
||||
DEFINE_PROP_BIT("ctrl_guest_offloads", VirtIONet, host_features,
|
||||
DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features,
|
||||
VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true),
|
||||
DEFINE_PROP_BIT("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
|
||||
DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false),
|
||||
DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf),
|
||||
DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer,
|
||||
TX_TIMER_INTERVAL),
|
||||
@@ -2160,6 +2185,8 @@ static Property virtio_net_properties[] = {
|
||||
DEFINE_PROP_UINT16("host_mtu", VirtIONet, net_conf.mtu, 0),
|
||||
DEFINE_PROP_BOOL("x-mtu-bypass-backend", VirtIONet, mtu_bypass_backend,
|
||||
true),
|
||||
DEFINE_PROP_INT32("speed", VirtIONet, net_conf.speed, SPEED_UNKNOWN),
|
||||
DEFINE_PROP_STRING("duplex", VirtIONet, net_conf.duplex_str),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
@@ -2048,18 +2048,6 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_default_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
|
||||
|
||||
if (pc->init) {
|
||||
if (pc->init(dev) < 0) {
|
||||
error_setg(errp, "Device initialization failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction,
|
||||
const char *name)
|
||||
{
|
||||
@@ -2532,13 +2520,11 @@ MemoryRegion *pci_address_space_io(PCIDevice *dev)
|
||||
static void pci_device_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *k = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->realize = pci_qdev_realize;
|
||||
k->unrealize = pci_qdev_unrealize;
|
||||
k->bus_type = TYPE_PCI_BUS;
|
||||
k->props = pci_props;
|
||||
pc->realize = pci_default_realize;
|
||||
}
|
||||
|
||||
static void pci_device_class_base_init(ObjectClass *klass, void *data)
|
||||
|
||||
+1
-2
@@ -722,8 +722,7 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt)
|
||||
}
|
||||
|
||||
if (hotplug_lmb_start) {
|
||||
MemoryDeviceInfoList **prev = &dimms;
|
||||
qmp_pc_dimm_device_list(qdev_get_machine(), &prev);
|
||||
dimms = qmp_pc_dimm_device_list();
|
||||
}
|
||||
|
||||
/* ibm,dynamic-memory */
|
||||
|
||||
+15
-1
@@ -3,9 +3,23 @@
|
||||
# hw/virtio/vhost.c
|
||||
vhost_commit(bool started, bool changed) "Started: %d Changed: %d"
|
||||
vhost_region_add_section(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64
|
||||
vhost_region_add_section_abut(const char *name, uint64_t new_size) "%s: 0x%"PRIx64
|
||||
vhost_region_add_section_merge(const char *name, uint64_t new_size, uint64_t gpa, uint64_t owr) "%s: size: 0x%"PRIx64 " gpa: 0x%"PRIx64 " owr: 0x%"PRIx64
|
||||
vhost_region_add_section_aligned(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64
|
||||
vhost_section(const char *name, int r) "%s:%d"
|
||||
|
||||
# hw/virtio/vhost-user.c
|
||||
vhost_user_postcopy_end_entry(void) ""
|
||||
vhost_user_postcopy_end_exit(void) ""
|
||||
vhost_user_postcopy_fault_handler(const char *name, uint64_t fault_address, int nregions) "%s: @0x%"PRIx64" nregions:%d"
|
||||
vhost_user_postcopy_fault_handler_loop(int i, uint64_t client_base, uint64_t size) "%d: client 0x%"PRIx64" +0x%"PRIx64
|
||||
vhost_user_postcopy_fault_handler_found(int i, uint64_t region_offset, uint64_t rb_offset) "%d: region_offset: 0x%"PRIx64" rb_offset:0x%"PRIx64
|
||||
vhost_user_postcopy_listen(void) ""
|
||||
vhost_user_set_mem_table_postcopy(uint64_t client_addr, uint64_t qhva, int reply_i, int region_i) "client:0x%"PRIx64" for hva: 0x%"PRIx64" reply %d region %d"
|
||||
vhost_user_set_mem_table_withfd(int index, const char *name, uint64_t memory_size, uint64_t guest_phys_addr, uint64_t userspace_addr, uint64_t offset) "%d:%s: size:0x%"PRIx64" GPA:0x%"PRIx64" QVA/userspace:0x%"PRIx64" RB offset:0x%"PRIx64
|
||||
vhost_user_postcopy_waker(const char *rb, uint64_t rb_offset) "%s + 0x%"PRIx64
|
||||
vhost_user_postcopy_waker_found(uint64_t client_addr) "0x%"PRIx64
|
||||
vhost_user_postcopy_waker_nomatch(const char *rb, uint64_t rb_offset) "%s + 0x%"PRIx64
|
||||
|
||||
# hw/virtio/virtio.c
|
||||
virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u"
|
||||
virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u"
|
||||
|
||||
+408
-5
@@ -18,11 +18,15 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "sysemu/cryptodev.h"
|
||||
#include "migration/migration.h"
|
||||
#include "migration/postcopy-ram.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <linux/vhost.h>
|
||||
#include <linux/userfaultfd.h>
|
||||
|
||||
#define VHOST_MEMORY_MAX_NREGIONS 8
|
||||
#define VHOST_USER_F_PROTOCOL_FEATURES 30
|
||||
@@ -41,7 +45,7 @@ enum VhostUserProtocolFeature {
|
||||
VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5,
|
||||
VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6,
|
||||
VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7,
|
||||
|
||||
VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
|
||||
VHOST_USER_PROTOCOL_F_MAX
|
||||
};
|
||||
|
||||
@@ -76,6 +80,9 @@ typedef enum VhostUserRequest {
|
||||
VHOST_USER_SET_CONFIG = 25,
|
||||
VHOST_USER_CREATE_CRYPTO_SESSION = 26,
|
||||
VHOST_USER_CLOSE_CRYPTO_SESSION = 27,
|
||||
VHOST_USER_POSTCOPY_ADVISE = 28,
|
||||
VHOST_USER_POSTCOPY_LISTEN = 29,
|
||||
VHOST_USER_POSTCOPY_END = 30,
|
||||
VHOST_USER_MAX
|
||||
} VhostUserRequest;
|
||||
|
||||
@@ -164,8 +171,23 @@ static VhostUserMsg m __attribute__ ((unused));
|
||||
#define VHOST_USER_VERSION (0x1)
|
||||
|
||||
struct vhost_user {
|
||||
struct vhost_dev *dev;
|
||||
CharBackend *chr;
|
||||
int slave_fd;
|
||||
NotifierWithReturn postcopy_notifier;
|
||||
struct PostCopyFD postcopy_fd;
|
||||
uint64_t postcopy_client_bases[VHOST_MEMORY_MAX_NREGIONS];
|
||||
/* Length of the region_rb and region_rb_offset arrays */
|
||||
size_t region_rb_len;
|
||||
/* RAMBlock associated with a given region */
|
||||
RAMBlock **region_rb;
|
||||
/* The offset from the start of the RAMBlock to the start of the
|
||||
* vhost region.
|
||||
*/
|
||||
ram_addr_t *region_rb_offset;
|
||||
|
||||
/* True once we've entered postcopy_listen */
|
||||
bool postcopy_listen;
|
||||
};
|
||||
|
||||
static bool ioeventfd_enabled(void)
|
||||
@@ -330,14 +352,167 @@ static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_user_set_mem_table(struct vhost_dev *dev,
|
||||
struct vhost_memory *mem)
|
||||
static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev,
|
||||
struct vhost_memory *mem)
|
||||
{
|
||||
struct vhost_user *u = dev->opaque;
|
||||
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
||||
int i, fd;
|
||||
size_t fd_num = 0;
|
||||
bool reply_supported = virtio_has_feature(dev->protocol_features,
|
||||
VHOST_USER_PROTOCOL_F_REPLY_ACK);
|
||||
VhostUserMsg msg_reply;
|
||||
int region_i, msg_i;
|
||||
|
||||
VhostUserMsg msg = {
|
||||
.hdr.request = VHOST_USER_SET_MEM_TABLE,
|
||||
.hdr.flags = VHOST_USER_VERSION,
|
||||
};
|
||||
|
||||
if (reply_supported) {
|
||||
msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK;
|
||||
}
|
||||
|
||||
if (u->region_rb_len < dev->mem->nregions) {
|
||||
u->region_rb = g_renew(RAMBlock*, u->region_rb, dev->mem->nregions);
|
||||
u->region_rb_offset = g_renew(ram_addr_t, u->region_rb_offset,
|
||||
dev->mem->nregions);
|
||||
memset(&(u->region_rb[u->region_rb_len]), '\0',
|
||||
sizeof(RAMBlock *) * (dev->mem->nregions - u->region_rb_len));
|
||||
memset(&(u->region_rb_offset[u->region_rb_len]), '\0',
|
||||
sizeof(ram_addr_t) * (dev->mem->nregions - u->region_rb_len));
|
||||
u->region_rb_len = dev->mem->nregions;
|
||||
}
|
||||
|
||||
for (i = 0; i < dev->mem->nregions; ++i) {
|
||||
struct vhost_memory_region *reg = dev->mem->regions + i;
|
||||
ram_addr_t offset;
|
||||
MemoryRegion *mr;
|
||||
|
||||
assert((uintptr_t)reg->userspace_addr == reg->userspace_addr);
|
||||
mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr,
|
||||
&offset);
|
||||
fd = memory_region_get_fd(mr);
|
||||
if (fd > 0) {
|
||||
trace_vhost_user_set_mem_table_withfd(fd_num, mr->name,
|
||||
reg->memory_size,
|
||||
reg->guest_phys_addr,
|
||||
reg->userspace_addr, offset);
|
||||
u->region_rb_offset[i] = offset;
|
||||
u->region_rb[i] = mr->ram_block;
|
||||
msg.payload.memory.regions[fd_num].userspace_addr =
|
||||
reg->userspace_addr;
|
||||
msg.payload.memory.regions[fd_num].memory_size = reg->memory_size;
|
||||
msg.payload.memory.regions[fd_num].guest_phys_addr =
|
||||
reg->guest_phys_addr;
|
||||
msg.payload.memory.regions[fd_num].mmap_offset = offset;
|
||||
assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
|
||||
fds[fd_num++] = fd;
|
||||
} else {
|
||||
u->region_rb_offset[i] = 0;
|
||||
u->region_rb[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
msg.payload.memory.nregions = fd_num;
|
||||
|
||||
if (!fd_num) {
|
||||
error_report("Failed initializing vhost-user memory map, "
|
||||
"consider using -object memory-backend-file share=on");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg.hdr.size = sizeof(msg.payload.memory.nregions);
|
||||
msg.hdr.size += sizeof(msg.payload.memory.padding);
|
||||
msg.hdr.size += fd_num * sizeof(VhostUserMemoryRegion);
|
||||
|
||||
if (vhost_user_write(dev, &msg, fds, fd_num) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vhost_user_read(dev, &msg_reply) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg_reply.hdr.request != VHOST_USER_SET_MEM_TABLE) {
|
||||
error_report("%s: Received unexpected msg type."
|
||||
"Expected %d received %d", __func__,
|
||||
VHOST_USER_SET_MEM_TABLE, msg_reply.hdr.request);
|
||||
return -1;
|
||||
}
|
||||
/* We're using the same structure, just reusing one of the
|
||||
* fields, so it should be the same size.
|
||||
*/
|
||||
if (msg_reply.hdr.size != msg.hdr.size) {
|
||||
error_report("%s: Unexpected size for postcopy reply "
|
||||
"%d vs %d", __func__, msg_reply.hdr.size, msg.hdr.size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(u->postcopy_client_bases, 0,
|
||||
sizeof(uint64_t) * VHOST_MEMORY_MAX_NREGIONS);
|
||||
|
||||
/* They're in the same order as the regions that were sent
|
||||
* but some of the regions were skipped (above) if they
|
||||
* didn't have fd's
|
||||
*/
|
||||
for (msg_i = 0, region_i = 0;
|
||||
region_i < dev->mem->nregions;
|
||||
region_i++) {
|
||||
if (msg_i < fd_num &&
|
||||
msg_reply.payload.memory.regions[msg_i].guest_phys_addr ==
|
||||
dev->mem->regions[region_i].guest_phys_addr) {
|
||||
u->postcopy_client_bases[region_i] =
|
||||
msg_reply.payload.memory.regions[msg_i].userspace_addr;
|
||||
trace_vhost_user_set_mem_table_postcopy(
|
||||
msg_reply.payload.memory.regions[msg_i].userspace_addr,
|
||||
msg.payload.memory.regions[msg_i].userspace_addr,
|
||||
msg_i, region_i);
|
||||
msg_i++;
|
||||
}
|
||||
}
|
||||
if (msg_i != fd_num) {
|
||||
error_report("%s: postcopy reply not fully consumed "
|
||||
"%d vs %zd",
|
||||
__func__, msg_i, fd_num);
|
||||
return -1;
|
||||
}
|
||||
/* Now we've registered this with the postcopy code, we ack to the client,
|
||||
* because now we're in the position to be able to deal with any faults
|
||||
* it generates.
|
||||
*/
|
||||
/* TODO: Use this for failure cases as well with a bad value */
|
||||
msg.hdr.size = sizeof(msg.payload.u64);
|
||||
msg.payload.u64 = 0; /* OK */
|
||||
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (reply_supported) {
|
||||
return process_message_reply(dev, &msg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_user_set_mem_table(struct vhost_dev *dev,
|
||||
struct vhost_memory *mem)
|
||||
{
|
||||
struct vhost_user *u = dev->opaque;
|
||||
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
||||
int i, fd;
|
||||
size_t fd_num = 0;
|
||||
bool do_postcopy = u->postcopy_listen && u->postcopy_fd.handler;
|
||||
bool reply_supported = virtio_has_feature(dev->protocol_features,
|
||||
VHOST_USER_PROTOCOL_F_REPLY_ACK) &&
|
||||
!do_postcopy;
|
||||
|
||||
if (do_postcopy) {
|
||||
/* Postcopy has enough differences that it's best done in it's own
|
||||
* version
|
||||
*/
|
||||
return vhost_user_set_mem_table_postcopy(dev, mem);
|
||||
}
|
||||
|
||||
VhostUserMsg msg = {
|
||||
.hdr.request = VHOST_USER_SET_MEM_TABLE,
|
||||
@@ -362,9 +537,11 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev,
|
||||
error_report("Failed preparing vhost-user memory table msg");
|
||||
return -1;
|
||||
}
|
||||
msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
|
||||
msg.payload.memory.regions[fd_num].userspace_addr =
|
||||
reg->userspace_addr;
|
||||
msg.payload.memory.regions[fd_num].memory_size = reg->memory_size;
|
||||
msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
|
||||
msg.payload.memory.regions[fd_num].guest_phys_addr =
|
||||
reg->guest_phys_addr;
|
||||
msg.payload.memory.regions[fd_num].mmap_offset = offset;
|
||||
fds[fd_num++] = fd;
|
||||
}
|
||||
@@ -791,6 +968,219 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called back from the postcopy fault thread when a fault is received on our
|
||||
* ufd.
|
||||
* TODO: This is Linux specific
|
||||
*/
|
||||
static int vhost_user_postcopy_fault_handler(struct PostCopyFD *pcfd,
|
||||
void *ufd)
|
||||
{
|
||||
struct vhost_dev *dev = pcfd->data;
|
||||
struct vhost_user *u = dev->opaque;
|
||||
struct uffd_msg *msg = ufd;
|
||||
uint64_t faultaddr = msg->arg.pagefault.address;
|
||||
RAMBlock *rb = NULL;
|
||||
uint64_t rb_offset;
|
||||
int i;
|
||||
|
||||
trace_vhost_user_postcopy_fault_handler(pcfd->idstr, faultaddr,
|
||||
dev->mem->nregions);
|
||||
for (i = 0; i < MIN(dev->mem->nregions, u->region_rb_len); i++) {
|
||||
trace_vhost_user_postcopy_fault_handler_loop(i,
|
||||
u->postcopy_client_bases[i], dev->mem->regions[i].memory_size);
|
||||
if (faultaddr >= u->postcopy_client_bases[i]) {
|
||||
/* Ofset of the fault address in the vhost region */
|
||||
uint64_t region_offset = faultaddr - u->postcopy_client_bases[i];
|
||||
if (region_offset < dev->mem->regions[i].memory_size) {
|
||||
rb_offset = region_offset + u->region_rb_offset[i];
|
||||
trace_vhost_user_postcopy_fault_handler_found(i,
|
||||
region_offset, rb_offset);
|
||||
rb = u->region_rb[i];
|
||||
return postcopy_request_shared_page(pcfd, rb, faultaddr,
|
||||
rb_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
error_report("%s: Failed to find region for fault %" PRIx64,
|
||||
__func__, faultaddr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int vhost_user_postcopy_waker(struct PostCopyFD *pcfd, RAMBlock *rb,
|
||||
uint64_t offset)
|
||||
{
|
||||
struct vhost_dev *dev = pcfd->data;
|
||||
struct vhost_user *u = dev->opaque;
|
||||
int i;
|
||||
|
||||
trace_vhost_user_postcopy_waker(qemu_ram_get_idstr(rb), offset);
|
||||
|
||||
if (!u) {
|
||||
return 0;
|
||||
}
|
||||
/* Translate the offset into an address in the clients address space */
|
||||
for (i = 0; i < MIN(dev->mem->nregions, u->region_rb_len); i++) {
|
||||
if (u->region_rb[i] == rb &&
|
||||
offset >= u->region_rb_offset[i] &&
|
||||
offset < (u->region_rb_offset[i] +
|
||||
dev->mem->regions[i].memory_size)) {
|
||||
uint64_t client_addr = (offset - u->region_rb_offset[i]) +
|
||||
u->postcopy_client_bases[i];
|
||||
trace_vhost_user_postcopy_waker_found(client_addr);
|
||||
return postcopy_wake_shared(pcfd, client_addr, rb);
|
||||
}
|
||||
}
|
||||
|
||||
trace_vhost_user_postcopy_waker_nomatch(qemu_ram_get_idstr(rb), offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at the start of an inbound postcopy on reception of the
|
||||
* 'advise' command.
|
||||
*/
|
||||
static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp)
|
||||
{
|
||||
struct vhost_user *u = dev->opaque;
|
||||
CharBackend *chr = u->chr;
|
||||
int ufd;
|
||||
VhostUserMsg msg = {
|
||||
.hdr.request = VHOST_USER_POSTCOPY_ADVISE,
|
||||
.hdr.flags = VHOST_USER_VERSION,
|
||||
};
|
||||
|
||||
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
||||
error_setg(errp, "Failed to send postcopy_advise to vhost");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vhost_user_read(dev, &msg) < 0) {
|
||||
error_setg(errp, "Failed to get postcopy_advise reply from vhost");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg.hdr.request != VHOST_USER_POSTCOPY_ADVISE) {
|
||||
error_setg(errp, "Unexpected msg type. Expected %d received %d",
|
||||
VHOST_USER_POSTCOPY_ADVISE, msg.hdr.request);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg.hdr.size) {
|
||||
error_setg(errp, "Received bad msg size.");
|
||||
return -1;
|
||||
}
|
||||
ufd = qemu_chr_fe_get_msgfd(chr);
|
||||
if (ufd < 0) {
|
||||
error_setg(errp, "%s: Failed to get ufd", __func__);
|
||||
return -1;
|
||||
}
|
||||
fcntl(ufd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
/* register ufd with userfault thread */
|
||||
u->postcopy_fd.fd = ufd;
|
||||
u->postcopy_fd.data = dev;
|
||||
u->postcopy_fd.handler = vhost_user_postcopy_fault_handler;
|
||||
u->postcopy_fd.waker = vhost_user_postcopy_waker;
|
||||
u->postcopy_fd.idstr = "vhost-user"; /* Need to find unique name */
|
||||
postcopy_register_shared_ufd(&u->postcopy_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at the switch to postcopy on reception of the 'listen' command.
|
||||
*/
|
||||
static int vhost_user_postcopy_listen(struct vhost_dev *dev, Error **errp)
|
||||
{
|
||||
struct vhost_user *u = dev->opaque;
|
||||
int ret;
|
||||
VhostUserMsg msg = {
|
||||
.hdr.request = VHOST_USER_POSTCOPY_LISTEN,
|
||||
.hdr.flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK,
|
||||
};
|
||||
u->postcopy_listen = true;
|
||||
trace_vhost_user_postcopy_listen();
|
||||
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
||||
error_setg(errp, "Failed to send postcopy_listen to vhost");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = process_message_reply(dev, &msg);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to receive reply to postcopy_listen");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at the end of postcopy
|
||||
*/
|
||||
static int vhost_user_postcopy_end(struct vhost_dev *dev, Error **errp)
|
||||
{
|
||||
VhostUserMsg msg = {
|
||||
.hdr.request = VHOST_USER_POSTCOPY_END,
|
||||
.hdr.flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK,
|
||||
};
|
||||
int ret;
|
||||
struct vhost_user *u = dev->opaque;
|
||||
|
||||
trace_vhost_user_postcopy_end_entry();
|
||||
if (vhost_user_write(dev, &msg, NULL, 0) < 0) {
|
||||
error_setg(errp, "Failed to send postcopy_end to vhost");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = process_message_reply(dev, &msg);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to receive reply to postcopy_end");
|
||||
return ret;
|
||||
}
|
||||
postcopy_unregister_shared_ufd(&u->postcopy_fd);
|
||||
u->postcopy_fd.handler = NULL;
|
||||
|
||||
trace_vhost_user_postcopy_end_exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier,
|
||||
void *opaque)
|
||||
{
|
||||
struct PostcopyNotifyData *pnd = opaque;
|
||||
struct vhost_user *u = container_of(notifier, struct vhost_user,
|
||||
postcopy_notifier);
|
||||
struct vhost_dev *dev = u->dev;
|
||||
|
||||
switch (pnd->reason) {
|
||||
case POSTCOPY_NOTIFY_PROBE:
|
||||
if (!virtio_has_feature(dev->protocol_features,
|
||||
VHOST_USER_PROTOCOL_F_PAGEFAULT)) {
|
||||
/* TODO: Get the device name into this error somehow */
|
||||
error_setg(pnd->errp,
|
||||
"vhost-user backend not capable of postcopy");
|
||||
return -ENOENT;
|
||||
}
|
||||
break;
|
||||
|
||||
case POSTCOPY_NOTIFY_INBOUND_ADVISE:
|
||||
return vhost_user_postcopy_advise(dev, pnd->errp);
|
||||
|
||||
case POSTCOPY_NOTIFY_INBOUND_LISTEN:
|
||||
return vhost_user_postcopy_listen(dev, pnd->errp);
|
||||
|
||||
case POSTCOPY_NOTIFY_INBOUND_END:
|
||||
return vhost_user_postcopy_end(dev, pnd->errp);
|
||||
|
||||
default:
|
||||
/* We ignore notifications we don't know */
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhost_user_init(struct vhost_dev *dev, void *opaque)
|
||||
{
|
||||
uint64_t features, protocol_features;
|
||||
@@ -802,6 +1192,7 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque)
|
||||
u = g_new0(struct vhost_user, 1);
|
||||
u->chr = opaque;
|
||||
u->slave_fd = -1;
|
||||
u->dev = dev;
|
||||
dev->opaque = u;
|
||||
|
||||
err = vhost_user_get_features(dev, &features);
|
||||
@@ -858,6 +1249,9 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque)
|
||||
return err;
|
||||
}
|
||||
|
||||
u->postcopy_notifier.notify = vhost_user_postcopy_notifier;
|
||||
postcopy_add_notifier(&u->postcopy_notifier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -868,11 +1262,20 @@ static int vhost_user_cleanup(struct vhost_dev *dev)
|
||||
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
|
||||
|
||||
u = dev->opaque;
|
||||
if (u->postcopy_notifier.notify) {
|
||||
postcopy_remove_notifier(&u->postcopy_notifier);
|
||||
u->postcopy_notifier.notify = NULL;
|
||||
}
|
||||
if (u->slave_fd >= 0) {
|
||||
qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL);
|
||||
close(u->slave_fd);
|
||||
u->slave_fd = -1;
|
||||
}
|
||||
g_free(u->region_rb);
|
||||
u->region_rb = NULL;
|
||||
g_free(u->region_rb_offset);
|
||||
u->region_rb_offset = NULL;
|
||||
u->region_rb_len = 0;
|
||||
g_free(u);
|
||||
dev->opaque = 0;
|
||||
|
||||
|
||||
+56
-10
@@ -522,10 +522,28 @@ static void vhost_region_add_section(struct vhost_dev *dev,
|
||||
uint64_t mrs_gpa = section->offset_within_address_space;
|
||||
uintptr_t mrs_host = (uintptr_t)memory_region_get_ram_ptr(section->mr) +
|
||||
section->offset_within_region;
|
||||
RAMBlock *mrs_rb = section->mr->ram_block;
|
||||
size_t mrs_page = qemu_ram_pagesize(mrs_rb);
|
||||
|
||||
trace_vhost_region_add_section(section->mr->name, mrs_gpa, mrs_size,
|
||||
mrs_host);
|
||||
|
||||
/* Round the section to it's page size */
|
||||
/* First align the start down to a page boundary */
|
||||
uint64_t alignage = mrs_host & (mrs_page - 1);
|
||||
if (alignage) {
|
||||
mrs_host -= alignage;
|
||||
mrs_size += alignage;
|
||||
mrs_gpa -= alignage;
|
||||
}
|
||||
/* Now align the size up to a page boundary */
|
||||
alignage = mrs_size & (mrs_page - 1);
|
||||
if (alignage) {
|
||||
mrs_size += mrs_page - alignage;
|
||||
}
|
||||
trace_vhost_region_add_section_aligned(section->mr->name, mrs_gpa, mrs_size,
|
||||
mrs_host);
|
||||
|
||||
if (dev->n_tmp_sections) {
|
||||
/* Since we already have at least one section, lets see if
|
||||
* this extends it; since we're scanning in order, we only
|
||||
@@ -542,18 +560,46 @@ static void vhost_region_add_section(struct vhost_dev *dev,
|
||||
prev_sec->offset_within_region;
|
||||
uint64_t prev_host_end = range_get_last(prev_host_start, prev_size);
|
||||
|
||||
if (prev_gpa_end + 1 == mrs_gpa &&
|
||||
prev_host_end + 1 == mrs_host &&
|
||||
section->mr == prev_sec->mr &&
|
||||
(!dev->vhost_ops->vhost_backend_can_merge ||
|
||||
dev->vhost_ops->vhost_backend_can_merge(dev,
|
||||
if (mrs_gpa <= (prev_gpa_end + 1)) {
|
||||
/* OK, looks like overlapping/intersecting - it's possible that
|
||||
* the rounding to page sizes has made them overlap, but they should
|
||||
* match up in the same RAMBlock if they do.
|
||||
*/
|
||||
if (mrs_gpa < prev_gpa_start) {
|
||||
error_report("%s:Section rounded to %"PRIx64
|
||||
" prior to previous %"PRIx64,
|
||||
__func__, mrs_gpa, prev_gpa_start);
|
||||
/* A way to cleanly fail here would be better */
|
||||
return;
|
||||
}
|
||||
/* Offset from the start of the previous GPA to this GPA */
|
||||
size_t offset = mrs_gpa - prev_gpa_start;
|
||||
|
||||
if (prev_host_start + offset == mrs_host &&
|
||||
section->mr == prev_sec->mr &&
|
||||
(!dev->vhost_ops->vhost_backend_can_merge ||
|
||||
dev->vhost_ops->vhost_backend_can_merge(dev,
|
||||
mrs_host, mrs_size,
|
||||
prev_host_start, prev_size))) {
|
||||
/* The two sections abut */
|
||||
need_add = false;
|
||||
prev_sec->size = int128_add(prev_sec->size, section->size);
|
||||
trace_vhost_region_add_section_abut(section->mr->name,
|
||||
mrs_size + prev_size);
|
||||
uint64_t max_end = MAX(prev_host_end, mrs_host + mrs_size);
|
||||
need_add = false;
|
||||
prev_sec->offset_within_address_space =
|
||||
MIN(prev_gpa_start, mrs_gpa);
|
||||
prev_sec->offset_within_region =
|
||||
MIN(prev_host_start, mrs_host) -
|
||||
(uintptr_t)memory_region_get_ram_ptr(prev_sec->mr);
|
||||
prev_sec->size = int128_make64(max_end - MIN(prev_host_start,
|
||||
mrs_host));
|
||||
trace_vhost_region_add_section_merge(section->mr->name,
|
||||
int128_get64(prev_sec->size),
|
||||
prev_sec->offset_within_address_space,
|
||||
prev_sec->offset_within_region);
|
||||
} else {
|
||||
error_report("%s: Overlapping but not coherent sections "
|
||||
"at %"PRIx64,
|
||||
__func__, mrs_gpa);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,10 +68,14 @@ ram_addr_t qemu_ram_addr_from_host(void *ptr);
|
||||
RAMBlock *qemu_ram_block_by_name(const char *name);
|
||||
RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset,
|
||||
ram_addr_t *offset);
|
||||
ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host);
|
||||
void qemu_ram_set_idstr(RAMBlock *block, const char *name, DeviceState *dev);
|
||||
void qemu_ram_unset_idstr(RAMBlock *block);
|
||||
const char *qemu_ram_get_idstr(RAMBlock *rb);
|
||||
bool qemu_ram_is_shared(RAMBlock *rb);
|
||||
bool qemu_ram_is_uf_zeroable(RAMBlock *rb);
|
||||
void qemu_ram_set_uf_zeroable(RAMBlock *rb);
|
||||
|
||||
size_t qemu_ram_pagesize(RAMBlock *block);
|
||||
size_t qemu_ram_pagesize_largest(void);
|
||||
|
||||
|
||||
+31
-103
@@ -40,18 +40,6 @@ enum {
|
||||
ACPI_FADT_F_LOW_POWER_S0_IDLE_CAPABLE,
|
||||
};
|
||||
|
||||
/*
|
||||
* ACPI 2.0 Generic Address Space definition.
|
||||
*/
|
||||
struct Acpi20GenericAddress {
|
||||
uint8_t address_space_id;
|
||||
uint8_t register_bit_width;
|
||||
uint8_t register_bit_offset;
|
||||
uint8_t reserved;
|
||||
uint64_t address;
|
||||
} QEMU_PACKED;
|
||||
typedef struct Acpi20GenericAddress Acpi20GenericAddress;
|
||||
|
||||
struct AcpiRsdpDescriptor { /* Root System Descriptor Pointer */
|
||||
uint64_t signature; /* ACPI signature, contains "RSD PTR " */
|
||||
uint8_t checksum; /* To make sum of struct == 0 */
|
||||
@@ -87,104 +75,44 @@ struct AcpiTableHeader {
|
||||
} QEMU_PACKED;
|
||||
typedef struct AcpiTableHeader AcpiTableHeader;
|
||||
|
||||
/*
|
||||
* ACPI Fixed ACPI Description Table (FADT)
|
||||
*/
|
||||
#define ACPI_FADT_COMMON_DEF /* FADT common definition */ \
|
||||
ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \
|
||||
uint32_t firmware_ctrl; /* Physical address of FACS */ \
|
||||
uint32_t dsdt; /* Physical address of DSDT */ \
|
||||
uint8_t model; /* System Interrupt Model */ \
|
||||
uint8_t reserved1; /* Reserved */ \
|
||||
uint16_t sci_int; /* System vector of SCI interrupt */ \
|
||||
uint32_t smi_cmd; /* Port address of SMI command port */ \
|
||||
uint8_t acpi_enable; /* Value to write to smi_cmd to enable ACPI */ \
|
||||
uint8_t acpi_disable; /* Value to write to smi_cmd to disable ACPI */ \
|
||||
/* Value to write to SMI CMD to enter S4BIOS state */ \
|
||||
uint8_t S4bios_req; \
|
||||
uint8_t reserved2; /* Reserved - must be zero */ \
|
||||
/* Port address of Power Mgt 1a acpi_event Reg Blk */ \
|
||||
uint32_t pm1a_evt_blk; \
|
||||
/* Port address of Power Mgt 1b acpi_event Reg Blk */ \
|
||||
uint32_t pm1b_evt_blk; \
|
||||
uint32_t pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ \
|
||||
uint32_t pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ \
|
||||
uint32_t pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ \
|
||||
uint32_t pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ \
|
||||
/* Port addr of General Purpose acpi_event 0 Reg Blk */ \
|
||||
uint32_t gpe0_blk; \
|
||||
/* Port addr of General Purpose acpi_event 1 Reg Blk */ \
|
||||
uint32_t gpe1_blk; \
|
||||
uint8_t pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */ \
|
||||
uint8_t pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */ \
|
||||
uint8_t pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ \
|
||||
uint8_t pm_tmr_len; /* Byte Length of ports at pm_tm_blk */ \
|
||||
uint8_t gpe0_blk_len; /* Byte Length of ports at gpe0_blk */ \
|
||||
uint8_t gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ \
|
||||
uint8_t gpe1_base; /* Offset in gpe model where gpe1 events start */ \
|
||||
uint8_t reserved3; /* Reserved */ \
|
||||
uint16_t plvl2_lat; /* Worst case HW latency to enter/exit C2 state */ \
|
||||
uint16_t plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ \
|
||||
uint16_t flush_size; /* Size of area read to flush caches */ \
|
||||
uint16_t flush_stride; /* Stride used in flushing caches */ \
|
||||
uint8_t duty_offset; /* Bit location of duty cycle field in p_cnt reg */ \
|
||||
uint8_t duty_width; /* Bit width of duty cycle field in p_cnt reg */ \
|
||||
uint8_t day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ \
|
||||
uint8_t mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ \
|
||||
uint8_t century; /* Index to century in RTC CMOS RAM */ \
|
||||
/* IA-PC Boot Architecture Flags (see below for individual flags) */ \
|
||||
uint16_t boot_flags; \
|
||||
uint8_t reserved; /* Reserved, must be zero */ \
|
||||
/* Miscellaneous flag bits (see below for individual flags) */ \
|
||||
uint32_t flags; \
|
||||
/* 64-bit address of the Reset register */ \
|
||||
struct AcpiGenericAddress reset_register; \
|
||||
/* Value to write to the reset_register port to reset the system */ \
|
||||
uint8_t reset_value; \
|
||||
/* ARM-Specific Boot Flags (see below for individual flags) (ACPI 5.1) */ \
|
||||
uint16_t arm_boot_flags; \
|
||||
uint8_t minor_revision; /* FADT Minor Revision (ACPI 5.1) */ \
|
||||
uint64_t x_facs; /* 64-bit physical address of FACS */ \
|
||||
uint64_t x_dsdt; /* 64-bit physical address of DSDT */ \
|
||||
/* 64-bit Extended Power Mgt 1a Event Reg Blk address */ \
|
||||
struct AcpiGenericAddress xpm1a_event_block; \
|
||||
/* 64-bit Extended Power Mgt 1b Event Reg Blk address */ \
|
||||
struct AcpiGenericAddress xpm1b_event_block; \
|
||||
/* 64-bit Extended Power Mgt 1a Control Reg Blk address */ \
|
||||
struct AcpiGenericAddress xpm1a_control_block; \
|
||||
/* 64-bit Extended Power Mgt 1b Control Reg Blk address */ \
|
||||
struct AcpiGenericAddress xpm1b_control_block; \
|
||||
/* 64-bit Extended Power Mgt 2 Control Reg Blk address */ \
|
||||
struct AcpiGenericAddress xpm2_control_block; \
|
||||
/* 64-bit Extended Power Mgt Timer Ctrl Reg Blk address */ \
|
||||
struct AcpiGenericAddress xpm_timer_block; \
|
||||
/* 64-bit Extended General Purpose Event 0 Reg Blk address */ \
|
||||
struct AcpiGenericAddress xgpe0_block; \
|
||||
/* 64-bit Extended General Purpose Event 1 Reg Blk address */ \
|
||||
struct AcpiGenericAddress xgpe1_block; \
|
||||
|
||||
struct AcpiGenericAddress {
|
||||
uint8_t space_id; /* Address space where struct or register exists */
|
||||
uint8_t bit_width; /* Size in bits of given register */
|
||||
uint8_t bit_offset; /* Bit offset within the register */
|
||||
uint8_t access_width; /* Minimum Access size (ACPI 3.0) */
|
||||
uint8_t access_width; /* ACPI 3.0: Minimum Access size (ACPI 3.0),
|
||||
ACPI 2.0: Reserved, Table 5-1 */
|
||||
uint64_t address; /* 64-bit address of struct or register */
|
||||
} QEMU_PACKED;
|
||||
|
||||
struct AcpiFadtDescriptorRev3 {
|
||||
ACPI_FADT_COMMON_DEF
|
||||
} QEMU_PACKED;
|
||||
typedef struct AcpiFadtDescriptorRev3 AcpiFadtDescriptorRev3;
|
||||
typedef struct AcpiFadtData {
|
||||
struct AcpiGenericAddress pm1a_cnt; /* PM1a_CNT_BLK */
|
||||
struct AcpiGenericAddress pm1a_evt; /* PM1a_EVT_BLK */
|
||||
struct AcpiGenericAddress pm_tmr; /* PM_TMR_BLK */
|
||||
struct AcpiGenericAddress gpe0_blk; /* GPE0_BLK */
|
||||
struct AcpiGenericAddress reset_reg; /* RESET_REG */
|
||||
uint8_t reset_val; /* RESET_VALUE */
|
||||
uint8_t rev; /* Revision */
|
||||
uint32_t flags; /* Flags */
|
||||
uint32_t smi_cmd; /* SMI_CMD */
|
||||
uint16_t sci_int; /* SCI_INT */
|
||||
uint8_t int_model; /* INT_MODEL */
|
||||
uint8_t acpi_enable_cmd; /* ACPI_ENABLE */
|
||||
uint8_t acpi_disable_cmd; /* ACPI_DISABLE */
|
||||
uint8_t rtc_century; /* CENTURY */
|
||||
uint16_t plvl2_lat; /* P_LVL2_LAT */
|
||||
uint16_t plvl3_lat; /* P_LVL3_LAT */
|
||||
uint16_t arm_boot_arch; /* ARM_BOOT_ARCH */
|
||||
uint8_t minor_ver; /* FADT Minor Version */
|
||||
|
||||
struct AcpiFadtDescriptorRev5_1 {
|
||||
ACPI_FADT_COMMON_DEF
|
||||
/* 64-bit Sleep Control register (ACPI 5.0) */
|
||||
struct AcpiGenericAddress sleep_control;
|
||||
/* 64-bit Sleep Status register (ACPI 5.0) */
|
||||
struct AcpiGenericAddress sleep_status;
|
||||
} QEMU_PACKED;
|
||||
|
||||
typedef struct AcpiFadtDescriptorRev5_1 AcpiFadtDescriptorRev5_1;
|
||||
/*
|
||||
* respective tables offsets within ACPI_BUILD_TABLE_FILE,
|
||||
* NULL if table doesn't exist (in that case field's value
|
||||
* won't be patched by linker and will be kept set to 0)
|
||||
*/
|
||||
unsigned *facs_tbl_offset; /* FACS offset in */
|
||||
unsigned *dsdt_tbl_offset;
|
||||
unsigned *xdsdt_tbl_offset;
|
||||
} AcpiFadtData;
|
||||
|
||||
#define ACPI_FADT_ARM_PSCI_COMPLIANT (1 << 0)
|
||||
#define ACPI_FADT_ARM_PSCI_USE_HVC (1 << 1)
|
||||
@@ -456,7 +384,7 @@ typedef struct AcpiGenericTimerTable AcpiGenericTimerTable;
|
||||
struct Acpi20Hpet {
|
||||
ACPI_TABLE_HEADER_DEF /* ACPI common table header */
|
||||
uint32_t timer_block_id;
|
||||
Acpi20GenericAddress addr;
|
||||
struct AcpiGenericAddress addr;
|
||||
uint8_t hpet_number;
|
||||
uint16_t min_tick;
|
||||
uint8_t page_protect;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user