Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
a63ffba266
@ -74,7 +74,6 @@ build-system-debian:
|
||||
job: amd64-debian-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
CONFIGURE_ARGS: --enable-fdt=system
|
||||
TARGETS: arm-softmmu avr-softmmu i386-softmmu mipsel-softmmu
|
||||
riscv64-softmmu sh4eb-softmmu sparc-softmmu xtensaeb-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
@ -202,85 +201,6 @@ acceptance-system-opensuse:
|
||||
MAKE_CHECK_ARGS: check-acceptance
|
||||
|
||||
|
||||
build-disabled:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS:
|
||||
--disable-attr
|
||||
--disable-auth-pam
|
||||
--disable-avx2
|
||||
--disable-bochs
|
||||
--disable-brlapi
|
||||
--disable-bzip2
|
||||
--disable-cap-ng
|
||||
--disable-capstone
|
||||
--disable-cloop
|
||||
--disable-coroutine-pool
|
||||
--disable-curl
|
||||
--disable-curses
|
||||
--disable-dmg
|
||||
--disable-docs
|
||||
--disable-gcrypt
|
||||
--disable-glusterfs
|
||||
--disable-gnutls
|
||||
--disable-gtk
|
||||
--disable-guest-agent
|
||||
--disable-iconv
|
||||
--disable-keyring
|
||||
--disable-kvm
|
||||
--disable-libiscsi
|
||||
--disable-libpmem
|
||||
--disable-libssh
|
||||
--disable-libudev
|
||||
--disable-libusb
|
||||
--disable-libxml2
|
||||
--disable-linux-aio
|
||||
--disable-live-block-migration
|
||||
--disable-lzo
|
||||
--disable-malloc-trim
|
||||
--disable-mpath
|
||||
--disable-nettle
|
||||
--disable-numa
|
||||
--disable-opengl
|
||||
--disable-parallels
|
||||
--disable-pie
|
||||
--disable-qcow1
|
||||
--disable-qed
|
||||
--disable-qom-cast-debug
|
||||
--disable-rbd
|
||||
--disable-rdma
|
||||
--disable-replication
|
||||
--disable-sdl
|
||||
--disable-seccomp
|
||||
--disable-slirp
|
||||
--disable-smartcard
|
||||
--disable-snappy
|
||||
--disable-sparse
|
||||
--disable-spice
|
||||
--disable-strip
|
||||
--disable-tpm
|
||||
--disable-usb-redir
|
||||
--disable-vdi
|
||||
--disable-vhost-crypto
|
||||
--disable-vhost-net
|
||||
--disable-vhost-scsi
|
||||
--disable-vhost-kernel
|
||||
--disable-vhost-user
|
||||
--disable-vhost-vdpa
|
||||
--disable-vhost-vsock
|
||||
--disable-virglrenderer
|
||||
--disable-vnc
|
||||
--disable-vte
|
||||
--disable-vvfat
|
||||
--disable-xen
|
||||
--disable-zstd
|
||||
TARGETS: arm-softmmu i386-softmmu ppc64-softmmu mips64-softmmu
|
||||
s390x-softmmu i386-linux-user
|
||||
MAKE_CHECK_ARGS: check-qtest SPEED=slow
|
||||
|
||||
# This jobs explicitly disable TCG (--disable-tcg), KVM is detected by
|
||||
# the configure script. The container doesn't contain Xen headers so
|
||||
# Xen accelerator is not detected / selected. As result it build the
|
||||
@ -305,11 +225,11 @@ build-tcg-disabled:
|
||||
- cd tests/qemu-iotests/
|
||||
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
|
||||
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
|
||||
170 171 183 184 192 194 208 221 222 226 227 236 253 277
|
||||
170 171 183 184 192 194 208 221 226 227 236 253 277 image-fleecing
|
||||
- ./check -qcow2 028 051 056 057 058 065 068 082 085 091 095 096 102 122
|
||||
124 132 139 142 144 145 151 152 155 157 165 194 196 200 202
|
||||
208 209 216 218 222 227 234 246 247 248 250 254 255 257 258
|
||||
260 261 262 263 264 270 272 273 277 279
|
||||
208 209 216 218 227 234 246 247 248 250 254 255 257 258
|
||||
260 261 262 263 264 270 272 273 277 279 image-fleecing
|
||||
|
||||
build-user:
|
||||
extends: .native_build_job_template
|
||||
@ -649,20 +569,26 @@ build-without-default-devices:
|
||||
build-without-default-features:
|
||||
extends: .native_build_job_template
|
||||
needs:
|
||||
job: amd64-debian-container
|
||||
job: amd64-fedora-container
|
||||
variables:
|
||||
IMAGE: debian-amd64
|
||||
CONFIGURE_ARGS: --without-default-features --disable-user
|
||||
--target-list-exclude=arm-softmmu,i386-softmmu,mipsel-softmmu,mips64-softmmu,ppc-softmmu
|
||||
MAKE_CHECK_ARGS: check-unit
|
||||
IMAGE: fedora
|
||||
CONFIGURE_ARGS:
|
||||
--without-default-features
|
||||
--disable-capstone
|
||||
--disable-fdt
|
||||
--disable-pie
|
||||
--disable-qom-cast-debug
|
||||
--disable-slirp
|
||||
--disable-strip
|
||||
TARGETS: avr-softmmu i386-softmmu mips64-softmmu s390x-softmmu sh4-softmmu
|
||||
sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user
|
||||
MAKE_CHECK_ARGS: check-unit check-qtest SPEED=slow
|
||||
|
||||
build-libvhost-user:
|
||||
stage: build
|
||||
image: $CI_REGISTRY_IMAGE/qemu/fedora:latest
|
||||
needs:
|
||||
job: amd64-fedora-container
|
||||
before_script:
|
||||
- dnf install -y meson ninja-build
|
||||
script:
|
||||
- mkdir subprojects/libvhost-user/build
|
||||
- cd subprojects/libvhost-user/build
|
||||
|
@ -137,7 +137,7 @@ ubuntu-20.04-aarch64-all-linux-static:
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
- if: "$AARCH64_RUNNER_AVAILABLE"
|
||||
script:
|
||||
# --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763
|
||||
# --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages
|
||||
@ -157,7 +157,7 @@ ubuntu-20.04-aarch64-all:
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
- if: "$AARCH64_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
@ -174,7 +174,7 @@ ubuntu-20.04-aarch64-alldbg:
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
- if: "$AARCH64_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
@ -193,7 +193,7 @@ ubuntu-20.04-aarch64-clang:
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
when: manual
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
- if: "$AARCH64_RUNNER_AVAILABLE"
|
||||
when: manual
|
||||
script:
|
||||
- mkdir build
|
||||
@ -211,7 +211,7 @@ ubuntu-20.04-aarch64-tci:
|
||||
- aarch64
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
- if: "$AARCH64_RUNNER_AVAILABLE"
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
@ -228,7 +228,7 @@ ubuntu-20.04-aarch64-notcg:
|
||||
rules:
|
||||
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
|
||||
when: manual
|
||||
- if: "$S390X_RUNNER_AVAILABLE"
|
||||
- if: "$AARCH64_RUNNER_AVAILABLE"
|
||||
when: manual
|
||||
script:
|
||||
- mkdir build
|
||||
|
75
MAINTAINERS
75
MAINTAINERS
@ -617,6 +617,7 @@ F: hw/intc/gic_internal.h
|
||||
F: hw/misc/a9scu.c
|
||||
F: hw/misc/arm11scu.c
|
||||
F: hw/misc/arm_l2x0.c
|
||||
F: hw/misc/armv7m_ras.c
|
||||
F: hw/timer/a9gtimer*
|
||||
F: hw/timer/arm*
|
||||
F: include/hw/arm/arm*.h
|
||||
@ -626,6 +627,7 @@ F: include/hw/misc/arm11scu.h
|
||||
F: include/hw/timer/a9gtimer.h
|
||||
F: include/hw/timer/arm_mptimer.h
|
||||
F: include/hw/timer/armv7m_systick.h
|
||||
F: include/hw/misc/armv7m_ras.h
|
||||
F: tests/qtest/test-arm-mptimer.c
|
||||
|
||||
Exynos
|
||||
@ -1660,6 +1662,16 @@ F: hw/net/opencores_eth.c
|
||||
|
||||
Devices
|
||||
-------
|
||||
Overall Audio frontends
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: hw/audio/
|
||||
F: include/hw/audio/
|
||||
F: tests/qtest/ac97-test.c
|
||||
F: tests/qtest/es1370-test.c
|
||||
F: tests/qtest/intel-hda-test.c
|
||||
F: tests/qtest/fuzz-sb16-test.c
|
||||
|
||||
Xilinx CAN
|
||||
M: Vikram Garhwal <fnu.vikram@xilinx.com>
|
||||
M: Francisco Iglesias <francisco.iglesias@xilinx.com>
|
||||
@ -1739,6 +1751,7 @@ F: docs/specs/*pci*
|
||||
ACPI/SMBIOS
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Igor Mammedov <imammedo@redhat.com>
|
||||
R: Ani Sinha <ani@anisinha.ca>
|
||||
S: Supported
|
||||
F: include/hw/acpi/*
|
||||
F: include/hw/firmware/smbios.h
|
||||
@ -1751,6 +1764,10 @@ F: qapi/acpi.json
|
||||
F: tests/qtest/bios-tables-test*
|
||||
F: tests/qtest/acpi-utils.[hc]
|
||||
F: tests/data/acpi/
|
||||
F: docs/specs/acpi_cpu_hotplug.rst
|
||||
F: docs/specs/acpi_mem_hotplug.rst
|
||||
F: docs/specs/acpi_pci_hotplug.rst
|
||||
F: docs/specs/acpi_hw_reduced_hotplug.rst
|
||||
|
||||
ACPI/HEST/GHES
|
||||
R: Dongjiu Geng <gengdongjiu1@gmail.com>
|
||||
@ -2057,6 +2074,7 @@ F: hw/acpi/nvdimm.c
|
||||
F: hw/mem/nvdimm.c
|
||||
F: include/hw/mem/nvdimm.h
|
||||
F: docs/nvdimm.txt
|
||||
F: docs/specs/acpi_nvdimm.rst
|
||||
|
||||
e1000x
|
||||
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
|
||||
@ -2256,17 +2274,54 @@ F: include/net/can_*.h
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
Overall Audio backends
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/
|
||||
F: hw/audio/
|
||||
F: include/hw/audio/
|
||||
X: audio/alsaaudio.c
|
||||
X: audio/coreaudio.c
|
||||
X: audio/dsound*
|
||||
X: audio/jackaudio.c
|
||||
X: audio/ossaudio.c
|
||||
X: audio/paaudio.c
|
||||
X: audio/sdlaudio.c
|
||||
X: audio/spiceaudio.c
|
||||
F: qapi/audio.json
|
||||
F: tests/qtest/ac97-test.c
|
||||
F: tests/qtest/es1370-test.c
|
||||
F: tests/qtest/intel-hda-test.c
|
||||
F: tests/qtest/fuzz-sb16-test.c
|
||||
|
||||
ALSA Audio backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/alsaaudio.c
|
||||
|
||||
Core Audio framework backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/coreaudio.c
|
||||
|
||||
DSound Audio backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/dsound*
|
||||
|
||||
JACK Audio Connection Kit backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/jackaudio.c
|
||||
|
||||
Open Sound System (OSS) Audio backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/ossaudio.c
|
||||
|
||||
PulseAudio backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/paaudio.c
|
||||
|
||||
SDL Audio backend
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: audio/sdlaudio.c
|
||||
|
||||
Block layer core
|
||||
M: Kevin Wolf <kwolf@redhat.com>
|
||||
@ -2337,8 +2392,8 @@ F: block/mirror.c
|
||||
F: qapi/job.json
|
||||
F: block/block-copy.c
|
||||
F: include/block/block-copy.c
|
||||
F: block/backup-top.h
|
||||
F: block/backup-top.c
|
||||
F: block/copy-before-write.h
|
||||
F: block/copy-before-write.c
|
||||
F: include/block/aio_task.h
|
||||
F: block/aio_task.c
|
||||
F: util/qemu-co-shared-resource.c
|
||||
@ -2706,6 +2761,8 @@ R: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Bandan Das <bsd@redhat.com>
|
||||
R: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
R: Thomas Huth <thuth@redhat.com>
|
||||
R: Darren Kenny <darren.kenny@oracle.com>
|
||||
R: Qiuhao Li <Qiuhao.Li@outlook.com>
|
||||
S: Maintained
|
||||
F: tests/qtest/fuzz/
|
||||
F: tests/qtest/fuzz-*test.c
|
||||
|
@ -492,8 +492,7 @@ static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
|
||||
error_setg(&tpm_emu->migration_blocker,
|
||||
"Migration disabled: TPM emulator does not support "
|
||||
"migration");
|
||||
migrate_add_blocker(tpm_emu->migration_blocker, &err);
|
||||
if (err) {
|
||||
if (migrate_add_blocker(tpm_emu->migration_blocker, &err) < 0) {
|
||||
error_report_err(err);
|
||||
error_free(tpm_emu->migration_blocker);
|
||||
tpm_emu->migration_blocker = NULL;
|
||||
|
31
block.c
31
block.c
@ -5048,6 +5048,37 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Not for empty child */
|
||||
int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
Transaction *tran = tran_new();
|
||||
g_autoptr(GHashTable) found = NULL;
|
||||
g_autoptr(GSList) refresh_list = NULL;
|
||||
BlockDriverState *old_bs = child->bs;
|
||||
|
||||
bdrv_ref(old_bs);
|
||||
bdrv_drained_begin(old_bs);
|
||||
bdrv_drained_begin(new_bs);
|
||||
|
||||
bdrv_replace_child_tran(child, new_bs, tran);
|
||||
|
||||
found = g_hash_table_new(NULL, NULL);
|
||||
refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs);
|
||||
refresh_list = bdrv_topological_dfs(refresh_list, found, new_bs);
|
||||
|
||||
ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
|
||||
|
||||
tran_finalize(tran, ret);
|
||||
|
||||
bdrv_drained_end(old_bs);
|
||||
bdrv_drained_end(new_bs);
|
||||
bdrv_unref(old_bs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bdrv_delete(BlockDriverState *bs)
|
||||
{
|
||||
assert(bdrv_op_blocker_is_empty(bs));
|
||||
|
@ -1,253 +0,0 @@
|
||||
/*
|
||||
* backup-top filter driver
|
||||
*
|
||||
* The driver performs Copy-Before-Write (CBW) operation: it is injected above
|
||||
* some node, and before each write it copies _old_ data to the target node.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Virtuozzo International GmbH.
|
||||
*
|
||||
* Author:
|
||||
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "block/block-copy.h"
|
||||
|
||||
#include "block/backup-top.h"
|
||||
|
||||
typedef struct BDRVBackupTopState {
|
||||
BlockCopyState *bcs;
|
||||
BdrvChild *target;
|
||||
int64_t cluster_size;
|
||||
} BDRVBackupTopState;
|
||||
|
||||
static coroutine_fn int backup_top_co_preadv(
|
||||
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVBackupTopState *s = bs->opaque;
|
||||
uint64_t off, end;
|
||||
|
||||
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
|
||||
|
||||
return block_copy(s->bcs, off, end - off, true);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
int ret = backup_top_cbw(bs, offset, bytes, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pdiscard(bs->backing, offset, bytes);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret = backup_top_cbw(bs, offset, bytes, flags);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
|
||||
}
|
||||
|
||||
static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
int ret = backup_top_cbw(bs, offset, bytes, flags);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_top_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
if (!bs->backing) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bdrv_co_flush(bs->backing->bs);
|
||||
}
|
||||
|
||||
static void backup_top_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
if (bs->backing == NULL) {
|
||||
/*
|
||||
* we can be here after failed bdrv_attach_child in
|
||||
* bdrv_set_backing_hd
|
||||
*/
|
||||
return;
|
||||
}
|
||||
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
bs->backing->bs->filename);
|
||||
}
|
||||
|
||||
static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
BdrvChildRole role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
if (!(role & BDRV_CHILD_FILTERED)) {
|
||||
/*
|
||||
* Target child
|
||||
*
|
||||
* Share write to target (child_file), to not interfere
|
||||
* with guest writes to its disk which may be in target backing chain.
|
||||
* Can't resize during a backup block job because we check the size
|
||||
* only upfront.
|
||||
*/
|
||||
*nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
|
||||
*nperm = BLK_PERM_WRITE;
|
||||
} else {
|
||||
/* Source child */
|
||||
bdrv_default_perms(bs, c, role, reopen_queue,
|
||||
perm, shared, nperm, nshared);
|
||||
|
||||
if (perm & BLK_PERM_WRITE) {
|
||||
*nperm = *nperm | BLK_PERM_CONSISTENT_READ;
|
||||
}
|
||||
*nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
}
|
||||
}
|
||||
|
||||
BlockDriver bdrv_backup_top_filter = {
|
||||
.format_name = "backup-top",
|
||||
.instance_size = sizeof(BDRVBackupTopState),
|
||||
|
||||
.bdrv_co_preadv = backup_top_co_preadv,
|
||||
.bdrv_co_pwritev = backup_top_co_pwritev,
|
||||
.bdrv_co_pwrite_zeroes = backup_top_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = backup_top_co_pdiscard,
|
||||
.bdrv_co_flush = backup_top_co_flush,
|
||||
|
||||
.bdrv_refresh_filename = backup_top_refresh_filename,
|
||||
|
||||
.bdrv_child_perm = backup_top_child_perm,
|
||||
|
||||
.is_filter = true,
|
||||
};
|
||||
|
||||
BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
|
||||
BlockDriverState *target,
|
||||
const char *filter_node_name,
|
||||
uint64_t cluster_size,
|
||||
BackupPerf *perf,
|
||||
BdrvRequestFlags write_flags,
|
||||
BlockCopyState **bcs,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
int ret;
|
||||
BDRVBackupTopState *state;
|
||||
BlockDriverState *top;
|
||||
bool appended = false;
|
||||
|
||||
assert(source->total_sectors == target->total_sectors);
|
||||
|
||||
top = bdrv_new_open_driver(&bdrv_backup_top_filter, filter_node_name,
|
||||
BDRV_O_RDWR, errp);
|
||||
if (!top) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state = top->opaque;
|
||||
top->total_sectors = source->total_sectors;
|
||||
top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
(BDRV_REQ_FUA & source->supported_write_flags);
|
||||
top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||
source->supported_zero_flags);
|
||||
|
||||
bdrv_ref(target);
|
||||
state->target = bdrv_attach_child(top, target, "target", &child_of_bds,
|
||||
BDRV_CHILD_DATA, errp);
|
||||
if (!state->target) {
|
||||
bdrv_unref(target);
|
||||
bdrv_unref(top);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bdrv_drained_begin(source);
|
||||
|
||||
ret = bdrv_append(top, source, errp);
|
||||
if (ret < 0) {
|
||||
error_prepend(errp, "Cannot append backup-top filter: ");
|
||||
goto fail;
|
||||
}
|
||||
appended = true;
|
||||
|
||||
state->cluster_size = cluster_size;
|
||||
state->bcs = block_copy_state_new(top->backing, state->target,
|
||||
cluster_size, perf->use_copy_range,
|
||||
write_flags, errp);
|
||||
if (!state->bcs) {
|
||||
error_prepend(errp, "Cannot create block-copy-state: ");
|
||||
goto fail;
|
||||
}
|
||||
*bcs = state->bcs;
|
||||
|
||||
bdrv_drained_end(source);
|
||||
|
||||
return top;
|
||||
|
||||
fail:
|
||||
if (appended) {
|
||||
bdrv_backup_top_drop(top);
|
||||
} else {
|
||||
bdrv_unref(top);
|
||||
}
|
||||
|
||||
bdrv_drained_end(source);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bdrv_backup_top_drop(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBackupTopState *s = bs->opaque;
|
||||
|
||||
bdrv_drop_filter(bs, &error_abort);
|
||||
|
||||
block_copy_state_free(s->bcs);
|
||||
|
||||
bdrv_unref(bs);
|
||||
}
|
106
block/backup.c
106
block/backup.c
@ -27,13 +27,11 @@
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#include "block/backup-top.h"
|
||||
|
||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||
#include "block/copy-before-write.h"
|
||||
|
||||
typedef struct BackupBlockJob {
|
||||
BlockJob common;
|
||||
BlockDriverState *backup_top;
|
||||
BlockDriverState *cbw;
|
||||
BlockDriverState *source_bs;
|
||||
BlockDriverState *target_bs;
|
||||
|
||||
@ -104,7 +102,7 @@ static void backup_clean(Job *job)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
block_job_remove_all_bdrv(&s->common);
|
||||
bdrv_backup_top_drop(s->backup_top);
|
||||
bdrv_cbw_drop(s->cbw);
|
||||
}
|
||||
|
||||
void backup_do_checkpoint(BlockJob *job, Error **errp)
|
||||
@ -235,19 +233,17 @@ static void backup_init_bcs_bitmap(BackupBlockJob *job)
|
||||
BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs);
|
||||
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
|
||||
bdrv_clear_dirty_bitmap(bcs_bitmap, NULL);
|
||||
ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap,
|
||||
NULL, true);
|
||||
assert(ret);
|
||||
} else {
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
} else if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
/*
|
||||
* We can't hog the coroutine to initialize this thoroughly.
|
||||
* Set a flag and resume work when we are able to yield safely.
|
||||
*/
|
||||
block_copy_set_skip_unallocated(job->bcs, true);
|
||||
}
|
||||
bdrv_set_dirty_bitmap(bcs_bitmap, 0, job->len);
|
||||
}
|
||||
|
||||
estimate = bdrv_get_dirty_count(bcs_bitmap);
|
||||
job_progress_set_remaining(&job->common.job, estimate);
|
||||
@ -354,43 +350,6 @@ static const BlockJobDriver backup_job_driver = {
|
||||
.set_speed = backup_set_speed,
|
||||
};
|
||||
|
||||
static int64_t backup_calculate_cluster_size(BlockDriverState *target,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BlockDriverInfo bdi;
|
||||
bool target_does_cow = bdrv_backing_chain_next(target);
|
||||
|
||||
/*
|
||||
* If there is no backing file on the target, we cannot rely on COW if our
|
||||
* backup cluster size is smaller than the target cluster size. Even for
|
||||
* targets with a backing file, try to avoid COW if possible.
|
||||
*/
|
||||
ret = bdrv_get_info(target, &bdi);
|
||||
if (ret == -ENOTSUP && !target_does_cow) {
|
||||
/* Cluster size is not defined */
|
||||
warn_report("The target block device doesn't provide "
|
||||
"information about the block size and it doesn't have a "
|
||||
"backing file. The default block size of %u bytes is "
|
||||
"used. If the actual block size of the target exceeds "
|
||||
"this default, the backup may be unusable",
|
||||
BACKUP_CLUSTER_SIZE_DEFAULT);
|
||||
return BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||
} else if (ret < 0 && !target_does_cow) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Couldn't determine the cluster size of the target image, "
|
||||
"which has no backing file");
|
||||
error_append_hint(errp,
|
||||
"Aborting, since this may create an unusable destination image\n");
|
||||
return ret;
|
||||
} else if (ret < 0 && target_does_cow) {
|
||||
/* Not fatal; just trudge on ahead. */
|
||||
return BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||
}
|
||||
|
||||
return MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||
}
|
||||
|
||||
BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target, int64_t speed,
|
||||
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
|
||||
@ -407,8 +366,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
int64_t len, target_len;
|
||||
BackupBlockJob *job = NULL;
|
||||
int64_t cluster_size;
|
||||
BdrvRequestFlags write_flags;
|
||||
BlockDriverState *backup_top = NULL;
|
||||
BlockDriverState *cbw = NULL;
|
||||
BlockCopyState *bcs = NULL;
|
||||
|
||||
assert(bs);
|
||||
@ -449,11 +407,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cluster_size = backup_calculate_cluster_size(target, errp);
|
||||
if (cluster_size < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (perf->max_workers < 1) {
|
||||
error_setg(errp, "max-workers must be greater than zero");
|
||||
return NULL;
|
||||
@ -465,13 +418,6 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (perf->max_chunk && perf->max_chunk < cluster_size) {
|
||||
error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup "
|
||||
"cluster size (%" PRIi64 ")", perf->max_chunk, cluster_size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
if (sync_bitmap) {
|
||||
/* If we need to write to this bitmap, check that we can: */
|
||||
if (bitmap_mode != BITMAP_SYNC_MODE_NEVER &&
|
||||
@ -504,39 +450,28 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* If source is in backing chain of target assume that target is going to be
|
||||
* used for "image fleecing", i.e. it should represent a kind of snapshot of
|
||||
* source at backup-start point in time. And target is going to be read by
|
||||
* somebody (for example, used as NBD export) during backup job.
|
||||
*
|
||||
* In this case, we need to add BDRV_REQ_SERIALISING write flag to avoid
|
||||
* intersection of backup writes and third party reads from target,
|
||||
* otherwise reading from target we may occasionally read already updated by
|
||||
* guest data.
|
||||
*
|
||||
* For more information see commit f8d59dfb40bb and test
|
||||
* tests/qemu-iotests/222
|
||||
*/
|
||||
write_flags = (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
|
||||
(compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
|
||||
cbw = bdrv_cbw_append(bs, target, filter_node_name, &bcs, errp);
|
||||
if (!cbw) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
backup_top = bdrv_backup_top_append(bs, target, filter_node_name,
|
||||
cluster_size, perf,
|
||||
write_flags, &bcs, errp);
|
||||
if (!backup_top) {
|
||||
cluster_size = block_copy_cluster_size(bcs);
|
||||
|
||||
if (perf->max_chunk && perf->max_chunk < cluster_size) {
|
||||
error_setg(errp, "Required max-chunk (%" PRIi64 ") is less than backup "
|
||||
"cluster size (%" PRIi64 ")", perf->max_chunk, cluster_size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* job->len is fixed, so we can't allow resize */
|
||||
job = block_job_create(job_id, &backup_job_driver, txn, backup_top,
|
||||
job = block_job_create(job_id, &backup_job_driver, txn, cbw,
|
||||
0, BLK_PERM_ALL,
|
||||
speed, creation_flags, cb, opaque, errp);
|
||||
if (!job) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
job->backup_top = backup_top;
|
||||
job->cbw = cbw;
|
||||
job->source_bs = bs;
|
||||
job->target_bs = target;
|
||||
job->on_source_error = on_source_error;
|
||||
@ -549,10 +484,11 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
job->len = len;
|
||||
job->perf = *perf;
|
||||
|
||||
block_copy_set_copy_opts(bcs, perf->use_copy_range, compress);
|
||||
block_copy_set_progress_meter(bcs, &job->common.job.progress);
|
||||
block_copy_set_speed(bcs, speed);
|
||||
|
||||
/* Required permissions are already taken by backup-top target */
|
||||
/* Required permissions are taken by copy-before-write filter target */
|
||||
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
|
||||
@ -562,8 +498,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||
if (sync_bitmap) {
|
||||
bdrv_reclaim_dirty_bitmap(sync_bitmap, NULL);
|
||||
}
|
||||
if (backup_top) {
|
||||
bdrv_backup_top_drop(backup_top);
|
||||
if (cbw) {
|
||||
bdrv_cbw_drop(cbw);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -869,6 +869,14 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change BlockDriverState associated with @blk.
|
||||
*/
|
||||
int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp)
|
||||
{
|
||||
return bdrv_replace_child_bs(blk->root, new_bs, errp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the permission bitmasks that the user of the BlockBackend needs.
|
||||
*/
|
||||
|
@ -21,12 +21,14 @@
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "block/aio_task.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define BLOCK_COPY_MAX_COPY_RANGE (16 * MiB)
|
||||
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
|
||||
#define BLOCK_COPY_MAX_MEM (128 * MiB)
|
||||
#define BLOCK_COPY_MAX_WORKERS 64
|
||||
#define BLOCK_COPY_SLICE_TIME 100000000ULL /* ns */
|
||||
#define BLOCK_COPY_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||
|
||||
typedef enum {
|
||||
COPY_READ_WRITE_CLUSTER,
|
||||
@ -290,9 +292,11 @@ static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret)
|
||||
bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->offset, task->bytes);
|
||||
}
|
||||
QLIST_REMOVE(task, list);
|
||||
if (task->s->progress) {
|
||||
progress_set_remaining(task->s->progress,
|
||||
bdrv_get_dirty_count(task->s->copy_bitmap) +
|
||||
task->s->in_flight_bytes);
|
||||
}
|
||||
qemu_co_queue_restart_all(&task->wait_queue);
|
||||
}
|
||||
|
||||
@ -315,35 +319,14 @@ static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target)
|
||||
target->bs->bl.max_transfer));
|
||||
}
|
||||
|
||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||
int64_t cluster_size, bool use_copy_range,
|
||||
BdrvRequestFlags write_flags, Error **errp)
|
||||
void block_copy_set_copy_opts(BlockCopyState *s, bool use_copy_range,
|
||||
bool compress)
|
||||
{
|
||||
BlockCopyState *s;
|
||||
BdrvDirtyBitmap *copy_bitmap;
|
||||
/* Keep BDRV_REQ_SERIALISING set (or not set) in block_copy_state_new() */
|
||||
s->write_flags = (s->write_flags & BDRV_REQ_SERIALISING) |
|
||||
(compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
|
||||
|
||||
copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
|
||||
errp);
|
||||
if (!copy_bitmap) {
|
||||
return NULL;
|
||||
}
|
||||
bdrv_disable_dirty_bitmap(copy_bitmap);
|
||||
|
||||
s = g_new(BlockCopyState, 1);
|
||||
*s = (BlockCopyState) {
|
||||
.source = source,
|
||||
.target = target,
|
||||
.copy_bitmap = copy_bitmap,
|
||||
.cluster_size = cluster_size,
|
||||
.len = bdrv_dirty_bitmap_size(copy_bitmap),
|
||||
.write_flags = write_flags,
|
||||
.mem = shres_create(BLOCK_COPY_MAX_MEM),
|
||||
.max_transfer = QEMU_ALIGN_DOWN(
|
||||
block_copy_max_transfer(source, target),
|
||||
cluster_size),
|
||||
};
|
||||
|
||||
if (s->max_transfer < cluster_size) {
|
||||
if (s->max_transfer < s->cluster_size) {
|
||||
/*
|
||||
* copy_range does not respect max_transfer. We don't want to bother
|
||||
* with requests smaller than block-copy cluster size, so fallback to
|
||||
@ -351,7 +334,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||
* behalf).
|
||||
*/
|
||||
s->method = COPY_READ_WRITE_CLUSTER;
|
||||
} else if (write_flags & BDRV_REQ_WRITE_COMPRESSED) {
|
||||
} else if (compress) {
|
||||
/* Compression supports only cluster-size writes and no copy-range. */
|
||||
s->method = COPY_READ_WRITE_CLUSTER;
|
||||
} else {
|
||||
@ -361,6 +344,96 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||
*/
|
||||
s->method = use_copy_range ? COPY_RANGE_SMALL : COPY_READ_WRITE;
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t block_copy_calculate_cluster_size(BlockDriverState *target,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
BlockDriverInfo bdi;
|
||||
bool target_does_cow = bdrv_backing_chain_next(target);
|
||||
|
||||
/*
|
||||
* If there is no backing file on the target, we cannot rely on COW if our
|
||||
* backup cluster size is smaller than the target cluster size. Even for
|
||||
* targets with a backing file, try to avoid COW if possible.
|
||||
*/
|
||||
ret = bdrv_get_info(target, &bdi);
|
||||
if (ret == -ENOTSUP && !target_does_cow) {
|
||||
/* Cluster size is not defined */
|
||||
warn_report("The target block device doesn't provide "
|
||||
"information about the block size and it doesn't have a "
|
||||
"backing file. The default block size of %u bytes is "
|
||||
"used. If the actual block size of the target exceeds "
|
||||
"this default, the backup may be unusable",
|
||||
BLOCK_COPY_CLUSTER_SIZE_DEFAULT);
|
||||
return BLOCK_COPY_CLUSTER_SIZE_DEFAULT;
|
||||
} else if (ret < 0 && !target_does_cow) {
|
||||
error_setg_errno(errp, -ret,
|
||||
"Couldn't determine the cluster size of the target image, "
|
||||
"which has no backing file");
|
||||
error_append_hint(errp,
|
||||
"Aborting, since this may create an unusable destination image\n");
|
||||
return ret;
|
||||
} else if (ret < 0 && target_does_cow) {
|
||||
/* Not fatal; just trudge on ahead. */
|
||||
return BLOCK_COPY_CLUSTER_SIZE_DEFAULT;
|
||||
}
|
||||
|
||||
return MAX(BLOCK_COPY_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||
}
|
||||
|
||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||
Error **errp)
|
||||
{
|
||||
BlockCopyState *s;
|
||||
int64_t cluster_size;
|
||||
BdrvDirtyBitmap *copy_bitmap;
|
||||
bool is_fleecing;
|
||||
|
||||
cluster_size = block_copy_calculate_cluster_size(target->bs, errp);
|
||||
if (cluster_size < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
|
||||
errp);
|
||||
if (!copy_bitmap) {
|
||||
return NULL;
|
||||
}
|
||||
bdrv_disable_dirty_bitmap(copy_bitmap);
|
||||
|
||||
/*
|
||||
* If source is in backing chain of target assume that target is going to be
|
||||
* used for "image fleecing", i.e. it should represent a kind of snapshot of
|
||||
* source at backup-start point in time. And target is going to be read by
|
||||
* somebody (for example, used as NBD export) during backup job.
|
||||
*
|
||||
* In this case, we need to add BDRV_REQ_SERIALISING write flag to avoid
|
||||
* intersection of backup writes and third party reads from target,
|
||||
* otherwise reading from target we may occasionally read already updated by
|
||||
* guest data.
|
||||
*
|
||||
* For more information see commit f8d59dfb40bb and test
|
||||
* tests/qemu-iotests/222
|
||||
*/
|
||||
is_fleecing = bdrv_chain_contains(target->bs, source->bs);
|
||||
|
||||
s = g_new(BlockCopyState, 1);
|
||||
*s = (BlockCopyState) {
|
||||
.source = source,
|
||||
.target = target,
|
||||
.copy_bitmap = copy_bitmap,
|
||||
.cluster_size = cluster_size,
|
||||
.len = bdrv_dirty_bitmap_size(copy_bitmap),
|
||||
.write_flags = (is_fleecing ? BDRV_REQ_SERIALISING : 0),
|
||||
.mem = shres_create(BLOCK_COPY_MAX_MEM),
|
||||
.max_transfer = QEMU_ALIGN_DOWN(
|
||||
block_copy_max_transfer(source, target),
|
||||
cluster_size),
|
||||
};
|
||||
|
||||
block_copy_set_copy_opts(s, false, false);
|
||||
|
||||
ratelimit_init(&s->rate_limit);
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
@ -522,7 +595,7 @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
|
||||
t->call_state->ret = ret;
|
||||
t->call_state->error_is_read = error_is_read;
|
||||
}
|
||||
} else {
|
||||
} else if (s->progress) {
|
||||
progress_work_done(s->progress, t->bytes);
|
||||
}
|
||||
}
|
||||
@ -628,9 +701,11 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
|
||||
if (!ret) {
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
||||
if (s->progress) {
|
||||
progress_set_remaining(s->progress,
|
||||
bdrv_get_dirty_count(s->copy_bitmap) +
|
||||
s->in_flight_bytes);
|
||||
}
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
}
|
||||
|
||||
@ -933,6 +1008,11 @@ BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
|
||||
return s->copy_bitmap;
|
||||
}
|
||||
|
||||
int64_t block_copy_cluster_size(BlockCopyState *s)
|
||||
{
|
||||
return s->cluster_size;
|
||||
}
|
||||
|
||||
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip)
|
||||
{
|
||||
qatomic_set(&s->skip_unallocated, skip);
|
||||
|
256
block/copy-before-write.c
Normal file
256
block/copy-before-write.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* copy-before-write filter driver
|
||||
*
|
||||
* The driver performs Copy-Before-Write (CBW) operation: it is injected above
|
||||
* some node, and before each write it copies _old_ data to the target node.
|
||||
*
|
||||
* Copyright (c) 2018-2021 Virtuozzo International GmbH.
|
||||
*
|
||||
* Author:
|
||||
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "block/block-copy.h"
|
||||
|
||||
#include "block/copy-before-write.h"
|
||||
|
||||
typedef struct BDRVCopyBeforeWriteState {
|
||||
BlockCopyState *bcs;
|
||||
BdrvChild *target;
|
||||
} BDRVCopyBeforeWriteState;
|
||||
|
||||
static coroutine_fn int cbw_co_preadv(
|
||||
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||
uint64_t off, end;
|
||||
int64_t cluster_size = block_copy_cluster_size(s->bcs);
|
||||
|
||||
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
off = QEMU_ALIGN_DOWN(offset, cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, cluster_size);
|
||||
|
||||
return block_copy(s->bcs, off, end - off, true);
|
||||
}
|
||||
|
||||
static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
int ret = cbw_do_copy_before_write(bs, offset, bytes, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
||||
}
|
||||
|
||||
static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes, BdrvRequestFlags flags)
|
||||
{
|
||||
int ret = cbw_do_copy_before_write(bs, offset, bytes, flags);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||
}
|
||||
|
||||
static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
int ret = cbw_do_copy_before_write(bs, offset, bytes, flags);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||
}
|
||||
|
||||
static int coroutine_fn cbw_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
if (!bs->file) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bdrv_co_flush(bs->file->bs);
|
||||
}
|
||||
|
||||
static void cbw_refresh_filename(BlockDriverState *bs)
|
||||
{
|
||||
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
bs->file->bs->filename);
|
||||
}
|
||||
|
||||
static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
BdrvChildRole role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
if (!(role & BDRV_CHILD_FILTERED)) {
|
||||
/*
|
||||
* Target child
|
||||
*
|
||||
* Share write to target (child_file), to not interfere
|
||||
* with guest writes to its disk which may be in target backing chain.
|
||||
* Can't resize during a backup block job because we check the size
|
||||
* only upfront.
|
||||
*/
|
||||
*nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
|
||||
*nperm = BLK_PERM_WRITE;
|
||||
} else {
|
||||
/* Source child */
|
||||
bdrv_default_perms(bs, c, role, reopen_queue,
|
||||
perm, shared, nperm, nshared);
|
||||
|
||||
if (!QLIST_EMPTY(&bs->parents)) {
|
||||
if (perm & BLK_PERM_WRITE) {
|
||||
*nperm = *nperm | BLK_PERM_CONSISTENT_READ;
|
||||
}
|
||||
*nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||
BdrvDirtyBitmap *copy_bitmap;
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
|
||||
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s->target = bdrv_open_child(NULL, options, "target", bs, &child_of_bds,
|
||||
BDRV_CHILD_DATA, false, errp);
|
||||
if (!s->target) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bs->total_sectors = bs->file->bs->total_sectors;
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
|
||||
s->bcs = block_copy_state_new(bs->file, s->target, errp);
|
||||
if (!s->bcs) {
|
||||
error_prepend(errp, "Cannot create block-copy-state: ");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
copy_bitmap = block_copy_dirty_bitmap(s->bcs);
|
||||
bdrv_set_dirty_bitmap(copy_bitmap, 0, bdrv_dirty_bitmap_size(copy_bitmap));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cbw_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||
|
||||
block_copy_state_free(s->bcs);
|
||||
s->bcs = NULL;
|
||||
}
|
||||
|
||||
BlockDriver bdrv_cbw_filter = {
|
||||
.format_name = "copy-before-write",
|
||||
.instance_size = sizeof(BDRVCopyBeforeWriteState),
|
||||
|
||||
.bdrv_open = cbw_open,
|
||||
.bdrv_close = cbw_close,
|
||||
|
||||
.bdrv_co_preadv = cbw_co_preadv,
|
||||
.bdrv_co_pwritev = cbw_co_pwritev,
|
||||
.bdrv_co_pwrite_zeroes = cbw_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = cbw_co_pdiscard,
|
||||
.bdrv_co_flush = cbw_co_flush,
|
||||
|
||||
.bdrv_refresh_filename = cbw_refresh_filename,
|
||||
|
||||
.bdrv_child_perm = cbw_child_perm,
|
||||
|
||||
.is_filter = true,
|
||||
};
|
||||
|
||||
BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
||||
BlockDriverState *target,
|
||||
const char *filter_node_name,
|
||||
BlockCopyState **bcs,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVCopyBeforeWriteState *state;
|
||||
BlockDriverState *top;
|
||||
QDict *opts;
|
||||
|
||||
assert(source->total_sectors == target->total_sectors);
|
||||
|
||||
opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", "copy-before-write");
|
||||
if (filter_node_name) {
|
||||
qdict_put_str(opts, "node-name", filter_node_name);
|
||||
}
|
||||
qdict_put_str(opts, "file", bdrv_get_node_name(source));
|
||||
qdict_put_str(opts, "target", bdrv_get_node_name(target));
|
||||
|
||||
top = bdrv_insert_node(source, opts, BDRV_O_RDWR, errp);
|
||||
if (!top) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state = top->opaque;
|
||||
*bcs = state->bcs;
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
void bdrv_cbw_drop(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_drop_filter(bs, &error_abort);
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
|
||||
static void cbw_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_cbw_filter);
|
||||
}
|
||||
|
||||
block_init(cbw_init);
|
@ -1,10 +1,10 @@
|
||||
/*
|
||||
* backup-top filter driver
|
||||
* copy-before-write filter driver
|
||||
*
|
||||
* The driver performs Copy-Before-Write (CBW) operation: it is injected above
|
||||
* some node, and before each write it copies _old_ data to the target node.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Virtuozzo International GmbH.
|
||||
* Copyright (c) 2018-2021 Virtuozzo International GmbH.
|
||||
*
|
||||
* Author:
|
||||
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
|
||||
@ -23,20 +23,17 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BACKUP_TOP_H
|
||||
#define BACKUP_TOP_H
|
||||
#ifndef COPY_BEFORE_WRITE_H
|
||||
#define COPY_BEFORE_WRITE_H
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "block/block-copy.h"
|
||||
|
||||
BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
|
||||
BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
|
||||
BlockDriverState *target,
|
||||
const char *filter_node_name,
|
||||
uint64_t cluster_size,
|
||||
BackupPerf *perf,
|
||||
BdrvRequestFlags write_flags,
|
||||
BlockCopyState **bcs,
|
||||
Error **errp);
|
||||
void bdrv_backup_top_drop(BlockDriverState *bs);
|
||||
void bdrv_cbw_drop(BlockDriverState *bs);
|
||||
|
||||
#endif /* BACKUP_TOP_H */
|
||||
#endif /* COPY_BEFORE_WRITE_H */
|
@ -31,6 +31,9 @@
|
||||
#include <fuse.h>
|
||||
#include <fuse_lowlevel.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
|
||||
/* Prevent overly long bounce buffer allocations */
|
||||
#define FUSE_MAX_BOUNCE_BYTES (MIN(BDRV_REQUEST_MAX_BYTES, 64 * 1024 * 1024))
|
||||
|
@ -58,6 +58,10 @@ typedef struct BDRVRawState {
|
||||
QEMUWin32AIOState *aio;
|
||||
} BDRVRawState;
|
||||
|
||||
typedef struct BDRVRawReopenState {
|
||||
HANDLE hfile;
|
||||
} BDRVRawReopenState;
|
||||
|
||||
/*
|
||||
* Read/writes the data to/from a given linear buffer.
|
||||
*
|
||||
@ -392,7 +396,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
s->hfile = CreateFile(filename, access_flags,
|
||||
FILE_SHARE_READ, NULL,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, overlapped, NULL);
|
||||
if (s->hfile == INVALID_HANDLE_VALUE) {
|
||||
int err = GetLastError();
|
||||
@ -634,6 +638,97 @@ static int coroutine_fn raw_co_create_opts(BlockDriver *drv,
|
||||
return raw_co_create(&options, errp);
|
||||
}
|
||||
|
||||
static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
BDRVRawState *s = state->bs->opaque;
|
||||
BDRVRawReopenState *rs;
|
||||
int access_flags;
|
||||
DWORD overlapped;
|
||||
int ret = 0;
|
||||
|
||||
if (s->type != FTYPE_FILE) {
|
||||
error_setg(errp, "Can only reopen files");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rs = g_new0(BDRVRawReopenState, 1);
|
||||
|
||||
/*
|
||||
* We do not support changing any options (only flags). By leaving
|
||||
* all options in state->options, we tell the generic reopen code
|
||||
* that we do not support changing any of them, so it will verify
|
||||
* that their values did not change.
|
||||
*/
|
||||
|
||||
raw_parse_flags(state->flags, s->aio != NULL, &access_flags, &overlapped);
|
||||
rs->hfile = CreateFile(state->bs->filename, access_flags,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
OPEN_EXISTING, overlapped, NULL);
|
||||
|
||||
if (rs->hfile == INVALID_HANDLE_VALUE) {
|
||||
int err = GetLastError();
|
||||
|
||||
error_setg_win32(errp, err, "Could not reopen '%s'",
|
||||
state->bs->filename);
|
||||
if (err == ERROR_ACCESS_DENIED) {
|
||||
ret = -EACCES;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (s->aio) {
|
||||
ret = win32_aio_attach(s->aio, rs->hfile);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not enable AIO");
|
||||
CloseHandle(rs->hfile);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
state->opaque = rs;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
g_free(rs);
|
||||
state->opaque = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void raw_reopen_commit(BDRVReopenState *state)
|
||||
{
|
||||
BDRVRawState *s = state->bs->opaque;
|
||||
BDRVRawReopenState *rs = state->opaque;
|
||||
|
||||
assert(rs != NULL);
|
||||
|
||||
CloseHandle(s->hfile);
|
||||
s->hfile = rs->hfile;
|
||||
|
||||
g_free(rs);
|
||||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
static void raw_reopen_abort(BDRVReopenState *state)
|
||||
{
|
||||
BDRVRawReopenState *rs = state->opaque;
|
||||
|
||||
if (!rs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rs->hfile != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(rs->hfile);
|
||||
}
|
||||
|
||||
g_free(rs);
|
||||
state->opaque = NULL;
|
||||
}
|
||||
|
||||
static QemuOptsList raw_create_opts = {
|
||||
.name = "raw-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
|
||||
@ -659,6 +754,10 @@ BlockDriver bdrv_file = {
|
||||
.bdrv_co_create_opts = raw_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
|
||||
.bdrv_reopen_prepare = raw_reopen_prepare,
|
||||
.bdrv_reopen_commit = raw_reopen_commit,
|
||||
.bdrv_reopen_abort = raw_reopen_abort,
|
||||
|
||||
.bdrv_aio_preadv = raw_aio_preadv,
|
||||
.bdrv_aio_pwritev = raw_aio_pwritev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
|
@ -4,7 +4,7 @@ block_ss.add(files(
|
||||
'aio_task.c',
|
||||
'amend.c',
|
||||
'backup.c',
|
||||
'backup-top.c',
|
||||
'copy-before-write.c',
|
||||
'blkdebug.c',
|
||||
'blklogwrites.c',
|
||||
'blkverify.c',
|
||||
|
@ -251,10 +251,10 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
|
||||
|
||||
if (!filename) {
|
||||
error_setg(&err, QERR_MISSING_PARAMETER, "target");
|
||||
hmp_handle_error(mon, err);
|
||||
return;
|
||||
goto end;
|
||||
}
|
||||
qmp_drive_mirror(&mirror, &err);
|
||||
end:
|
||||
hmp_handle_error(mon, err);
|
||||
}
|
||||
|
||||
@ -281,11 +281,11 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)
|
||||
|
||||
if (!filename) {
|
||||
error_setg(&err, QERR_MISSING_PARAMETER, "target");
|
||||
hmp_handle_error(mon, err);
|
||||
return;
|
||||
goto end;
|
||||
}
|
||||
|
||||
qmp_drive_backup(&backup, &err);
|
||||
end:
|
||||
hmp_handle_error(mon, err);
|
||||
}
|
||||
|
||||
@ -356,8 +356,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
|
||||
* will be taken internally. Today it's actually required.
|
||||
*/
|
||||
error_setg(&err, QERR_MISSING_PARAMETER, "snapshot-file");
|
||||
hmp_handle_error(mon, err);
|
||||
return;
|
||||
goto end;
|
||||
}
|
||||
|
||||
mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
@ -365,6 +364,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
|
||||
filename, false, NULL,
|
||||
!!format, format,
|
||||
true, mode, &err);
|
||||
end:
|
||||
hmp_handle_error(mon, err);
|
||||
}
|
||||
|
||||
|
29
block/nvme.c
29
block/nvme.c
@ -176,12 +176,11 @@ static bool nvme_init_queue(BDRVNVMeState *s, NVMeQueue *q,
|
||||
return false;
|
||||
}
|
||||
memset(q->queue, 0, bytes);
|
||||
r = qemu_vfio_dma_map(s->vfio, q->queue, bytes, false, &q->iova);
|
||||
r = qemu_vfio_dma_map(s->vfio, q->queue, bytes, false, &q->iova, errp);
|
||||
if (r) {
|
||||
error_setg(errp, "Cannot map queue");
|
||||
return false;
|
||||
error_prepend(errp, "Cannot map queue: ");
|
||||
}
|
||||
return true;
|
||||
return r == 0;
|
||||
}
|
||||
|
||||
static void nvme_free_queue_pair(NVMeQueuePair *q)
|
||||
@ -220,6 +219,7 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s,
|
||||
|
||||
q = g_try_new0(NVMeQueuePair, 1);
|
||||
if (!q) {
|
||||
error_setg(errp, "Cannot allocate queue pair");
|
||||
return NULL;
|
||||
}
|
||||
trace_nvme_create_queue_pair(idx, q, size, aio_context,
|
||||
@ -228,6 +228,7 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s,
|
||||
qemu_real_host_page_size);
|
||||
q->prp_list_pages = qemu_try_memalign(qemu_real_host_page_size, bytes);
|
||||
if (!q->prp_list_pages) {
|
||||
error_setg(errp, "Cannot allocate PRP page list");
|
||||
goto fail;
|
||||
}
|
||||
memset(q->prp_list_pages, 0, bytes);
|
||||
@ -237,8 +238,9 @@ static NVMeQueuePair *nvme_create_queue_pair(BDRVNVMeState *s,
|
||||
qemu_co_queue_init(&q->free_req_queue);
|
||||
q->completion_bh = aio_bh_new(aio_context, nvme_process_completion_bh, q);
|
||||
r = qemu_vfio_dma_map(s->vfio, q->prp_list_pages, bytes,
|
||||
false, &prp_list_iova);
|
||||
false, &prp_list_iova, errp);
|
||||
if (r) {
|
||||
error_prepend(errp, "Cannot map buffer for DMA: ");
|
||||
goto fail;
|
||||
}
|
||||
q->free_req_head = -1;
|
||||
@ -531,9 +533,9 @@ static bool nvme_identify(BlockDriverState *bs, int namespace, Error **errp)
|
||||
error_setg(errp, "Cannot allocate buffer for identify response");
|
||||
goto out;
|
||||
}
|
||||
r = qemu_vfio_dma_map(s->vfio, id, id_size, true, &iova);
|
||||
r = qemu_vfio_dma_map(s->vfio, id, id_size, true, &iova, errp);
|
||||
if (r) {
|
||||
error_setg(errp, "Cannot map buffer for DMA");
|
||||
error_prepend(errp, "Cannot map buffer for DMA: ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1017,6 +1019,7 @@ static coroutine_fn int nvme_cmd_map_qiov(BlockDriverState *bs, NvmeCmd *cmd,
|
||||
uint64_t *pagelist = req->prp_list_page;
|
||||
int i, j, r;
|
||||
int entries = 0;
|
||||
Error *local_err = NULL, **errp = NULL;
|
||||
|
||||
assert(qiov->size);
|
||||
assert(QEMU_IS_ALIGNED(qiov->size, s->page_size));
|
||||
@ -1029,7 +1032,7 @@ static coroutine_fn int nvme_cmd_map_qiov(BlockDriverState *bs, NvmeCmd *cmd,
|
||||
try_map:
|
||||
r = qemu_vfio_dma_map(s->vfio,
|
||||
qiov->iov[i].iov_base,
|
||||
len, true, &iova);
|
||||
len, true, &iova, errp);
|
||||
if (r == -ENOSPC) {
|
||||
/*
|
||||
* In addition to the -ENOMEM error, the VFIO_IOMMU_MAP_DMA
|
||||
@ -1064,6 +1067,8 @@ try_map:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
errp = &local_err;
|
||||
|
||||
goto try_map;
|
||||
}
|
||||
if (r) {
|
||||
@ -1107,6 +1112,9 @@ fail:
|
||||
* because they are already mapped before calling this function; for
|
||||
* temporary mappings, a later nvme_cmd_(un)map_qiov will reclaim by
|
||||
* calling qemu_vfio_dma_reset_temporary when necessary. */
|
||||
if (local_err) {
|
||||
error_reportf_err(local_err, "Cannot map buffer for DMA: ");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -1521,14 +1529,15 @@ static void nvme_aio_unplug(BlockDriverState *bs)
|
||||
static void nvme_register_buf(BlockDriverState *bs, void *host, size_t size)
|
||||
{
|
||||
int ret;
|
||||
Error *local_err = NULL;
|
||||
BDRVNVMeState *s = bs->opaque;
|
||||
|
||||
ret = qemu_vfio_dma_map(s->vfio, host, size, false, NULL);
|
||||
ret = qemu_vfio_dma_map(s->vfio, host, size, false, NULL, &local_err);
|
||||
if (ret) {
|
||||
/* FIXME: we may run out of IOVA addresses after repeated
|
||||
* bdrv_register_buf/bdrv_unregister_buf, because nvme_vfio_dma_unmap
|
||||
* doesn't reclaim addresses for fixed mappings. */
|
||||
error_report("nvme_register_buf failed: %s", strerror(-ret));
|
||||
error_reportf_err(local_err, "nvme_register_buf failed: ");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -580,6 +580,25 @@ static void raw_cancel_in_flight(BlockDriverState *bs)
|
||||
bdrv_cancel_in_flight(bs->file->bs);
|
||||
}
|
||||
|
||||
static void raw_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
BdrvChildRole role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t parent_perm, uint64_t parent_shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
bdrv_default_perms(bs, c, role, reopen_queue, parent_perm,
|
||||
parent_shared, nperm, nshared);
|
||||
|
||||
/*
|
||||
* bdrv_default_perms() may add WRITE and/or RESIZE (see comment in
|
||||
* bdrv_default_perms_for_storage() for an explanation) but we only need
|
||||
* them if they are in parent_perm. Drop WRITE and RESIZE whenever possible
|
||||
* to avoid permission conflicts.
|
||||
*/
|
||||
*nperm &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
*nperm |= parent_perm & (BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
}
|
||||
|
||||
BlockDriver bdrv_raw = {
|
||||
.format_name = "raw",
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
@ -588,7 +607,7 @@ BlockDriver bdrv_raw = {
|
||||
.bdrv_reopen_commit = &raw_reopen_commit,
|
||||
.bdrv_reopen_abort = &raw_reopen_abort,
|
||||
.bdrv_open = &raw_open,
|
||||
.bdrv_child_perm = bdrv_default_perms,
|
||||
.bdrv_child_perm = raw_child_perm,
|
||||
.bdrv_co_create_opts = &raw_co_create_opts,
|
||||
.bdrv_co_preadv = &raw_co_preadv,
|
||||
.bdrv_co_pwritev = &raw_co_pwritev,
|
||||
|
@ -156,7 +156,7 @@ nvme_dsm(void *s, uint64_t offset, uint64_t bytes) "s %p offset 0x%"PRIx64" byte
|
||||
nvme_dsm_done(void *s, uint64_t offset, uint64_t bytes, int ret) "s %p offset 0x%"PRIx64" bytes %"PRId64" ret %d"
|
||||
nvme_dma_map_flush(void *s) "s %p"
|
||||
nvme_free_req_queue_wait(void *s, unsigned q_index) "s %p q #%u"
|
||||
nvme_create_queue_pair(unsigned q_index, void *q, unsigned size, void *aio_context, int fd) "index %u q %p size %u aioctx %p fd %d"
|
||||
nvme_create_queue_pair(unsigned q_index, void *q, size_t size, void *aio_context, int fd) "index %u q %p size %zu aioctx %p fd %d"
|
||||
nvme_free_queue_pair(unsigned q_index, void *q) "index %u q %p"
|
||||
nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d"
|
||||
nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64
|
||||
|
@ -56,7 +56,6 @@
|
||||
#include "sysemu/iothread.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/trace.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
@ -18,10 +18,7 @@ CONFIG_PCSPK=y
|
||||
CONFIG_PCKBD=y
|
||||
CONFIG_FDC=y
|
||||
CONFIG_ACPI=y
|
||||
CONFIG_ACPI_X86=y
|
||||
CONFIG_ACPI_MEMORY_HOTPLUG=y
|
||||
CONFIG_ACPI_NVDIMM=y
|
||||
CONFIG_ACPI_CPU_HOTPLUG=y
|
||||
CONFIG_ACPI_PIIX4=y
|
||||
CONFIG_APM=y
|
||||
CONFIG_I8257=y
|
||||
CONFIG_PIIX4=y
|
||||
|
37
configure
vendored
37
configure
vendored
@ -3192,9 +3192,8 @@ glib_req_ver=2.56
|
||||
glib_modules=gthread-2.0
|
||||
if test "$modules" = yes; then
|
||||
glib_modules="$glib_modules gmodule-export-2.0"
|
||||
fi
|
||||
if test "$plugins" = "yes"; then
|
||||
glib_modules="$glib_modules gmodule-2.0"
|
||||
elif test "$plugins" = "yes"; then
|
||||
glib_modules="$glib_modules gmodule-no-export-2.0"
|
||||
fi
|
||||
|
||||
for i in $glib_modules; do
|
||||
@ -3457,13 +3456,6 @@ esac
|
||||
##########################################
|
||||
# opengl probe (for sdl2, gtk)
|
||||
|
||||
gbm="no"
|
||||
if $pkg_config gbm; then
|
||||
gbm_cflags="$($pkg_config --cflags gbm)"
|
||||
gbm_libs="$($pkg_config --libs gbm)"
|
||||
gbm="yes"
|
||||
fi
|
||||
|
||||
if test "$opengl" != "no" ; then
|
||||
epoxy=no
|
||||
if $pkg_config epoxy; then
|
||||
@ -4203,8 +4195,9 @@ fi
|
||||
|
||||
##########################################
|
||||
# checks for fuzzer
|
||||
if test "$fuzzing" = "yes" && test -z "${LIB_FUZZING_ENGINE+xxx}"; then
|
||||
if test "$fuzzing" = "yes" ; then
|
||||
write_c_fuzzer_skeleton
|
||||
if test -z "${LIB_FUZZING_ENGINE+xxx}"; then
|
||||
if compile_prog "$CPU_CFLAGS -Werror -fsanitize=fuzzer" ""; then
|
||||
have_fuzzer=yes
|
||||
else
|
||||
@ -4213,6 +4206,13 @@ if test "$fuzzing" = "yes" && test -z "${LIB_FUZZING_ENGINE+xxx}"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
have_clang_coverage_filter=no
|
||||
echo > $TMPTXT
|
||||
if compile_prog "$CPU_CFLAGS -Werror -fsanitize=fuzzer -fsanitize-coverage-allowlist=$TMPTXT" ""; then
|
||||
have_clang_coverage_filter=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
# Thread sanitizer is, for now, much noisier than the other sanitizers;
|
||||
# keep it separate until that is not the case.
|
||||
if test "$tsan" = "yes" && test "$sanitizers" = "yes"; then
|
||||
@ -4686,13 +4686,6 @@ if test "$opengl" = "yes" ; then
|
||||
echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$gbm" = "yes" ; then
|
||||
echo "CONFIG_GBM=y" >> $config_host_mak
|
||||
echo "GBM_LIBS=$gbm_libs" >> $config_host_mak
|
||||
echo "GBM_CFLAGS=$gbm_cflags" >> $config_host_mak
|
||||
fi
|
||||
|
||||
|
||||
if test "$avx2_opt" = "yes" ; then
|
||||
echo "CONFIG_AVX2_OPT=y" >> $config_host_mak
|
||||
fi
|
||||
@ -4889,6 +4882,14 @@ if test "$fuzzing" = "yes" ; then
|
||||
else
|
||||
FUZZ_EXE_LDFLAGS="$LIB_FUZZING_ENGINE"
|
||||
fi
|
||||
|
||||
# Specify a filter to only instrument code that is directly related to
|
||||
# virtual-devices.
|
||||
if test "$have_clang_coverage_filter" = "yes" ; then
|
||||
cp "$source_path/scripts/oss-fuzz/instrumentation-filter-template" \
|
||||
instrumentation-filter
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS -fsanitize-coverage-allowlist=instrumentation-filter"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$plugins" = "yes" ; then
|
||||
|
@ -17,18 +17,12 @@ static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
|
||||
|
||||
static GHashTable *miss_ht;
|
||||
|
||||
static GMutex mtx;
|
||||
static GMutex hashtable_lock;
|
||||
static GRand *rng;
|
||||
|
||||
static int limit;
|
||||
static bool sys;
|
||||
|
||||
static uint64_t dmem_accesses;
|
||||
static uint64_t dmisses;
|
||||
|
||||
static uint64_t imem_accesses;
|
||||
static uint64_t imisses;
|
||||
|
||||
enum EvictionPolicy {
|
||||
LRU,
|
||||
FIFO,
|
||||
@ -80,6 +74,8 @@ typedef struct {
|
||||
int blksize_shift;
|
||||
uint64_t set_mask;
|
||||
uint64_t tag_mask;
|
||||
uint64_t accesses;
|
||||
uint64_t misses;
|
||||
} Cache;
|
||||
|
||||
typedef struct {
|
||||
@ -96,7 +92,16 @@ void (*update_miss)(Cache *cache, int set, int blk);
|
||||
void (*metadata_init)(Cache *cache);
|
||||
void (*metadata_destroy)(Cache *cache);
|
||||
|
||||
Cache *dcache, *icache;
|
||||
static int cores;
|
||||
static Cache **dcaches, **icaches;
|
||||
|
||||
static GMutex *dcache_locks;
|
||||
static GMutex *icache_locks;
|
||||
|
||||
static uint64_t all_dmem_accesses;
|
||||
static uint64_t all_imem_accesses;
|
||||
static uint64_t all_imisses;
|
||||
static uint64_t all_dmisses;
|
||||
|
||||
static int pow_of_two(int num)
|
||||
{
|
||||
@ -233,20 +238,24 @@ static bool bad_cache_params(int blksize, int assoc, int cachesize)
|
||||
|
||||
static Cache *cache_init(int blksize, int assoc, int cachesize)
|
||||
{
|
||||
if (bad_cache_params(blksize, assoc, cachesize)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Cache *cache;
|
||||
int i;
|
||||
uint64_t blk_mask;
|
||||
|
||||
/*
|
||||
* This function shall not be called directly, and hence expects suitable
|
||||
* parameters.
|
||||
*/
|
||||
g_assert(!bad_cache_params(blksize, assoc, cachesize));
|
||||
|
||||
cache = g_new(Cache, 1);
|
||||
cache->assoc = assoc;
|
||||
cache->cachesize = cachesize;
|
||||
cache->num_sets = cachesize / (blksize * assoc);
|
||||
cache->sets = g_new(CacheSet, cache->num_sets);
|
||||
cache->blksize_shift = pow_of_two(blksize);
|
||||
cache->accesses = 0;
|
||||
cache->misses = 0;
|
||||
|
||||
for (i = 0; i < cache->num_sets; i++) {
|
||||
cache->sets[i].blocks = g_new0(CacheBlock, assoc);
|
||||
@ -263,6 +272,24 @@ static Cache *cache_init(int blksize, int assoc, int cachesize)
|
||||
return cache;
|
||||
}
|
||||
|
||||
static Cache **caches_init(int blksize, int assoc, int cachesize)
|
||||
{
|
||||
Cache **caches;
|
||||
int i;
|
||||
|
||||
if (bad_cache_params(blksize, assoc, cachesize)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
caches = g_new(Cache *, cores);
|
||||
|
||||
for (i = 0; i < cores; i++) {
|
||||
caches[i] = cache_init(blksize, assoc, cachesize);
|
||||
}
|
||||
|
||||
return caches;
|
||||
}
|
||||
|
||||
static int get_invalid_block(Cache *cache, uint64_t set)
|
||||
{
|
||||
int i;
|
||||
@ -353,6 +380,7 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info,
|
||||
{
|
||||
uint64_t effective_addr;
|
||||
struct qemu_plugin_hwaddr *hwaddr;
|
||||
int cache_idx;
|
||||
InsnData *insn;
|
||||
|
||||
hwaddr = qemu_plugin_get_hwaddr(info, vaddr);
|
||||
@ -361,32 +389,35 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info,
|
||||
}
|
||||
|
||||
effective_addr = hwaddr ? qemu_plugin_hwaddr_phys_addr(hwaddr) : vaddr;
|
||||
cache_idx = vcpu_index % cores;
|
||||
|
||||
g_mutex_lock(&mtx);
|
||||
if (!access_cache(dcache, effective_addr)) {
|
||||
g_mutex_lock(&dcache_locks[cache_idx]);
|
||||
if (!access_cache(dcaches[cache_idx], effective_addr)) {
|
||||
insn = (InsnData *) userdata;
|
||||
insn->dmisses++;
|
||||
dmisses++;
|
||||
__atomic_fetch_add(&insn->dmisses, 1, __ATOMIC_SEQ_CST);
|
||||
dcaches[cache_idx]->misses++;
|
||||
}
|
||||
dmem_accesses++;
|
||||
g_mutex_unlock(&mtx);
|
||||
dcaches[cache_idx]->accesses++;
|
||||
g_mutex_unlock(&dcache_locks[cache_idx]);
|
||||
}
|
||||
|
||||
static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata)
|
||||
{
|
||||
uint64_t insn_addr;
|
||||
InsnData *insn;
|
||||
int cache_idx;
|
||||
|
||||
g_mutex_lock(&mtx);
|
||||
insn_addr = ((InsnData *) userdata)->addr;
|
||||
|
||||
if (!access_cache(icache, insn_addr)) {
|
||||
cache_idx = vcpu_index % cores;
|
||||
g_mutex_lock(&icache_locks[cache_idx]);
|
||||
if (!access_cache(icaches[cache_idx], insn_addr)) {
|
||||
insn = (InsnData *) userdata;
|
||||
insn->imisses++;
|
||||
imisses++;
|
||||
__atomic_fetch_add(&insn->imisses, 1, __ATOMIC_SEQ_CST);
|
||||
icaches[cache_idx]->misses++;
|
||||
}
|
||||
imem_accesses++;
|
||||
g_mutex_unlock(&mtx);
|
||||
icaches[cache_idx]->accesses++;
|
||||
g_mutex_unlock(&icache_locks[cache_idx]);
|
||||
}
|
||||
|
||||
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
@ -411,7 +442,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
* new entries for those instructions. Instead, we fetch the same
|
||||
* entry from the hash table and register it for the callback again.
|
||||
*/
|
||||
g_mutex_lock(&mtx);
|
||||
g_mutex_lock(&hashtable_lock);
|
||||
data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr));
|
||||
if (data == NULL) {
|
||||
data = g_new0(InsnData, 1);
|
||||
@ -421,7 +452,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
|
||||
g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr),
|
||||
(gpointer) data);
|
||||
}
|
||||
g_mutex_unlock(&mtx);
|
||||
g_mutex_unlock(&hashtable_lock);
|
||||
|
||||
qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access,
|
||||
QEMU_PLUGIN_CB_NO_REGS,
|
||||
@ -453,6 +484,15 @@ static void cache_free(Cache *cache)
|
||||
g_free(cache);
|
||||
}
|
||||
|
||||
static void caches_free(Cache **caches)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cores; i++) {
|
||||
cache_free(caches[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int dcmp(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
InsnData *insn_a = (InsnData *) a;
|
||||
@ -461,6 +501,37 @@ static int dcmp(gconstpointer a, gconstpointer b)
|
||||
return insn_a->dmisses < insn_b->dmisses ? 1 : -1;
|
||||
}
|
||||
|
||||
static void append_stats_line(GString *line, uint64_t daccess, uint64_t dmisses,
|
||||
uint64_t iaccess, uint64_t imisses)
|
||||
{
|
||||
double dmiss_rate, imiss_rate;
|
||||
|
||||
dmiss_rate = ((double) dmisses) / (daccess) * 100.0;
|
||||
imiss_rate = ((double) imisses) / (iaccess) * 100.0;
|
||||
|
||||
g_string_append_printf(line, "%-14lu %-12lu %9.4lf%% %-14lu %-12lu"
|
||||
" %9.4lf%%\n",
|
||||
daccess,
|
||||
dmisses,
|
||||
daccess ? dmiss_rate : 0.0,
|
||||
iaccess,
|
||||
imisses,
|
||||
iaccess ? imiss_rate : 0.0);
|
||||
}
|
||||
|
||||
static void sum_stats(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
g_assert(cores > 1);
|
||||
for (i = 0; i < cores; i++) {
|
||||
all_imisses += icaches[i]->misses;
|
||||
all_dmisses += dcaches[i]->misses;
|
||||
all_imem_accesses += icaches[i]->accesses;
|
||||
all_dmem_accesses += dcaches[i]->accesses;
|
||||
}
|
||||
}
|
||||
|
||||
static int icmp(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
InsnData *insn_a = (InsnData *) a;
|
||||
@ -471,19 +542,29 @@ static int icmp(gconstpointer a, gconstpointer b)
|
||||
|
||||
static void log_stats(void)
|
||||
{
|
||||
g_autoptr(GString) rep = g_string_new("");
|
||||
g_string_append_printf(rep,
|
||||
"Data accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n",
|
||||
dmem_accesses,
|
||||
dmisses,
|
||||
((double) dmisses / (double) dmem_accesses) * 100.0);
|
||||
int i;
|
||||
Cache *icache, *dcache;
|
||||
|
||||
g_string_append_printf(rep,
|
||||
"Instruction accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n",
|
||||
imem_accesses,
|
||||
imisses,
|
||||
((double) imisses / (double) imem_accesses) * 100.0);
|
||||
g_autoptr(GString) rep = g_string_new("core #, data accesses, data misses,"
|
||||
" dmiss rate, insn accesses,"
|
||||
" insn misses, imiss rate\n");
|
||||
|
||||
for (i = 0; i < cores; i++) {
|
||||
g_string_append_printf(rep, "%-8d", i);
|
||||
dcache = dcaches[i];
|
||||
icache = icaches[i];
|
||||
append_stats_line(rep, dcache->accesses, dcache->misses,
|
||||
icache->accesses, icache->misses);
|
||||
}
|
||||
|
||||
if (cores > 1) {
|
||||
sum_stats();
|
||||
g_string_append_printf(rep, "%-8s", "sum");
|
||||
append_stats_line(rep, all_dmem_accesses, all_dmisses,
|
||||
all_imem_accesses, all_imisses);
|
||||
}
|
||||
|
||||
g_string_append(rep, "\n");
|
||||
qemu_plugin_outs(rep->str);
|
||||
}
|
||||
|
||||
@ -530,8 +611,8 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
|
||||
log_stats();
|
||||
log_top_insns();
|
||||
|
||||
cache_free(dcache);
|
||||
cache_free(icache);
|
||||
caches_free(dcaches);
|
||||
caches_free(icaches);
|
||||
|
||||
g_hash_table_destroy(miss_ht);
|
||||
}
|
||||
@ -579,6 +660,8 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
|
||||
|
||||
policy = LRU;
|
||||
|
||||
cores = sys ? qemu_plugin_n_vcpus() : 1;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
char *opt = argv[i];
|
||||
if (g_str_has_prefix(opt, "iblksize=")) {
|
||||
@ -595,6 +678,8 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
|
||||
dcachesize = g_ascii_strtoll(opt + 11, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "limit=")) {
|
||||
limit = g_ascii_strtoll(opt + 6, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "cores=")) {
|
||||
cores = g_ascii_strtoll(opt + 6, NULL, 10);
|
||||
} else if (g_str_has_prefix(opt, "evict=")) {
|
||||
gchar *p = opt + 6;
|
||||
if (g_strcmp0(p, "rand") == 0) {
|
||||
@ -615,22 +700,25 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
|
||||
|
||||
policy_init();
|
||||
|
||||
dcache = cache_init(dblksize, dassoc, dcachesize);
|
||||
if (!dcache) {
|
||||
dcaches = caches_init(dblksize, dassoc, dcachesize);
|
||||
if (!dcaches) {
|
||||
const char *err = cache_config_error(dblksize, dassoc, dcachesize);
|
||||
fprintf(stderr, "dcache cannot be constructed from given parameters\n");
|
||||
fprintf(stderr, "%s\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
icache = cache_init(iblksize, iassoc, icachesize);
|
||||
if (!icache) {
|
||||
icaches = caches_init(iblksize, iassoc, icachesize);
|
||||
if (!icaches) {
|
||||
const char *err = cache_config_error(iblksize, iassoc, icachesize);
|
||||
fprintf(stderr, "icache cannot be constructed from given parameters\n");
|
||||
fprintf(stderr, "%s\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dcache_locks = g_new0(GMutex, cores);
|
||||
icache_locks = g_new0(GMutex, cores);
|
||||
|
||||
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
|
||||
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
|
||||
|
||||
|
@ -67,7 +67,7 @@ static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
|
||||
/* Print previous instruction in cache */
|
||||
if (s->len) {
|
||||
qemu_plugin_outs(s->str);
|
||||
qemu_plugin_outs("s\n");
|
||||
qemu_plugin_outs("\n");
|
||||
}
|
||||
|
||||
/* Store new instruction in cache */
|
||||
|
@ -133,8 +133,18 @@ QEMU_PLUGIN_EXPORT
|
||||
int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
|
||||
int argc, char **argv)
|
||||
{
|
||||
if (argc && strcmp(argv[0], "inline") == 0) {
|
||||
do_inline = true;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
char *opt = argv[i];
|
||||
g_autofree char **tokens = g_strsplit(opt, "=", 2);
|
||||
if (g_strcmp0(tokens[0], "inline") == 0) {
|
||||
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
|
||||
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "option parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
plugin_init();
|
||||
|
@ -169,16 +169,26 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
char *opt = argv[i];
|
||||
if (g_strcmp0(opt, "reads") == 0) {
|
||||
g_autofree char **tokens = g_strsplit(opt, "=", -1);
|
||||
|
||||
if (g_strcmp0(tokens[0], "sortby") == 0) {
|
||||
if (g_strcmp0(tokens[1], "reads") == 0) {
|
||||
sort_by = SORT_R;
|
||||
} else if (g_strcmp0(opt, "writes") == 0) {
|
||||
} else if (g_strcmp0(tokens[1], "writes") == 0) {
|
||||
sort_by = SORT_W;
|
||||
} else if (g_strcmp0(opt, "address") == 0) {
|
||||
} else if (g_strcmp0(tokens[1], "address") == 0) {
|
||||
sort_by = SORT_A;
|
||||
} else if (g_strcmp0(opt, "io") == 0) {
|
||||
track_io = true;
|
||||
} else if (g_str_has_prefix(opt, "pagesize=")) {
|
||||
page_size = g_ascii_strtoull(opt + 9, NULL, 10);
|
||||
} else {
|
||||
fprintf(stderr, "invalid value to sortby: %s\n", tokens[1]);
|
||||
return -1;
|
||||
}
|
||||
} else if (g_strcmp0(tokens[0], "io") == 0) {
|
||||
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &track_io)) {
|
||||
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
} else if (g_strcmp0(tokens[0], "pagesize") == 0) {
|
||||
page_size = g_ascii_strtoull(tokens[1], NULL, 10);
|
||||
} else {
|
||||
fprintf(stderr, "option parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
|
@ -333,23 +333,34 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
char *p = argv[i];
|
||||
if (strcmp(p, "inline") == 0) {
|
||||
do_inline = true;
|
||||
} else if (strcmp(p, "verbose") == 0) {
|
||||
verbose = true;
|
||||
} else {
|
||||
g_autofree char **tokens = g_strsplit(p, "=", -1);
|
||||
if (g_strcmp0(tokens[0], "inline") == 0) {
|
||||
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
|
||||
fprintf(stderr, "boolean argument parsing failed: %s\n", p);
|
||||
return -1;
|
||||
}
|
||||
} else if (g_strcmp0(tokens[0], "verbose") == 0) {
|
||||
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &verbose)) {
|
||||
fprintf(stderr, "boolean argument parsing failed: %s\n", p);
|
||||
return -1;
|
||||
}
|
||||
} else if (g_strcmp0(tokens[0], "count") == 0) {
|
||||
char *value = tokens[1];
|
||||
int j;
|
||||
CountType type = COUNT_INDIVIDUAL;
|
||||
if (*p == '!') {
|
||||
if (*value == '!') {
|
||||
type = COUNT_NONE;
|
||||
p++;
|
||||
value++;
|
||||
}
|
||||
for (j = 0; j < class_table_sz; j++) {
|
||||
if (strcmp(p, class_table[j].opt) == 0) {
|
||||
if (strcmp(value, class_table[j].opt) == 0) {
|
||||
class_table[j].what = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "option parsing failed: %s\n", p);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,27 +259,42 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
|
||||
int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
g_autoptr(GString) matches_raw = g_string_new("");
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
char *opt = argv[i];
|
||||
if (g_strcmp0(opt, "read") == 0) {
|
||||
g_autofree char **tokens = g_strsplit(opt, "=", 2);
|
||||
|
||||
if (g_strcmp0(tokens[0], "track") == 0) {
|
||||
if (g_strcmp0(tokens[1], "read") == 0) {
|
||||
rw = QEMU_PLUGIN_MEM_R;
|
||||
} else if (g_strcmp0(opt, "write") == 0) {
|
||||
} else if (g_strcmp0(tokens[1], "write") == 0) {
|
||||
rw = QEMU_PLUGIN_MEM_W;
|
||||
} else if (g_strcmp0(opt, "pattern") == 0) {
|
||||
pattern = true;
|
||||
} else if (g_strcmp0(opt, "source") == 0) {
|
||||
source = true;
|
||||
} else if (g_str_has_prefix(opt, "match")) {
|
||||
gchar **parts = g_strsplit(opt, "=", 2);
|
||||
} else {
|
||||
fprintf(stderr, "invalid value for track: %s\n", tokens[1]);
|
||||
return -1;
|
||||
}
|
||||
} else if (g_strcmp0(tokens[0], "pattern") == 0) {
|
||||
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &pattern)) {
|
||||
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
} else if (g_strcmp0(tokens[0], "source") == 0) {
|
||||
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &source)) {
|
||||
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
} else if (g_strcmp0(tokens[0], "match") == 0) {
|
||||
check_match = true;
|
||||
matches = g_strsplit(parts[1], ",", -1);
|
||||
g_strfreev(parts);
|
||||
g_string_append_printf(matches_raw, "%s,", tokens[1]);
|
||||
} else {
|
||||
fprintf(stderr, "option parsing failed: %s\n", opt);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (check_match) {
|
||||
matches = g_strsplit(matches_raw->str, ",", -1);
|
||||
}
|
||||
|
||||
if (source && pattern) {
|
||||
fprintf(stderr, "can only currently track either source or pattern.\n");
|
||||
|
@ -319,20 +319,33 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
|
||||
int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!argc || !argv[0]) {
|
||||
qemu_plugin_outs("Need a socket path to talk to other instance.");
|
||||
return -1;
|
||||
}
|
||||
g_autofree char *sock_path = NULL;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
char *p = argv[i];
|
||||
if (strcmp(p, "verbose") == 0) {
|
||||
verbose = true;
|
||||
} else if (!setup_unix_socket(argv[0])) {
|
||||
qemu_plugin_outs("Failed to setup socket for communications.");
|
||||
g_autofree char **tokens = g_strsplit(p, "=", 2);
|
||||
|
||||
if (g_strcmp0(tokens[0], "verbose") == 0) {
|
||||
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &verbose)) {
|
||||
fprintf(stderr, "boolean argument parsing failed: %s\n", p);
|
||||
return -1;
|
||||
}
|
||||
} else if (g_strcmp0(tokens[0], "sockpath") == 0) {
|
||||
sock_path = tokens[1];
|
||||
} else {
|
||||
fprintf(stderr, "option parsing failed: %s\n", p);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (sock_path == NULL) {
|
||||
fprintf(stderr, "Need a socket path to talk to other instance.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!setup_unix_socket(sock_path)) {
|
||||
fprintf(stderr, "Failed to setup socket for communications.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
our_id = id;
|
||||
|
@ -1,6 +1,5 @@
|
||||
if 'CONFIG_TOOLS' in config_host and virgl.found() \
|
||||
and 'CONFIG_GBM' in config_host and 'CONFIG_LINUX' in config_host \
|
||||
and pixman.found()
|
||||
if 'CONFIG_TOOLS' in config_host and virgl.found() and gbm.found() \
|
||||
and 'CONFIG_LINUX' in config_host and pixman.found()
|
||||
executable('vhost-user-gpu', files('vhost-user-gpu.c', 'virgl.c', 'vugbm.c'),
|
||||
dependencies: [qemuutil, pixman, gbm, virgl, vhost_user, opengl],
|
||||
install: true,
|
||||
|
@ -107,8 +107,8 @@ the process listing. This is replaced by the new ``password-secret``
|
||||
option which lets the password be securely provided on the command
|
||||
line using a ``secret`` object instance.
|
||||
|
||||
``opened`` property of ``rng-*`` objects (since 6.0.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``opened`` property of ``rng-*`` objects (since 6.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The only effect of specifying ``opened=on`` in the command line or QMP
|
||||
``object-add`` is that the device is opened immediately, possibly before all
|
||||
@ -116,8 +116,8 @@ other options have been processed. This will either have no effect (if
|
||||
``opened`` was the last option) or cause errors. The property is therefore
|
||||
useless and should not be specified.
|
||||
|
||||
``loaded`` property of ``secret`` and ``secret_keyring`` objects (since 6.0.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``loaded`` property of ``secret`` and ``secret_keyring`` objects (since 6.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The only effect of specifying ``loaded=on`` in the command line or QMP
|
||||
``object-add`` is that the secret is loaded immediately, possibly before all
|
||||
@ -138,37 +138,69 @@ an underscore between "window" and "close").
|
||||
The ``-no-quit`` is a synonym for ``-display ...,window-close=off`` which
|
||||
should be used instead.
|
||||
|
||||
``-alt-grab`` and ``-display sdl,alt_grab=on`` (since 6.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use ``-display sdl,grab-mod=lshift-lctrl-lalt`` instead.
|
||||
|
||||
``-ctrl-grab`` and ``-display sdl,ctrl_grab=on`` (since 6.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use ``-display sdl,grab-mod=rctrl`` instead.
|
||||
|
||||
``-sdl`` (since 6.2)
|
||||
''''''''''''''''''''
|
||||
|
||||
Use ``-display sdl`` instead.
|
||||
|
||||
``-curses`` (since 6.2)
|
||||
'''''''''''''''''''''''
|
||||
|
||||
Use ``-display curses`` instead.
|
||||
|
||||
|
||||
Plugin argument passing through ``arg=<string>`` (since 6.1)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Passing TCG plugins arguments through ``arg=`` is redundant is makes the
|
||||
command-line less readable, especially when the argument itself consist of a
|
||||
name and a value, e.g. ``-plugin plugin_name,arg="arg_name=arg_value"``.
|
||||
Therefore, the usage of ``arg`` is redundant. Single-word arguments are treated
|
||||
as short-form boolean values, and passed to plugins as ``arg_name=on``.
|
||||
However, short-form booleans are deprecated and full explicit ``arg_name=on``
|
||||
form is preferred.
|
||||
|
||||
|
||||
QEMU Machine Protocol (QMP) commands
|
||||
------------------------------------
|
||||
|
||||
``blockdev-open-tray``, ``blockdev-close-tray`` argument ``device`` (since 2.8.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``blockdev-open-tray``, ``blockdev-close-tray`` argument ``device`` (since 2.8)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use argument ``id`` instead.
|
||||
|
||||
``eject`` argument ``device`` (since 2.8.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''
|
||||
``eject`` argument ``device`` (since 2.8)
|
||||
'''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use argument ``id`` instead.
|
||||
|
||||
``blockdev-change-medium`` argument ``device`` (since 2.8.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``blockdev-change-medium`` argument ``device`` (since 2.8)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use argument ``id`` instead.
|
||||
|
||||
``block_set_io_throttle`` argument ``device`` (since 2.8.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``block_set_io_throttle`` argument ``device`` (since 2.8)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use argument ``id`` instead.
|
||||
|
||||
``blockdev-add`` empty string argument ``backing`` (since 2.10.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``blockdev-add`` empty string argument ``backing`` (since 2.10)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use argument value ``null`` instead.
|
||||
|
||||
``block-commit`` arguments ``base`` and ``top`` (since 3.1.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``block-commit`` arguments ``base`` and ``top`` (since 3.1)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Use arguments ``base-node`` and ``top-node`` instead.
|
||||
|
||||
@ -191,8 +223,8 @@ from Linux upstream kernel, declare it deprecated.
|
||||
System emulator CPUS
|
||||
--------------------
|
||||
|
||||
``Icelake-Client`` CPU Model (since 5.2.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''
|
||||
``Icelake-Client`` CPU Model (since 5.2)
|
||||
''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
``Icelake-Client`` CPU Models are deprecated. Use ``Icelake-Server`` CPU
|
||||
Models instead.
|
||||
@ -207,13 +239,6 @@ this CPU is also deprecated.
|
||||
System emulator machines
|
||||
------------------------
|
||||
|
||||
Raspberry Pi ``raspi2`` and ``raspi3`` machines (since 5.2)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The Raspberry Pi machines come in various models (A, A+, B, B+). To be able
|
||||
to distinguish which model QEMU is implementing, the ``raspi2`` and ``raspi3``
|
||||
machines have been renamed ``raspi2b`` and ``raspi3b``.
|
||||
|
||||
Aspeed ``swift-bmc`` machine (since 6.1)
|
||||
''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
@ -245,8 +270,8 @@ Device options
|
||||
Emulated device options
|
||||
'''''''''''''''''''''''
|
||||
|
||||
``-device virtio-blk,scsi=on|off`` (since 5.0.0)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
``-device virtio-blk,scsi=on|off`` (since 5.0)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The virtio-blk SCSI passthrough feature is a legacy VIRTIO feature. VIRTIO 1.0
|
||||
and later do not support it because the virtio-scsi device was introduced for
|
||||
@ -258,14 +283,14 @@ alias.
|
||||
Block device options
|
||||
''''''''''''''''''''
|
||||
|
||||
``"backing": ""`` (since 2.12.0)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
``"backing": ""`` (since 2.12)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In order to prevent QEMU from automatically opening an image's backing
|
||||
chain, use ``"backing": null`` instead.
|
||||
|
||||
``rbd`` keyvalue pair encoded filenames: ``""`` (since 3.1.0)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
``rbd`` keyvalue pair encoded filenames: ``""`` (since 3.1)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Options for ``rbd`` should be specified according to its runtime options,
|
||||
like other block drivers. Legacy parsing of keyvalue pair encoded
|
||||
@ -283,8 +308,8 @@ The above, converted to the current supported format::
|
||||
linux-user mode CPUs
|
||||
--------------------
|
||||
|
||||
``ppc64abi32`` CPUs (since 5.2.0)
|
||||
'''''''''''''''''''''''''''''''''
|
||||
``ppc64abi32`` CPUs (since 5.2)
|
||||
'''''''''''''''''''''''''''''''
|
||||
|
||||
The ``ppc64abi32`` architecture has a number of issues which regularly
|
||||
trip up our CI testing and is suspected to be quite broken. For that
|
||||
@ -303,8 +328,8 @@ Related binaries
|
||||
Backwards compatibility
|
||||
-----------------------
|
||||
|
||||
Runnability guarantee of CPU models (since 4.1.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
Runnability guarantee of CPU models (since 4.1)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Previous versions of QEMU never changed existing CPU models in
|
||||
ways that introduced additional host software or hardware
|
||||
|
@ -140,18 +140,79 @@ Use ``-rtc driftfix=slew`` instead.
|
||||
|
||||
Replaced by ``-rtc base=date``.
|
||||
|
||||
``-vnc ...,tls=...``, ``-vnc ...,x509=...`` & ``-vnc ...,x509verify=...``
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``-vnc ...,tls=...``, ``-vnc ...,x509=...`` & ``-vnc ...,x509verify=...`` (removed in 3.1)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The "tls-creds" option should be used instead to point to a "tls-creds-x509"
|
||||
object created using "-object".
|
||||
|
||||
``-mem-path`` fallback to RAM (removed in 5.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
If guest RAM allocation from file pointed by ``mem-path`` failed,
|
||||
QEMU was falling back to allocating from RAM, which might have resulted
|
||||
in unpredictable behavior since the backing file specified by the user
|
||||
as ignored. Currently, users are responsible for making sure the backing storage
|
||||
specified with ``-mem-path`` can actually provide the guest RAM configured with
|
||||
``-m`` and QEMU fails to start up if RAM allocation is unsuccessful.
|
||||
|
||||
``-net ...,name=...`` (removed in 5.1)
|
||||
''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The ``name`` parameter of the ``-net`` option was a synonym
|
||||
for the ``id`` parameter, which should now be used instead.
|
||||
|
||||
``-numa node,mem=...`` (removed in 5.1)
|
||||
'''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The parameter ``mem`` of ``-numa node`` was used to assign a part of guest RAM
|
||||
to a NUMA node. But when using it, it's impossible to manage a specified RAM
|
||||
chunk on the host side (like bind it to a host node, setting bind policy, ...),
|
||||
so the guest ends up with the fake NUMA configuration with suboptiomal
|
||||
performance.
|
||||
However since 2014 there is an alternative way to assign RAM to a NUMA node
|
||||
using parameter ``memdev``, which does the same as ``mem`` and adds
|
||||
means to actually manage node RAM on the host side. Use parameter ``memdev``
|
||||
with *memory-backend-ram* backend as replacement for parameter ``mem``
|
||||
to achieve the same fake NUMA effect or a properly configured
|
||||
*memory-backend-file* backend to actually benefit from NUMA configuration.
|
||||
New machine versions (since 5.1) will not accept the option but it will still
|
||||
work with old machine types. User can check the QAPI schema to see if the legacy
|
||||
option is supported by looking at MachineInfo::numa-mem-supported property.
|
||||
|
||||
``-numa`` node (without memory specified) (removed in 5.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Splitting RAM by default between NUMA nodes had the same issues as ``mem``
|
||||
parameter with the difference that the role of the user plays QEMU using
|
||||
implicit generic or board specific splitting rule.
|
||||
Use ``memdev`` with *memory-backend-ram* backend or ``mem`` (if
|
||||
it's supported by used machine type) to define mapping explicitly instead.
|
||||
Users of existing VMs, wishing to preserve the same RAM distribution, should
|
||||
configure it explicitly using ``-numa node,memdev`` options. Current RAM
|
||||
distribution can be retrieved using HMP command ``info numa`` and if separate
|
||||
memory devices (pc|nv-dimm) are present use ``info memory-device`` and subtract
|
||||
device memory from output of ``info numa``.
|
||||
|
||||
``-smp`` (invalid topologies) (removed in 5.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
CPU topology properties should describe whole machine topology including
|
||||
possible CPUs.
|
||||
|
||||
However, historically it was possible to start QEMU with an incorrect topology
|
||||
where *n* <= *sockets* * *cores* * *threads* < *maxcpus*,
|
||||
which could lead to an incorrect topology enumeration by the guest.
|
||||
Support for invalid topologies is removed, the user must ensure
|
||||
topologies described with -smp include all possible cpus, i.e.
|
||||
*sockets* * *cores* * *threads* = *maxcpus*.
|
||||
|
||||
``-machine enforce-config-section=on|off`` (removed in 5.2)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The ``enforce-config-section`` property was replaced by the
|
||||
``-global migration.send-configuration={on|off}`` option.
|
||||
|
||||
``-no-kvm`` (removed in 5.2)
|
||||
''''''''''''''''''''''''''''
|
||||
|
||||
@ -194,8 +255,8 @@ by the ``tls-authz`` and ``sasl-authz`` options.
|
||||
The ``pretty=on|off`` switch has no effect for HMP monitors and
|
||||
its use is rejected.
|
||||
|
||||
``-drive file=json:{...{'driver':'file'}}`` (removed 6.0)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``-drive file=json:{...{'driver':'file'}}`` (removed in 6.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The 'file' driver for drives is no longer appropriate for character or host
|
||||
devices and will only accept regular files (S_IFREG). The correct driver
|
||||
@ -272,8 +333,8 @@ for the RISC-V ``virt`` machine and ``sifive_u`` machine.
|
||||
QEMU Machine Protocol (QMP) commands
|
||||
------------------------------------
|
||||
|
||||
``block-dirty-bitmap-add`` "autoload" parameter (removed in 4.2.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
``block-dirty-bitmap-add`` "autoload" parameter (removed in 4.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The "autoload" parameter has been ignored since 2.12.0. All bitmaps
|
||||
are automatically loaded from qcow2 images.
|
||||
@ -456,15 +517,15 @@ Nobody was using this CPU emulation in QEMU, and there were no test images
|
||||
available to make sure that the code is still working, so it has been removed
|
||||
without replacement.
|
||||
|
||||
``lm32`` CPUs (removed in 6.1.0)
|
||||
''''''''''''''''''''''''''''''''
|
||||
``lm32`` CPUs (removed in 6.1)
|
||||
''''''''''''''''''''''''''''''
|
||||
|
||||
The only public user of this architecture was the milkymist project,
|
||||
which has been dead for years; there was never an upstream Linux
|
||||
port. Removed without replacement.
|
||||
|
||||
``unicore32`` CPUs (since 6.1.0)
|
||||
''''''''''''''''''''''''''''''''
|
||||
``unicore32`` CPUs (removed in 6.1)
|
||||
'''''''''''''''''''''''''''''''''''
|
||||
|
||||
Support for this CPU was removed from the upstream Linux kernel, and
|
||||
there is no available upstream toolchain to build binaries for it.
|
||||
@ -513,6 +574,13 @@ This machine has been renamed ``fuloong2e``.
|
||||
These machine types were very old and likely could not be used for live
|
||||
migration from old QEMU versions anymore. Use a newer machine type instead.
|
||||
|
||||
Raspberry Pi ``raspi2`` and ``raspi3`` machines (removed in 6.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The Raspberry Pi machines come in various models (A, A+, B, B+). To be able
|
||||
to distinguish which model QEMU is implementing, the ``raspi2`` and ``raspi3``
|
||||
machines have been renamed ``raspi2b`` and ``raspi3b``.
|
||||
|
||||
|
||||
linux-user mode CPUs
|
||||
--------------------
|
||||
@ -590,82 +658,6 @@ enforce that any failure to open the backing image (including if the
|
||||
backing file is missing or an incorrect format was specified) is an
|
||||
error when ``-u`` is not used.
|
||||
|
||||
Command line options
|
||||
--------------------
|
||||
|
||||
``-smp`` (invalid topologies) (removed 5.2)
|
||||
'''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
CPU topology properties should describe whole machine topology including
|
||||
possible CPUs.
|
||||
|
||||
However, historically it was possible to start QEMU with an incorrect topology
|
||||
where *n* <= *sockets* * *cores* * *threads* < *maxcpus*,
|
||||
which could lead to an incorrect topology enumeration by the guest.
|
||||
Support for invalid topologies is removed, the user must ensure
|
||||
topologies described with -smp include all possible cpus, i.e.
|
||||
*sockets* * *cores* * *threads* = *maxcpus*.
|
||||
|
||||
``-numa`` node (without memory specified) (removed 5.2)
|
||||
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Splitting RAM by default between NUMA nodes had the same issues as ``mem``
|
||||
parameter with the difference that the role of the user plays QEMU using
|
||||
implicit generic or board specific splitting rule.
|
||||
Use ``memdev`` with *memory-backend-ram* backend or ``mem`` (if
|
||||
it's supported by used machine type) to define mapping explicitly instead.
|
||||
Users of existing VMs, wishing to preserve the same RAM distribution, should
|
||||
configure it explicitly using ``-numa node,memdev`` options. Current RAM
|
||||
distribution can be retrieved using HMP command ``info numa`` and if separate
|
||||
memory devices (pc|nv-dimm) are present use ``info memory-device`` and subtract
|
||||
device memory from output of ``info numa``.
|
||||
|
||||
``-numa node,mem=``\ *size* (removed in 5.1)
|
||||
''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The parameter ``mem`` of ``-numa node`` was used to assign a part of
|
||||
guest RAM to a NUMA node. But when using it, it's impossible to manage a specified
|
||||
RAM chunk on the host side (like bind it to a host node, setting bind policy, ...),
|
||||
so the guest ends up with the fake NUMA configuration with suboptiomal performance.
|
||||
However since 2014 there is an alternative way to assign RAM to a NUMA node
|
||||
using parameter ``memdev``, which does the same as ``mem`` and adds
|
||||
means to actually manage node RAM on the host side. Use parameter ``memdev``
|
||||
with *memory-backend-ram* backend as replacement for parameter ``mem``
|
||||
to achieve the same fake NUMA effect or a properly configured
|
||||
*memory-backend-file* backend to actually benefit from NUMA configuration.
|
||||
New machine versions (since 5.1) will not accept the option but it will still
|
||||
work with old machine types. User can check the QAPI schema to see if the legacy
|
||||
option is supported by looking at MachineInfo::numa-mem-supported property.
|
||||
|
||||
``-mem-path`` fallback to RAM (removed in 5.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
If guest RAM allocation from file pointed by ``mem-path`` failed,
|
||||
QEMU was falling back to allocating from RAM, which might have resulted
|
||||
in unpredictable behavior since the backing file specified by the user
|
||||
as ignored. Currently, users are responsible for making sure the backing storage
|
||||
specified with ``-mem-path`` can actually provide the guest RAM configured with
|
||||
``-m`` and QEMU fails to start up if RAM allocation is unsuccessful.
|
||||
|
||||
``-smp`` (invalid topologies) (removed 5.2)
|
||||
'''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
CPU topology properties should describe whole machine topology including
|
||||
possible CPUs.
|
||||
|
||||
However, historically it was possible to start QEMU with an incorrect topology
|
||||
where *n* <= *sockets* * *cores* * *threads* < *maxcpus*,
|
||||
which could lead to an incorrect topology enumeration by the guest.
|
||||
Support for invalid topologies is removed, the user must ensure
|
||||
topologies described with -smp include all possible cpus, i.e.
|
||||
*sockets* * *cores* * *threads* = *maxcpus*.
|
||||
|
||||
``-machine enforce-config-section=on|off`` (removed 5.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The ``enforce-config-section`` property was replaced by the
|
||||
``-global migration.send-configuration={on|off}`` option.
|
||||
|
||||
qemu-img amend to adjust backing file (removed in 6.1)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
|
121
docs/devel/ci-definitions.rst
Normal file
121
docs/devel/ci-definitions.rst
Normal file
@ -0,0 +1,121 @@
|
||||
Definition of terms
|
||||
===================
|
||||
|
||||
This section defines the terms used in this document and correlates them with
|
||||
what is currently used on QEMU.
|
||||
|
||||
Automated tests
|
||||
---------------
|
||||
|
||||
An automated test is written on a test framework using its generic test
|
||||
functions/classes. The test framework can run the tests and report their
|
||||
success or failure [1]_.
|
||||
|
||||
An automated test has essentially three parts:
|
||||
|
||||
1. The test initialization of the parameters, where the expected parameters,
|
||||
like inputs and expected results, are set up;
|
||||
2. The call to the code that should be tested;
|
||||
3. An assertion, comparing the result from the previous call with the expected
|
||||
result set during the initialization of the parameters. If the result
|
||||
matches the expected result, the test has been successful; otherwise, it has
|
||||
failed.
|
||||
|
||||
Unit testing
|
||||
------------
|
||||
|
||||
A unit test is responsible for exercising individual software components as a
|
||||
unit, like interfaces, data structures, and functionality, uncovering errors
|
||||
within the boundaries of a component. The verification effort is in the
|
||||
smallest software unit and focuses on the internal processing logic and data
|
||||
structures. A test case of unit tests should be designed to uncover errors due
|
||||
to erroneous computations, incorrect comparisons, or improper control flow [2]_.
|
||||
|
||||
On QEMU, unit testing is represented by the 'check-unit' target from 'make'.
|
||||
|
||||
Functional testing
|
||||
------------------
|
||||
|
||||
A functional test focuses on the functional requirement of the software.
|
||||
Deriving sets of input conditions, the functional tests should fully exercise
|
||||
all the functional requirements for a program. Functional testing is
|
||||
complementary to other testing techniques, attempting to find errors like
|
||||
incorrect or missing functions, interface errors, behavior errors, and
|
||||
initialization and termination errors [3]_.
|
||||
|
||||
On QEMU, functional testing is represented by the 'check-qtest' target from
|
||||
'make'.
|
||||
|
||||
System testing
|
||||
--------------
|
||||
|
||||
System tests ensure all application elements mesh properly while the overall
|
||||
functionality and performance are achieved [4]_. Some or all system components
|
||||
are integrated to create a complete system to be tested as a whole. System
|
||||
testing ensures that components are compatible, interact correctly, and
|
||||
transfer the right data at the right time across their interfaces. As system
|
||||
testing focuses on interactions, use case-based testing is a practical approach
|
||||
to system testing [5]_. Note that, in some cases, system testing may require
|
||||
interaction with third-party software, like operating system images, databases,
|
||||
networks, and so on.
|
||||
|
||||
On QEMU, system testing is represented by the 'check-acceptance' target from
|
||||
'make'.
|
||||
|
||||
Flaky tests
|
||||
-----------
|
||||
|
||||
A flaky test is defined as a test that exhibits both a passing and a failing
|
||||
result with the same code on different runs. Some usual reasons for an
|
||||
intermittent/flaky test are async wait, concurrency, and test order dependency
|
||||
[6]_.
|
||||
|
||||
Gating
|
||||
------
|
||||
|
||||
A gate restricts the move of code from one stage to another on a
|
||||
test/deployment pipeline. The step move is granted with approval. The approval
|
||||
can be a manual intervention or a set of tests succeeding [7]_.
|
||||
|
||||
On QEMU, the gating process happens during the pull request. The approval is
|
||||
done by the project leader running its own set of tests. The pull request gets
|
||||
merged when the tests succeed.
|
||||
|
||||
Continuous Integration (CI)
|
||||
---------------------------
|
||||
|
||||
Continuous integration (CI) requires the builds of the entire application and
|
||||
the execution of a comprehensive set of automated tests every time there is a
|
||||
need to commit any set of changes [8]_. The automated tests can be composed of
|
||||
the unit, functional, system, and other tests.
|
||||
|
||||
Keynotes about continuous integration (CI) [9]_:
|
||||
|
||||
1. System tests may depend on external software (operating system images,
|
||||
firmware, database, network).
|
||||
2. It may take a long time to build and test. It may be impractical to build
|
||||
the system being developed several times per day.
|
||||
3. If the development platform is different from the target platform, it may
|
||||
not be possible to run system tests in the developer’s private workspace.
|
||||
There may be differences in hardware, operating system, or installed
|
||||
software. Therefore, more time is required for testing the system.
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
.. [1] Sommerville, Ian (2016). Software Engineering. p. 233.
|
||||
.. [2] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering,
|
||||
A Practitioner’s Approach. p. 48, 376, 378, 381.
|
||||
.. [3] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering,
|
||||
A Practitioner’s Approach. p. 388.
|
||||
.. [4] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering,
|
||||
A Practitioner’s Approach. Software Engineering, p. 377.
|
||||
.. [5] Sommerville, Ian (2016). Software Engineering. p. 59, 232, 240.
|
||||
.. [6] Luo, Qingzhou, et al. An empirical analysis of flaky tests.
|
||||
Proceedings of the 22nd ACM SIGSOFT International Symposium on
|
||||
Foundations of Software Engineering. 2014.
|
||||
.. [7] Humble, Jez & Farley, David (2010). Continuous Delivery:
|
||||
Reliable Software Releases Through Build, Test, and Deployment, p. 122.
|
||||
.. [8] Humble, Jez & Farley, David (2010). Continuous Delivery:
|
||||
Reliable Software Releases Through Build, Test, and Deployment, p. 55.
|
||||
.. [9] Sommerville, Ian (2016). Software Engineering. p. 743.
|
51
docs/devel/ci-jobs.rst
Normal file
51
docs/devel/ci-jobs.rst
Normal file
@ -0,0 +1,51 @@
|
||||
Custom CI/CD variables
|
||||
======================
|
||||
|
||||
QEMU CI pipelines can be tuned by setting some CI environment variables.
|
||||
|
||||
Set variable globally in the user's CI namespace
|
||||
------------------------------------------------
|
||||
|
||||
Variables can be set globally in the user's CI namespace setting.
|
||||
|
||||
For further information about how to set these variables, please refer to::
|
||||
|
||||
https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project
|
||||
|
||||
Set variable manually when pushing a branch or tag to the user's repository
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
Variables can be set manually when pushing a branch or tag, using
|
||||
git-push command line arguments.
|
||||
|
||||
Example setting the QEMU_CI_EXAMPLE_VAR variable:
|
||||
|
||||
.. code::
|
||||
|
||||
git push -o ci.variable="QEMU_CI_EXAMPLE_VAR=value" myrepo mybranch
|
||||
|
||||
For further information about how to set these variables, please refer to::
|
||||
|
||||
https://docs.gitlab.com/ee/user/project/push_options.html#push-options-for-gitlab-cicd
|
||||
|
||||
Here is a list of the most used variables:
|
||||
|
||||
QEMU_CI_AVOCADO_TESTING
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
By default, tests using the Avocado framework are not run automatically in
|
||||
the pipelines (because multiple artifacts have to be downloaded, and if
|
||||
these artifacts are not already cached, downloading them make the jobs
|
||||
reach the timeout limit). Set this variable to have the tests using the
|
||||
Avocado framework run automatically.
|
||||
|
||||
AARCH64_RUNNER_AVAILABLE
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
If you've got access to an aarch64 host that can be used as a gitlab-CI
|
||||
runner, you can set this variable to enable the tests that require this
|
||||
kind of host. The runner should be tagged with "aarch64".
|
||||
|
||||
S390X_RUNNER_AVAILABLE
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
If you've got access to an IBM Z host that can be used as a gitlab-CI
|
||||
runner, you can set this variable to enable the tests that require this
|
||||
kind of host. The runner should be tagged with "s390x".
|
117
docs/devel/ci-runners.rst
Normal file
117
docs/devel/ci-runners.rst
Normal file
@ -0,0 +1,117 @@
|
||||
Jobs on Custom Runners
|
||||
======================
|
||||
|
||||
Besides the jobs run under the various CI systems listed before, there
|
||||
are a number additional jobs that will run before an actual merge.
|
||||
These use the same GitLab CI's service/framework already used for all
|
||||
other GitLab based CI jobs, but rely on additional systems, not the
|
||||
ones provided by GitLab as "shared runners".
|
||||
|
||||
The architecture of GitLab's CI service allows different machines to
|
||||
be set up with GitLab's "agent", called gitlab-runner, which will take
|
||||
care of running jobs created by events such as a push to a branch.
|
||||
Here, the combination of a machine, properly configured with GitLab's
|
||||
gitlab-runner, is called a "custom runner".
|
||||
|
||||
The GitLab CI jobs definition for the custom runners are located under::
|
||||
|
||||
.gitlab-ci.d/custom-runners.yml
|
||||
|
||||
Custom runners entail custom machines. To see a list of the machines
|
||||
currently deployed in the QEMU GitLab CI and their maintainers, please
|
||||
refer to the QEMU `wiki <https://wiki.qemu.org/AdminContacts>`__.
|
||||
|
||||
Machine Setup Howto
|
||||
-------------------
|
||||
|
||||
For all Linux based systems, the setup can be mostly automated by the
|
||||
execution of two Ansible playbooks. Create an ``inventory`` file
|
||||
under ``scripts/ci/setup``, such as this::
|
||||
|
||||
fully.qualified.domain
|
||||
other.machine.hostname
|
||||
|
||||
You may need to set some variables in the inventory file itself. One
|
||||
very common need is to tell Ansible to use a Python 3 interpreter on
|
||||
those hosts. This would look like::
|
||||
|
||||
fully.qualified.domain ansible_python_interpreter=/usr/bin/python3
|
||||
other.machine.hostname ansible_python_interpreter=/usr/bin/python3
|
||||
|
||||
Build environment
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``scripts/ci/setup/build-environment.yml`` Ansible playbook will
|
||||
set up machines with the environment needed to perform builds and run
|
||||
QEMU tests. This playbook consists on the installation of various
|
||||
required packages (and a general package update while at it). It
|
||||
currently covers a number of different Linux distributions, but it can
|
||||
be expanded to cover other systems.
|
||||
|
||||
The minimum required version of Ansible successfully tested in this
|
||||
playbook is 2.8.0 (a version check is embedded within the playbook
|
||||
itself). To run the playbook, execute::
|
||||
|
||||
cd scripts/ci/setup
|
||||
ansible-playbook -i inventory build-environment.yml
|
||||
|
||||
Please note that most of the tasks in the playbook require superuser
|
||||
privileges, such as those from the ``root`` account or those obtained
|
||||
by ``sudo``. If necessary, please refer to ``ansible-playbook``
|
||||
options such as ``--become``, ``--become-method``, ``--become-user``
|
||||
and ``--ask-become-pass``.
|
||||
|
||||
gitlab-runner setup and registration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The gitlab-runner agent needs to be installed on each machine that
|
||||
will run jobs. The association between a machine and a GitLab project
|
||||
happens with a registration token. To find the registration token for
|
||||
your repository/project, navigate on GitLab's web UI to:
|
||||
|
||||
* Settings (the gears-like icon at the bottom of the left hand side
|
||||
vertical toolbar), then
|
||||
* CI/CD, then
|
||||
* Runners, and click on the "Expand" button, then
|
||||
* Under "Set up a specific Runner manually", look for the value under
|
||||
"And this registration token:"
|
||||
|
||||
Copy the ``scripts/ci/setup/vars.yml.template`` file to
|
||||
``scripts/ci/setup/vars.yml``. Then, set the
|
||||
``gitlab_runner_registration_token`` variable to the value obtained
|
||||
earlier.
|
||||
|
||||
To run the playbook, execute::
|
||||
|
||||
cd scripts/ci/setup
|
||||
ansible-playbook -i inventory gitlab-runner.yml
|
||||
|
||||
Following the registration, it's necessary to configure the runner tags,
|
||||
and optionally other configurations on the GitLab UI. Navigate to:
|
||||
|
||||
* Settings (the gears like icon), then
|
||||
* CI/CD, then
|
||||
* Runners, and click on the "Expand" button, then
|
||||
* "Runners activated for this project", then
|
||||
* Click on the "Edit" icon (next to the "Lock" Icon)
|
||||
|
||||
Tags are very important as they are used to route specific jobs to
|
||||
specific types of runners, so it's a good idea to double check that
|
||||
the automatically created tags are consistent with the OS and
|
||||
architecture. For instance, an Ubuntu 20.04 aarch64 system should
|
||||
have tags set as::
|
||||
|
||||
ubuntu_20.04,aarch64
|
||||
|
||||
Because the job definition at ``.gitlab-ci.d/custom-runners.yml``
|
||||
would contain::
|
||||
|
||||
ubuntu-20.04-aarch64-all:
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
|
||||
It's also recommended to:
|
||||
|
||||
* increase the "Maximum job timeout" to something like ``2h``
|
||||
* give it a better Description
|
@ -8,160 +8,6 @@ found at::
|
||||
|
||||
https://wiki.qemu.org/Testing/CI
|
||||
|
||||
Custom CI/CD variables
|
||||
======================
|
||||
|
||||
QEMU CI pipelines can be tuned by setting some CI environment variables.
|
||||
|
||||
Set variable globally in the user's CI namespace
|
||||
------------------------------------------------
|
||||
|
||||
Variables can be set globally in the user's CI namespace setting.
|
||||
|
||||
For further information about how to set these variables, please refer to::
|
||||
|
||||
https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project
|
||||
|
||||
Set variable manually when pushing a branch or tag to the user's repository
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
Variables can be set manually when pushing a branch or tag, using
|
||||
git-push command line arguments.
|
||||
|
||||
Example setting the QEMU_CI_EXAMPLE_VAR variable:
|
||||
|
||||
.. code::
|
||||
|
||||
git push -o ci.variable="QEMU_CI_EXAMPLE_VAR=value" myrepo mybranch
|
||||
|
||||
For further information about how to set these variables, please refer to::
|
||||
|
||||
https://docs.gitlab.com/ee/user/project/push_options.html#push-options-for-gitlab-cicd
|
||||
|
||||
Here is a list of the most used variables:
|
||||
|
||||
QEMU_CI_AVOCADO_TESTING
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
By default, tests using the Avocado framework are not run automatically in
|
||||
the pipelines (because multiple artifacts have to be downloaded, and if
|
||||
these artifacts are not already cached, downloading them make the jobs
|
||||
reach the timeout limit). Set this variable to have the tests using the
|
||||
Avocado framework run automatically.
|
||||
|
||||
Jobs on Custom Runners
|
||||
======================
|
||||
|
||||
Besides the jobs run under the various CI systems listed before, there
|
||||
are a number additional jobs that will run before an actual merge.
|
||||
These use the same GitLab CI's service/framework already used for all
|
||||
other GitLab based CI jobs, but rely on additional systems, not the
|
||||
ones provided by GitLab as "shared runners".
|
||||
|
||||
The architecture of GitLab's CI service allows different machines to
|
||||
be set up with GitLab's "agent", called gitlab-runner, which will take
|
||||
care of running jobs created by events such as a push to a branch.
|
||||
Here, the combination of a machine, properly configured with GitLab's
|
||||
gitlab-runner, is called a "custom runner".
|
||||
|
||||
The GitLab CI jobs definition for the custom runners are located under::
|
||||
|
||||
.gitlab-ci.d/custom-runners.yml
|
||||
|
||||
Custom runners entail custom machines. To see a list of the machines
|
||||
currently deployed in the QEMU GitLab CI and their maintainers, please
|
||||
refer to the QEMU `wiki <https://wiki.qemu.org/AdminContacts>`__.
|
||||
|
||||
Machine Setup Howto
|
||||
-------------------
|
||||
|
||||
For all Linux based systems, the setup can be mostly automated by the
|
||||
execution of two Ansible playbooks. Create an ``inventory`` file
|
||||
under ``scripts/ci/setup``, such as this::
|
||||
|
||||
fully.qualified.domain
|
||||
other.machine.hostname
|
||||
|
||||
You may need to set some variables in the inventory file itself. One
|
||||
very common need is to tell Ansible to use a Python 3 interpreter on
|
||||
those hosts. This would look like::
|
||||
|
||||
fully.qualified.domain ansible_python_interpreter=/usr/bin/python3
|
||||
other.machine.hostname ansible_python_interpreter=/usr/bin/python3
|
||||
|
||||
Build environment
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``scripts/ci/setup/build-environment.yml`` Ansible playbook will
|
||||
set up machines with the environment needed to perform builds and run
|
||||
QEMU tests. This playbook consists on the installation of various
|
||||
required packages (and a general package update while at it). It
|
||||
currently covers a number of different Linux distributions, but it can
|
||||
be expanded to cover other systems.
|
||||
|
||||
The minimum required version of Ansible successfully tested in this
|
||||
playbook is 2.8.0 (a version check is embedded within the playbook
|
||||
itself). To run the playbook, execute::
|
||||
|
||||
cd scripts/ci/setup
|
||||
ansible-playbook -i inventory build-environment.yml
|
||||
|
||||
Please note that most of the tasks in the playbook require superuser
|
||||
privileges, such as those from the ``root`` account or those obtained
|
||||
by ``sudo``. If necessary, please refer to ``ansible-playbook``
|
||||
options such as ``--become``, ``--become-method``, ``--become-user``
|
||||
and ``--ask-become-pass``.
|
||||
|
||||
gitlab-runner setup and registration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The gitlab-runner agent needs to be installed on each machine that
|
||||
will run jobs. The association between a machine and a GitLab project
|
||||
happens with a registration token. To find the registration token for
|
||||
your repository/project, navigate on GitLab's web UI to:
|
||||
|
||||
* Settings (the gears-like icon at the bottom of the left hand side
|
||||
vertical toolbar), then
|
||||
* CI/CD, then
|
||||
* Runners, and click on the "Expand" button, then
|
||||
* Under "Set up a specific Runner manually", look for the value under
|
||||
"And this registration token:"
|
||||
|
||||
Copy the ``scripts/ci/setup/vars.yml.template`` file to
|
||||
``scripts/ci/setup/vars.yml``. Then, set the
|
||||
``gitlab_runner_registration_token`` variable to the value obtained
|
||||
earlier.
|
||||
|
||||
To run the playbook, execute::
|
||||
|
||||
cd scripts/ci/setup
|
||||
ansible-playbook -i inventory gitlab-runner.yml
|
||||
|
||||
Following the registration, it's necessary to configure the runner tags,
|
||||
and optionally other configurations on the GitLab UI. Navigate to:
|
||||
|
||||
* Settings (the gears like icon), then
|
||||
* CI/CD, then
|
||||
* Runners, and click on the "Expand" button, then
|
||||
* "Runners activated for this project", then
|
||||
* Click on the "Edit" icon (next to the "Lock" Icon)
|
||||
|
||||
Tags are very important as they are used to route specific jobs to
|
||||
specific types of runners, so it's a good idea to double check that
|
||||
the automatically created tags are consistent with the OS and
|
||||
architecture. For instance, an Ubuntu 20.04 aarch64 system should
|
||||
have tags set as::
|
||||
|
||||
ubuntu_20.04,aarch64
|
||||
|
||||
Because the job definition at ``.gitlab-ci.d/custom-runners.yml``
|
||||
would contain::
|
||||
|
||||
ubuntu-20.04-aarch64-all:
|
||||
tags:
|
||||
- ubuntu_20.04
|
||||
- aarch64
|
||||
|
||||
It's also recommended to:
|
||||
|
||||
* increase the "Maximum job timeout" to something like ``2h``
|
||||
* give it a better Description
|
||||
.. include:: ci-definitions.rst
|
||||
.. include:: ci-jobs.rst
|
||||
.. include:: ci-runners.rst
|
||||
|
@ -260,6 +260,29 @@ clocks get the new clock period value: *Clock 2*, *Clock 3* and *Clock 4*.
|
||||
It is not possible to disconnect a clock or to change the clock connection
|
||||
after it is connected.
|
||||
|
||||
Clock multiplier and divider settings
|
||||
-------------------------------------
|
||||
|
||||
By default, when clocks are connected together, the child
|
||||
clocks run with the same period as their source (parent) clock.
|
||||
The Clock API supports a built-in period multiplier/divider
|
||||
mechanism so you can configure a clock to make its children
|
||||
run at a different period from its own. If you call the
|
||||
``clock_set_mul_div()`` function you can specify the clock's
|
||||
multiplier and divider values. The children of that clock
|
||||
will all run with a period of ``parent_period * multiplier / divider``.
|
||||
For instance, if the clock has a frequency of 8MHz and you set its
|
||||
multiplier to 2 and its divider to 3, the child clocks will run
|
||||
at 12MHz.
|
||||
|
||||
You can change the multiplier and divider of a clock at runtime,
|
||||
so you can use this to model clock controller devices which
|
||||
have guest-programmable frequency multipliers or dividers.
|
||||
|
||||
Note that ``clock_set_mul_div()`` does not automatically call
|
||||
``clock_propagate()``. If you make a runtime change to the
|
||||
multiplier or divider you must call clock_propagate() yourself.
|
||||
|
||||
Unconnected input clocks
|
||||
------------------------
|
||||
|
||||
|
@ -826,25 +826,31 @@ Configuring the schema
|
||||
Syntax::
|
||||
|
||||
COND = STRING
|
||||
| [ STRING, ... ]
|
||||
| { 'all: [ COND, ... ] }
|
||||
| { 'any: [ COND, ... ] }
|
||||
| { 'not': COND }
|
||||
|
||||
All definitions take an optional 'if' member. Its value must be a
|
||||
string or a list of strings. A string is shorthand for a list
|
||||
containing just that string. The code generated for the definition
|
||||
will then be guarded by #if STRING for each STRING in the COND list.
|
||||
string, or an object with a single member 'all', 'any' or 'not'.
|
||||
|
||||
The C code generated for the definition will then be guarded by an #if
|
||||
preprocessing directive with an operand generated from that condition:
|
||||
|
||||
* STRING will generate defined(STRING)
|
||||
* { 'all': [COND, ...] } will generate (COND && ...)
|
||||
* { 'any': [COND, ...] } will generate (COND || ...)
|
||||
* { 'not': COND } will generate !COND
|
||||
|
||||
Example: a conditional struct ::
|
||||
|
||||
{ 'struct': 'IfStruct', 'data': { 'foo': 'int' },
|
||||
'if': ['defined(CONFIG_FOO)', 'defined(HAVE_BAR)'] }
|
||||
'if': { 'all': [ 'CONFIG_FOO', 'HAVE_BAR' ] } }
|
||||
|
||||
gets its generated code guarded like this::
|
||||
|
||||
#if defined(CONFIG_FOO)
|
||||
#if defined(HAVE_BAR)
|
||||
#if defined(CONFIG_FOO) && defined(HAVE_BAR)
|
||||
... generated code ...
|
||||
#endif /* defined(HAVE_BAR) */
|
||||
#endif /* defined(CONFIG_FOO) */
|
||||
#endif /* defined(HAVE_BAR) && defined(CONFIG_FOO) */
|
||||
|
||||
Individual members of complex types, commands arguments, and
|
||||
event-specific data can also be made conditional. This requires the
|
||||
@ -855,7 +861,7 @@ member 'bar' ::
|
||||
|
||||
{ 'struct': 'IfStruct', 'data':
|
||||
{ 'foo': 'int',
|
||||
'bar': { 'type': 'int', 'if': 'defined(IFCOND)'} } }
|
||||
'bar': { 'type': 'int', 'if': 'IFCOND'} } }
|
||||
|
||||
A union's discriminator may not be conditional.
|
||||
|
||||
@ -867,7 +873,7 @@ value 'bar' ::
|
||||
|
||||
{ 'enum': 'IfEnum', 'data':
|
||||
[ 'foo',
|
||||
{ 'name' : 'bar', 'if': 'defined(IFCOND)' } ] }
|
||||
{ 'name' : 'bar', 'if': 'IFCOND' } ] }
|
||||
|
||||
Likewise, features can be conditional. This requires the longhand
|
||||
form of FEATURE_.
|
||||
@ -877,7 +883,7 @@ Example: a struct with conditional feature 'allow-negative-numbers' ::
|
||||
{ 'struct': 'TestType',
|
||||
'data': { 'number': 'int' },
|
||||
'features': [ { 'name': 'allow-negative-numbers',
|
||||
'if': 'defined(IFCOND)' } ] }
|
||||
'if': 'IFCOND' } ] }
|
||||
|
||||
Please note that you are responsible to ensure that the C code will
|
||||
compile with an arbitrary combination of conditions, since the
|
||||
|
@ -41,7 +41,7 @@ Nodes
|
||||
|
||||
A node can be of four types:
|
||||
|
||||
- **QNODE_MACHINE**: for example ``arm/raspi2``
|
||||
- **QNODE_MACHINE**: for example ``arm/raspi2b``
|
||||
- **QNODE_DRIVER**: for example ``generic-sdhci``
|
||||
- **QNODE_INTERFACE**: for example ``sdhci`` (interface for all ``-sdhci``
|
||||
drivers).
|
||||
@ -119,12 +119,12 @@ It is possible to troubleshoot unavailable tests by running::
|
||||
# |-> dest='i440FX-pcihost' type=0 (node=0x5591421117f0)
|
||||
# src=''
|
||||
# |-> dest='x86_64/pc' type=0 (node=0x559142111600)
|
||||
# |-> dest='arm/raspi2' type=0 (node=0x559142110740)
|
||||
# |-> dest='arm/raspi2b' type=0 (node=0x559142110740)
|
||||
...
|
||||
# }
|
||||
# ALL QGRAPH NODES: {
|
||||
# name='virtio-net-tests/announce-self' type=3 cmd_line='(null)' [available]
|
||||
# name='arm/raspi2' type=0 cmd_line='-M raspi2 ' [UNAVAILABLE]
|
||||
# name='arm/raspi2b' type=0 cmd_line='-M raspi2b ' [UNAVAILABLE]
|
||||
...
|
||||
# }
|
||||
|
||||
@ -135,8 +135,8 @@ qgraph path in the "ALL QGRAPH EDGES" output as follows: '' -> 'x86_64/pc' ->
|
||||
'virtio-net'. The root of the qgraph is '' and the depth first search begins
|
||||
there.
|
||||
|
||||
The ``arm/raspi`` machine node is listed as "UNAVAILABLE". Although it is
|
||||
reachable from the root via '' -> 'arm/raspi2' the node is unavailable because
|
||||
The ``arm/raspi2b`` machine node is listed as "UNAVAILABLE". Although it is
|
||||
reachable from the root via '' -> 'arm/raspi2b' the node is unavailable because
|
||||
the QEMU binary did not list it when queried by the framework. This is expected
|
||||
because we used the ``qemu-system-x86_64`` binary which does not support ARM
|
||||
machine types.
|
||||
@ -158,7 +158,7 @@ Here we continue the ``sdhci`` use case, with the following scenario:
|
||||
- ``sdhci-test`` aims to test the ``read[q,w], writeq`` functions
|
||||
offered by the ``sdhci`` drivers.
|
||||
- The current ``sdhci`` device is supported by both ``x86_64/pc`` and ``ARM``
|
||||
(in this example we focus on the ``arm-raspi2``) machines.
|
||||
(in this example we focus on the ``arm-raspi2b``) machines.
|
||||
- QEMU offers 2 types of drivers: ``QSDHCI_MemoryMapped`` for ``ARM`` and
|
||||
``QSDHCI_PCI`` for ``x86_64/pc``. Both implement the
|
||||
``read[q,w], writeq`` functions.
|
||||
@ -180,11 +180,11 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
||||
all the pci drivers available)
|
||||
|
||||
``sdhci-pci --consumes--> pci-bus``
|
||||
- Create an ``arm/raspi2`` machine node. This machine ``contains``
|
||||
- Create an ``arm/raspi2b`` machine node. This machine ``contains``
|
||||
a ``generic-sdhci`` memory mapped ``sdhci`` driver node, representing
|
||||
``QSDHCI_MemoryMapped``.
|
||||
|
||||
``arm/raspi2 --contains--> generic-sdhci``
|
||||
``arm/raspi2b --contains--> generic-sdhci``
|
||||
- Create the ``sdhci`` interface node. This interface offers the
|
||||
functions that are shared by all ``sdhci`` devices.
|
||||
The interface is produced by ``sdhci-pci`` and ``generic-sdhci``,
|
||||
@ -199,7 +199,7 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
||||
|
||||
``sdhci-test --consumes--> sdhci``
|
||||
|
||||
``arm-raspi2`` machine, simplified from
|
||||
``arm-raspi2b`` machine, simplified from
|
||||
``tests/qtest/libqos/arm-raspi2-machine.c``::
|
||||
|
||||
#include "qgraph.h"
|
||||
@ -217,7 +217,7 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
||||
return &machine->alloc;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/raspi2\n", interface);
|
||||
fprintf(stderr, "%s not present in arm/raspi2b\n", interface);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
@ -229,7 +229,7 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
||||
return &machine->sdhci.obj;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s not present in arm/raspi2\n", device);
|
||||
fprintf(stderr, "%s not present in arm/raspi2b\n", device);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
@ -253,10 +253,10 @@ In order to implement such scenario in qgraph, the test developer needs to:
|
||||
|
||||
static void raspi2_register_nodes(void)
|
||||
{
|
||||
/* arm/raspi2 --contains--> generic-sdhci */
|
||||
qos_node_create_machine("arm/raspi2",
|
||||
/* arm/raspi2b --contains--> generic-sdhci */
|
||||
qos_node_create_machine("arm/raspi2b",
|
||||
qos_create_machine_arm_raspi2);
|
||||
qos_node_contains("arm/raspi2", "generic-sdhci", NULL);
|
||||
qos_node_contains("arm/raspi2b", "generic-sdhci", NULL);
|
||||
}
|
||||
|
||||
libqos_init(raspi2_register_nodes);
|
||||
@ -470,7 +470,7 @@ In the above example, all possible types of relations are created::
|
||||
|
|
||||
+--produces-- +
|
||||
|
|
||||
arm/raspi2 --contains--> generic-sdhci
|
||||
arm/raspi2b --contains--> generic-sdhci
|
||||
|
||||
or inverting the consumes edge in consumed_by::
|
||||
|
||||
@ -486,7 +486,7 @@ or inverting the consumes edge in consumed_by::
|
||||
|
|
||||
+--produces-- +
|
||||
|
|
||||
arm/raspi2 --contains--> generic-sdhci
|
||||
arm/raspi2b --contains--> generic-sdhci
|
||||
|
||||
Adding a new test
|
||||
"""""""""""""""""
|
||||
@ -536,7 +536,7 @@ Final graph will be like this::
|
||||
|
|
||||
+--produces-- +
|
||||
|
|
||||
arm/raspi2 --contains--> generic-sdhci
|
||||
arm/raspi2b --contains--> generic-sdhci
|
||||
|
||||
or inverting the consumes edge in consumed_by::
|
||||
|
||||
@ -552,7 +552,7 @@ or inverting the consumes edge in consumed_by::
|
||||
|
|
||||
+--produces-- +
|
||||
|
|
||||
arm/raspi2 --contains--> generic-sdhci
|
||||
arm/raspi2b --contains--> generic-sdhci
|
||||
|
||||
Assuming there the binary is
|
||||
``QTEST_QEMU_BINARY=./qemu-system-x86_64``
|
||||
@ -561,7 +561,7 @@ a valid test path will be:
|
||||
|
||||
and for the binary ``QTEST_QEMU_BINARY=./qemu-system-arm``:
|
||||
|
||||
``/arm/raspi2/generic-sdhci/sdhci/sdhci-test``
|
||||
``/arm/raspi2b/generic-sdhci/sdhci/sdhci-test``
|
||||
|
||||
Additional examples are also in ``test-qgraph.c``
|
||||
|
||||
|
@ -92,6 +92,3 @@ When the translator is handling an instruction of this kind:
|
||||
}
|
||||
|
||||
* it must end the TB immediately after this instruction
|
||||
|
||||
Note that some older front-ends call a "gen_io_end()" function:
|
||||
this is obsolete and should not be used.
|
||||
|
@ -80,7 +80,7 @@ Once built a program can be run with multiple plugins loaded each with
|
||||
their own arguments::
|
||||
|
||||
$QEMU $OTHER_QEMU_ARGS \
|
||||
-plugin tests/plugin/libhowvec.so,arg=inline,arg=hint \
|
||||
-plugin tests/plugin/libhowvec.so,inline=on,count=hint \
|
||||
-plugin tests/plugin/libhotblocks.so
|
||||
|
||||
Arguments are plugin specific and can be used to modify their
|
||||
@ -193,17 +193,32 @@ Similar to hotblocks but this time tracks memory accesses::
|
||||
0x0000000048b000, 0x0001, 130594, 0x0001, 355
|
||||
0x0000000048a000, 0x0001, 1826, 0x0001, 11
|
||||
|
||||
The hotpages plugin can be configured using the following arguments:
|
||||
|
||||
* sortby=reads|writes|address
|
||||
|
||||
Log the data sorted by either the number of reads, the number of writes, or
|
||||
memory address. (Default: entries are sorted by the sum of reads and writes)
|
||||
|
||||
* io=on
|
||||
|
||||
Track IO addresses. Only relevant to full system emulation. (Default: off)
|
||||
|
||||
* pagesize=N
|
||||
|
||||
The page size used. (Default: N = 4096)
|
||||
|
||||
- contrib/plugins/howvec.c
|
||||
|
||||
This is an instruction classifier so can be used to count different
|
||||
types of instructions. It has a number of options to refine which get
|
||||
counted. You can give an argument for a class of instructions to break
|
||||
it down fully, so for example to see all the system registers
|
||||
accesses::
|
||||
counted. You can give a value to the `count` argument for a class of
|
||||
instructions to break it down fully, so for example to see all the system
|
||||
registers accesses::
|
||||
|
||||
./aarch64-softmmu/qemu-system-aarch64 $(QEMU_ARGS) \
|
||||
-append "root=/dev/sda2 systemd.unit=benchmark.service" \
|
||||
-smp 4 -plugin ./contrib/plugins/libhowvec.so,arg=sreg -d plugin
|
||||
-smp 4 -plugin ./contrib/plugins/libhowvec.so,count=sreg -d plugin
|
||||
|
||||
which will lead to a sorted list after the class breakdown::
|
||||
|
||||
@ -271,7 +286,7 @@ communicate over::
|
||||
|
||||
./sparc-softmmu/qemu-system-sparc -monitor none -parallel none \
|
||||
-net none -M SS-20 -m 256 -kernel day11/zImage.elf \
|
||||
-plugin ./contrib/plugins/liblockstep.so,arg=lockstep-sparc.sock \
|
||||
-plugin ./contrib/plugins/liblockstep.so,sockpath=lockstep-sparc.sock \
|
||||
-d plugin,nochain
|
||||
|
||||
which will eventually report::
|
||||
@ -286,27 +301,27 @@ which will eventually report::
|
||||
previously @ 0x000000ffd08098/5 (809900593 insns)
|
||||
previously @ 0x000000ffd080c0/1 (809900588 insns)
|
||||
|
||||
- contrib/plugins/hwprofile
|
||||
- contrib/plugins/hwprofile.c
|
||||
|
||||
The hwprofile tool can only be used with system emulation and allows
|
||||
the user to see what hardware is accessed how often. It has a number of options:
|
||||
|
||||
* arg=read or arg=write
|
||||
* track=read or track=write
|
||||
|
||||
By default the plugin tracks both reads and writes. You can use one
|
||||
of these options to limit the tracking to just one class of accesses.
|
||||
|
||||
* arg=source
|
||||
* source
|
||||
|
||||
Will include a detailed break down of what the guest PC that made the
|
||||
access was. Not compatible with arg=pattern. Example output::
|
||||
access was. Not compatible with the pattern option. Example output::
|
||||
|
||||
cirrus-low-memory @ 0xfffffd00000a0000
|
||||
pc:fffffc0000005cdc, 1, 256
|
||||
pc:fffffc0000005ce8, 1, 256
|
||||
pc:fffffc0000005cec, 1, 256
|
||||
|
||||
* arg=pattern
|
||||
* pattern
|
||||
|
||||
Instead break down the accesses based on the offset into the HW
|
||||
region. This can be useful for seeing the most used registers of a
|
||||
@ -345,7 +360,7 @@ which will output an execution trace following this structure::
|
||||
0, 0xd34, 0xf9c8f000, "bl #0x10c8"
|
||||
0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM
|
||||
|
||||
- contrib/plugins/cache
|
||||
- contrib/plugins/cache.c
|
||||
|
||||
Cache modelling plugin that measures the performance of a given cache
|
||||
configuration when a given working set is run::
|
||||
@ -355,11 +370,8 @@ configuration when a given working set is run::
|
||||
|
||||
will report the following::
|
||||
|
||||
Data accesses: 996479, Misses: 507
|
||||
Miss rate: 0.050879%
|
||||
|
||||
Instruction accesses: 2641737, Misses: 18617
|
||||
Miss rate: 0.704726%
|
||||
core #, data accesses, data misses, dmiss rate, insn accesses, insn misses, imiss rate
|
||||
0 996695 508 0.0510% 2642799 18617 0.7044%
|
||||
|
||||
address, data misses, instruction
|
||||
0x424f1e (_int_malloc), 109, movq %rax, 8(%rcx)
|
||||
@ -377,29 +389,35 @@ will report the following::
|
||||
|
||||
The plugin has a number of arguments, all of them are optional:
|
||||
|
||||
* arg="limit=N"
|
||||
* limit=N
|
||||
|
||||
Print top N icache and dcache thrashing instructions along with their
|
||||
address, number of misses, and its disassembly. (default: 32)
|
||||
|
||||
* arg="icachesize=N"
|
||||
* arg="iblksize=B"
|
||||
* arg="iassoc=A"
|
||||
* icachesize=N
|
||||
* iblksize=B
|
||||
* iassoc=A
|
||||
|
||||
Instruction cache configuration arguments. They specify the cache size, block
|
||||
size, and associativity of the instruction cache, respectively.
|
||||
(default: N = 16384, B = 64, A = 8)
|
||||
|
||||
* arg="dcachesize=N"
|
||||
* arg="dblksize=B"
|
||||
* arg="dassoc=A"
|
||||
* dcachesize=N
|
||||
* dblksize=B
|
||||
* dassoc=A
|
||||
|
||||
Data cache configuration arguments. They specify the cache size, block size,
|
||||
and associativity of the data cache, respectively.
|
||||
(default: N = 16384, B = 64, A = 8)
|
||||
|
||||
* arg="evict=POLICY"
|
||||
* evict=POLICY
|
||||
|
||||
Sets the eviction policy to POLICY. Available policies are: :code:`lru`,
|
||||
:code:`fifo`, and :code:`rand`. The plugin will use the specified policy for
|
||||
both instruction and data caches. (default: POLICY = :code:`lru`)
|
||||
|
||||
* cores=N
|
||||
|
||||
Sets the number of cores for which we maintain separate icache and dcache.
|
||||
(default: for linux-user, N = 1, for full system emulation: N = cores
|
||||
available to guest)
|
||||
|
@ -224,6 +224,35 @@ another application on the host may have locked the file, possibly leading to a
|
||||
test failure. If using such devices are explicitly desired, consider adding
|
||||
``locking=off`` option to disable image locking.
|
||||
|
||||
Debugging a test case
|
||||
-----------------------
|
||||
The following options to the ``check`` script can be useful when debugging
|
||||
a failing test:
|
||||
|
||||
* ``-gdb`` wraps every QEMU invocation in a ``gdbserver``, which waits for a
|
||||
connection from a gdb client. The options given to ``gdbserver`` (e.g. the
|
||||
address on which to listen for connections) are taken from the ``$GDB_OPTIONS``
|
||||
environment variable. By default (if ``$GDB_OPTIONS`` is empty), it listens on
|
||||
``localhost:12345``.
|
||||
It is possible to connect to it for example with
|
||||
``gdb -iex "target remote $addr"``, where ``$addr`` is the address
|
||||
``gdbserver`` listens on.
|
||||
If the ``-gdb`` option is not used, ``$GDB_OPTIONS`` is ignored,
|
||||
regardless of whether it is set or not.
|
||||
|
||||
* ``-valgrind`` attaches a valgrind instance to QEMU. If it detects
|
||||
warnings, it will print and save the log in
|
||||
``$TEST_DIR/<valgrind_pid>.valgrind``.
|
||||
The final command line will be ``valgrind --log-file=$TEST_DIR/
|
||||
<valgrind_pid>.valgrind --error-exitcode=99 $QEMU ...``
|
||||
|
||||
* ``-d`` (debug) just increases the logging verbosity, showing
|
||||
for example the QMP commands and answers.
|
||||
|
||||
* ``-p`` (print) redirects QEMU’s stdout and stderr to the test output,
|
||||
instead of saving it into a log file in
|
||||
``$TEST_DIR/qemu-machine-<random_string>``.
|
||||
|
||||
Test case groups
|
||||
----------------
|
||||
|
||||
|
@ -9,7 +9,7 @@ endif
|
||||
# Check if tools are available to build documentation.
|
||||
build_docs = false
|
||||
if sphinx_build.found()
|
||||
SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build]
|
||||
SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build, '-q']
|
||||
# If we're making warnings fatal, apply this to Sphinx runs as well
|
||||
if get_option('werror')
|
||||
SPHINX_ARGS += [ '-W' ]
|
||||
|
235
docs/specs/acpi_cpu_hotplug.rst
Normal file
235
docs/specs/acpi_cpu_hotplug.rst
Normal file
@ -0,0 +1,235 @@
|
||||
QEMU<->ACPI BIOS CPU hotplug interface
|
||||
======================================
|
||||
|
||||
QEMU supports CPU hotplug via ACPI. This document
|
||||
describes the interface between QEMU and the ACPI BIOS.
|
||||
|
||||
ACPI BIOS GPE.2 handler is dedicated for notifying OS about CPU hot-add
|
||||
and hot-remove events.
|
||||
|
||||
|
||||
Legacy ACPI CPU hotplug interface registers
|
||||
-------------------------------------------
|
||||
|
||||
CPU present bitmap for:
|
||||
|
||||
- ICH9-LPC (IO port 0x0cd8-0xcf7, 1-byte access)
|
||||
- PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access)
|
||||
- One bit per CPU. Bit position reflects corresponding CPU APIC ID. Read-only.
|
||||
- The first DWORD in bitmap is used in write mode to switch from legacy
|
||||
to modern CPU hotplug interface, write 0 into it to do switch.
|
||||
|
||||
QEMU sets corresponding CPU bit on hot-add event and issues SCI
|
||||
with GPE.2 event set. CPU present map is read by ACPI BIOS GPE.2 handler
|
||||
to notify OS about CPU hot-add events. CPU hot-remove isn't supported.
|
||||
|
||||
|
||||
Modern ACPI CPU hotplug interface registers
|
||||
-------------------------------------------
|
||||
|
||||
Register block base address:
|
||||
|
||||
- ICH9-LPC IO port 0x0cd8
|
||||
- PIIX-PM IO port 0xaf00
|
||||
|
||||
Register block size:
|
||||
|
||||
- ACPI_CPU_HOTPLUG_REG_LEN = 12
|
||||
|
||||
All accesses to registers described below, imply little-endian byte order.
|
||||
|
||||
Reserved registers behavior:
|
||||
|
||||
- write accesses are ignored
|
||||
- read accesses return all bits set to 0.
|
||||
|
||||
The last stored value in 'CPU selector' must refer to a possible CPU, otherwise
|
||||
|
||||
- reads from any register return 0
|
||||
- writes to any other register are ignored until valid value is stored into it
|
||||
|
||||
On QEMU start, 'CPU selector' is initialized to a valid value, on reset it
|
||||
keeps the current value.
|
||||
|
||||
Read access behavior
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
offset [0x0-0x3]
|
||||
Command data 2: (DWORD access)
|
||||
|
||||
If value last stored in 'Command field' is:
|
||||
|
||||
0:
|
||||
reads as 0x0
|
||||
3:
|
||||
upper 32 bits of architecture specific CPU ID value
|
||||
other values:
|
||||
reserved
|
||||
|
||||
offset [0x4]
|
||||
CPU device status fields: (1 byte access)
|
||||
|
||||
bits:
|
||||
|
||||
0:
|
||||
Device is enabled and may be used by guest
|
||||
1:
|
||||
Device insert event, used to distinguish device for which
|
||||
no device check event to OSPM was issued.
|
||||
It's valid only when bit 0 is set.
|
||||
2:
|
||||
Device remove event, used to distinguish device for which
|
||||
no device eject request to OSPM was issued. Firmware must
|
||||
ignore this bit.
|
||||
3:
|
||||
reserved and should be ignored by OSPM
|
||||
4:
|
||||
if set to 1, OSPM requests firmware to perform device eject.
|
||||
5-7:
|
||||
reserved and should be ignored by OSPM
|
||||
|
||||
offset [0x5-0x7]
|
||||
reserved
|
||||
|
||||
offset [0x8]
|
||||
Command data: (DWORD access)
|
||||
|
||||
If value last stored in 'Command field' is one of:
|
||||
|
||||
0:
|
||||
contains 'CPU selector' value of a CPU with pending event[s]
|
||||
3:
|
||||
lower 32 bits of architecture specific CPU ID value
|
||||
(in x86 case: APIC ID)
|
||||
otherwise:
|
||||
contains 0
|
||||
|
||||
Write access behavior
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
offset [0x0-0x3]
|
||||
CPU selector: (DWORD access)
|
||||
|
||||
Selects active CPU device. All following accesses to other
|
||||
registers will read/store data from/to selected CPU.
|
||||
Valid values: [0 .. max_cpus)
|
||||
|
||||
offset [0x4]
|
||||
CPU device control fields: (1 byte access)
|
||||
|
||||
bits:
|
||||
|
||||
0:
|
||||
reserved, OSPM must clear it before writing to register.
|
||||
1:
|
||||
if set to 1 clears device insert event, set by OSPM
|
||||
after it has emitted device check event for the
|
||||
selected CPU device
|
||||
2:
|
||||
if set to 1 clears device remove event, set by OSPM
|
||||
after it has emitted device eject request for the
|
||||
selected CPU device.
|
||||
3:
|
||||
if set to 1 initiates device eject, set by OSPM when it
|
||||
triggers CPU device removal and calls _EJ0 method or by firmware
|
||||
when bit #4 is set. In case bit #4 were set, it's cleared as
|
||||
part of device eject.
|
||||
4:
|
||||
if set to 1, OSPM hands over device eject to firmware.
|
||||
Firmware shall issue device eject request as described above
|
||||
(bit #3) and OSPM should not touch device eject bit (#3) in case
|
||||
it's asked firmware to perform CPU device eject.
|
||||
5-7:
|
||||
reserved, OSPM must clear them before writing to register
|
||||
|
||||
offset[0x5]
|
||||
Command field: (1 byte access)
|
||||
|
||||
value:
|
||||
|
||||
0:
|
||||
selects a CPU device with inserting/removing events and
|
||||
following reads from 'Command data' register return
|
||||
selected CPU ('CPU selector' value).
|
||||
If no CPU with events found, the current 'CPU selector' doesn't
|
||||
change and corresponding insert/remove event flags are not modified.
|
||||
|
||||
1:
|
||||
following writes to 'Command data' register set OST event
|
||||
register in QEMU
|
||||
2:
|
||||
following writes to 'Command data' register set OST status
|
||||
register in QEMU
|
||||
3:
|
||||
following reads from 'Command data' and 'Command data 2' return
|
||||
architecture specific CPU ID value for currently selected CPU.
|
||||
other values:
|
||||
reserved
|
||||
|
||||
offset [0x6-0x7]
|
||||
reserved
|
||||
|
||||
offset [0x8]
|
||||
Command data: (DWORD access)
|
||||
|
||||
If last stored 'Command field' value is:
|
||||
|
||||
1:
|
||||
stores value into OST event register
|
||||
2:
|
||||
stores value into OST status register, triggers
|
||||
ACPI_DEVICE_OST QMP event from QEMU to external applications
|
||||
with current values of OST event and status registers.
|
||||
other values:
|
||||
reserved
|
||||
|
||||
Typical usecases
|
||||
----------------
|
||||
|
||||
(x86) Detecting and enabling modern CPU hotplug interface
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
QEMU starts with legacy CPU hotplug interface enabled. Detecting and
|
||||
switching to modern interface is based on the 2 legacy CPU hotplug features:
|
||||
|
||||
#. Writes into CPU bitmap are ignored.
|
||||
#. CPU bitmap always has bit #0 set, corresponding to boot CPU.
|
||||
|
||||
Use following steps to detect and enable modern CPU hotplug interface:
|
||||
|
||||
#. Store 0x0 to the 'CPU selector' register, attempting to switch to modern mode
|
||||
#. Store 0x0 to the 'CPU selector' register, to ensure valid selector value
|
||||
#. Store 0x0 to the 'Command field' register
|
||||
#. Read the 'Command data 2' register.
|
||||
If read value is 0x0, the modern interface is enabled.
|
||||
Otherwise legacy or no CPU hotplug interface available
|
||||
|
||||
Get a cpu with pending event
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#. Store 0x0 to the 'CPU selector' register.
|
||||
#. Store 0x0 to the 'Command field' register.
|
||||
#. Read the 'CPU device status fields' register.
|
||||
#. If both bit #1 and bit #2 are clear in the value read, there is no CPU
|
||||
with a pending event and selected CPU remains unchanged.
|
||||
#. Otherwise, read the 'Command data' register. The value read is the
|
||||
selector of the CPU with the pending event (which is already selected).
|
||||
|
||||
Enumerate CPUs present/non present CPUs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
#. Set the present CPU count to 0.
|
||||
#. Set the iterator to 0.
|
||||
#. Store 0x0 to the 'CPU selector' register, to ensure that it's in
|
||||
a valid state and that access to other registers won't be ignored.
|
||||
#. Store 0x0 to the 'Command field' register to make 'Command data'
|
||||
register return 'CPU selector' value of selected CPU
|
||||
#. Read the 'CPU device status fields' register.
|
||||
#. If bit #0 is set, increment the present CPU count.
|
||||
#. Increment the iterator.
|
||||
#. Store the iterator to the 'CPU selector' register.
|
||||
#. Read the 'Command data' register.
|
||||
#. If the value read is not zero, goto 05.
|
||||
#. Otherwise store 0x0 to the 'CPU selector' register, to put it
|
||||
into a valid state and exit.
|
||||
The iterator at this point equals "max_cpus".
|
@ -1,160 +0,0 @@
|
||||
QEMU<->ACPI BIOS CPU hotplug interface
|
||||
--------------------------------------
|
||||
|
||||
QEMU supports CPU hotplug via ACPI. This document
|
||||
describes the interface between QEMU and the ACPI BIOS.
|
||||
|
||||
ACPI BIOS GPE.2 handler is dedicated for notifying OS about CPU hot-add
|
||||
and hot-remove events.
|
||||
|
||||
============================================
|
||||
Legacy ACPI CPU hotplug interface registers:
|
||||
--------------------------------------------
|
||||
CPU present bitmap for:
|
||||
ICH9-LPC (IO port 0x0cd8-0xcf7, 1-byte access)
|
||||
PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access)
|
||||
One bit per CPU. Bit position reflects corresponding CPU APIC ID. Read-only.
|
||||
The first DWORD in bitmap is used in write mode to switch from legacy
|
||||
to modern CPU hotplug interface, write 0 into it to do switch.
|
||||
---------------------------------------------------------------
|
||||
QEMU sets corresponding CPU bit on hot-add event and issues SCI
|
||||
with GPE.2 event set. CPU present map is read by ACPI BIOS GPE.2 handler
|
||||
to notify OS about CPU hot-add events. CPU hot-remove isn't supported.
|
||||
|
||||
=====================================
|
||||
Modern ACPI CPU hotplug interface registers:
|
||||
-------------------------------------
|
||||
Register block base address:
|
||||
ICH9-LPC IO port 0x0cd8
|
||||
PIIX-PM IO port 0xaf00
|
||||
Register block size:
|
||||
ACPI_CPU_HOTPLUG_REG_LEN = 12
|
||||
|
||||
All accesses to registers described below, imply little-endian byte order.
|
||||
|
||||
Reserved resisters behavior:
|
||||
- write accesses are ignored
|
||||
- read accesses return all bits set to 0.
|
||||
|
||||
The last stored value in 'CPU selector' must refer to a possible CPU, otherwise
|
||||
- reads from any register return 0
|
||||
- writes to any other register are ignored until valid value is stored into it
|
||||
On QEMU start, 'CPU selector' is initialized to a valid value, on reset it
|
||||
keeps the current value.
|
||||
|
||||
read access:
|
||||
offset:
|
||||
[0x0-0x3] Command data 2: (DWORD access)
|
||||
if value last stored in 'Command field':
|
||||
0: reads as 0x0
|
||||
3: upper 32 bits of architecture specific CPU ID value
|
||||
other values: reserved
|
||||
[0x4] CPU device status fields: (1 byte access)
|
||||
bits:
|
||||
0: Device is enabled and may be used by guest
|
||||
1: Device insert event, used to distinguish device for which
|
||||
no device check event to OSPM was issued.
|
||||
It's valid only when bit 0 is set.
|
||||
2: Device remove event, used to distinguish device for which
|
||||
no device eject request to OSPM was issued. Firmware must
|
||||
ignore this bit.
|
||||
3: reserved and should be ignored by OSPM
|
||||
4: if set to 1, OSPM requests firmware to perform device eject.
|
||||
5-7: reserved and should be ignored by OSPM
|
||||
[0x5-0x7] reserved
|
||||
[0x8] Command data: (DWORD access)
|
||||
contains 0 unless value last stored in 'Command field' is one of:
|
||||
0: contains 'CPU selector' value of a CPU with pending event[s]
|
||||
3: lower 32 bits of architecture specific CPU ID value
|
||||
(in x86 case: APIC ID)
|
||||
|
||||
write access:
|
||||
offset:
|
||||
[0x0-0x3] CPU selector: (DWORD access)
|
||||
selects active CPU device. All following accesses to other
|
||||
registers will read/store data from/to selected CPU.
|
||||
Valid values: [0 .. max_cpus)
|
||||
[0x4] CPU device control fields: (1 byte access)
|
||||
bits:
|
||||
0: reserved, OSPM must clear it before writing to register.
|
||||
1: if set to 1 clears device insert event, set by OSPM
|
||||
after it has emitted device check event for the
|
||||
selected CPU device
|
||||
2: if set to 1 clears device remove event, set by OSPM
|
||||
after it has emitted device eject request for the
|
||||
selected CPU device.
|
||||
3: if set to 1 initiates device eject, set by OSPM when it
|
||||
triggers CPU device removal and calls _EJ0 method or by firmware
|
||||
when bit #4 is set. In case bit #4 were set, it's cleared as
|
||||
part of device eject.
|
||||
4: if set to 1, OSPM hands over device eject to firmware.
|
||||
Firmware shall issue device eject request as described above
|
||||
(bit #3) and OSPM should not touch device eject bit (#3) in case
|
||||
it's asked firmware to perform CPU device eject.
|
||||
5-7: reserved, OSPM must clear them before writing to register
|
||||
[0x5] Command field: (1 byte access)
|
||||
value:
|
||||
0: selects a CPU device with inserting/removing events and
|
||||
following reads from 'Command data' register return
|
||||
selected CPU ('CPU selector' value).
|
||||
If no CPU with events found, the current 'CPU selector' doesn't
|
||||
change and corresponding insert/remove event flags are not modified.
|
||||
1: following writes to 'Command data' register set OST event
|
||||
register in QEMU
|
||||
2: following writes to 'Command data' register set OST status
|
||||
register in QEMU
|
||||
3: following reads from 'Command data' and 'Command data 2' return
|
||||
architecture specific CPU ID value for currently selected CPU.
|
||||
other values: reserved
|
||||
[0x6-0x7] reserved
|
||||
[0x8] Command data: (DWORD access)
|
||||
if last stored 'Command field' value:
|
||||
1: stores value into OST event register
|
||||
2: stores value into OST status register, triggers
|
||||
ACPI_DEVICE_OST QMP event from QEMU to external applications
|
||||
with current values of OST event and status registers.
|
||||
other values: reserved
|
||||
|
||||
Typical usecases:
|
||||
- (x86) Detecting and enabling modern CPU hotplug interface.
|
||||
QEMU starts with legacy CPU hotplug interface enabled. Detecting and
|
||||
switching to modern interface is based on the 2 legacy CPU hotplug features:
|
||||
1. Writes into CPU bitmap are ignored.
|
||||
2. CPU bitmap always has bit#0 set, corresponding to boot CPU.
|
||||
|
||||
Use following steps to detect and enable modern CPU hotplug interface:
|
||||
1. Store 0x0 to the 'CPU selector' register,
|
||||
attempting to switch to modern mode
|
||||
2. Store 0x0 to the 'CPU selector' register,
|
||||
to ensure valid selector value
|
||||
3. Store 0x0 to the 'Command field' register,
|
||||
4. Read the 'Command data 2' register.
|
||||
If read value is 0x0, the modern interface is enabled.
|
||||
Otherwise legacy or no CPU hotplug interface available
|
||||
|
||||
- Get a cpu with pending event
|
||||
1. Store 0x0 to the 'CPU selector' register.
|
||||
2. Store 0x0 to the 'Command field' register.
|
||||
3. Read the 'CPU device status fields' register.
|
||||
4. If both bit#1 and bit#2 are clear in the value read, there is no CPU
|
||||
with a pending event and selected CPU remains unchanged.
|
||||
5. Otherwise, read the 'Command data' register. The value read is the
|
||||
selector of the CPU with the pending event (which is already
|
||||
selected).
|
||||
|
||||
- Enumerate CPUs present/non present CPUs
|
||||
01. Set the present CPU count to 0.
|
||||
02. Set the iterator to 0.
|
||||
03. Store 0x0 to the 'CPU selector' register, to ensure that it's in
|
||||
a valid state and that access to other registers won't be ignored.
|
||||
04. Store 0x0 to the 'Command field' register to make 'Command data'
|
||||
register return 'CPU selector' value of selected CPU
|
||||
05. Read the 'CPU device status fields' register.
|
||||
06. If bit#0 is set, increment the present CPU count.
|
||||
07. Increment the iterator.
|
||||
08. Store the iterator to the 'CPU selector' register.
|
||||
09. Read the 'Command data' register.
|
||||
10. If the value read is not zero, goto 05.
|
||||
11. Otherwise store 0x0 to the 'CPU selector' register, to put it
|
||||
into a valid state and exit.
|
||||
The iterator at this point equals "max_cpus".
|
128
docs/specs/acpi_mem_hotplug.rst
Normal file
128
docs/specs/acpi_mem_hotplug.rst
Normal file
@ -0,0 +1,128 @@
|
||||
QEMU<->ACPI BIOS memory hotplug interface
|
||||
=========================================
|
||||
|
||||
ACPI BIOS GPE.3 handler is dedicated for notifying OS about memory hot-add
|
||||
and hot-remove events.
|
||||
|
||||
Memory hot-plug interface (IO port 0xa00-0xa17, 1-4 byte access)
|
||||
----------------------------------------------------------------
|
||||
|
||||
Read access behavior
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
[0x0-0x3]
|
||||
Lo part of memory device phys address
|
||||
[0x4-0x7]
|
||||
Hi part of memory device phys address
|
||||
[0x8-0xb]
|
||||
Lo part of memory device size in bytes
|
||||
[0xc-0xf]
|
||||
Hi part of memory device size in bytes
|
||||
[0x10-0x13]
|
||||
Memory device proximity domain
|
||||
[0x14]
|
||||
Memory device status fields
|
||||
|
||||
bits:
|
||||
|
||||
0:
|
||||
Device is enabled and may be used by guest
|
||||
1:
|
||||
Device insert event, used to distinguish device for which
|
||||
no device check event to OSPM was issued.
|
||||
It's valid only when bit 1 is set.
|
||||
2:
|
||||
Device remove event, used to distinguish device for which
|
||||
no device eject request to OSPM was issued.
|
||||
3-7:
|
||||
reserved and should be ignored by OSPM
|
||||
|
||||
[0x15-0x17]
|
||||
reserved
|
||||
|
||||
Write access behavior
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
[0x0-0x3]
|
||||
Memory device slot selector, selects active memory device.
|
||||
All following accesses to other registers in 0xa00-0xa17
|
||||
region will read/store data from/to selected memory device.
|
||||
[0x4-0x7]
|
||||
OST event code reported by OSPM
|
||||
[0x8-0xb]
|
||||
OST status code reported by OSPM
|
||||
[0xc-0x13]
|
||||
reserved, writes into it are ignored
|
||||
[0x14]
|
||||
Memory device control fields
|
||||
|
||||
bits:
|
||||
|
||||
0:
|
||||
reserved, OSPM must clear it before writing to register.
|
||||
Due to BUG in versions prior 2.4 that field isn't cleared
|
||||
when other fields are written. Keep it reserved and don't
|
||||
try to reuse it.
|
||||
1:
|
||||
if set to 1 clears device insert event, set by OSPM
|
||||
after it has emitted device check event for the
|
||||
selected memory device
|
||||
2:
|
||||
if set to 1 clears device remove event, set by OSPM
|
||||
after it has emitted device eject request for the
|
||||
selected memory device
|
||||
3:
|
||||
if set to 1 initiates device eject, set by OSPM when it
|
||||
triggers memory device removal and calls _EJ0 method
|
||||
4-7:
|
||||
reserved, OSPM must clear them before writing to register
|
||||
|
||||
Selecting memory device slot beyond present range has no effect on platform:
|
||||
|
||||
- write accesses to memory hot-plug registers not documented above are ignored
|
||||
- read accesses to memory hot-plug registers not documented above return
|
||||
all bits set to 1.
|
||||
|
||||
Memory hot remove process diagram
|
||||
---------------------------------
|
||||
|
||||
::
|
||||
|
||||
+-------------+ +-----------------------+ +------------------+
|
||||
| 1. QEMU | | 2. QEMU | |3. QEMU |
|
||||
| device_del +---->+ device unplug request +----->+Send SCI to guest,|
|
||||
| | | cb | |return control to |
|
||||
| | | | |management |
|
||||
+-------------+ +-----------------------+ +------------------+
|
||||
|
||||
+---------------------------------------------------------------------+
|
||||
|
||||
+---------------------+ +-------------------------+
|
||||
| OSPM: | remove event | OSPM: |
|
||||
| send Eject Request, | | Scan memory devices |
|
||||
| clear remove event +<-------------+ for event flags |
|
||||
| | | |
|
||||
+---------------------+ +-------------------------+
|
||||
|
|
||||
|
|
||||
+---------v--------+ +-----------------------+
|
||||
| Guest OS: | success | OSPM: |
|
||||
| process Ejection +----------->+ Execute _EJ0 method, |
|
||||
| request | | set eject bit in flags|
|
||||
+------------------+ +-----------------------+
|
||||
|failure |
|
||||
v v
|
||||
+------------------------+ +-----------------------+
|
||||
| OSPM: | | QEMU: |
|
||||
| set OST event & status | | call device unplug cb |
|
||||
| fields | | |
|
||||
+------------------------+ +-----------------------+
|
||||
| |
|
||||
v v
|
||||
+------------------+ +-------------------+
|
||||
|QEMU: | |QEMU: |
|
||||
|Send OST QMP event| |Send device deleted|
|
||||
| | |QMP event |
|
||||
+------------------+ | |
|
||||
+-------------------+
|
@ -1,94 +0,0 @@
|
||||
QEMU<->ACPI BIOS memory hotplug interface
|
||||
--------------------------------------
|
||||
|
||||
ACPI BIOS GPE.3 handler is dedicated for notifying OS about memory hot-add
|
||||
and hot-remove events.
|
||||
|
||||
Memory hot-plug interface (IO port 0xa00-0xa17, 1-4 byte access):
|
||||
---------------------------------------------------------------
|
||||
0xa00:
|
||||
read access:
|
||||
[0x0-0x3] Lo part of memory device phys address
|
||||
[0x4-0x7] Hi part of memory device phys address
|
||||
[0x8-0xb] Lo part of memory device size in bytes
|
||||
[0xc-0xf] Hi part of memory device size in bytes
|
||||
[0x10-0x13] Memory device proximity domain
|
||||
[0x14] Memory device status fields
|
||||
bits:
|
||||
0: Device is enabled and may be used by guest
|
||||
1: Device insert event, used to distinguish device for which
|
||||
no device check event to OSPM was issued.
|
||||
It's valid only when bit 1 is set.
|
||||
2: Device remove event, used to distinguish device for which
|
||||
no device eject request to OSPM was issued.
|
||||
3-7: reserved and should be ignored by OSPM
|
||||
[0x15-0x17] reserved
|
||||
|
||||
write access:
|
||||
[0x0-0x3] Memory device slot selector, selects active memory device.
|
||||
All following accesses to other registers in 0xa00-0xa17
|
||||
region will read/store data from/to selected memory device.
|
||||
[0x4-0x7] OST event code reported by OSPM
|
||||
[0x8-0xb] OST status code reported by OSPM
|
||||
[0xc-0x13] reserved, writes into it are ignored
|
||||
[0x14] Memory device control fields
|
||||
bits:
|
||||
0: reserved, OSPM must clear it before writing to register.
|
||||
Due to BUG in versions prior 2.4 that field isn't cleared
|
||||
when other fields are written. Keep it reserved and don't
|
||||
try to reuse it.
|
||||
1: if set to 1 clears device insert event, set by OSPM
|
||||
after it has emitted device check event for the
|
||||
selected memory device
|
||||
2: if set to 1 clears device remove event, set by OSPM
|
||||
after it has emitted device eject request for the
|
||||
selected memory device
|
||||
3: if set to 1 initiates device eject, set by OSPM when it
|
||||
triggers memory device removal and calls _EJ0 method
|
||||
4-7: reserved, OSPM must clear them before writing to register
|
||||
|
||||
Selecting memory device slot beyond present range has no effect on platform:
|
||||
- write accesses to memory hot-plug registers not documented above are
|
||||
ignored
|
||||
- read accesses to memory hot-plug registers not documented above return
|
||||
all bits set to 1.
|
||||
|
||||
Memory hot remove process diagram:
|
||||
----------------------------------
|
||||
+-------------+ +-----------------------+ +------------------+
|
||||
| 1. QEMU | | 2. QEMU | |3. QEMU |
|
||||
| device_del +---->+ device unplug request +----->+Send SCI to guest,|
|
||||
| | | cb | |return control to |
|
||||
+-------------+ +-----------------------+ |management |
|
||||
+------------------+
|
||||
|
||||
+---------------------------------------------------------------------+
|
||||
|
||||
+---------------------+ +-------------------------+
|
||||
| OSPM: | remove event | OSPM: |
|
||||
| send Eject Request, | | Scan memory devices |
|
||||
| clear remove event +<-------------+ for event flags |
|
||||
| | | |
|
||||
+---------------------+ +-------------------------+
|
||||
|
|
||||
|
|
||||
+---------v--------+ +-----------------------+
|
||||
| Guest OS: | success | OSPM: |
|
||||
| process Ejection +----------->+ Execute _EJ0 method, |
|
||||
| request | | set eject bit in flags|
|
||||
+------------------+ +-----------------------+
|
||||
|failure |
|
||||
v v
|
||||
+------------------------+ +-----------------------+
|
||||
| OSPM: | | QEMU: |
|
||||
| set OST event & status | | call device unplug cb |
|
||||
| fields | | |
|
||||
+------------------------+ +-----------------------+
|
||||
| |
|
||||
v v
|
||||
+------------------+ +-------------------+
|
||||
|QEMU: | |QEMU: |
|
||||
|Send OST QMP event| |Send device deleted|
|
||||
| | |QMP event |
|
||||
+------------------+ | |
|
||||
+-------------------+
|
228
docs/specs/acpi_nvdimm.rst
Normal file
228
docs/specs/acpi_nvdimm.rst
Normal file
@ -0,0 +1,228 @@
|
||||
QEMU<->ACPI BIOS NVDIMM interface
|
||||
=================================
|
||||
|
||||
QEMU supports NVDIMM via ACPI. This document describes the basic concepts of
|
||||
NVDIMM ACPI and the interface between QEMU and the ACPI BIOS.
|
||||
|
||||
NVDIMM ACPI Background
|
||||
----------------------
|
||||
|
||||
NVDIMM is introduced in ACPI 6.0 which defines an NVDIMM root device under
|
||||
_SB scope with a _HID of "ACPI0012". For each NVDIMM present or intended
|
||||
to be supported by platform, platform firmware also exposes an ACPI
|
||||
Namespace Device under the root device.
|
||||
|
||||
The NVDIMM child devices under the NVDIMM root device are defined with _ADR
|
||||
corresponding to the NFIT device handle. The NVDIMM root device and the
|
||||
NVDIMM devices can have device specific methods (_DSM) to provide additional
|
||||
functions specific to a particular NVDIMM implementation.
|
||||
|
||||
This is an example from ACPI 6.0, a platform contains one NVDIMM::
|
||||
|
||||
Scope (\_SB){
|
||||
Device (NVDR) // Root device
|
||||
{
|
||||
Name (_HID, "ACPI0012")
|
||||
Method (_STA) {...}
|
||||
Method (_FIT) {...}
|
||||
Method (_DSM, ...) {...}
|
||||
Device (NVD)
|
||||
{
|
||||
Name(_ADR, h) //where h is NFIT Device Handle for this NVDIMM
|
||||
Method (_DSM, ...) {...}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Methods supported on both NVDIMM root device and NVDIMM device
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
_DSM (Device Specific Method)
|
||||
It is a control method that enables devices to provide device specific
|
||||
control functions that are consumed by the device driver.
|
||||
The NVDIMM DSM specification can be found at
|
||||
http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
|
||||
|
||||
Arguments:
|
||||
|
||||
Arg0
|
||||
A Buffer containing a UUID (16 Bytes)
|
||||
Arg1
|
||||
An Integer containing the Revision ID (4 Bytes)
|
||||
Arg2
|
||||
An Integer containing the Function Index (4 Bytes)
|
||||
Arg3
|
||||
A package containing parameters for the function specified by the
|
||||
UUID, Revision ID, and Function Index
|
||||
|
||||
Return Value:
|
||||
|
||||
If Function Index = 0, a Buffer containing a function index bitfield.
|
||||
Otherwise, the return value and type depends on the UUID, revision ID
|
||||
and function index which are described in the DSM specification.
|
||||
|
||||
Methods on NVDIMM ROOT Device
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
_FIT(Firmware Interface Table)
|
||||
It evaluates to a buffer returning data in the format of a series of NFIT
|
||||
Type Structure.
|
||||
|
||||
Arguments: None
|
||||
|
||||
Return Value:
|
||||
A Buffer containing a list of NFIT Type structure entries.
|
||||
|
||||
The detailed definition of the structure can be found at ACPI 6.0: 5.2.25
|
||||
NVDIMM Firmware Interface Table (NFIT).
|
||||
|
||||
QEMU NVDIMM Implementation
|
||||
--------------------------
|
||||
|
||||
QEMU uses 4 bytes IO Port starting from 0x0a18 and a RAM-based memory page
|
||||
for NVDIMM ACPI.
|
||||
|
||||
Memory:
|
||||
QEMU uses BIOS Linker/loader feature to ask BIOS to allocate a memory
|
||||
page and dynamically patch its address into an int32 object named "MEMA"
|
||||
in ACPI.
|
||||
|
||||
This page is RAM-based and it is used to transfer data between _DSM
|
||||
method and QEMU. If ACPI has control, this pages is owned by ACPI which
|
||||
writes _DSM input data to it, otherwise, it is owned by QEMU which
|
||||
emulates _DSM access and writes the output data to it.
|
||||
|
||||
ACPI writes _DSM Input Data (based on the offset in the page):
|
||||
|
||||
[0x0 - 0x3]
|
||||
4 bytes, NVDIMM Device Handle.
|
||||
|
||||
The handle is completely QEMU internal thing, the values in
|
||||
range [1, 0xFFFF] indicate nvdimm device. Other values are
|
||||
reserved for other purposes.
|
||||
|
||||
Reserved handles:
|
||||
|
||||
- 0 is reserved for nvdimm root device named NVDR.
|
||||
- 0x10000 is reserved for QEMU internal DSM function called on
|
||||
the root device.
|
||||
|
||||
[0x4 - 0x7]
|
||||
4 bytes, Revision ID, that is the Arg1 of _DSM method.
|
||||
|
||||
[0x8 - 0xB]
|
||||
4 bytes. Function Index, that is the Arg2 of _DSM method.
|
||||
|
||||
[0xC - 0xFFF]
|
||||
4084 bytes, the Arg3 of _DSM method.
|
||||
|
||||
QEMU writes Output Data (based on the offset in the page):
|
||||
|
||||
[0x0 - 0x3]
|
||||
4 bytes, the length of result
|
||||
|
||||
[0x4 - 0xFFF]
|
||||
4092 bytes, the DSM result filled by QEMU
|
||||
|
||||
IO Port 0x0a18 - 0xa1b:
|
||||
ACPI writes the address of the memory page allocated by BIOS to this
|
||||
port then QEMU gets the control and fills the result in the memory page.
|
||||
|
||||
Write Access:
|
||||
|
||||
[0x0a18 - 0xa1b]
|
||||
4 bytes, the address of the memory page allocated by BIOS.
|
||||
|
||||
_DSM process diagram
|
||||
--------------------
|
||||
|
||||
"MEMA" indicates the address of memory page allocated by BIOS.
|
||||
|
||||
::
|
||||
|
||||
+----------------------+ +-----------------------+
|
||||
| 1. OSPM | | 2. OSPM |
|
||||
| save _DSM input data | | write "MEMA" to | Exit to QEMU
|
||||
| to the page +----->| IO port 0x0a18 +------------+
|
||||
| indicated by "MEMA" | | | |
|
||||
+----------------------+ +-----------------------+ |
|
||||
|
|
||||
v
|
||||
+--------------------+ +-----------+ +------------------+--------+
|
||||
| 5 QEMU | | 4 QEMU | | 3. QEMU |
|
||||
| write _DSM result | | emulate | | get _DSM input data from |
|
||||
| to the page +<------+ _DSM +<-----+ the page indicated by the |
|
||||
| | | | | value from the IO port |
|
||||
+--------+-----------+ +-----------+ +---------------------------+
|
||||
|
|
||||
| Enter Guest
|
||||
|
|
||||
v
|
||||
+--------------------------+ +--------------+
|
||||
| 6 OSPM | | 7 OSPM |
|
||||
| result size is returned | | _DSM return |
|
||||
| by reading DSM +----->+ |
|
||||
| result from the page | | |
|
||||
+--------------------------+ +--------------+
|
||||
|
||||
NVDIMM hotplug
|
||||
--------------
|
||||
|
||||
ACPI BIOS GPE.4 handler is dedicated for notifying OS about nvdimm device
|
||||
hot-add event.
|
||||
|
||||
QEMU internal use only _DSM functions
|
||||
-------------------------------------
|
||||
|
||||
Read FIT
|
||||
^^^^^^^^
|
||||
|
||||
_FIT method uses _DSM method to fetch NFIT structures blob from QEMU
|
||||
in 1 page sized increments which are then concatenated and returned
|
||||
as _FIT method result.
|
||||
|
||||
Input parameters:
|
||||
|
||||
Arg0
|
||||
UUID {set to 648B9CF2-CDA1-4312-8AD9-49C4AF32BD62}
|
||||
Arg1
|
||||
Revision ID (set to 1)
|
||||
Arg2
|
||||
Function Index, 0x1
|
||||
Arg3
|
||||
A package containing a buffer whose layout is as follows:
|
||||
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| Field | Length | Offset | Description |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| offset | 4 | 0 | offset in QEMU's NFIT structures blob to |
|
||||
| | | | read from |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
|
||||
Output layout in the dsm memory page:
|
||||
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| Field | Length | Offset | Description |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| length | 4 | 0 | length of entire returned data |
|
||||
| | | | (including this header) |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| | | | return status codes |
|
||||
| | | | |
|
||||
| | | | - 0x0 - success |
|
||||
| | | | - 0x100 - error caused by NFIT update |
|
||||
| status | 4 | 4 | while read by _FIT wasn't completed |
|
||||
| | | | - other codes follow Chapter 3 in |
|
||||
| | | | DSM Spec Rev1 |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| fit data | Varies | 8 | contains FIT data. This field is present |
|
||||
| | | | if status field is 0. |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
|
||||
The FIT offset is maintained by the OSPM itself, current offset plus
|
||||
the size of the fit data returned by the function is the next offset
|
||||
OSPM should read. When all FIT data has been read out, zero fit data
|
||||
size is returned.
|
||||
|
||||
If it returns status code 0x100, OSPM should restart to read FIT (read
|
||||
from offset 0 again).
|
@ -1,188 +0,0 @@
|
||||
QEMU<->ACPI BIOS NVDIMM interface
|
||||
---------------------------------
|
||||
|
||||
QEMU supports NVDIMM via ACPI. This document describes the basic concepts of
|
||||
NVDIMM ACPI and the interface between QEMU and the ACPI BIOS.
|
||||
|
||||
NVDIMM ACPI Background
|
||||
----------------------
|
||||
NVDIMM is introduced in ACPI 6.0 which defines an NVDIMM root device under
|
||||
_SB scope with a _HID of “ACPI0012”. For each NVDIMM present or intended
|
||||
to be supported by platform, platform firmware also exposes an ACPI
|
||||
Namespace Device under the root device.
|
||||
|
||||
The NVDIMM child devices under the NVDIMM root device are defined with _ADR
|
||||
corresponding to the NFIT device handle. The NVDIMM root device and the
|
||||
NVDIMM devices can have device specific methods (_DSM) to provide additional
|
||||
functions specific to a particular NVDIMM implementation.
|
||||
|
||||
This is an example from ACPI 6.0, a platform contains one NVDIMM:
|
||||
|
||||
Scope (\_SB){
|
||||
Device (NVDR) // Root device
|
||||
{
|
||||
Name (_HID, “ACPI0012”)
|
||||
Method (_STA) {...}
|
||||
Method (_FIT) {...}
|
||||
Method (_DSM, ...) {...}
|
||||
Device (NVD)
|
||||
{
|
||||
Name(_ADR, h) //where h is NFIT Device Handle for this NVDIMM
|
||||
Method (_DSM, ...) {...}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method supported on both NVDIMM root device and NVDIMM device
|
||||
_DSM (Device Specific Method)
|
||||
It is a control method that enables devices to provide device specific
|
||||
control functions that are consumed by the device driver.
|
||||
The NVDIMM DSM specification can be found at:
|
||||
http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
|
||||
|
||||
Arguments:
|
||||
Arg0 – A Buffer containing a UUID (16 Bytes)
|
||||
Arg1 – An Integer containing the Revision ID (4 Bytes)
|
||||
Arg2 – An Integer containing the Function Index (4 Bytes)
|
||||
Arg3 – A package containing parameters for the function specified by the
|
||||
UUID, Revision ID, and Function Index
|
||||
|
||||
Return Value:
|
||||
If Function Index = 0, a Buffer containing a function index bitfield.
|
||||
Otherwise, the return value and type depends on the UUID, revision ID
|
||||
and function index which are described in the DSM specification.
|
||||
|
||||
Methods on NVDIMM ROOT Device
|
||||
_FIT(Firmware Interface Table)
|
||||
It evaluates to a buffer returning data in the format of a series of NFIT
|
||||
Type Structure.
|
||||
|
||||
Arguments: None
|
||||
|
||||
Return Value:
|
||||
A Buffer containing a list of NFIT Type structure entries.
|
||||
|
||||
The detailed definition of the structure can be found at ACPI 6.0: 5.2.25
|
||||
NVDIMM Firmware Interface Table (NFIT).
|
||||
|
||||
QEMU NVDIMM Implementation
|
||||
==========================
|
||||
QEMU uses 4 bytes IO Port starting from 0x0a18 and a RAM-based memory page
|
||||
for NVDIMM ACPI.
|
||||
|
||||
Memory:
|
||||
QEMU uses BIOS Linker/loader feature to ask BIOS to allocate a memory
|
||||
page and dynamically patch its address into an int32 object named "MEMA"
|
||||
in ACPI.
|
||||
|
||||
This page is RAM-based and it is used to transfer data between _DSM
|
||||
method and QEMU. If ACPI has control, this pages is owned by ACPI which
|
||||
writes _DSM input data to it, otherwise, it is owned by QEMU which
|
||||
emulates _DSM access and writes the output data to it.
|
||||
|
||||
ACPI writes _DSM Input Data (based on the offset in the page):
|
||||
[0x0 - 0x3]: 4 bytes, NVDIMM Device Handle.
|
||||
|
||||
The handle is completely QEMU internal thing, the values in
|
||||
range [1, 0xFFFF] indicate nvdimm device. Other values are
|
||||
reserved for other purposes.
|
||||
|
||||
Reserved handles:
|
||||
0 is reserved for nvdimm root device named NVDR.
|
||||
0x10000 is reserved for QEMU internal DSM function called on
|
||||
the root device.
|
||||
|
||||
[0x4 - 0x7]: 4 bytes, Revision ID, that is the Arg1 of _DSM method.
|
||||
[0x8 - 0xB]: 4 bytes. Function Index, that is the Arg2 of _DSM method.
|
||||
[0xC - 0xFFF]: 4084 bytes, the Arg3 of _DSM method.
|
||||
|
||||
QEMU Writes Output Data (based on the offset in the page):
|
||||
[0x0 - 0x3]: 4 bytes, the length of result
|
||||
[0x4 - 0xFFF]: 4092 bytes, the DSM result filled by QEMU
|
||||
|
||||
IO Port 0x0a18 - 0xa1b:
|
||||
ACPI writes the address of the memory page allocated by BIOS to this
|
||||
port then QEMU gets the control and fills the result in the memory page.
|
||||
|
||||
write Access:
|
||||
[0x0a18 - 0xa1b]: 4 bytes, the address of the memory page allocated
|
||||
by BIOS.
|
||||
|
||||
_DSM process diagram:
|
||||
---------------------
|
||||
"MEMA" indicates the address of memory page allocated by BIOS.
|
||||
|
||||
+----------------------+ +-----------------------+
|
||||
| 1. OSPM | | 2. OSPM |
|
||||
| save _DSM input data | | write "MEMA" to | Exit to QEMU
|
||||
| to the page +----->| IO port 0x0a18 +------------+
|
||||
| indicated by "MEMA" | | | |
|
||||
+----------------------+ +-----------------------+ |
|
||||
|
|
||||
v
|
||||
+------------- ----+ +-----------+ +------------------+--------+
|
||||
| 5 QEMU | | 4 QEMU | | 3. QEMU |
|
||||
| write _DSM result | | emulate | | get _DSM input data from |
|
||||
| to the page +<------+ _DSM +<-----+ the page indicated by the |
|
||||
| | | | | value from the IO port |
|
||||
+--------+-----------+ +-----------+ +---------------------------+
|
||||
|
|
||||
| Enter Guest
|
||||
|
|
||||
v
|
||||
+--------------------------+ +--------------+
|
||||
| 6 OSPM | | 7 OSPM |
|
||||
| result size is returned | | _DSM return |
|
||||
| by reading DSM +----->+ |
|
||||
| result from the page | | |
|
||||
+--------------------------+ +--------------+
|
||||
|
||||
NVDIMM hotplug
|
||||
--------------
|
||||
ACPI BIOS GPE.4 handler is dedicated for notifying OS about nvdimm device
|
||||
hot-add event.
|
||||
|
||||
QEMU internal use only _DSM function
|
||||
------------------------------------
|
||||
1) Read FIT
|
||||
_FIT method uses _DSM method to fetch NFIT structures blob from QEMU
|
||||
in 1 page sized increments which are then concatenated and returned
|
||||
as _FIT method result.
|
||||
|
||||
Input parameters:
|
||||
Arg0 – UUID {set to 648B9CF2-CDA1-4312-8AD9-49C4AF32BD62}
|
||||
Arg1 – Revision ID (set to 1)
|
||||
Arg2 - Function Index, 0x1
|
||||
Arg3 - A package containing a buffer whose layout is as follows:
|
||||
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| Field | Length | Offset | Description |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| offset | 4 | 0 | offset in QEMU's NFIT structures blob to |
|
||||
| | | | read from |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
|
||||
Output layout in the dsm memory page:
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| Field | Length | Offset | Description |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
| length | 4 | 0 | length of entire returned data |
|
||||
| | | | (including this header) |
|
||||
+----------+-----------------+-------------------------------------------+
|
||||
| | | | return status codes |
|
||||
| | | | 0x0 - success |
|
||||
| | | | 0x100 - error caused by NFIT update while |
|
||||
| status | 4 | 4 | read by _FIT wasn't completed, other |
|
||||
| | | | codes follow Chapter 3 in DSM Spec Rev1 |
|
||||
+----------+-----------------+-------------------------------------------+
|
||||
| fit data | Varies | 8 | contains FIT data, this field is present |
|
||||
| | | | if status field is 0; |
|
||||
+----------+--------+--------+-------------------------------------------+
|
||||
|
||||
The FIT offset is maintained by the OSPM itself, current offset plus
|
||||
the size of the fit data returned by the function is the next offset
|
||||
OSPM should read. When all FIT data has been read out, zero fit data
|
||||
size is returned.
|
||||
|
||||
If it returns status code 0x100, OSPM should restart to read FIT (read
|
||||
from offset 0 again).
|
@ -1,45 +1,48 @@
|
||||
QEMU<->ACPI BIOS PCI hotplug interface
|
||||
--------------------------------------
|
||||
======================================
|
||||
|
||||
QEMU supports PCI hotplug via ACPI, for PCI bus 0. This document
|
||||
describes the interface between QEMU and the ACPI BIOS.
|
||||
|
||||
ACPI GPE block (IO ports 0xafe0-0xafe3, byte access):
|
||||
-----------------------------------------
|
||||
ACPI GPE block (IO ports 0xafe0-0xafe3, byte access)
|
||||
----------------------------------------------------
|
||||
|
||||
Generic ACPI GPE block. Bit 1 (GPE.1) used to notify PCI hotplug/eject
|
||||
event to ACPI BIOS, via SCI interrupt.
|
||||
|
||||
PCI slot injection notification pending (IO port 0xae00-0xae03, 4-byte access):
|
||||
---------------------------------------------------------------
|
||||
PCI slot injection notification pending (IO port 0xae00-0xae03, 4-byte access)
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Slot injection notification pending. One bit per slot.
|
||||
|
||||
Read by ACPI BIOS GPE.1 handler to notify OS of injection
|
||||
events. Read-only.
|
||||
|
||||
PCI slot removal notification (IO port 0xae04-0xae07, 4-byte access):
|
||||
-----------------------------------------------------
|
||||
PCI slot removal notification (IO port 0xae04-0xae07, 4-byte access)
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Slot removal notification pending. One bit per slot.
|
||||
|
||||
Read by ACPI BIOS GPE.1 handler to notify OS of removal
|
||||
events. Read-only.
|
||||
|
||||
PCI device eject (IO port 0xae08-0xae0b, 4-byte access):
|
||||
----------------------------------------
|
||||
PCI device eject (IO port 0xae08-0xae0b, 4-byte access)
|
||||
-------------------------------------------------------
|
||||
|
||||
Write: Used by ACPI BIOS _EJ0 method to request device removal.
|
||||
One bit per slot.
|
||||
|
||||
Read: Hotplug features register. Used by platform to identify features
|
||||
available. Current base feature set (no bits set):
|
||||
|
||||
- Read-only "up" register @0xae00, 4-byte access, bit per slot
|
||||
- Read-only "down" register @0xae04, 4-byte access, bit per slot
|
||||
- Read/write "eject" register @0xae08, 4-byte access,
|
||||
write: bit per slot eject, read: hotplug feature set
|
||||
- Read-only hotplug capable register @0xae0c, 4-byte access, bit per slot
|
||||
|
||||
PCI removability status (IO port 0xae0c-0xae0f, 4-byte access):
|
||||
-----------------------------------------------
|
||||
PCI removability status (IO port 0xae0c-0xae0f, 4-byte access)
|
||||
--------------------------------------------------------------
|
||||
|
||||
Used by ACPI BIOS _RMV method to indicate removability status to OS. One
|
||||
bit per slot. Read-only
|
||||
bit per slot. Read-only.
|
@ -13,3 +13,7 @@ guest hardware that is specific to QEMU.
|
||||
acpi_hw_reduced_hotplug
|
||||
tpm
|
||||
acpi_hest_ghes
|
||||
acpi_cpu_hotplug
|
||||
acpi_mem_hotplug
|
||||
acpi_pci_hotplug
|
||||
acpi_nvdimm
|
||||
|
@ -112,17 +112,19 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
|
||||
def _nodes_for_ifcond(self, ifcond, with_if=True):
|
||||
"""Return list of Text, literal nodes for the ifcond
|
||||
|
||||
Return a list which gives text like ' (If: cond1, cond2, cond3)', where
|
||||
the conditions are in literal-text and the commas are not.
|
||||
Return a list which gives text like ' (If: condition)'.
|
||||
If with_if is False, we don't return the "(If: " and ")".
|
||||
"""
|
||||
condlist = intersperse([nodes.literal('', c) for c in ifcond],
|
||||
nodes.Text(', '))
|
||||
|
||||
doc = ifcond.docgen()
|
||||
if not doc:
|
||||
return []
|
||||
doc = nodes.literal('', doc)
|
||||
if not with_if:
|
||||
return condlist
|
||||
return [doc]
|
||||
|
||||
nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')]
|
||||
nodelist.extend(condlist)
|
||||
nodelist.append(doc)
|
||||
nodelist.append(nodes.Text(')'))
|
||||
return nodelist
|
||||
|
||||
@ -139,7 +141,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
|
||||
term.append(nodes.literal('', member.type.doc_type()))
|
||||
if member.optional:
|
||||
term.append(nodes.Text(' (optional)'))
|
||||
if member.ifcond:
|
||||
if member.ifcond.is_present():
|
||||
term.extend(self._nodes_for_ifcond(member.ifcond))
|
||||
return term
|
||||
|
||||
@ -154,7 +156,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
|
||||
nodes.literal('', variants.tag_member.name),
|
||||
nodes.Text(' is '),
|
||||
nodes.literal('', '"%s"' % variant.name)]
|
||||
if variant.ifcond:
|
||||
if variant.ifcond.is_present():
|
||||
term.extend(self._nodes_for_ifcond(variant.ifcond))
|
||||
return term
|
||||
|
||||
@ -209,7 +211,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
|
||||
dlnode = nodes.definition_list()
|
||||
for section in doc.args.values():
|
||||
termtext = [nodes.literal('', section.member.name)]
|
||||
if section.member.ifcond:
|
||||
if section.member.ifcond.is_present():
|
||||
termtext.extend(self._nodes_for_ifcond(section.member.ifcond))
|
||||
# TODO drop fallbacks when undocumented members are outlawed
|
||||
if section.text:
|
||||
@ -277,7 +279,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
|
||||
def _nodes_for_if_section(self, ifcond):
|
||||
"""Return list of doctree nodes for the "If" section"""
|
||||
nodelist = []
|
||||
if ifcond:
|
||||
if ifcond.is_present():
|
||||
snode = self._make_section('If')
|
||||
snode += nodes.paragraph(
|
||||
'', '', *self._nodes_for_ifcond(ifcond, with_if=False)
|
||||
|
@ -55,6 +55,7 @@ Supported guest CPU types:
|
||||
- ``cortex-a53`` (64-bit)
|
||||
- ``cortex-a57`` (64-bit)
|
||||
- ``cortex-a72`` (64-bit)
|
||||
- ``a64fx`` (64-bit)
|
||||
- ``host`` (with KVM only)
|
||||
- ``max`` (same as ``host`` for KVM; best possible emulation with TCG)
|
||||
|
||||
|
@ -15,7 +15,8 @@ The ``-s`` option will make QEMU listen for an incoming connection
|
||||
from gdb on TCP port 1234, and ``-S`` will make QEMU not start the
|
||||
guest until you tell it to from gdb. (If you want to specify which
|
||||
TCP port to use or to use something other than TCP for the gdbstub
|
||||
connection, use the ``-gdb dev`` option instead of ``-s``.)
|
||||
connection, use the ``-gdb dev`` option instead of ``-s``. See
|
||||
`Using unix sockets`_ for an example.)
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
@ -100,6 +101,29 @@ not just those in the cluster you are currently working on::
|
||||
|
||||
(gdb) set schedule-multiple on
|
||||
|
||||
Using unix sockets
|
||||
==================
|
||||
|
||||
An alternate method for connecting gdb to the QEMU gdbstub is to use
|
||||
a unix socket (if supported by your operating system). This is useful when
|
||||
running several tests in parallel, or if you do not have a known free TCP
|
||||
port (e.g. when running automated tests).
|
||||
|
||||
First create a chardev with the appropriate options, then
|
||||
instruct the gdbserver to use that device:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system| -chardev socket,path=/tmp/gdb-socket,server=on,wait=off,id=gdb0 -gdb chardev:gdb0 -S ...
|
||||
|
||||
Start gdb as before, but this time connect using the path to
|
||||
the socket::
|
||||
|
||||
(gdb) target remote /tmp/gdb-socket
|
||||
|
||||
Note that to use a unix socket for the connection you will need
|
||||
gdb version 9.0 or newer.
|
||||
|
||||
Advanced debugging options
|
||||
==========================
|
||||
|
||||
|
@ -198,7 +198,6 @@ static void parts128_default_nan(FloatParts128 *p, float_status *status)
|
||||
static uint64_t parts_silence_nan_frac(uint64_t frac, float_status *status)
|
||||
{
|
||||
g_assert(!no_signaling_nans(status));
|
||||
g_assert(!status->default_nan_mode);
|
||||
|
||||
/* The only snan_bit_is_one target without default_nan_mode is HPPA. */
|
||||
if (snan_bit_is_one(status)) {
|
||||
|
@ -3218,7 +3218,7 @@ static bool gdb_accept_socket(int gdb_fd)
|
||||
|
||||
static int gdbserver_open_socket(const char *path)
|
||||
{
|
||||
struct sockaddr_un sockaddr;
|
||||
struct sockaddr_un sockaddr = {};
|
||||
int fd, ret;
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
@ -3247,7 +3247,7 @@ static int gdbserver_open_socket(const char *path)
|
||||
|
||||
static bool gdb_accept_tcp(int gdb_fd)
|
||||
{
|
||||
struct sockaddr_in sockaddr;
|
||||
struct sockaddr_in sockaddr = {};
|
||||
socklen_t len;
|
||||
int fd;
|
||||
|
||||
|
15
hw/9pfs/9p.c
15
hw/9pfs/9p.c
@ -1703,11 +1703,12 @@ static bool same_stat_id(const struct stat *a, const struct stat *b)
|
||||
static void coroutine_fn v9fs_walk(void *opaque)
|
||||
{
|
||||
int name_idx;
|
||||
V9fsQID *qids = NULL;
|
||||
g_autofree V9fsQID *qids = NULL;
|
||||
int i, err = 0;
|
||||
V9fsPath dpath, path, *pathes = NULL;
|
||||
uint16_t nwnames;
|
||||
struct stat stbuf, fidst, *stbufs = NULL;
|
||||
struct stat stbuf, fidst;
|
||||
g_autofree struct stat *stbufs = NULL;
|
||||
size_t offset = 7;
|
||||
int32_t fid, newfid;
|
||||
V9fsString *wnames = NULL;
|
||||
@ -1787,7 +1788,8 @@ static void coroutine_fn v9fs_walk(void *opaque)
|
||||
strcmp("..", wnames[name_idx].data))
|
||||
{
|
||||
err = s->ops->name_to_path(&s->ctx, &dpath,
|
||||
wnames[name_idx].data, &path);
|
||||
wnames[name_idx].data,
|
||||
&pathes[name_idx]);
|
||||
if (err < 0) {
|
||||
err = -errno;
|
||||
break;
|
||||
@ -1796,14 +1798,13 @@ static void coroutine_fn v9fs_walk(void *opaque)
|
||||
err = -EINTR;
|
||||
break;
|
||||
}
|
||||
err = s->ops->lstat(&s->ctx, &path, &stbuf);
|
||||
err = s->ops->lstat(&s->ctx, &pathes[name_idx], &stbuf);
|
||||
if (err < 0) {
|
||||
err = -errno;
|
||||
break;
|
||||
}
|
||||
stbufs[name_idx] = stbuf;
|
||||
v9fs_path_copy(&dpath, &path);
|
||||
v9fs_path_copy(&pathes[name_idx], &path);
|
||||
v9fs_path_copy(&dpath, &pathes[name_idx]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1872,8 +1873,6 @@ out_nofid:
|
||||
v9fs_path_free(&pathes[name_idx]);
|
||||
}
|
||||
g_free(wnames);
|
||||
g_free(qids);
|
||||
g_free(stbufs);
|
||||
g_free(pathes);
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,9 @@
|
||||
*/ \
|
||||
qemu_coroutine_yield(); \
|
||||
qemu_bh_delete(co_bh); \
|
||||
do { \
|
||||
code_block; \
|
||||
} while (0); \
|
||||
/* re-enter back to qemu thread */ \
|
||||
qemu_coroutine_yield(); \
|
||||
} while (0)
|
||||
|
@ -8,6 +8,8 @@ config ACPI_X86
|
||||
select ACPI_CPU_HOTPLUG
|
||||
select ACPI_MEMORY_HOTPLUG
|
||||
select ACPI_HMAT
|
||||
select ACPI_PIIX4
|
||||
select ACPI_PCIHP
|
||||
|
||||
config ACPI_X86_ICH
|
||||
bool
|
||||
@ -24,6 +26,14 @@ config ACPI_NVDIMM
|
||||
bool
|
||||
depends on ACPI
|
||||
|
||||
config ACPI_PIIX4
|
||||
bool
|
||||
depends on ACPI
|
||||
|
||||
config ACPI_PCIHP
|
||||
bool
|
||||
depends on ACPI
|
||||
|
||||
config ACPI_HMAT
|
||||
bool
|
||||
depends on ACPI
|
||||
|
50
hw/acpi/acpi-cpu-hotplug-stub.c
Normal file
50
hw/acpi/acpi-cpu-hotplug-stub.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/acpi/cpu_hotplug.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
|
||||
/* Following stubs are all related to ACPI cpu hotplug */
|
||||
const VMStateDescription vmstate_cpu_hotplug;
|
||||
|
||||
void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu,
|
||||
CPUHotplugState *cpuhp_state,
|
||||
uint16_t io_port)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner,
|
||||
AcpiCpuHotplug *gpe_cpu, uint16_t base)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev,
|
||||
CPUHotplugState *cpu_st, DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev,
|
||||
AcpiCpuHotplug *g, DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_cpu_unplug_cb(CPUHotplugState *cpu_st,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev,
|
||||
CPUHotplugState *cpu_st,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
35
hw/acpi/acpi-mem-hotplug-stub.c
Normal file
35
hw/acpi/acpi-mem-hotplug-stub.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/acpi/memory_hotplug.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
const VMStateDescription vmstate_memory_hotplug;
|
||||
|
||||
void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
|
||||
MemHotplugState *state, hwaddr io_base)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_memory_unplug_cb(MemHotplugState *mem_st,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_memory_unplug_request_cb(HotplugHandler *hotplug_dev,
|
||||
MemHotplugState *mem_st,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
8
hw/acpi/acpi-nvdimm-stub.c
Normal file
8
hw/acpi/acpi-nvdimm-stub.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
#include "hw/hotplug.h"
|
||||
|
||||
void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev)
|
||||
{
|
||||
return;
|
||||
}
|
47
hw/acpi/acpi-pci-hotplug-stub.c
Normal file
47
hw/acpi/acpi-pci-hotplug-stub.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/acpi/pcihp.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
const VMStateDescription vmstate_acpi_pcihp_pci_status;
|
||||
|
||||
void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
|
||||
MemoryRegion *address_space_io, bool bridges_enabled,
|
||||
uint16_t io_base)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev,
|
||||
AcpiPciHpState *s, DeviceState *dev,
|
||||
Error **errp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void acpi_pcihp_reset(AcpiPciHpState *s, bool acpihp_root_off)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool vmstate_acpi_pcihp_use_acpi_index(void *opaque, int version_id)
|
||||
{
|
||||
return false;
|
||||
}
|
@ -451,7 +451,7 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm)
|
||||
object_property_add_bool(obj, ACPI_PM_PROP_TCO_ENABLED,
|
||||
ich9_pm_get_enable_tco,
|
||||
ich9_pm_set_enable_tco);
|
||||
object_property_add_bool(obj, "acpi-pci-hotplug-with-bridge-support",
|
||||
object_property_add_bool(obj, ACPI_PM_PROP_ACPI_PCIHP_BRIDGE,
|
||||
ich9_pm_get_acpi_pci_hotplug,
|
||||
ich9_pm_set_acpi_pci_hotplug);
|
||||
}
|
||||
|
@ -6,16 +6,20 @@ acpi_ss.add(files(
|
||||
'core.c',
|
||||
'utils.c',
|
||||
))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_CPU_HOTPLUG', if_true: files('cpu.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_CPU_HOTPLUG', if_true: files('cpu_hotplug.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_CPU_HOTPLUG', if_true: files('cpu.c', 'cpu_hotplug.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_CPU_HOTPLUG', if_false: files('acpi-cpu-hotplug-stub.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_MEMORY_HOTPLUG', if_true: files('memory_hotplug.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_MEMORY_HOTPLUG', if_false: files('acpi-mem-hotplug-stub.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_NVDIMM', if_true: files('nvdimm.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_NVDIMM', if_false: files('acpi-nvdimm-stub.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_PCI', if_true: files('pci.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_VMGENID', if_true: files('vmgenid.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_HMAT', if_true: files('hmat.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_APEI', if_true: files('ghes.c'), if_false: files('ghes-stub.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_X86', if_true: files('piix4.c', 'pcihp.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_PIIX4', if_true: files('piix4.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_true: files('pcihp.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_false: files('acpi-pci-hotplug-stub.c'))
|
||||
acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c'))
|
||||
acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c'))
|
||||
acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c'))
|
||||
@ -23,4 +27,6 @@ acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c'))
|
||||
softmmu_ss.add_all(when: 'CONFIG_ACPI', if_true: acpi_ss)
|
||||
softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-stub.c', 'aml-build-stub.c',
|
||||
'acpi-x86-stub.c', 'ipmi-stub.c', 'ghes-stub.c'))
|
||||
'acpi-x86-stub.c', 'ipmi-stub.c', 'ghes-stub.c',
|
||||
'acpi-mem-hotplug-stub.c', 'acpi-cpu-hotplug-stub.c',
|
||||
'acpi-pci-hotplug-stub.c', 'acpi-nvdimm-stub.c'))
|
||||
|
@ -283,7 +283,7 @@ void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev,
|
||||
|
||||
/* Only hotplugged devices need the hotplug capability. */
|
||||
if (dev->hotplugged &&
|
||||
acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev))) < 0) {
|
||||
acpi_pcihp_get_bsel(pci_get_bus(pdev)) < 0) {
|
||||
error_setg(errp, "Unsupported bus. Bus doesn't have property '"
|
||||
ACPI_PCIHP_PROP_BSEL "' set");
|
||||
return;
|
||||
@ -363,8 +363,8 @@ void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
|
||||
{
|
||||
PCIDevice *pdev = PCI_DEVICE(dev);
|
||||
|
||||
trace_acpi_pci_unplug(PCI_SLOT(PCI_DEVICE(dev)->devfn),
|
||||
acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev))));
|
||||
trace_acpi_pci_unplug(PCI_SLOT(pdev->devfn),
|
||||
acpi_pcihp_get_bsel(pci_get_bus(pdev)));
|
||||
|
||||
/*
|
||||
* clean up acpi-index so it could reused by another device
|
||||
|
@ -647,9 +647,9 @@ static Property piix4_pm_properties[] = {
|
||||
DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0),
|
||||
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0),
|
||||
DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2),
|
||||
DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState,
|
||||
DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, PIIX4PMState,
|
||||
use_acpi_hotplug_bridge, true),
|
||||
DEFINE_PROP_BOOL("acpi-root-pci-hotplug", PIIX4PMState,
|
||||
DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCI_ROOTHP, PIIX4PMState,
|
||||
use_acpi_root_pci_hotplug, true),
|
||||
DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState,
|
||||
acpi_memory_hotplug.is_enabled, true),
|
||||
|
@ -25,9 +25,7 @@ config ARM_VIRT
|
||||
select ACPI_PCI
|
||||
select MEM_DEVICE
|
||||
select DIMM
|
||||
select ACPI_MEMORY_HOTPLUG
|
||||
select ACPI_HW_REDUCED
|
||||
select ACPI_NVDIMM
|
||||
select ACPI_APEI
|
||||
|
||||
config CHEETAH
|
||||
@ -237,6 +235,7 @@ config STELLARIS
|
||||
select SSI_SD
|
||||
select STELLARIS_INPUT
|
||||
select STELLARIS_ENET # ethernet
|
||||
select STELLARIS_GPTM # general purpose timer module
|
||||
select UNIMP
|
||||
|
||||
config STM32VLDISCOVERY
|
||||
|
@ -689,17 +689,6 @@ static void armsse_forward_sec_resp_cfg(ARMSSE *s)
|
||||
qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
|
||||
}
|
||||
|
||||
static void armsse_mainclk_update(void *opaque, ClockEvent event)
|
||||
{
|
||||
ARMSSE *s = ARM_SSE(opaque);
|
||||
|
||||
/*
|
||||
* Set system_clock_scale from our Clock input; this is what
|
||||
* controls the tick rate of the CPU SysTick timer.
|
||||
*/
|
||||
system_clock_scale = clock_ticks_to_ns(s->mainclk, 1);
|
||||
}
|
||||
|
||||
static void armsse_init(Object *obj)
|
||||
{
|
||||
ARMSSE *s = ARM_SSE(obj);
|
||||
@ -711,8 +700,7 @@ static void armsse_init(Object *obj)
|
||||
assert(info->sram_banks <= MAX_SRAM_BANKS);
|
||||
assert(info->num_cpus <= SSE_MAX_CPUS);
|
||||
|
||||
s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK",
|
||||
armsse_mainclk_update, s, ClockUpdate);
|
||||
s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK", NULL, NULL, 0);
|
||||
s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL, 0);
|
||||
|
||||
memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX);
|
||||
@ -995,6 +983,9 @@ static void armsse_realize(DeviceState *dev, Error **errp)
|
||||
int j;
|
||||
char *gpioname;
|
||||
|
||||
qdev_connect_clock_in(cpudev, "cpuclk", s->mainclk);
|
||||
/* The SSE subsystems do not wire up a systick refclk */
|
||||
|
||||
qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + NUM_SSE_IRQS);
|
||||
/*
|
||||
* In real hardware the initial Secure VTOR is set from the INITSVTOR*
|
||||
@ -1651,9 +1642,6 @@ static void armsse_realize(DeviceState *dev, Error **errp)
|
||||
* devices in the ARMSSE.
|
||||
*/
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
|
||||
|
||||
/* Set initial system_clock_scale from MAINCLK */
|
||||
armsse_mainclk_update(s, ClockUpdate);
|
||||
}
|
||||
|
||||
static void armsse_idau_check(IDAUInterface *ii, uint32_t address,
|
||||
|
260
hw/arm/armv7m.c
260
hw/arm/armv7m.c
@ -14,11 +14,14 @@
|
||||
#include "hw/arm/boot.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "elf.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/log.h"
|
||||
#include "target/arm/idau.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
/* Bitbanded IO. Each word corresponds to a single bit. */
|
||||
|
||||
@ -124,6 +127,122 @@ static const hwaddr bitband_output_addr[ARMV7M_NUM_BITBANDS] = {
|
||||
0x22000000, 0x42000000
|
||||
};
|
||||
|
||||
static MemTxResult v7m_sysreg_ns_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
MemoryRegion *mr = opaque;
|
||||
|
||||
if (attrs.secure) {
|
||||
/* S accesses to the alias act like NS accesses to the real region */
|
||||
attrs.secure = 0;
|
||||
return memory_region_dispatch_write(mr, addr, value,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
} else {
|
||||
/* NS attrs are RAZ/WI for privileged, and BusFault for user */
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static MemTxResult v7m_sysreg_ns_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
MemoryRegion *mr = opaque;
|
||||
|
||||
if (attrs.secure) {
|
||||
/* S accesses to the alias act like NS accesses to the real region */
|
||||
attrs.secure = 0;
|
||||
return memory_region_dispatch_read(mr, addr, data,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
} else {
|
||||
/* NS attrs are RAZ/WI for privileged, and BusFault for user */
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps v7m_sysreg_ns_ops = {
|
||||
.read_with_attrs = v7m_sysreg_ns_read,
|
||||
.write_with_attrs = v7m_sysreg_ns_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static MemTxResult v7m_systick_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
ARMv7MState *s = opaque;
|
||||
MemoryRegion *mr;
|
||||
|
||||
/* Direct the access to the correct systick */
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
|
||||
return memory_region_dispatch_write(mr, addr, value,
|
||||
size_memop(size) | MO_TE, attrs);
|
||||
}
|
||||
|
||||
static MemTxResult v7m_systick_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
ARMv7MState *s = opaque;
|
||||
MemoryRegion *mr;
|
||||
|
||||
/* Direct the access to the correct systick */
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->systick[attrs.secure]), 0);
|
||||
return memory_region_dispatch_read(mr, addr, data, size_memop(size) | MO_TE,
|
||||
attrs);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps v7m_systick_ops = {
|
||||
.read_with_attrs = v7m_systick_read,
|
||||
.write_with_attrs = v7m_systick_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
/*
|
||||
* Unassigned portions of the PPB space are RAZ/WI for privileged
|
||||
* accesses, and fault for non-privileged accesses.
|
||||
*/
|
||||
static MemTxResult ppb_default_read(void *opaque, hwaddr addr,
|
||||
uint64_t *data, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "Read of unassigned area of PPB: offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
*data = 0;
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static MemTxResult ppb_default_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size,
|
||||
MemTxAttrs attrs)
|
||||
{
|
||||
qemu_log_mask(LOG_UNIMP, "Write of unassigned area of PPB: offset 0x%x\n",
|
||||
(uint32_t)addr);
|
||||
if (attrs.user) {
|
||||
return MEMTX_ERROR;
|
||||
}
|
||||
return MEMTX_OK;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ppb_default_ops = {
|
||||
.read_with_attrs = ppb_default_read,
|
||||
.write_with_attrs = ppb_default_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 1,
|
||||
.valid.max_access_size = 8,
|
||||
};
|
||||
|
||||
static void armv7m_instance_init(Object *obj)
|
||||
{
|
||||
ARMv7MState *s = ARMV7M(obj);
|
||||
@ -137,10 +256,20 @@ static void armv7m_instance_init(Object *obj)
|
||||
object_property_add_alias(obj, "num-irq",
|
||||
OBJECT(&s->nvic), "num-irq");
|
||||
|
||||
object_initialize_child(obj, "systick-reg-ns", &s->systick[M_REG_NS],
|
||||
TYPE_SYSTICK);
|
||||
/*
|
||||
* We can't initialize the secure systick here, as we don't know
|
||||
* yet if we need it.
|
||||
*/
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->bitband); i++) {
|
||||
object_initialize_child(obj, "bitband[*]", &s->bitband[i],
|
||||
TYPE_BITBAND);
|
||||
}
|
||||
|
||||
s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk", NULL, NULL, 0);
|
||||
s->cpuclk = qdev_init_clock_in(DEVICE(obj), "cpuclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void armv7m_realize(DeviceState *dev, Error **errp)
|
||||
@ -223,13 +352,130 @@ static void armv7m_realize(DeviceState *dev, Error **errp)
|
||||
qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ");
|
||||
qdev_pass_gpios(DEVICE(&s->nvic), dev, "NMI");
|
||||
|
||||
/*
|
||||
* We map various devices into the container MR at their architected
|
||||
* addresses. In particular, we map everything corresponding to the
|
||||
* "System PPB" space. This is the range from 0xe0000000 to 0xe00fffff
|
||||
* and includes the NVIC, the System Control Space (system registers),
|
||||
* the systick timer, and for CPUs with the Security extension an NS
|
||||
* banked version of all of these.
|
||||
*
|
||||
* The default behaviour for unimplemented registers/ranges
|
||||
* (for instance the Data Watchpoint and Trace unit at 0xe0001000)
|
||||
* is to RAZ/WI for privileged access and BusFault for non-privileged
|
||||
* access.
|
||||
*
|
||||
* The NVIC and System Control Space (SCS) starts at 0xe000e000
|
||||
* and looks like this:
|
||||
* 0x004 - ICTR
|
||||
* 0x010 - 0xff - systick
|
||||
* 0x100..0x7ec - NVIC
|
||||
* 0x7f0..0xcff - Reserved
|
||||
* 0xd00..0xd3c - SCS registers
|
||||
* 0xd40..0xeff - Reserved or Not implemented
|
||||
* 0xf00 - STIR
|
||||
*
|
||||
* Some registers within this space are banked between security states.
|
||||
* In v8M there is a second range 0xe002e000..0xe002efff which is the
|
||||
* NonSecure alias SCS; secure accesses to this behave like NS accesses
|
||||
* to the main SCS range, and non-secure accesses (including when
|
||||
* the security extension is not implemented) are RAZ/WI.
|
||||
* Note that both the main SCS range and the alias range are defined
|
||||
* to be exempt from memory attribution (R_BLJT) and so the memory
|
||||
* transaction attribute always matches the current CPU security
|
||||
* state (attrs.secure == env->v7m.secure). In the v7m_sysreg_ns_ops
|
||||
* wrappers we change attrs.secure to indicate the NS access; so
|
||||
* generally code determining which banked register to use should
|
||||
* use attrs.secure; code determining actual behaviour of the system
|
||||
* should use env->v7m.secure.
|
||||
*
|
||||
* Within the PPB space, some MRs overlap, and the priority
|
||||
* of overlapping regions is:
|
||||
* - default region (for RAZ/WI and BusFault) : -1
|
||||
* - system register regions (provided by the NVIC) : 0
|
||||
* - systick : 1
|
||||
* This is because the systick device is a small block of registers
|
||||
* in the middle of the other system control registers.
|
||||
*/
|
||||
|
||||
memory_region_init_io(&s->defaultmem, OBJECT(s), &ppb_default_ops, s,
|
||||
"nvic-default", 0x100000);
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe0000000,
|
||||
&s->defaultmem, -1);
|
||||
|
||||
/* Wire the NVIC up to the CPU */
|
||||
sbd = SYS_BUS_DEVICE(&s->nvic);
|
||||
sysbus_connect_irq(sbd, 0,
|
||||
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
|
||||
|
||||
memory_region_add_subregion(&s->container, 0xe0000000,
|
||||
memory_region_add_subregion(&s->container, 0xe000e000,
|
||||
sysbus_mmio_get_region(sbd, 0));
|
||||
if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) {
|
||||
/* Create the NS alias region for the NVIC sysregs */
|
||||
memory_region_init_io(&s->sysreg_ns_mem, OBJECT(s),
|
||||
&v7m_sysreg_ns_ops,
|
||||
sysbus_mmio_get_region(sbd, 0),
|
||||
"nvic_sysregs_ns", 0x1000);
|
||||
memory_region_add_subregion(&s->container, 0xe002e000,
|
||||
&s->sysreg_ns_mem);
|
||||
}
|
||||
|
||||
/* Create and map the systick devices */
|
||||
qdev_connect_clock_in(DEVICE(&s->systick[M_REG_NS]), "refclk", s->refclk);
|
||||
qdev_connect_clock_in(DEVICE(&s->systick[M_REG_NS]), "cpuclk", s->cpuclk);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_NS]), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->nvic),
|
||||
"systick-trigger", M_REG_NS));
|
||||
|
||||
if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY)) {
|
||||
/*
|
||||
* We couldn't init the secure systick device in instance_init
|
||||
* as we didn't know then if the CPU had the security extensions;
|
||||
* so we have to do it here.
|
||||
*/
|
||||
object_initialize_child(OBJECT(dev), "systick-reg-s",
|
||||
&s->systick[M_REG_S], TYPE_SYSTICK);
|
||||
qdev_connect_clock_in(DEVICE(&s->systick[M_REG_S]), "refclk",
|
||||
s->refclk);
|
||||
qdev_connect_clock_in(DEVICE(&s->systick[M_REG_S]), "cpuclk",
|
||||
s->cpuclk);
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->systick[M_REG_S]), errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->systick[M_REG_S]), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->nvic),
|
||||
"systick-trigger", M_REG_S));
|
||||
}
|
||||
|
||||
memory_region_init_io(&s->systickmem, OBJECT(s),
|
||||
&v7m_systick_ops, s,
|
||||
"v7m_systick", 0xe0);
|
||||
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe000e010,
|
||||
&s->systickmem, 1);
|
||||
if (arm_feature(&s->cpu->env, ARM_FEATURE_V8)) {
|
||||
memory_region_init_io(&s->systick_ns_mem, OBJECT(s),
|
||||
&v7m_sysreg_ns_ops, &s->systickmem,
|
||||
"v7m_systick_ns", 0xe0);
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe002e010,
|
||||
&s->systick_ns_mem, 1);
|
||||
}
|
||||
|
||||
/* If the CPU has RAS support, create the RAS register block */
|
||||
if (cpu_isar_feature(aa32_ras, s->cpu)) {
|
||||
object_initialize_child(OBJECT(dev), "armv7m-ras",
|
||||
&s->ras, TYPE_ARMV7M_RAS);
|
||||
sbd = SYS_BUS_DEVICE(&s->ras);
|
||||
if (!sysbus_realize(sbd, errp)) {
|
||||
return;
|
||||
}
|
||||
memory_region_add_subregion_overlap(&s->container, 0xe0005000,
|
||||
sysbus_mmio_get_region(sbd, 0), 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s->bitband); i++) {
|
||||
if (s->enable_bitband) {
|
||||
@ -269,11 +515,23 @@ static Property armv7m_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_armv7m = {
|
||||
.name = "armv7m",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_CLOCK(refclk, SysTickState),
|
||||
VMSTATE_CLOCK(cpuclk, SysTickState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void armv7m_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = armv7m_realize;
|
||||
dc->vmsd = &vmstate_armv7m;
|
||||
device_class_set_props(dc, armv7m_properties);
|
||||
}
|
||||
|
||||
|
@ -173,6 +173,9 @@ static DeviceState *pl330_create(uint32_t base, qemu_or_irq *orgate,
|
||||
int i;
|
||||
|
||||
dev = qdev_new("pl330");
|
||||
object_property_set_link(OBJECT(dev), "memory",
|
||||
OBJECT(get_system_memory()),
|
||||
&error_fatal);
|
||||
qdev_prop_set_uint8(dev, "num_events", nevents);
|
||||
qdev_prop_set_uint8(dev, "num_chnls", 8);
|
||||
qdev_prop_set_uint8(dev, "num_periph_req", nreq);
|
||||
|
@ -534,6 +534,13 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp)
|
||||
*/
|
||||
create_unimplemented_device("sdma", FSL_IMX6UL_SDMA_ADDR, 0x4000);
|
||||
|
||||
/*
|
||||
* SAI (Audio SSI (Synchronous Serial Interface))
|
||||
*/
|
||||
create_unimplemented_device("sai1", FSL_IMX6UL_SAI1_ADDR, 0x4000);
|
||||
create_unimplemented_device("sai2", FSL_IMX6UL_SAI2_ADDR, 0x4000);
|
||||
create_unimplemented_device("sai3", FSL_IMX6UL_SAI3_ADDR, 0x4000);
|
||||
|
||||
/*
|
||||
* PWM
|
||||
*/
|
||||
@ -542,6 +549,11 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp)
|
||||
create_unimplemented_device("pwm3", FSL_IMX6UL_PWM3_ADDR, 0x4000);
|
||||
create_unimplemented_device("pwm4", FSL_IMX6UL_PWM4_ADDR, 0x4000);
|
||||
|
||||
/*
|
||||
* Audio ASRC (asynchronous sample rate converter)
|
||||
*/
|
||||
create_unimplemented_device("asrc", FSL_IMX6UL_ASRC_ADDR, 0x4000);
|
||||
|
||||
/*
|
||||
* CAN
|
||||
*/
|
||||
|
@ -467,6 +467,13 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp)
|
||||
create_unimplemented_device("can1", FSL_IMX7_CAN1_ADDR, FSL_IMX7_CANn_SIZE);
|
||||
create_unimplemented_device("can2", FSL_IMX7_CAN2_ADDR, FSL_IMX7_CANn_SIZE);
|
||||
|
||||
/*
|
||||
* SAI (Audio SSI (Synchronous Serial Interface))
|
||||
*/
|
||||
create_unimplemented_device("sai1", FSL_IMX7_SAI1_ADDR, FSL_IMX7_SAIn_SIZE);
|
||||
create_unimplemented_device("sai2", FSL_IMX7_SAI2_ADDR, FSL_IMX7_SAIn_SIZE);
|
||||
create_unimplemented_device("sai2", FSL_IMX7_SAI3_ADDR, FSL_IMX7_SAIn_SIZE);
|
||||
|
||||
/*
|
||||
* OCOTP
|
||||
*/
|
||||
|
@ -86,6 +86,7 @@ struct MPS2MachineState {
|
||||
CMSDKAPBWatchdog watchdog;
|
||||
CMSDKAPBTimer timer[2];
|
||||
Clock *sysclk;
|
||||
Clock *refclk;
|
||||
};
|
||||
|
||||
#define TYPE_MPS2_MACHINE "mps2"
|
||||
@ -99,6 +100,15 @@ OBJECT_DECLARE_TYPE(MPS2MachineState, MPS2MachineClass, MPS2_MACHINE)
|
||||
/* Main SYSCLK frequency in Hz */
|
||||
#define SYSCLK_FRQ 25000000
|
||||
|
||||
/*
|
||||
* The Application Notes don't say anything about how the
|
||||
* systick reference clock is configured. (Quite possibly
|
||||
* they don't have one at all.) This 1MHz clock matches the
|
||||
* pre-existing behaviour that used to be hardcoded in the
|
||||
* armv7m_systick implementation.
|
||||
*/
|
||||
#define REFCLK_FRQ (1 * 1000 * 1000)
|
||||
|
||||
/* Initialize the auxiliary RAM region @mr and map it into
|
||||
* the memory map at @base.
|
||||
*/
|
||||
@ -146,6 +156,9 @@ static void mps2_common_init(MachineState *machine)
|
||||
mms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(mms->sysclk, SYSCLK_FRQ);
|
||||
|
||||
mms->refclk = clock_new(OBJECT(machine), "REFCLK");
|
||||
clock_set_hz(mms->refclk, REFCLK_FRQ);
|
||||
|
||||
/* The FPGA images have an odd combination of different RAMs,
|
||||
* because in hardware they are different implementations and
|
||||
* connected to different buses, giving varying performance/size
|
||||
@ -223,6 +236,8 @@ static void mps2_common_init(MachineState *machine)
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", mms->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", mms->refclk);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", machine->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
object_property_set_link(OBJECT(&mms->armv7m), "memory",
|
||||
@ -424,8 +439,6 @@ static void mps2_common_init(MachineState *machine)
|
||||
qdev_get_gpio_in(armv7m,
|
||||
mmc->fpga_type == FPGA_AN511 ? 47 : 13));
|
||||
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
|
||||
0x400000);
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/arm/msf2-soc.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#define MSF2_TIMER_BASE 0x40004000
|
||||
@ -73,6 +74,9 @@ static void m2sxxx_soc_initfn(Object *obj)
|
||||
}
|
||||
|
||||
object_initialize_child(obj, "emac", &s->emac, TYPE_MSS_EMAC);
|
||||
|
||||
s->m3clk = qdev_init_clock_in(DEVICE(obj), "m3clk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
@ -83,11 +87,34 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
int i;
|
||||
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *nvm = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *nvm_alias = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
|
||||
memory_region_init_rom(nvm, OBJECT(dev_soc), "MSF2.eNVM", s->envm_size,
|
||||
if (!clock_has_source(s->m3clk)) {
|
||||
error_setg(errp, "m3clk must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC SYSTICK_CR register at 0xe0042038,
|
||||
* which allows the guest to program the divisor between the m3clk and
|
||||
* the systick refclk to either /4, /8, /16 or /32, as well as setting
|
||||
* the value the guest can read in the STCALIB register. Currently we
|
||||
* implement the divisor as a fixed /32, which matches the reset value
|
||||
* of SYSTICK_CR.
|
||||
*/
|
||||
clock_set_mul_div(s->refclk, 32, 1);
|
||||
clock_set_source(s->refclk, s->m3clk);
|
||||
|
||||
memory_region_init_rom(&s->nvm, OBJECT(dev_soc), "MSF2.eNVM", s->envm_size,
|
||||
&error_fatal);
|
||||
/*
|
||||
* On power-on, the eNVM region 0x60000000 is automatically
|
||||
@ -95,34 +122,28 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
* start address (0x0). We do not support remapping other eNVM,
|
||||
* eSRAM and DDR regions by guest(via Sysreg) currently.
|
||||
*/
|
||||
memory_region_init_alias(nvm_alias, OBJECT(dev_soc), "MSF2.eNVM", nvm, 0,
|
||||
s->envm_size);
|
||||
memory_region_init_alias(&s->nvm_alias, OBJECT(dev_soc), "MSF2.eNVM",
|
||||
&s->nvm, 0, s->envm_size);
|
||||
|
||||
memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, nvm);
|
||||
memory_region_add_subregion(system_memory, 0, nvm_alias);
|
||||
memory_region_add_subregion(system_memory, ENVM_BASE_ADDRESS, &s->nvm);
|
||||
memory_region_add_subregion(system_memory, 0, &s->nvm_alias);
|
||||
|
||||
memory_region_init_ram(sram, NULL, "MSF2.eSRAM", s->esram_size,
|
||||
memory_region_init_ram(&s->sram, NULL, "MSF2.eSRAM", s->esram_size,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
|
||||
|
||||
armv7m = DEVICE(&s->armv7m);
|
||||
qdev_prop_set_uint32(armv7m, "num-irq", 81);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->m3clk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s->m3clk) {
|
||||
error_setg(errp, "Invalid m3clk value");
|
||||
error_append_hint(errp, "m3clk can not be zero\n");
|
||||
return;
|
||||
}
|
||||
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / s->m3clk;
|
||||
|
||||
for (i = 0; i < MSF2_NUM_UARTS; i++) {
|
||||
if (serial_hd(i)) {
|
||||
serial_mm_init(get_system_memory(), uart_addr[i], 2,
|
||||
@ -132,8 +153,13 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
}
|
||||
|
||||
dev = DEVICE(&s->timer);
|
||||
/* APB0 clock is the timer input clock */
|
||||
qdev_prop_set_uint32(dev, "clock-frequency", s->m3clk / s->apb0div);
|
||||
/*
|
||||
* APB0 clock is the timer input clock.
|
||||
* TODO: ideally the MSF2 timer device should use a Clock rather than a
|
||||
* clock-frequency integer property.
|
||||
*/
|
||||
qdev_prop_set_uint32(dev, "clock-frequency",
|
||||
clock_get_hz(s->m3clk) / s->apb0div);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->timer), errp)) {
|
||||
return;
|
||||
}
|
||||
@ -210,8 +236,6 @@ static Property m2sxxx_soc_properties[] = {
|
||||
DEFINE_PROP_UINT64("eNVM-size", MSF2State, envm_size, MSF2_ENVM_MAX_SIZE),
|
||||
DEFINE_PROP_UINT64("eSRAM-size", MSF2State, esram_size,
|
||||
MSF2_ESRAM_MAX_SIZE),
|
||||
/* Libero GUI shows 100Mhz as default for clocks */
|
||||
DEFINE_PROP_UINT32("m3clk", MSF2State, m3clk, 100 * 1000000),
|
||||
/* default divisors in Libero GUI */
|
||||
DEFINE_PROP_UINT8("apb0div", MSF2State, apb0div, 2),
|
||||
DEFINE_PROP_UINT8("apb1div", MSF2State, apb1div, 2),
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/msf2-soc.h"
|
||||
|
||||
@ -49,6 +50,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
|
||||
BusState *spi_bus;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *ddr = g_new(MemoryRegion, 1);
|
||||
Clock *m3clk;
|
||||
|
||||
if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
|
||||
error_report("This board can only be used with CPU %s",
|
||||
@ -72,7 +74,10 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
|
||||
* in Libero. CPU clock is divided by APB0 and APB1 divisors for
|
||||
* peripherals. Emcraft's SoM kit comes with these settings by default.
|
||||
*/
|
||||
qdev_prop_set_uint32(dev, "m3clk", 142 * 1000000);
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
m3clk = clock_new(OBJECT(machine), "m3clk");
|
||||
clock_set_hz(m3clk, 142 * 1000000);
|
||||
qdev_connect_clock_in(dev, "m3clk", m3clk);
|
||||
qdev_prop_set_uint32(dev, "apb0div", 2);
|
||||
qdev_prop_set_uint32(dev, "apb1div", 2);
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/stm32f205_soc.h"
|
||||
#include "hw/arm/boot.h"
|
||||
@ -36,16 +37,15 @@
|
||||
static void netduino2_init(MachineState *machine)
|
||||
{
|
||||
DeviceState *dev;
|
||||
Clock *sysclk;
|
||||
|
||||
/*
|
||||
* TODO: ideally we would model the SoC RCC and let it handle
|
||||
* system_clock_scale, including its ability to define different
|
||||
* possible SYSCLK sources.
|
||||
*/
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(sysclk, SYSCLK_FRQ);
|
||||
|
||||
dev = qdev_new(TYPE_STM32F205_SOC);
|
||||
qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
|
||||
qdev_connect_clock_in(dev, "sysclk", sysclk);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/stm32f405_soc.h"
|
||||
#include "hw/arm/boot.h"
|
||||
@ -36,16 +37,15 @@
|
||||
static void netduinoplus2_init(MachineState *machine)
|
||||
{
|
||||
DeviceState *dev;
|
||||
Clock *sysclk;
|
||||
|
||||
/*
|
||||
* TODO: ideally we would model the SoC RCC and let it handle
|
||||
* system_clock_scale, including its ability to define different
|
||||
* possible SYSCLK sources.
|
||||
*/
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(sysclk, SYSCLK_FRQ);
|
||||
|
||||
dev = qdev_new(TYPE_STM32F405_SOC);
|
||||
qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
|
||||
qdev_connect_clock_in(dev, "sysclk", sysclk);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu),
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "qemu/log.h"
|
||||
|
||||
@ -66,7 +67,22 @@ static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / HCLK_FRQ;
|
||||
/*
|
||||
* HCLK on this SoC is fixed, so we set up sysclk ourselves and
|
||||
* the board shouldn't connect it.
|
||||
*/
|
||||
if (clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
clock_set_hz(s->sysclk, HCLK_FRQ);
|
||||
qdev_connect_clock_in(DEVICE(&s->cpu), "cpuclk", s->sysclk);
|
||||
/*
|
||||
* This SoC has no systick device, so don't connect refclk.
|
||||
* TODO: model the lack of systick (currently the armv7m object
|
||||
* will always provide one).
|
||||
*/
|
||||
|
||||
object_property_set_link(OBJECT(&s->cpu), "memory", OBJECT(&s->container),
|
||||
&error_abort);
|
||||
@ -191,6 +207,8 @@ static void nrf51_soc_init(Object *obj)
|
||||
TYPE_NRF51_TIMER);
|
||||
|
||||
}
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static Property nrf51_soc_properties[] = {
|
||||
|
@ -281,7 +281,7 @@ static void raspi_machine_init(MachineState *machine)
|
||||
object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(machine->ram));
|
||||
object_property_set_int(OBJECT(&s->soc), "board-rev", board_rev,
|
||||
&error_abort);
|
||||
qdev_realize(DEVICE(&s->soc), NULL, &error_abort);
|
||||
qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
|
||||
|
||||
/* Create and plug in the SD cards */
|
||||
di = drive_get_next(IF_SD);
|
||||
@ -340,7 +340,6 @@ static void raspi2b_machine_class_init(ObjectClass *oc, void *data)
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
|
||||
|
||||
mc->alias = "raspi2";
|
||||
rmc->board_rev = 0xa21041;
|
||||
raspi_machine_class_common_init(mc, rmc->board_rev);
|
||||
};
|
||||
@ -360,7 +359,6 @@ static void raspi3b_machine_class_init(ObjectClass *oc, void *data)
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
RaspiMachineClass *rmc = RASPI_MACHINE_CLASS(oc);
|
||||
|
||||
mc->alias = "raspi3";
|
||||
rmc->board_rev = 0xa02082;
|
||||
raspi_machine_class_common_init(mc, rmc->board_rev);
|
||||
};
|
||||
|
@ -65,7 +65,7 @@ enum {
|
||||
SBSA_GIC_DIST,
|
||||
SBSA_GIC_REDIST,
|
||||
SBSA_SECURE_EC,
|
||||
SBSA_GWDT,
|
||||
SBSA_GWDT_WS0,
|
||||
SBSA_GWDT_REFRESH,
|
||||
SBSA_GWDT_CONTROL,
|
||||
SBSA_SMMU,
|
||||
@ -140,7 +140,7 @@ static const int sbsa_ref_irqmap[] = {
|
||||
[SBSA_AHCI] = 10,
|
||||
[SBSA_EHCI] = 11,
|
||||
[SBSA_SMMU] = 12, /* ... to 15 */
|
||||
[SBSA_GWDT] = 16,
|
||||
[SBSA_GWDT_WS0] = 16,
|
||||
};
|
||||
|
||||
static const char * const valid_cpus[] = {
|
||||
@ -481,7 +481,7 @@ static void create_wdt(const SBSAMachineState *sms)
|
||||
hwaddr cbase = sbsa_ref_memmap[SBSA_GWDT_CONTROL].base;
|
||||
DeviceState *dev = qdev_new(TYPE_WDT_SBSA);
|
||||
SysBusDevice *s = SYS_BUS_DEVICE(dev);
|
||||
int irq = sbsa_ref_irqmap[SBSA_GWDT];
|
||||
int irq = sbsa_ref_irqmap[SBSA_GWDT_WS0];
|
||||
|
||||
sysbus_realize_and_unref(s, &error_fatal);
|
||||
sysbus_mmio_map(s, 0, rbase);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "hw/watchdog/cmsdk-apb-watchdog.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/timer/stellaris-gptm.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
@ -55,306 +56,6 @@ typedef const struct {
|
||||
uint32_t peripherals;
|
||||
} stellaris_board_info;
|
||||
|
||||
/* General purpose timer module. */
|
||||
|
||||
#define TYPE_STELLARIS_GPTM "stellaris-gptm"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(gptm_state, STELLARIS_GPTM)
|
||||
|
||||
struct gptm_state {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
uint32_t config;
|
||||
uint32_t mode[2];
|
||||
uint32_t control;
|
||||
uint32_t state;
|
||||
uint32_t mask;
|
||||
uint32_t load[2];
|
||||
uint32_t match[2];
|
||||
uint32_t prescale[2];
|
||||
uint32_t match_prescale[2];
|
||||
uint32_t rtc;
|
||||
int64_t tick[2];
|
||||
struct gptm_state *opaque[2];
|
||||
QEMUTimer *timer[2];
|
||||
/* The timers have an alternate output used to trigger the ADC. */
|
||||
qemu_irq trigger;
|
||||
qemu_irq irq;
|
||||
};
|
||||
|
||||
static void gptm_update_irq(gptm_state *s)
|
||||
{
|
||||
int level;
|
||||
level = (s->state & s->mask) != 0;
|
||||
qemu_set_irq(s->irq, level);
|
||||
}
|
||||
|
||||
static void gptm_stop(gptm_state *s, int n)
|
||||
{
|
||||
timer_del(s->timer[n]);
|
||||
}
|
||||
|
||||
static void gptm_reload(gptm_state *s, int n, int reset)
|
||||
{
|
||||
int64_t tick;
|
||||
if (reset)
|
||||
tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
else
|
||||
tick = s->tick[n];
|
||||
|
||||
if (s->config == 0) {
|
||||
/* 32-bit CountDown. */
|
||||
uint32_t count;
|
||||
count = s->load[0] | (s->load[1] << 16);
|
||||
tick += (int64_t)count * system_clock_scale;
|
||||
} else if (s->config == 1) {
|
||||
/* 32-bit RTC. 1Hz tick. */
|
||||
tick += NANOSECONDS_PER_SECOND;
|
||||
} else if (s->mode[n] == 0xa) {
|
||||
/* PWM mode. Not implemented. */
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: 16-bit timer mode unimplemented: 0x%x\n",
|
||||
s->mode[n]);
|
||||
return;
|
||||
}
|
||||
s->tick[n] = tick;
|
||||
timer_mod(s->timer[n], tick);
|
||||
}
|
||||
|
||||
static void gptm_tick(void *opaque)
|
||||
{
|
||||
gptm_state **p = (gptm_state **)opaque;
|
||||
gptm_state *s;
|
||||
int n;
|
||||
|
||||
s = *p;
|
||||
n = p - s->opaque;
|
||||
if (s->config == 0) {
|
||||
s->state |= 1;
|
||||
if ((s->control & 0x20)) {
|
||||
/* Output trigger. */
|
||||
qemu_irq_pulse(s->trigger);
|
||||
}
|
||||
if (s->mode[0] & 1) {
|
||||
/* One-shot. */
|
||||
s->control &= ~1;
|
||||
} else {
|
||||
/* Periodic. */
|
||||
gptm_reload(s, 0, 0);
|
||||
}
|
||||
} else if (s->config == 1) {
|
||||
/* RTC. */
|
||||
uint32_t match;
|
||||
s->rtc++;
|
||||
match = s->match[0] | (s->match[1] << 16);
|
||||
if (s->rtc > match)
|
||||
s->rtc = 0;
|
||||
if (s->rtc == 0) {
|
||||
s->state |= 8;
|
||||
}
|
||||
gptm_reload(s, 0, 0);
|
||||
} else if (s->mode[n] == 0xa) {
|
||||
/* PWM mode. Not implemented. */
|
||||
} else {
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: 16-bit timer mode unimplemented: 0x%x\n",
|
||||
s->mode[n]);
|
||||
}
|
||||
gptm_update_irq(s);
|
||||
}
|
||||
|
||||
static uint64_t gptm_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
gptm_state *s = (gptm_state *)opaque;
|
||||
|
||||
switch (offset) {
|
||||
case 0x00: /* CFG */
|
||||
return s->config;
|
||||
case 0x04: /* TAMR */
|
||||
return s->mode[0];
|
||||
case 0x08: /* TBMR */
|
||||
return s->mode[1];
|
||||
case 0x0c: /* CTL */
|
||||
return s->control;
|
||||
case 0x18: /* IMR */
|
||||
return s->mask;
|
||||
case 0x1c: /* RIS */
|
||||
return s->state;
|
||||
case 0x20: /* MIS */
|
||||
return s->state & s->mask;
|
||||
case 0x24: /* CR */
|
||||
return 0;
|
||||
case 0x28: /* TAILR */
|
||||
return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
|
||||
case 0x2c: /* TBILR */
|
||||
return s->load[1];
|
||||
case 0x30: /* TAMARCHR */
|
||||
return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
|
||||
case 0x34: /* TBMATCHR */
|
||||
return s->match[1];
|
||||
case 0x38: /* TAPR */
|
||||
return s->prescale[0];
|
||||
case 0x3c: /* TBPR */
|
||||
return s->prescale[1];
|
||||
case 0x40: /* TAPMR */
|
||||
return s->match_prescale[0];
|
||||
case 0x44: /* TBPMR */
|
||||
return s->match_prescale[1];
|
||||
case 0x48: /* TAR */
|
||||
if (s->config == 1) {
|
||||
return s->rtc;
|
||||
}
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: read of TAR but timer read not supported\n");
|
||||
return 0;
|
||||
case 0x4c: /* TBR */
|
||||
qemu_log_mask(LOG_UNIMP,
|
||||
"GPTM: read of TBR but timer read not supported\n");
|
||||
return 0;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n",
|
||||
offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void gptm_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
gptm_state *s = (gptm_state *)opaque;
|
||||
uint32_t oldval;
|
||||
|
||||
/* The timers should be disabled before changing the configuration.
|
||||
We take advantage of this and defer everything until the timer
|
||||
is enabled. */
|
||||
switch (offset) {
|
||||
case 0x00: /* CFG */
|
||||
s->config = value;
|
||||
break;
|
||||
case 0x04: /* TAMR */
|
||||
s->mode[0] = value;
|
||||
break;
|
||||
case 0x08: /* TBMR */
|
||||
s->mode[1] = value;
|
||||
break;
|
||||
case 0x0c: /* CTL */
|
||||
oldval = s->control;
|
||||
s->control = value;
|
||||
/* TODO: Implement pause. */
|
||||
if ((oldval ^ value) & 1) {
|
||||
if (value & 1) {
|
||||
gptm_reload(s, 0, 1);
|
||||
} else {
|
||||
gptm_stop(s, 0);
|
||||
}
|
||||
}
|
||||
if (((oldval ^ value) & 0x100) && s->config >= 4) {
|
||||
if (value & 0x100) {
|
||||
gptm_reload(s, 1, 1);
|
||||
} else {
|
||||
gptm_stop(s, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x18: /* IMR */
|
||||
s->mask = value & 0x77;
|
||||
gptm_update_irq(s);
|
||||
break;
|
||||
case 0x24: /* CR */
|
||||
s->state &= ~value;
|
||||
break;
|
||||
case 0x28: /* TAILR */
|
||||
s->load[0] = value & 0xffff;
|
||||
if (s->config < 4) {
|
||||
s->load[1] = value >> 16;
|
||||
}
|
||||
break;
|
||||
case 0x2c: /* TBILR */
|
||||
s->load[1] = value & 0xffff;
|
||||
break;
|
||||
case 0x30: /* TAMARCHR */
|
||||
s->match[0] = value & 0xffff;
|
||||
if (s->config < 4) {
|
||||
s->match[1] = value >> 16;
|
||||
}
|
||||
break;
|
||||
case 0x34: /* TBMATCHR */
|
||||
s->match[1] = value >> 16;
|
||||
break;
|
||||
case 0x38: /* TAPR */
|
||||
s->prescale[0] = value;
|
||||
break;
|
||||
case 0x3c: /* TBPR */
|
||||
s->prescale[1] = value;
|
||||
break;
|
||||
case 0x40: /* TAPMR */
|
||||
s->match_prescale[0] = value;
|
||||
break;
|
||||
case 0x44: /* TBPMR */
|
||||
s->match_prescale[0] = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n",
|
||||
offset);
|
||||
}
|
||||
gptm_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps gptm_ops = {
|
||||
.read = gptm_read,
|
||||
.write = gptm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_stellaris_gptm = {
|
||||
.name = "stellaris_gptm",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(config, gptm_state),
|
||||
VMSTATE_UINT32_ARRAY(mode, gptm_state, 2),
|
||||
VMSTATE_UINT32(control, gptm_state),
|
||||
VMSTATE_UINT32(state, gptm_state),
|
||||
VMSTATE_UINT32(mask, gptm_state),
|
||||
VMSTATE_UNUSED(8),
|
||||
VMSTATE_UINT32_ARRAY(load, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(match, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(prescale, gptm_state, 2),
|
||||
VMSTATE_UINT32_ARRAY(match_prescale, gptm_state, 2),
|
||||
VMSTATE_UINT32(rtc, gptm_state),
|
||||
VMSTATE_INT64_ARRAY(tick, gptm_state, 2),
|
||||
VMSTATE_TIMER_PTR_ARRAY(timer, gptm_state, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void stellaris_gptm_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
gptm_state *s = STELLARIS_GPTM(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
qdev_init_gpio_out(dev, &s->trigger, 1);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &gptm_ops, s,
|
||||
"gptm", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
s->opaque[0] = s->opaque[1] = s;
|
||||
}
|
||||
|
||||
static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
gptm_state *s = STELLARIS_GPTM(dev);
|
||||
s->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[0]);
|
||||
s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]);
|
||||
}
|
||||
|
||||
/* System controller. */
|
||||
|
||||
#define TYPE_STELLARIS_SYS "stellaris-sys"
|
||||
@ -562,17 +263,18 @@ static bool ssys_use_rcc2(ssys_state *s)
|
||||
*/
|
||||
static void ssys_calculate_system_clock(ssys_state *s, bool propagate_clock)
|
||||
{
|
||||
int period_ns;
|
||||
/*
|
||||
* SYSDIV field specifies divisor: 0 == /1, 1 == /2, etc. Input
|
||||
* clock is 200MHz, which is a period of 5 ns. Dividing the clock
|
||||
* frequency by X is the same as multiplying the period by X.
|
||||
*/
|
||||
if (ssys_use_rcc2(s)) {
|
||||
system_clock_scale = 5 * (((s->rcc2 >> 23) & 0x3f) + 1);
|
||||
period_ns = 5 * (((s->rcc2 >> 23) & 0x3f) + 1);
|
||||
} else {
|
||||
system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1);
|
||||
period_ns = 5 * (((s->rcc >> 23) & 0xf) + 1);
|
||||
}
|
||||
clock_set_ns(s->sysclk, system_clock_scale);
|
||||
clock_set_ns(s->sysclk, period_ns);
|
||||
if (propagate_clock) {
|
||||
clock_propagate(s->sysclk);
|
||||
}
|
||||
@ -755,33 +457,6 @@ static void stellaris_sys_instance_init(Object *obj)
|
||||
s->sysclk = qdev_init_clock_out(DEVICE(s), "SYSCLK");
|
||||
}
|
||||
|
||||
static DeviceState *stellaris_sys_init(uint32_t base, qemu_irq irq,
|
||||
stellaris_board_info *board,
|
||||
uint8_t *macaddr)
|
||||
{
|
||||
DeviceState *dev = qdev_new(TYPE_STELLARIS_SYS);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
|
||||
/* Most devices come preprogrammed with a MAC address in the user data. */
|
||||
qdev_prop_set_uint32(dev, "user0",
|
||||
macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16));
|
||||
qdev_prop_set_uint32(dev, "user1",
|
||||
macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16));
|
||||
qdev_prop_set_uint32(dev, "did0", board->did0);
|
||||
qdev_prop_set_uint32(dev, "did1", board->did1);
|
||||
qdev_prop_set_uint32(dev, "dc0", board->dc0);
|
||||
qdev_prop_set_uint32(dev, "dc1", board->dc1);
|
||||
qdev_prop_set_uint32(dev, "dc2", board->dc2);
|
||||
qdev_prop_set_uint32(dev, "dc3", board->dc3);
|
||||
qdev_prop_set_uint32(dev, "dc4", board->dc4);
|
||||
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
sysbus_mmio_map(sbd, 0, base);
|
||||
sysbus_connect_irq(sbd, 0, irq);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
/* I2C controller. */
|
||||
|
||||
#define TYPE_STELLARIS_I2C "stellaris-i2c"
|
||||
@ -1349,6 +1024,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
|
||||
DeviceState *ssys_dev;
|
||||
int i;
|
||||
int j;
|
||||
const uint8_t *macaddr;
|
||||
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash = g_new(MemoryRegion, 1);
|
||||
@ -1366,15 +1042,42 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(system_memory, 0x20000000, sram);
|
||||
|
||||
/*
|
||||
* Create the system-registers object early, because we will
|
||||
* need its sysclk output.
|
||||
*/
|
||||
ssys_dev = qdev_new(TYPE_STELLARIS_SYS);
|
||||
/* Most devices come preprogrammed with a MAC address in the user data. */
|
||||
macaddr = nd_table[0].macaddr.a;
|
||||
qdev_prop_set_uint32(ssys_dev, "user0",
|
||||
macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16));
|
||||
qdev_prop_set_uint32(ssys_dev, "user1",
|
||||
macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16));
|
||||
qdev_prop_set_uint32(ssys_dev, "did0", board->did0);
|
||||
qdev_prop_set_uint32(ssys_dev, "did1", board->did1);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc0", board->dc0);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc1", board->dc1);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc2", board->dc2);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc3", board->dc3);
|
||||
qdev_prop_set_uint32(ssys_dev, "dc4", board->dc4);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(ssys_dev), &error_fatal);
|
||||
|
||||
nvic = qdev_new(TYPE_ARMV7M);
|
||||
qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES);
|
||||
qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type);
|
||||
qdev_prop_set_bit(nvic, "enable-bitband", true);
|
||||
qdev_connect_clock_in(nvic, "cpuclk",
|
||||
qdev_get_clock_out(ssys_dev, "SYSCLK"));
|
||||
/* This SoC does not connect the systick reference clock */
|
||||
object_property_set_link(OBJECT(nvic), "memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
/* This will exit with an error if the user passed us a bad cpu_type */
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(nvic), &error_fatal);
|
||||
|
||||
/* Now we can wire up the IRQ and MMIO of the system registers */
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(ssys_dev), 0, 0x400fe000);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(ssys_dev), 0, qdev_get_gpio_in(nvic, 28));
|
||||
|
||||
if (board->dc1 & (1 << 16)) {
|
||||
dev = sysbus_create_varargs(TYPE_STELLARIS_ADC, 0x40038000,
|
||||
qdev_get_gpio_in(nvic, 14),
|
||||
@ -1388,19 +1091,21 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
|
||||
}
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (board->dc2 & (0x10000 << i)) {
|
||||
dev = sysbus_create_simple(TYPE_STELLARIS_GPTM,
|
||||
0x40030000 + i * 0x1000,
|
||||
qdev_get_gpio_in(nvic, timer_irq[i]));
|
||||
SysBusDevice *sbd;
|
||||
|
||||
dev = qdev_new(TYPE_STELLARIS_GPTM);
|
||||
sbd = SYS_BUS_DEVICE(dev);
|
||||
qdev_connect_clock_in(dev, "clk",
|
||||
qdev_get_clock_out(ssys_dev, "SYSCLK"));
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
sysbus_mmio_map(sbd, 0, 0x40030000 + i * 0x1000);
|
||||
sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(nvic, timer_irq[i]));
|
||||
/* TODO: This is incorrect, but we get away with it because
|
||||
the ADC output is only ever pulsed. */
|
||||
qdev_connect_gpio_out(dev, 0, adc);
|
||||
}
|
||||
}
|
||||
|
||||
ssys_dev = stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
|
||||
board, nd_table[0].macaddr.a);
|
||||
|
||||
|
||||
if (board->dc1 & (1 << 3)) { /* watchdog present */
|
||||
dev = qdev_new(TYPE_LUMINARY_WATCHDOG);
|
||||
|
||||
@ -1642,22 +1347,6 @@ static const TypeInfo stellaris_i2c_info = {
|
||||
.class_init = stellaris_i2c_class_init,
|
||||
};
|
||||
|
||||
static void stellaris_gptm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_stellaris_gptm;
|
||||
dc->realize = stellaris_gptm_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo stellaris_gptm_info = {
|
||||
.name = TYPE_STELLARIS_GPTM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(gptm_state),
|
||||
.instance_init = stellaris_gptm_init,
|
||||
.class_init = stellaris_gptm_class_init,
|
||||
};
|
||||
|
||||
static void stellaris_adc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
@ -1696,7 +1385,6 @@ static const TypeInfo stellaris_sys_info = {
|
||||
static void stellaris_register_types(void)
|
||||
{
|
||||
type_register_static(&stellaris_i2c_info);
|
||||
type_register_static(&stellaris_gptm_info);
|
||||
type_register_static(&stellaris_adc_info);
|
||||
type_register_static(&stellaris_sys_info);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/stm32f100_soc.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
@ -57,6 +58,9 @@ static void stm32f100_soc_initfn(Object *obj)
|
||||
for (i = 0; i < STM_NUM_SPIS; i++) {
|
||||
object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI);
|
||||
}
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
@ -67,31 +71,54 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
int i;
|
||||
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash_alias = g_new(MemoryRegion, 1);
|
||||
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC RCC and its ability to
|
||||
* change the sysclk frequency and define different sysclk sources.
|
||||
*/
|
||||
|
||||
/* The refclk always runs at frequency HCLK / 8 */
|
||||
clock_set_mul_div(s->refclk, 8, 1);
|
||||
clock_set_source(s->refclk, s->sysclk);
|
||||
|
||||
/*
|
||||
* Init flash region
|
||||
* Flash starts at 0x08000000 and then is aliased to boot memory at 0x0
|
||||
*/
|
||||
memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F100.flash",
|
||||
memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F100.flash",
|
||||
FLASH_SIZE, &error_fatal);
|
||||
memory_region_init_alias(flash_alias, OBJECT(dev_soc),
|
||||
"STM32F100.flash.alias", flash, 0, FLASH_SIZE);
|
||||
memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash);
|
||||
memory_region_add_subregion(system_memory, 0, flash_alias);
|
||||
memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc),
|
||||
"STM32F100.flash.alias", &s->flash, 0, FLASH_SIZE);
|
||||
memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
|
||||
memory_region_add_subregion(system_memory, 0, &s->flash_alias);
|
||||
|
||||
/* Init SRAM region */
|
||||
memory_region_init_ram(sram, NULL, "STM32F100.sram", SRAM_SIZE,
|
||||
memory_region_init_ram(&s->sram, NULL, "STM32F100.sram", SRAM_SIZE,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
|
||||
|
||||
/* Init ARMv7m */
|
||||
armv7m = DEVICE(&s->armv7m);
|
||||
qdev_prop_set_uint32(armv7m, "num-irq", 61);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/arm/stm32f205_soc.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
/* At the moment only Timer 2 to 5 are modelled */
|
||||
@ -74,6 +75,9 @@ static void stm32f205_soc_initfn(Object *obj)
|
||||
for (i = 0; i < STM_NUM_SPIS; i++) {
|
||||
object_initialize_child(obj, "spi[*]", &s->spi[i], TYPE_STM32F2XX_SPI);
|
||||
}
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
@ -84,26 +88,49 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
int i;
|
||||
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash_alias = g_new(MemoryRegion, 1);
|
||||
|
||||
memory_region_init_rom(flash, OBJECT(dev_soc), "STM32F205.flash",
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC RCC and its ability to
|
||||
* change the sysclk frequency and define different sysclk sources.
|
||||
*/
|
||||
|
||||
/* The refclk always runs at frequency HCLK / 8 */
|
||||
clock_set_mul_div(s->refclk, 8, 1);
|
||||
clock_set_source(s->refclk, s->sysclk);
|
||||
|
||||
memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F205.flash",
|
||||
FLASH_SIZE, &error_fatal);
|
||||
memory_region_init_alias(flash_alias, OBJECT(dev_soc),
|
||||
"STM32F205.flash.alias", flash, 0, FLASH_SIZE);
|
||||
memory_region_init_alias(&s->flash_alias, OBJECT(dev_soc),
|
||||
"STM32F205.flash.alias", &s->flash, 0, FLASH_SIZE);
|
||||
|
||||
memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, flash);
|
||||
memory_region_add_subregion(system_memory, 0, flash_alias);
|
||||
memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
|
||||
memory_region_add_subregion(system_memory, 0, &s->flash_alias);
|
||||
|
||||
memory_region_init_ram(sram, NULL, "STM32F205.sram", SRAM_SIZE,
|
||||
memory_region_init_ram(&s->sram, NULL, "STM32F205.sram", SRAM_SIZE,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, sram);
|
||||
memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
|
||||
|
||||
armv7m = DEVICE(&s->armv7m);
|
||||
qdev_prop_set_uint32(armv7m, "num-irq", 96);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(get_system_memory()), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "exec/address-spaces.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/arm/stm32f405_soc.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
|
||||
#define SYSCFG_ADD 0x40013800
|
||||
@ -80,6 +81,9 @@ static void stm32f405_soc_initfn(Object *obj)
|
||||
}
|
||||
|
||||
object_initialize_child(obj, "exti", &s->exti, TYPE_STM32F4XX_EXTI);
|
||||
|
||||
s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
|
||||
s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
|
||||
}
|
||||
|
||||
static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
@ -91,6 +95,30 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
Error *err = NULL;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We use s->refclk internally and only define it with qdev_init_clock_in()
|
||||
* so it is correctly parented and not leaked on an init/deinit; it is not
|
||||
* intended as an externally exposed clock.
|
||||
*/
|
||||
if (clock_has_source(s->refclk)) {
|
||||
error_setg(errp, "refclk clock must not be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clock_has_source(s->sysclk)) {
|
||||
error_setg(errp, "sysclk clock must be wired up by the board code");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: ideally we should model the SoC RCC and its ability to
|
||||
* change the sysclk frequency and define different sysclk sources.
|
||||
*/
|
||||
|
||||
/* The refclk always runs at frequency HCLK / 8 */
|
||||
clock_set_mul_div(s->refclk, 8, 1);
|
||||
clock_set_source(s->refclk, s->sysclk);
|
||||
|
||||
memory_region_init_rom(&s->flash, OBJECT(dev_soc), "STM32F405.flash",
|
||||
FLASH_SIZE, &err);
|
||||
if (err != NULL) {
|
||||
@ -116,6 +144,8 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
qdev_prop_set_uint32(armv7m, "num-irq", 96);
|
||||
qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type);
|
||||
qdev_prop_set_bit(armv7m, "enable-bitband", true);
|
||||
qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
|
||||
qdev_connect_clock_in(armv7m, "refclk", s->refclk);
|
||||
object_property_set_link(OBJECT(&s->armv7m), "memory",
|
||||
OBJECT(system_memory), &error_abort);
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/arm/stm32f100_soc.h"
|
||||
#include "hw/arm/boot.h"
|
||||
@ -39,16 +40,15 @@
|
||||
static void stm32vldiscovery_init(MachineState *machine)
|
||||
{
|
||||
DeviceState *dev;
|
||||
Clock *sysclk;
|
||||
|
||||
/*
|
||||
* TODO: ideally we would model the SoC RCC and let it handle
|
||||
* system_clock_scale, including its ability to define different
|
||||
* possible SYSCLK sources.
|
||||
*/
|
||||
system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
|
||||
/* This clock doesn't need migration because it is fixed-frequency */
|
||||
sysclk = clock_new(OBJECT(machine), "SYSCLK");
|
||||
clock_set_hz(sysclk, SYSCLK_FRQ);
|
||||
|
||||
dev = qdev_new(TYPE_STM32F100_SOC);
|
||||
qdev_prop_set_string(dev, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m3"));
|
||||
qdev_connect_clock_in(dev, "sysclk", sysclk);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
armv7m_load_kernel(ARM_CPU(first_cpu),
|
||||
@ -63,4 +63,3 @@ static void stm32vldiscovery_machine_init(MachineClass *mc)
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("stm32vldiscovery", stm32vldiscovery_machine_init)
|
||||
|
||||
|
@ -200,6 +200,7 @@ static const char *valid_cpus[] = {
|
||||
ARM_CPU_TYPE_NAME("cortex-a53"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a57"),
|
||||
ARM_CPU_TYPE_NAME("cortex-a72"),
|
||||
ARM_CPU_TYPE_NAME("a64fx"),
|
||||
ARM_CPU_TYPE_NAME("host"),
|
||||
ARM_CPU_TYPE_NAME("max"),
|
||||
};
|
||||
@ -1852,11 +1853,6 @@ static void machvirt_init(MachineState *machine)
|
||||
}
|
||||
|
||||
if (vms->secure) {
|
||||
if (kvm_enabled()) {
|
||||
error_report("mach-virt: KVM does not support Security extensions");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The Secure view of the world is the same as the NonSecure,
|
||||
* but with a few extra devices. Create it as a container region
|
||||
@ -2788,10 +2784,17 @@ static void machvirt_machine_init(void)
|
||||
}
|
||||
type_init(machvirt_machine_init);
|
||||
|
||||
static void virt_machine_6_1_options(MachineClass *mc)
|
||||
static void virt_machine_6_2_options(MachineClass *mc)
|
||||
{
|
||||
}
|
||||
DEFINE_VIRT_MACHINE_AS_LATEST(6, 1)
|
||||
DEFINE_VIRT_MACHINE_AS_LATEST(6, 2)
|
||||
|
||||
static void virt_machine_6_1_options(MachineClass *mc)
|
||||
{
|
||||
virt_machine_6_2_options(mc);
|
||||
compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len);
|
||||
}
|
||||
DEFINE_VIRT_MACHINE(6, 1)
|
||||
|
||||
static void virt_machine_6_0_options(MachineClass *mc)
|
||||
{
|
||||
|
@ -312,6 +312,9 @@ static void zynq_init(MachineState *machine)
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[39-IRQ_OFFSET]);
|
||||
|
||||
dev = qdev_new("pl330");
|
||||
object_property_set_link(OBJECT(dev), "memory",
|
||||
OBJECT(address_space_mem),
|
||||
&error_fatal);
|
||||
qdev_prop_set_uint8(dev, "num_chnls", 8);
|
||||
qdev_prop_set_uint8(dev, "num_periph_req", 4);
|
||||
qdev_prop_set_uint8(dev, "num_events", 16);
|
||||
|
@ -218,6 +218,8 @@ static void versal_create_admas(Versal *s, qemu_irq *pic)
|
||||
TYPE_XLNX_ZDMA);
|
||||
dev = DEVICE(&s->lpd.iou.adma[i]);
|
||||
object_property_set_int(OBJECT(dev), "bus-width", 128, &error_abort);
|
||||
object_property_set_link(OBJECT(dev), "dma",
|
||||
OBJECT(get_system_memory()), &error_fatal);
|
||||
sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
|
||||
@ -376,6 +378,8 @@ static void versal_unimp(Versal *s)
|
||||
MM_CRL, MM_CRL_SIZE);
|
||||
versal_unimp_area(s, "crf", &s->mr_ps,
|
||||
MM_FPD_CRF, MM_FPD_CRF_SIZE);
|
||||
versal_unimp_area(s, "apu", &s->mr_ps,
|
||||
MM_FPD_FPD_APU, MM_FPD_FPD_APU_SIZE);
|
||||
versal_unimp_area(s, "crp", &s->mr_ps,
|
||||
MM_PMC_CRP, MM_PMC_CRP_SIZE);
|
||||
versal_unimp_area(s, "iou-scntr", &s->mr_ps,
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "qemu/module.h"
|
||||
#include "hw/arm/xlnx-zynqmp.h"
|
||||
#include "hw/intc/arm_gic_common.h"
|
||||
#include "hw/misc/unimp.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
@ -56,6 +57,9 @@
|
||||
#define DPDMA_ADDR 0xfd4c0000
|
||||
#define DPDMA_IRQ 116
|
||||
|
||||
#define APU_ADDR 0xfd5c0000
|
||||
#define APU_SIZE 0x100
|
||||
|
||||
#define IPI_ADDR 0xFF300000
|
||||
#define IPI_IRQ 64
|
||||
|
||||
@ -222,6 +226,32 @@ static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s,
|
||||
qdev_realize(DEVICE(&s->rpu_cluster), NULL, &error_fatal);
|
||||
}
|
||||
|
||||
static void xlnx_zynqmp_create_unimp_mmio(XlnxZynqMPState *s)
|
||||
{
|
||||
static const struct UnimpInfo {
|
||||
const char *name;
|
||||
hwaddr base;
|
||||
hwaddr size;
|
||||
} unimp_areas[ARRAY_SIZE(s->mr_unimp)] = {
|
||||
{ .name = "apu", APU_ADDR, APU_SIZE },
|
||||
};
|
||||
unsigned int nr;
|
||||
|
||||
for (nr = 0; nr < ARRAY_SIZE(unimp_areas); nr++) {
|
||||
const struct UnimpInfo *info = &unimp_areas[nr];
|
||||
DeviceState *dev = qdev_new(TYPE_UNIMPLEMENTED_DEVICE);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
|
||||
assert(info->name && info->base && info->size > 0);
|
||||
qdev_prop_set_string(dev, "name", info->name);
|
||||
qdev_prop_set_uint64(dev, "size", info->size);
|
||||
object_property_add_child(OBJECT(s), info->name, OBJECT(dev));
|
||||
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
sysbus_mmio_map(sbd, 0, info->base);
|
||||
}
|
||||
}
|
||||
|
||||
static void xlnx_zynqmp_init(Object *obj)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
@ -570,26 +600,6 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
g_free(bus_name);
|
||||
}
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi), errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 0, QSPI_ADDR);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 1, LQSPI_ADDR);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, gic_spi[QSPI_IRQ]);
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_BUS; i++) {
|
||||
gchar *bus_name;
|
||||
gchar *target_bus;
|
||||
|
||||
/* Alias controller SPI bus to the SoC itself */
|
||||
bus_name = g_strdup_printf("qspi%d", i);
|
||||
target_bus = g_strdup_printf("spi%d", i);
|
||||
object_property_add_alias(OBJECT(s), bus_name,
|
||||
OBJECT(&s->qspi), target_bus);
|
||||
g_free(bus_name);
|
||||
g_free(target_bus);
|
||||
}
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->dp), errp)) {
|
||||
return;
|
||||
}
|
||||
@ -616,11 +626,17 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]);
|
||||
|
||||
xlnx_zynqmp_create_unimp_mmio(s);
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) {
|
||||
if (!object_property_set_uint(OBJECT(&s->gdma[i]), "bus-width", 128,
|
||||
errp)) {
|
||||
return;
|
||||
}
|
||||
if (!object_property_set_link(OBJECT(&s->gdma[i]), "dma",
|
||||
OBJECT(system_memory), errp)) {
|
||||
return;
|
||||
}
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->gdma[i]), errp)) {
|
||||
return;
|
||||
}
|
||||
@ -631,6 +647,10 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA_CH; i++) {
|
||||
if (!object_property_set_link(OBJECT(&s->adma[i]), "dma",
|
||||
OBJECT(system_memory), errp)) {
|
||||
return;
|
||||
}
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->adma[i]), errp)) {
|
||||
return;
|
||||
}
|
||||
@ -640,14 +660,36 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
gic_spi[adma_ch_intr[i]]);
|
||||
}
|
||||
|
||||
if (!object_property_set_link(OBJECT(&s->qspi_dma), "dma",
|
||||
OBJECT(system_memory), errp)) {
|
||||
return;
|
||||
}
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi_dma), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi_dma), 0, QSPI_DMA_ADDR);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi_dma), 0, gic_spi[QSPI_IRQ]);
|
||||
object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma",
|
||||
OBJECT(&s->qspi_dma), errp);
|
||||
|
||||
if (!object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma",
|
||||
OBJECT(&s->qspi_dma), errp)) {
|
||||
return;
|
||||
}
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->qspi), errp)) {
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 0, QSPI_ADDR);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 1, LQSPI_ADDR);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, gic_spi[QSPI_IRQ]);
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_BUS; i++) {
|
||||
g_autofree gchar *bus_name = g_strdup_printf("qspi%d", i);
|
||||
g_autofree gchar *target_bus = g_strdup_printf("spi%d", i);
|
||||
|
||||
/* Alias controller SPI bus to the SoC itself */
|
||||
object_property_add_alias(OBJECT(s), bus_name,
|
||||
OBJECT(&s->qspi), target_bus);
|
||||
}
|
||||
}
|
||||
|
||||
static Property xlnx_zynqmp_props[] = {
|
||||
|
@ -68,3 +68,6 @@ config SIFIVE_UART
|
||||
|
||||
config GOLDFISH_TTY
|
||||
bool
|
||||
|
||||
config SHAKTI_UART
|
||||
bool
|
||||
|
251
hw/char/escc.c
251
hw/char/escc.c
@ -86,12 +86,15 @@
|
||||
#define W_INTR 1
|
||||
#define INTR_INTALL 0x01
|
||||
#define INTR_TXINT 0x02
|
||||
#define INTR_PAR_SPEC 0x04
|
||||
#define INTR_RXMODEMSK 0x18
|
||||
#define INTR_RXINT1ST 0x08
|
||||
#define INTR_RXINTALL 0x10
|
||||
#define INTR_WTRQ_TXRX 0x20
|
||||
#define W_IVEC 2
|
||||
#define W_RXCTRL 3
|
||||
#define RXCTRL_RXEN 0x01
|
||||
#define RXCTRL_HUNT 0x10
|
||||
#define W_TXCTRL1 4
|
||||
#define TXCTRL1_PAREN 0x01
|
||||
#define TXCTRL1_PAREV 0x02
|
||||
@ -105,6 +108,7 @@
|
||||
#define TXCTRL1_CLK64X 0xc0
|
||||
#define TXCTRL1_CLKMSK 0xc0
|
||||
#define W_TXCTRL2 5
|
||||
#define TXCTRL2_TXCRC 0x01
|
||||
#define TXCTRL2_TXEN 0x08
|
||||
#define TXCTRL2_BITMSK 0x60
|
||||
#define TXCTRL2_5BITS 0x00
|
||||
@ -115,18 +119,27 @@
|
||||
#define W_SYNC2 7
|
||||
#define W_TXBUF 8
|
||||
#define W_MINTR 9
|
||||
#define MINTR_VIS 0x01
|
||||
#define MINTR_NV 0x02
|
||||
#define MINTR_STATUSHI 0x10
|
||||
#define MINTR_SOFTIACK 0x20
|
||||
#define MINTR_RST_MASK 0xc0
|
||||
#define MINTR_RST_B 0x40
|
||||
#define MINTR_RST_A 0x80
|
||||
#define MINTR_RST_ALL 0xc0
|
||||
#define W_MISC1 10
|
||||
#define MISC1_ENC_MASK 0x60
|
||||
#define W_CLOCK 11
|
||||
#define CLOCK_TRXC 0x08
|
||||
#define W_BRGLO 12
|
||||
#define W_BRGHI 13
|
||||
#define W_MISC2 14
|
||||
#define MISC2_PLLDIS 0x30
|
||||
#define MISC2_BRG_EN 0x01
|
||||
#define MISC2_BRG_SRC 0x02
|
||||
#define MISC2_LCL_LOOP 0x10
|
||||
#define MISC2_PLLCMD0 0x20
|
||||
#define MISC2_PLLCMD1 0x40
|
||||
#define MISC2_PLLCMD2 0x80
|
||||
#define W_EXTINT 15
|
||||
#define EXTINT_DCD 0x08
|
||||
#define EXTINT_SYNCINT 0x10
|
||||
@ -170,6 +183,7 @@
|
||||
#define R_RXBUF 8
|
||||
#define R_RXCTRL 9
|
||||
#define R_MISC 10
|
||||
#define MISC_2CLKMISS 0x40
|
||||
#define R_MISC1 11
|
||||
#define R_BRGLO 12
|
||||
#define R_BRGHI 13
|
||||
@ -230,20 +244,23 @@ static uint32_t get_queue(void *opaque)
|
||||
q->count--;
|
||||
}
|
||||
trace_escc_get_queue(CHN_C(s), val);
|
||||
if (q->count > 0)
|
||||
if (q->count > 0) {
|
||||
serial_receive_byte(s, 0);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static int escc_update_irq_chn(ESCCChannelState *s)
|
||||
{
|
||||
if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) ||
|
||||
// tx ints enabled, pending
|
||||
/* tx ints enabled, pending */
|
||||
((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) ||
|
||||
((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) &&
|
||||
s->rxint == 1) || // rx ints enabled, pending
|
||||
s->rxint == 1) ||
|
||||
/* rx ints enabled, pending */
|
||||
((s->wregs[W_EXTINT] & EXTINT_BRKINT) &&
|
||||
(s->rregs[R_STATUS] & STATUS_BRK)))) { // break int e&p
|
||||
(s->rregs[R_STATUS] & STATUS_BRK)))) {
|
||||
/* break int e&p */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -262,26 +279,7 @@ static void escc_update_irq(ESCCChannelState *s)
|
||||
|
||||
static void escc_reset_chn(ESCCChannelState *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
s->reg = 0;
|
||||
for (i = 0; i < ESCC_SERIAL_REGS; i++) {
|
||||
s->rregs[i] = 0;
|
||||
s->wregs[i] = 0;
|
||||
}
|
||||
s->wregs[W_TXCTRL1] = TXCTRL1_1STOP; // 1X divisor, 1 stop bit, no parity
|
||||
s->wregs[W_MINTR] = MINTR_RST_ALL;
|
||||
s->wregs[W_CLOCK] = CLOCK_TRXC; // Synch mode tx clock = TRxC
|
||||
s->wregs[W_MISC2] = MISC2_PLLDIS; // PLL disabled
|
||||
s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT |
|
||||
EXTINT_TXUNDRN | EXTINT_BRKINT; // Enable most interrupts
|
||||
if (s->disabled)
|
||||
s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_DCD | STATUS_SYNC |
|
||||
STATUS_CTS | STATUS_TXUNDRN;
|
||||
else
|
||||
s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_TXUNDRN;
|
||||
s->rregs[R_SPEC] = SPEC_BITS8 | SPEC_ALLSENT;
|
||||
|
||||
s->rx = s->tx = 0;
|
||||
s->rxint = s->txint = 0;
|
||||
s->rxint_under_svc = s->txint_under_svc = 0;
|
||||
@ -289,33 +287,100 @@ static void escc_reset_chn(ESCCChannelState *s)
|
||||
clear_queue(s);
|
||||
}
|
||||
|
||||
static void escc_soft_reset_chn(ESCCChannelState *s)
|
||||
{
|
||||
escc_reset_chn(s);
|
||||
|
||||
s->wregs[W_CMD] = 0;
|
||||
s->wregs[W_INTR] &= INTR_PAR_SPEC | INTR_WTRQ_TXRX;
|
||||
s->wregs[W_RXCTRL] &= ~RXCTRL_RXEN;
|
||||
/* 1 stop bit */
|
||||
s->wregs[W_TXCTRL1] |= TXCTRL1_1STOP;
|
||||
s->wregs[W_TXCTRL2] &= TXCTRL2_TXCRC | TXCTRL2_8BITS;
|
||||
s->wregs[W_MINTR] &= ~MINTR_SOFTIACK;
|
||||
s->wregs[W_MISC1] &= MISC1_ENC_MASK;
|
||||
/* PLL disabled */
|
||||
s->wregs[W_MISC2] &= MISC2_BRG_EN | MISC2_BRG_SRC |
|
||||
MISC2_PLLCMD1 | MISC2_PLLCMD2;
|
||||
s->wregs[W_MISC2] |= MISC2_PLLCMD0;
|
||||
/* Enable most interrupts */
|
||||
s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT |
|
||||
EXTINT_TXUNDRN | EXTINT_BRKINT;
|
||||
|
||||
s->rregs[R_STATUS] &= STATUS_DCD | STATUS_SYNC | STATUS_CTS | STATUS_BRK;
|
||||
s->rregs[R_STATUS] |= STATUS_TXEMPTY | STATUS_TXUNDRN;
|
||||
if (s->disabled) {
|
||||
s->rregs[R_STATUS] |= STATUS_DCD | STATUS_SYNC | STATUS_CTS;
|
||||
}
|
||||
s->rregs[R_SPEC] &= SPEC_ALLSENT;
|
||||
s->rregs[R_SPEC] |= SPEC_BITS8;
|
||||
s->rregs[R_INTR] = 0;
|
||||
s->rregs[R_MISC] &= MISC_2CLKMISS;
|
||||
}
|
||||
|
||||
static void escc_hard_reset_chn(ESCCChannelState *s)
|
||||
{
|
||||
escc_soft_reset_chn(s);
|
||||
|
||||
/*
|
||||
* Hard reset is almost identical to soft reset above, except that the
|
||||
* values of WR9 (W_MINTR), WR10 (W_MISC1), WR11 (W_CLOCK) and WR14
|
||||
* (W_MISC2) have extra bits forced to 0/1
|
||||
*/
|
||||
s->wregs[W_MINTR] &= MINTR_VIS | MINTR_NV;
|
||||
s->wregs[W_MINTR] |= MINTR_RST_B | MINTR_RST_A;
|
||||
s->wregs[W_MISC1] = 0;
|
||||
s->wregs[W_CLOCK] = CLOCK_TRXC;
|
||||
s->wregs[W_MISC2] &= MISC2_PLLCMD1 | MISC2_PLLCMD2;
|
||||
s->wregs[W_MISC2] |= MISC2_LCL_LOOP | MISC2_PLLCMD0;
|
||||
}
|
||||
|
||||
static void escc_reset(DeviceState *d)
|
||||
{
|
||||
ESCCState *s = ESCC(d);
|
||||
int i, j;
|
||||
|
||||
escc_reset_chn(&s->chn[0]);
|
||||
escc_reset_chn(&s->chn[1]);
|
||||
for (i = 0; i < 2; i++) {
|
||||
ESCCChannelState *cs = &s->chn[i];
|
||||
|
||||
/*
|
||||
* According to the ESCC datasheet "Miscellaneous Questions" section
|
||||
* on page 384, the values of the ESCC registers are not guaranteed on
|
||||
* power-on until an explicit hardware or software reset has been
|
||||
* issued. For now we zero the registers so that a device reset always
|
||||
* returns the emulated device to a fixed state.
|
||||
*/
|
||||
for (j = 0; j < ESCC_SERIAL_REGS; j++) {
|
||||
cs->rregs[j] = 0;
|
||||
cs->wregs[j] = 0;
|
||||
}
|
||||
escc_reset_chn(cs);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void set_rxint(ESCCChannelState *s)
|
||||
{
|
||||
s->rxint = 1;
|
||||
/* XXX: missing daisy chainnig: escc_chn_b rx should have a lower priority
|
||||
than chn_a rx/tx/special_condition service*/
|
||||
/*
|
||||
* XXX: missing daisy chaining: escc_chn_b rx should have a lower priority
|
||||
* than chn_a rx/tx/special_condition service
|
||||
*/
|
||||
s->rxint_under_svc = 1;
|
||||
if (s->chn == escc_chn_a) {
|
||||
s->rregs[R_INTR] |= INTR_RXINTA;
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI) {
|
||||
s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
|
||||
else
|
||||
} else {
|
||||
s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
|
||||
}
|
||||
} else {
|
||||
s->otherchn->rregs[R_INTR] |= INTR_RXINTB;
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI) {
|
||||
s->rregs[R_IVEC] = IVEC_HIRXINTB;
|
||||
else
|
||||
} else {
|
||||
s->rregs[R_IVEC] = IVEC_LORXINTB;
|
||||
}
|
||||
}
|
||||
escc_update_irq(s);
|
||||
}
|
||||
|
||||
@ -328,10 +393,11 @@ static inline void set_txint(ESCCChannelState *s)
|
||||
if (s->wregs[W_INTR] & INTR_TXINT) {
|
||||
s->rregs[R_INTR] |= INTR_TXINTA;
|
||||
}
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI) {
|
||||
s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
|
||||
else
|
||||
} else {
|
||||
s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
|
||||
}
|
||||
} else {
|
||||
s->rregs[R_IVEC] = IVEC_TXINTB;
|
||||
if (s->wregs[W_INTR] & INTR_TXINT) {
|
||||
@ -347,20 +413,23 @@ static inline void clr_rxint(ESCCChannelState *s)
|
||||
s->rxint = 0;
|
||||
s->rxint_under_svc = 0;
|
||||
if (s->chn == escc_chn_a) {
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI) {
|
||||
s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
|
||||
else
|
||||
} else {
|
||||
s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
|
||||
}
|
||||
s->rregs[R_INTR] &= ~INTR_RXINTA;
|
||||
} else {
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI) {
|
||||
s->rregs[R_IVEC] = IVEC_HINOINT;
|
||||
else
|
||||
} else {
|
||||
s->rregs[R_IVEC] = IVEC_LONOINT;
|
||||
}
|
||||
s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB;
|
||||
}
|
||||
if (s->txint)
|
||||
if (s->txint) {
|
||||
set_txint(s);
|
||||
}
|
||||
escc_update_irq(s);
|
||||
}
|
||||
|
||||
@ -369,21 +438,24 @@ static inline void clr_txint(ESCCChannelState *s)
|
||||
s->txint = 0;
|
||||
s->txint_under_svc = 0;
|
||||
if (s->chn == escc_chn_a) {
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI) {
|
||||
s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
|
||||
else
|
||||
} else {
|
||||
s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
|
||||
}
|
||||
s->rregs[R_INTR] &= ~INTR_TXINTA;
|
||||
} else {
|
||||
s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI)
|
||||
if (s->wregs[W_MINTR] & MINTR_STATUSHI) {
|
||||
s->rregs[R_IVEC] = IVEC_HINOINT;
|
||||
else
|
||||
} else {
|
||||
s->rregs[R_IVEC] = IVEC_LONOINT;
|
||||
}
|
||||
s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
|
||||
}
|
||||
if (s->rxint)
|
||||
if (s->rxint) {
|
||||
set_rxint(s);
|
||||
}
|
||||
escc_update_irq(s);
|
||||
}
|
||||
|
||||
@ -392,21 +464,24 @@ static void escc_update_parameters(ESCCChannelState *s)
|
||||
int speed, parity, data_bits, stop_bits;
|
||||
QEMUSerialSetParams ssp;
|
||||
|
||||
if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != escc_serial)
|
||||
if (!qemu_chr_fe_backend_connected(&s->chr) || s->type != escc_serial) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) {
|
||||
if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV)
|
||||
if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV) {
|
||||
parity = 'E';
|
||||
else
|
||||
} else {
|
||||
parity = 'O';
|
||||
}
|
||||
} else {
|
||||
parity = 'N';
|
||||
}
|
||||
if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP)
|
||||
if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP) {
|
||||
stop_bits = 2;
|
||||
else
|
||||
} else {
|
||||
stop_bits = 1;
|
||||
}
|
||||
switch (s->wregs[W_TXCTRL2] & TXCTRL2_BITMSK) {
|
||||
case TXCTRL2_5BITS:
|
||||
data_bits = 5;
|
||||
@ -487,7 +562,13 @@ static void escc_mem_write(void *opaque, hwaddr addr,
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case W_INTR ... W_RXCTRL:
|
||||
case W_RXCTRL:
|
||||
s->wregs[s->reg] = val;
|
||||
if (val & RXCTRL_HUNT) {
|
||||
s->rregs[R_STATUS] |= STATUS_SYNC;
|
||||
}
|
||||
break;
|
||||
case W_INTR ... W_IVEC:
|
||||
case W_SYNC1 ... W_TXBUF:
|
||||
case W_MISC1 ... W_CLOCK:
|
||||
case W_MISC2 ... W_EXTINT:
|
||||
@ -510,23 +591,28 @@ static void escc_mem_write(void *opaque, hwaddr addr,
|
||||
default:
|
||||
break;
|
||||
case MINTR_RST_B:
|
||||
escc_reset_chn(&serial->chn[0]);
|
||||
trace_escc_soft_reset_chn(CHN_C(&serial->chn[0]));
|
||||
escc_soft_reset_chn(&serial->chn[0]);
|
||||
return;
|
||||
case MINTR_RST_A:
|
||||
escc_reset_chn(&serial->chn[1]);
|
||||
trace_escc_soft_reset_chn(CHN_C(&serial->chn[1]));
|
||||
escc_soft_reset_chn(&serial->chn[1]);
|
||||
return;
|
||||
case MINTR_RST_ALL:
|
||||
escc_reset(DEVICE(serial));
|
||||
trace_escc_hard_reset();
|
||||
escc_hard_reset_chn(&serial->chn[0]);
|
||||
escc_hard_reset_chn(&serial->chn[1]);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (s->reg == 0)
|
||||
if (s->reg == 0) {
|
||||
s->reg = newreg;
|
||||
else
|
||||
} else {
|
||||
s->reg = 0;
|
||||
}
|
||||
break;
|
||||
case SERIAL_DATA:
|
||||
trace_escc_mem_writeb_data(CHN_C(s), val);
|
||||
@ -538,17 +624,19 @@ static void escc_mem_write(void *opaque, hwaddr addr,
|
||||
s->txint = 0;
|
||||
escc_update_irq(s);
|
||||
s->tx = val;
|
||||
if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled
|
||||
if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { /* tx enabled */
|
||||
if (qemu_chr_fe_backend_connected(&s->chr)) {
|
||||
/* XXX this blocks entire thread. Rewrite to use
|
||||
* qemu_chr_fe_write and background I/O callbacks */
|
||||
/*
|
||||
* XXX this blocks entire thread. Rewrite to use
|
||||
* qemu_chr_fe_write and background I/O callbacks
|
||||
*/
|
||||
qemu_chr_fe_write_all(&s->chr, &s->tx, 1);
|
||||
} else if (s->type == escc_kbd && !s->disabled) {
|
||||
handle_kbd_command(s, val);
|
||||
}
|
||||
}
|
||||
s->rregs[R_STATUS] |= STATUS_TXEMPTY; // Tx buffer empty
|
||||
s->rregs[R_SPEC] |= SPEC_ALLSENT; // All sent
|
||||
s->rregs[R_STATUS] |= STATUS_TXEMPTY; /* Tx buffer empty */
|
||||
s->rregs[R_SPEC] |= SPEC_ALLSENT; /* All sent */
|
||||
set_txint(s);
|
||||
break;
|
||||
default:
|
||||
@ -606,12 +694,13 @@ static int serial_can_receive(void *opaque)
|
||||
ESCCChannelState *s = opaque;
|
||||
int ret;
|
||||
|
||||
if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) // Rx not enabled
|
||||
|| ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV))
|
||||
// char already available
|
||||
if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) /* Rx not enabled */
|
||||
|| ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV)) {
|
||||
/* char already available */
|
||||
ret = 0;
|
||||
else
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -638,9 +727,10 @@ static void serial_receive1(void *opaque, const uint8_t *buf, int size)
|
||||
static void serial_event(void *opaque, QEMUChrEvent event)
|
||||
{
|
||||
ESCCChannelState *s = opaque;
|
||||
if (event == CHR_EVENT_BREAK)
|
||||
if (event == CHR_EVENT_BREAK) {
|
||||
serial_receive_break(s);
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_escc_chn = {
|
||||
.name = "escc_chn",
|
||||
@ -734,21 +824,21 @@ static QemuInputHandler sunkbd_handler = {
|
||||
static void handle_kbd_command(ESCCChannelState *s, int val)
|
||||
{
|
||||
trace_escc_kbd_command(val);
|
||||
if (s->led_mode) { // Ignore led byte
|
||||
if (s->led_mode) { /* Ignore led byte */
|
||||
s->led_mode = 0;
|
||||
return;
|
||||
}
|
||||
switch (val) {
|
||||
case 1: // Reset, return type code
|
||||
case 1: /* Reset, return type code */
|
||||
clear_queue(s);
|
||||
put_queue(s, 0xff);
|
||||
put_queue(s, 4); // Type 4
|
||||
put_queue(s, 4); /* Type 4 */
|
||||
put_queue(s, 0x7f);
|
||||
break;
|
||||
case 0xe: // Set leds
|
||||
case 0xe: /* Set leds */
|
||||
s->led_mode = 1;
|
||||
break;
|
||||
case 7: // Query layout
|
||||
case 7: /* Query layout */
|
||||
case 0xf:
|
||||
clear_queue(s);
|
||||
put_queue(s, 0xfe);
|
||||
@ -768,34 +858,39 @@ static void sunmouse_event(void *opaque,
|
||||
trace_escc_sunmouse_event(dx, dy, buttons_state);
|
||||
ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */
|
||||
|
||||
if (buttons_state & MOUSE_EVENT_LBUTTON)
|
||||
if (buttons_state & MOUSE_EVENT_LBUTTON) {
|
||||
ch ^= 0x4;
|
||||
if (buttons_state & MOUSE_EVENT_MBUTTON)
|
||||
}
|
||||
if (buttons_state & MOUSE_EVENT_MBUTTON) {
|
||||
ch ^= 0x2;
|
||||
if (buttons_state & MOUSE_EVENT_RBUTTON)
|
||||
}
|
||||
if (buttons_state & MOUSE_EVENT_RBUTTON) {
|
||||
ch ^= 0x1;
|
||||
}
|
||||
|
||||
put_queue(s, ch);
|
||||
|
||||
ch = dx;
|
||||
|
||||
if (ch > 127)
|
||||
if (ch > 127) {
|
||||
ch = 127;
|
||||
else if (ch < -127)
|
||||
} else if (ch < -127) {
|
||||
ch = -127;
|
||||
}
|
||||
|
||||
put_queue(s, ch & 0xff);
|
||||
|
||||
ch = -dy;
|
||||
|
||||
if (ch > 127)
|
||||
if (ch > 127) {
|
||||
ch = 127;
|
||||
else if (ch < -127)
|
||||
} else if (ch < -127) {
|
||||
ch = -127;
|
||||
}
|
||||
|
||||
put_queue(s, ch & 0xff);
|
||||
|
||||
// MSC protocol specify two extra motion bytes
|
||||
/* MSC protocol specifies two extra motion bytes */
|
||||
|
||||
put_queue(s, 0);
|
||||
put_queue(s, 0);
|
||||
|
@ -16,7 +16,7 @@ softmmu_ss.add(when: 'CONFIG_SERIAL', if_true: files('serial.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SERIAL_PCI', if_true: files('serial-pci.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SERIAL_PCI_MULTI', if_true: files('serial-pci-multi.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SHAKTI', if_true: files('shakti_uart.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SHAKTI_UART', if_true: files('shakti_uart.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_VIRTIO_SERIAL', if_true: files('virtio-console.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_XEN', if_true: files('xen_console.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_uartlite.c'))
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "hw/qdev-properties-system.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "chardev/char-serial.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "trace.h"
|
||||
@ -231,6 +232,11 @@ static void pl011_write(void *opaque, hwaddr offset,
|
||||
s->read_count = 0;
|
||||
s->read_pos = 0;
|
||||
}
|
||||
if ((s->lcr ^ value) & 0x1) {
|
||||
int break_enable = value & 0x1;
|
||||
qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
|
||||
&break_enable);
|
||||
}
|
||||
s->lcr = value;
|
||||
pl011_set_read_trigger(s);
|
||||
break;
|
||||
|
@ -36,6 +36,8 @@ grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" va
|
||||
grlib_apbuart_readl_unknown(uint64_t addr) "addr 0x%"PRIx64
|
||||
|
||||
# escc.c
|
||||
escc_hard_reset(void) "hard reset"
|
||||
escc_soft_reset_chn(char channel) "soft reset channel %c"
|
||||
escc_put_queue(char channel, int b) "channel %c put: 0x%02x"
|
||||
escc_get_queue(char channel, int val) "channel %c get 0x%02x"
|
||||
escc_update_irq(int irq) "IRQ = %d"
|
||||
|
@ -14,12 +14,50 @@
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
static bool muldiv_needed(void *opaque)
|
||||
{
|
||||
Clock *clk = opaque;
|
||||
|
||||
return clk->multiplier != 1 || clk->divider != 1;
|
||||
}
|
||||
|
||||
static int clock_pre_load(void *opaque)
|
||||
{
|
||||
Clock *clk = opaque;
|
||||
/*
|
||||
* The initial out-of-reset settings of the Clock might have been
|
||||
* configured by the device to be different from what we set
|
||||
* in clock_initfn(), so we must here set the default values to
|
||||
* be used if they are not in the inbound migration state.
|
||||
*/
|
||||
clk->multiplier = 1;
|
||||
clk->divider = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_muldiv = {
|
||||
.name = "clock/muldiv",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = muldiv_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(multiplier, Clock),
|
||||
VMSTATE_UINT32(divider, Clock),
|
||||
},
|
||||
};
|
||||
|
||||
const VMStateDescription vmstate_clock = {
|
||||
.name = "clock",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.pre_load = clock_pre_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(period, Clock),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
},
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_muldiv,
|
||||
NULL
|
||||
},
|
||||
};
|
||||
|
@ -64,6 +64,15 @@ bool clock_set(Clock *clk, uint64_t period)
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t clock_get_child_period(Clock *clk)
|
||||
{
|
||||
/*
|
||||
* Return the period to be used for child clocks, which is the parent
|
||||
* clock period adjusted for for multiplier and divider effects.
|
||||
*/
|
||||
return muldiv64(clk->period, clk->multiplier, clk->divider);
|
||||
}
|
||||
|
||||
static void clock_call_callback(Clock *clk, ClockEvent event)
|
||||
{
|
||||
/*
|
||||
@ -78,15 +87,16 @@ static void clock_call_callback(Clock *clk, ClockEvent event)
|
||||
static void clock_propagate_period(Clock *clk, bool call_callbacks)
|
||||
{
|
||||
Clock *child;
|
||||
uint64_t child_period = clock_get_child_period(clk);
|
||||
|
||||
QLIST_FOREACH(child, &clk->children, sibling) {
|
||||
if (child->period != clk->period) {
|
||||
if (child->period != child_period) {
|
||||
if (call_callbacks) {
|
||||
clock_call_callback(child, ClockPreUpdate);
|
||||
}
|
||||
child->period = clk->period;
|
||||
child->period = child_period;
|
||||
trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
|
||||
CLOCK_PERIOD_TO_HZ(clk->period),
|
||||
CLOCK_PERIOD_TO_HZ(child->period),
|
||||
call_callbacks);
|
||||
if (call_callbacks) {
|
||||
clock_call_callback(child, ClockUpdate);
|
||||
@ -110,7 +120,7 @@ void clock_set_source(Clock *clk, Clock *src)
|
||||
|
||||
trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src));
|
||||
|
||||
clk->period = src->period;
|
||||
clk->period = clock_get_child_period(src);
|
||||
QLIST_INSERT_HEAD(&src->children, clk, sibling);
|
||||
clk->source = src;
|
||||
clock_propagate_period(clk, false);
|
||||
@ -133,10 +143,23 @@ char *clock_display_freq(Clock *clk)
|
||||
return freq_to_str(clock_get_hz(clk));
|
||||
}
|
||||
|
||||
void clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider)
|
||||
{
|
||||
assert(divider != 0);
|
||||
|
||||
trace_clock_set_mul_div(CLOCK_PATH(clk), clk->multiplier, multiplier,
|
||||
clk->divider, divider);
|
||||
clk->multiplier = multiplier;
|
||||
clk->divider = divider;
|
||||
}
|
||||
|
||||
static void clock_initfn(Object *obj)
|
||||
{
|
||||
Clock *clk = CLOCK(obj);
|
||||
|
||||
clk->multiplier = 1;
|
||||
clk->divider = 1;
|
||||
|
||||
QLIST_INIT(&clk->children);
|
||||
}
|
||||
|
||||
|
@ -555,24 +555,35 @@ ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen)
|
||||
|
||||
/* skip header */
|
||||
i = 10;
|
||||
if (srclen < 4) {
|
||||
goto toosmall;
|
||||
}
|
||||
flags = src[3];
|
||||
if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
|
||||
puts ("Error: Bad gzipped data\n");
|
||||
return -1;
|
||||
}
|
||||
if ((flags & EXTRA_FIELD) != 0)
|
||||
if ((flags & EXTRA_FIELD) != 0) {
|
||||
if (srclen < 12) {
|
||||
goto toosmall;
|
||||
}
|
||||
i = 12 + src[10] + (src[11] << 8);
|
||||
if ((flags & ORIG_NAME) != 0)
|
||||
while (src[i++] != 0)
|
||||
;
|
||||
if ((flags & COMMENT) != 0)
|
||||
while (src[i++] != 0)
|
||||
;
|
||||
if ((flags & HEAD_CRC) != 0)
|
||||
}
|
||||
if ((flags & ORIG_NAME) != 0) {
|
||||
while (i < srclen && src[i++] != 0) {
|
||||
/* do nothing */
|
||||
}
|
||||
}
|
||||
if ((flags & COMMENT) != 0) {
|
||||
while (i < srclen && src[i++] != 0) {
|
||||
/* do nothing */
|
||||
}
|
||||
}
|
||||
if ((flags & HEAD_CRC) != 0) {
|
||||
i += 2;
|
||||
}
|
||||
if (i >= srclen) {
|
||||
puts ("Error: gunzip out of data in header\n");
|
||||
return -1;
|
||||
goto toosmall;
|
||||
}
|
||||
|
||||
s.zalloc = zalloc;
|
||||
@ -596,6 +607,10 @@ ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen)
|
||||
inflateEnd(&s);
|
||||
|
||||
return dstbytes;
|
||||
|
||||
toosmall:
|
||||
puts("Error: gunzip out of data in header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Load a U-Boot image. */
|
||||
|
@ -37,6 +37,9 @@
|
||||
#include "hw/virtio/virtio.h"
|
||||
#include "hw/virtio/virtio-pci.h"
|
||||
|
||||
GlobalProperty hw_compat_6_1[] = {};
|
||||
const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1);
|
||||
|
||||
GlobalProperty hw_compat_6_0[] = {
|
||||
{ "gpex-pcihost", "allow-unmapped-accesses", "false" },
|
||||
{ "i8042", "extended-state", "false"},
|
||||
|
@ -36,11 +36,11 @@
|
||||
|
||||
static bool check_prop_still_unset(Object *obj, const char *name,
|
||||
const void *old_val, const char *new_val,
|
||||
Error **errp)
|
||||
bool allow_override, Error **errp)
|
||||
{
|
||||
const GlobalProperty *prop = qdev_find_global_prop(obj, name);
|
||||
|
||||
if (!old_val) {
|
||||
if (!old_val || (!prop && allow_override)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -93,16 +93,34 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name,
|
||||
BlockBackend *blk;
|
||||
bool blk_created = false;
|
||||
int ret;
|
||||
BlockDriverState *bs;
|
||||
AioContext *ctx;
|
||||
|
||||
if (!visit_type_str(v, name, &str, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO Should this really be an error? If no, the old value
|
||||
* needs to be released before we store the new one.
|
||||
*/
|
||||
if (!check_prop_still_unset(obj, name, *ptr, str, errp)) {
|
||||
if (!check_prop_still_unset(obj, name, *ptr, str, true, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ptr) {
|
||||
/* BlockBackend alread exists. So, we want to change attached node */
|
||||
blk = *ptr;
|
||||
ctx = blk_get_aio_context(blk);
|
||||
bs = bdrv_lookup_bs(NULL, str, errp);
|
||||
if (!bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx != bdrv_get_aio_context(bs)) {
|
||||
error_setg(errp, "Different aio context is not supported for new "
|
||||
"node");
|
||||
}
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
blk_replace_bs(blk, bs, errp);
|
||||
aio_context_release(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -114,7 +132,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name,
|
||||
|
||||
blk = blk_by_name(str);
|
||||
if (!blk) {
|
||||
BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
|
||||
bs = bdrv_lookup_bs(NULL, str, NULL);
|
||||
if (bs) {
|
||||
/*
|
||||
* If the device supports iothreads, it will make sure to move the
|
||||
@ -123,8 +141,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name,
|
||||
* aware of iothreads require their BlockBackends to be in the main
|
||||
* AioContext.
|
||||
*/
|
||||
AioContext *ctx = iothread ? bdrv_get_aio_context(bs) :
|
||||
qemu_get_aio_context();
|
||||
ctx = iothread ? bdrv_get_aio_context(bs) : qemu_get_aio_context();
|
||||
blk = blk_new(ctx, 0, BLK_PERM_ALL);
|
||||
blk_created = true;
|
||||
|
||||
@ -196,6 +213,7 @@ static void release_drive(Object *obj, const char *name, void *opaque)
|
||||
const PropertyInfo qdev_prop_drive = {
|
||||
.name = "str",
|
||||
.description = "Node name or ID of a block device to use as a backend",
|
||||
.realized_set_allowed = true,
|
||||
.get = get_drive,
|
||||
.set = set_drive,
|
||||
.release = release_drive,
|
||||
@ -204,6 +222,7 @@ const PropertyInfo qdev_prop_drive = {
|
||||
const PropertyInfo qdev_prop_drive_iothread = {
|
||||
.name = "str",
|
||||
.description = "Node name or ID of a block device to use as a backend",
|
||||
.realized_set_allowed = true,
|
||||
.get = get_drive,
|
||||
.set = set_drive_iothread,
|
||||
.release = release_drive,
|
||||
@ -238,7 +257,7 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
|
||||
* TODO Should this really be an error? If no, the old value
|
||||
* needs to be released before we store the new one.
|
||||
*/
|
||||
if (!check_prop_still_unset(obj, name, be->chr, str, errp)) {
|
||||
if (!check_prop_still_unset(obj, name, be->chr, str, false, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -408,7 +427,7 @@ static void set_netdev(Object *obj, Visitor *v, const char *name,
|
||||
* TODO Should this really be an error? If no, the old value
|
||||
* needs to be released before we store the new one.
|
||||
*/
|
||||
if (!check_prop_still_unset(obj, name, ncs[i], str, errp)) {
|
||||
if (!check_prop_still_unset(obj, name, ncs[i], str, false, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user