Merge remote-tracking branch 'upstream/master' into main
This commit is contained in:
commit
ac7bfe4325
@ -13,7 +13,7 @@
|
||||
#############################################################
|
||||
|
||||
# Cirrus jobs can't run unless the creds / target repo are set
|
||||
- if: '$QEMU_JOB_CIRRUS && ($CIRRUS_GITHUB_REPO == "" || $CIRRUS_API_TOKEN == "")'
|
||||
- if: '$QEMU_JOB_CIRRUS && ($CIRRUS_GITHUB_REPO == null || $CIRRUS_API_TOKEN == null)'
|
||||
when: never
|
||||
|
||||
# Publishing jobs should only run on the default branch in upstream
|
||||
|
@ -168,6 +168,7 @@ build-system-centos:
|
||||
IMAGE: centos8
|
||||
CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system
|
||||
--enable-modules --enable-trace-backends=dtrace --enable-docs
|
||||
--enable-vfio-user-server
|
||||
TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu
|
||||
x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
@ -357,16 +358,15 @@ build-cfi-aarch64:
|
||||
--enable-safe-stack --enable-slirp=git
|
||||
TARGETS: aarch64-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
variables:
|
||||
# FIXME: This job is often failing, likely due to out-of-memory problems in
|
||||
# the constrained containers of the shared runners. Thus this is marked as
|
||||
# skipped until the situation has been solved.
|
||||
QEMU_JOB_SKIPPED: 1
|
||||
timeout: 90m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-aarch64:
|
||||
extends: .native_test_job_template
|
||||
@ -398,16 +398,15 @@ build-cfi-ppc64-s390x:
|
||||
--enable-safe-stack --enable-slirp=git
|
||||
TARGETS: ppc64-softmmu s390x-softmmu
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
timeout: 70m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
variables:
|
||||
# FIXME: This job is often failing, likely due to out-of-memory problems in
|
||||
# the constrained containers of the shared runners. Thus this is marked as
|
||||
# skipped until the situation has been solved.
|
||||
QEMU_JOB_SKIPPED: 1
|
||||
timeout: 80m
|
||||
artifacts:
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build
|
||||
|
||||
check-cfi-ppc64-s390x:
|
||||
extends: .native_test_job_template
|
||||
|
@ -19,5 +19,5 @@ ubuntu-20.04-aarch32-all:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --cross-prefix=arm-linux-gnueabihf-
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|
@ -17,9 +17,9 @@ ubuntu-20.04-aarch64-all-linux-static:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --enable-debug --static --disable-system --disable-glusterfs --disable-libssh
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
- make --output-sync -j`nproc` check-tcg V=1
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
- make --output-sync -j`nproc --ignore=40` check-tcg V=1
|
||||
|
||||
ubuntu-20.04-aarch64-all:
|
||||
needs: []
|
||||
@ -38,8 +38,8 @@ ubuntu-20.04-aarch64-all:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|
||||
ubuntu-20.04-aarch64-alldbg:
|
||||
needs: []
|
||||
@ -55,8 +55,8 @@ ubuntu-20.04-aarch64-alldbg:
|
||||
- cd build
|
||||
- ../configure --enable-debug --disable-libssh
|
||||
- make clean
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|
||||
ubuntu-20.04-aarch64-clang:
|
||||
needs: []
|
||||
@ -75,8 +75,8 @@ ubuntu-20.04-aarch64-clang:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --cc=clang-10 --cxx=clang++-10 --enable-sanitizers
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|
||||
ubuntu-20.04-aarch64-tci:
|
||||
needs: []
|
||||
@ -95,7 +95,7 @@ ubuntu-20.04-aarch64-tci:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --enable-tcg-interpreter
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
|
||||
ubuntu-20.04-aarch64-notcg:
|
||||
needs: []
|
||||
@ -114,5 +114,5 @@ ubuntu-20.04-aarch64-notcg:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- ../configure --disable-libssh --disable-tcg
|
||||
- make --output-sync -j`nproc`
|
||||
- make --output-sync -j`nproc` check V=1
|
||||
- make --output-sync -j`nproc --ignore=40`
|
||||
- make --output-sync -j`nproc --ignore=40` check V=1
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -64,3 +64,6 @@
|
||||
[submodule "tests/lcitool/libvirt-ci"]
|
||||
path = tests/lcitool/libvirt-ci
|
||||
url = https://gitlab.com/libvirt/libvirt-ci.git
|
||||
[submodule "subprojects/libvfio-user"]
|
||||
path = subprojects/libvfio-user
|
||||
url = https://gitlab.com/qemu-project/libvfio-user.git
|
||||
|
@ -42,3 +42,7 @@ config MULTIPROCESS_ALLOWED
|
||||
config FUZZ
|
||||
bool
|
||||
select SPARSE_MEM
|
||||
|
||||
config VFIO_USER_SERVER_ALLOWED
|
||||
bool
|
||||
imply VFIO_USER_SERVER
|
||||
|
38
MAINTAINERS
38
MAINTAINERS
@ -246,7 +246,8 @@ F: docs/system/cpu-models-mips.rst.inc
|
||||
F: tests/tcg/mips/
|
||||
|
||||
MIPS TCG CPUs (nanoMIPS ISA)
|
||||
S: Orphan
|
||||
M: Stefan Pejic <stefan.pejic@syrmia.com>
|
||||
S: Maintained
|
||||
F: disas/nanomips.*
|
||||
F: target/mips/tcg/*nanomips*
|
||||
|
||||
@ -2426,6 +2427,14 @@ F: hw/intc/s390_flic*.c
|
||||
F: include/hw/s390x/s390_flic.h
|
||||
L: qemu-s390x@nongnu.org
|
||||
|
||||
CanoKey
|
||||
M: Hongren (Zenithal) Zheng <i@zenithal.me>
|
||||
S: Maintained
|
||||
R: Canokeys.org <contact@canokeys.org>
|
||||
F: hw/usb/canokey.c
|
||||
F: hw/usb/canokey.h
|
||||
F: docs/system/devices/canokey.rst
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Overall Audio backends
|
||||
@ -2537,7 +2546,7 @@ F: scsi/*
|
||||
|
||||
Block Jobs
|
||||
M: John Snow <jsnow@redhat.com>
|
||||
M: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: blockjob.c
|
||||
@ -2562,7 +2571,7 @@ F: block/aio_task.c
|
||||
F: util/qemu-co-shared-resource.c
|
||||
F: include/qemu/co-shared-resource.h
|
||||
T: git https://gitlab.com/jsnow/qemu.git jobs
|
||||
T: git https://src.openvz.org/scm/~vsementsov/qemu.git jobs
|
||||
T: git https://gitlab.com/vsementsov/qemu.git block
|
||||
|
||||
Block QAPI, monitor, command line
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
@ -2583,7 +2592,7 @@ F: include/hw/cxl/
|
||||
|
||||
Dirty Bitmaps
|
||||
M: Eric Blake <eblake@redhat.com>
|
||||
M: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
R: John Snow <jsnow@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
@ -2597,6 +2606,7 @@ F: util/hbitmap.c
|
||||
F: tests/unit/test-hbitmap.c
|
||||
F: docs/interop/bitmaps.rst
|
||||
T: git https://repo.or.cz/qemu/ericb.git bitmaps
|
||||
T: git https://gitlab.com/vsementsov/qemu.git block
|
||||
|
||||
Character device backends
|
||||
M: Marc-André Lureau <marcandre.lureau@redhat.com>
|
||||
@ -2807,16 +2817,17 @@ F: scripts/*.py
|
||||
F: tests/*.py
|
||||
|
||||
Benchmark util
|
||||
M: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
S: Maintained
|
||||
F: scripts/simplebench/
|
||||
T: git https://src.openvz.org/scm/~vsementsov/qemu.git simplebench
|
||||
T: git https://gitlab.com/vsementsov/qemu.git simplebench
|
||||
|
||||
Transactions helper
|
||||
M: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
S: Maintained
|
||||
F: include/qemu/transactions.h
|
||||
F: util/transactions.c
|
||||
T: git https://gitlab.com/vsementsov/qemu.git block
|
||||
|
||||
QAPI
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
@ -3393,7 +3404,7 @@ F: block/iscsi-opts.c
|
||||
|
||||
Network Block Device (NBD)
|
||||
M: Eric Blake <eblake@redhat.com>
|
||||
M: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Maintained
|
||||
F: block/nbd*
|
||||
@ -3405,7 +3416,7 @@ F: docs/interop/nbd.txt
|
||||
F: docs/tools/qemu-nbd.rst
|
||||
F: tests/qemu-iotests/tests/*nbd*
|
||||
T: git https://repo.or.cz/qemu/ericb.git nbd
|
||||
T: git https://src.openvz.org/scm/~vsementsov/qemu.git nbd
|
||||
T: git https://gitlab.com/vsementsov/qemu.git block
|
||||
|
||||
NFS
|
||||
M: Peter Lieven <pl@kamp.de>
|
||||
@ -3490,13 +3501,13 @@ F: block/dmg.c
|
||||
parallels
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
M: Denis V. Lunev <den@openvz.org>
|
||||
M: Vladimir Sementsov-Ogievskiy <v.sementsov-og@mail.ru>
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/parallels.c
|
||||
F: block/parallels-ext.c
|
||||
F: docs/interop/parallels.txt
|
||||
T: git https://src.openvz.org/scm/~vsementsov/qemu.git parallels
|
||||
T: git https://gitlab.com/vsementsov/qemu.git block
|
||||
|
||||
qed
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
@ -3631,6 +3642,11 @@ F: hw/remote/proxy-memory-listener.c
|
||||
F: include/hw/remote/proxy-memory-listener.h
|
||||
F: hw/remote/iohub.c
|
||||
F: include/hw/remote/iohub.h
|
||||
F: subprojects/libvfio-user
|
||||
F: hw/remote/vfio-user-obj.c
|
||||
F: include/hw/remote/vfio-user-obj.h
|
||||
F: hw/remote/iommu.c
|
||||
F: include/hw/remote/iommu.h
|
||||
|
||||
EBPF:
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
|
@ -1088,7 +1088,7 @@ void tcg_exec_unrealizefn(CPUState *cpu)
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
|
||||
void dump_drift_info(GString *buf)
|
||||
static void dump_drift_info(GString *buf)
|
||||
{
|
||||
if (!icount_enabled()) {
|
||||
return;
|
||||
@ -1131,7 +1131,7 @@ HumanReadableText *qmp_x_query_opcount(Error **errp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dump_opcount_info(buf);
|
||||
tcg_dump_op_count(buf);
|
||||
|
||||
return human_readable_text_from_str(buf);
|
||||
}
|
||||
|
@ -2931,11 +2931,6 @@ void dump_exec_info(GString *buf)
|
||||
tcg_dump_info(buf);
|
||||
}
|
||||
|
||||
void dump_opcount_info(GString *buf)
|
||||
{
|
||||
tcg_dump_op_count(buf);
|
||||
}
|
||||
|
||||
#else /* CONFIG_USER_ONLY */
|
||||
|
||||
void cpu_interrupt(CPUState *cpu, int mask)
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
/* io_uring ring size */
|
||||
#define MAX_ENTRIES 128
|
||||
|
||||
@ -434,8 +435,17 @@ LuringState *luring_init(Error **errp)
|
||||
}
|
||||
|
||||
ioq_init(&s->io_q);
|
||||
return s;
|
||||
#ifdef CONFIG_LIBURING_REGISTER_RING_FD
|
||||
if (io_uring_register_ring_fd(&s->ring) < 0) {
|
||||
/*
|
||||
* Only warn about this error: we will fallback to the non-optimized
|
||||
* io_uring operations.
|
||||
*/
|
||||
warn_report("failed to register linux io_uring ring file descriptor");
|
||||
}
|
||||
#endif
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void luring_cleanup(LuringState *s)
|
||||
|
@ -363,8 +363,16 @@ void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s,
|
||||
uint64_t dev_max_batch)
|
||||
{
|
||||
assert(s->io_q.plugged);
|
||||
s->io_q.plugged--;
|
||||
|
||||
/*
|
||||
* Why max batch checking is performed here:
|
||||
* Another BDS may have queued requests with a higher dev_max_batch and
|
||||
* therefore in_queue could now exceed our dev_max_batch. Re-check the max
|
||||
* batch so we can honor our device's dev_max_batch.
|
||||
*/
|
||||
if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch) ||
|
||||
(--s->io_q.plugged == 0 &&
|
||||
(!s->io_q.plugged &&
|
||||
!s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending))) {
|
||||
ioq_submit(s);
|
||||
}
|
||||
|
@ -27,4 +27,167 @@ extern struct iovec *lock_iovec(int type, abi_ulong target_addr, int count,
|
||||
extern void unlock_iovec(struct iovec *vec, abi_ulong target_addr, int count,
|
||||
int copy);
|
||||
|
||||
ssize_t safe_read(int fd, void *buf, size_t nbytes);
|
||||
ssize_t safe_pread(int fd, void *buf, size_t nbytes, off_t offset);
|
||||
ssize_t safe_readv(int fd, const struct iovec *iov, int iovcnt);
|
||||
ssize_t safe_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
|
||||
|
||||
ssize_t safe_write(int fd, void *buf, size_t nbytes);
|
||||
ssize_t safe_pwrite(int fd, void *buf, size_t nbytes, off_t offset);
|
||||
ssize_t safe_writev(int fd, const struct iovec *iov, int iovcnt);
|
||||
ssize_t safe_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
|
||||
|
||||
/* read(2) */
|
||||
static abi_long do_bsd_read(abi_long arg1, abi_long arg2, abi_long arg3)
|
||||
{
|
||||
abi_long ret;
|
||||
void *p;
|
||||
|
||||
p = lock_user(VERIFY_WRITE, arg2, arg3, 0);
|
||||
if (p == NULL) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
ret = get_errno(safe_read(arg1, p, arg3));
|
||||
unlock_user(p, arg2, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* pread(2) */
|
||||
static abi_long do_bsd_pread(void *cpu_env, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
abi_long ret;
|
||||
void *p;
|
||||
|
||||
p = lock_user(VERIFY_WRITE, arg2, arg3, 0);
|
||||
if (p == NULL) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
if (regpairs_aligned(cpu_env) != 0) {
|
||||
arg4 = arg5;
|
||||
arg5 = arg6;
|
||||
}
|
||||
ret = get_errno(safe_pread(arg1, p, arg3, target_arg64(arg4, arg5)));
|
||||
unlock_user(p, arg2, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* readv(2) */
|
||||
static abi_long do_bsd_readv(abi_long arg1, abi_long arg2, abi_long arg3)
|
||||
{
|
||||
abi_long ret;
|
||||
struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0);
|
||||
|
||||
if (vec != NULL) {
|
||||
ret = get_errno(safe_readv(arg1, vec, arg3));
|
||||
unlock_iovec(vec, arg2, arg3, 1);
|
||||
} else {
|
||||
ret = -host_to_target_errno(errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* preadv(2) */
|
||||
static abi_long do_bsd_preadv(void *cpu_env, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
abi_long ret;
|
||||
struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 1);
|
||||
|
||||
if (vec != NULL) {
|
||||
if (regpairs_aligned(cpu_env) != 0) {
|
||||
arg4 = arg5;
|
||||
arg5 = arg6;
|
||||
}
|
||||
ret = get_errno(safe_preadv(arg1, vec, arg3, target_arg64(arg4, arg5)));
|
||||
unlock_iovec(vec, arg2, arg3, 0);
|
||||
} else {
|
||||
ret = -host_to_target_errno(errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* write(2) */
|
||||
static abi_long do_bsd_write(abi_long arg1, abi_long arg2, abi_long arg3)
|
||||
{
|
||||
abi_long nbytes, ret;
|
||||
void *p;
|
||||
|
||||
/* nbytes < 0 implies that it was larger than SIZE_MAX. */
|
||||
nbytes = arg3;
|
||||
if (nbytes < 0) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
p = lock_user(VERIFY_READ, arg2, nbytes, 1);
|
||||
if (p == NULL) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
ret = get_errno(safe_write(arg1, p, arg3));
|
||||
unlock_user(p, arg2, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* pwrite(2) */
|
||||
static abi_long do_bsd_pwrite(void *cpu_env, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
abi_long ret;
|
||||
void *p;
|
||||
|
||||
p = lock_user(VERIFY_READ, arg2, arg3, 1);
|
||||
if (p == NULL) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
if (regpairs_aligned(cpu_env) != 0) {
|
||||
arg4 = arg5;
|
||||
arg5 = arg6;
|
||||
}
|
||||
ret = get_errno(safe_pwrite(arg1, p, arg3, target_arg64(arg4, arg5)));
|
||||
unlock_user(p, arg2, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* writev(2) */
|
||||
static abi_long do_bsd_writev(abi_long arg1, abi_long arg2, abi_long arg3)
|
||||
{
|
||||
abi_long ret;
|
||||
struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
|
||||
|
||||
if (vec != NULL) {
|
||||
ret = get_errno(safe_writev(arg1, vec, arg3));
|
||||
unlock_iovec(vec, arg2, arg3, 0);
|
||||
} else {
|
||||
ret = -host_to_target_errno(errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* pwritev(2) */
|
||||
static abi_long do_bsd_pwritev(void *cpu_env, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6)
|
||||
{
|
||||
abi_long ret;
|
||||
struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
|
||||
|
||||
if (vec != NULL) {
|
||||
if (regpairs_aligned(cpu_env) != 0) {
|
||||
arg4 = arg5;
|
||||
arg5 = arg6;
|
||||
}
|
||||
ret = get_errno(safe_pwritev(arg1, vec, arg3, target_arg64(arg4, arg5)));
|
||||
unlock_iovec(vec, arg2, arg3, 0);
|
||||
} else {
|
||||
ret = -host_to_target_errno(errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* BSD_FILE_H */
|
||||
|
42
bsd-user/bsd-proc.h
Normal file
42
bsd-user/bsd-proc.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* process related system call shims and definitions
|
||||
*
|
||||
* Copyright (c) 2013-2014 Stacey D. Son
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef BSD_PROC_H_
|
||||
#define BSD_PROC_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* exit(2) */
|
||||
static inline abi_long do_bsd_exit(void *cpu_env, abi_long arg1)
|
||||
{
|
||||
#ifdef TARGET_GPROF
|
||||
_mcleanup();
|
||||
#endif
|
||||
gdb_exit(arg1);
|
||||
qemu_plugin_user_exit();
|
||||
_exit(arg1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !BSD_PROC_H_ */
|
@ -41,6 +41,22 @@
|
||||
#include "user/syscall-trace.h"
|
||||
|
||||
#include "bsd-file.h"
|
||||
#include "bsd-proc.h"
|
||||
|
||||
/* I/O */
|
||||
safe_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes);
|
||||
safe_syscall4(ssize_t, pread, int, fd, void *, buf, size_t, nbytes, off_t,
|
||||
offset);
|
||||
safe_syscall3(ssize_t, readv, int, fd, const struct iovec *, iov, int, iovcnt);
|
||||
safe_syscall4(ssize_t, preadv, int, fd, const struct iovec *, iov, int, iovcnt,
|
||||
off_t, offset);
|
||||
|
||||
safe_syscall3(ssize_t, write, int, fd, void *, buf, size_t, nbytes);
|
||||
safe_syscall4(ssize_t, pwrite, int, fd, void *, buf, size_t, nbytes, off_t,
|
||||
offset);
|
||||
safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt);
|
||||
safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt,
|
||||
off_t, offset);
|
||||
|
||||
void target_set_brk(abi_ulong new_brk)
|
||||
{
|
||||
@ -74,16 +90,209 @@ bool is_error(abi_long ret)
|
||||
}
|
||||
|
||||
/*
|
||||
* do_syscall() should always have a single exit point at the end so that
|
||||
* actions, such as logging of syscall results, can be performed. All errnos
|
||||
* that do_syscall() returns must be -TARGET_<errcode>.
|
||||
* Unlocks a iovec. Unlike unlock_iovec, it assumes the tvec array itself is
|
||||
* already locked from target_addr. It will be unlocked as well as all the iovec
|
||||
* elements.
|
||||
*/
|
||||
static void helper_unlock_iovec(struct target_iovec *target_vec,
|
||||
abi_ulong target_addr, struct iovec *vec,
|
||||
int count, int copy)
|
||||
{
|
||||
for (int i = 0; i < count; i++) {
|
||||
abi_ulong base = tswapal(target_vec[i].iov_base);
|
||||
|
||||
if (vec[i].iov_base) {
|
||||
unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
|
||||
}
|
||||
}
|
||||
unlock_user(target_vec, target_addr, 0);
|
||||
}
|
||||
|
||||
struct iovec *lock_iovec(int type, abi_ulong target_addr,
|
||||
int count, int copy)
|
||||
{
|
||||
struct target_iovec *target_vec;
|
||||
struct iovec *vec;
|
||||
abi_ulong total_len, max_len;
|
||||
int i;
|
||||
int err = 0;
|
||||
|
||||
if (count == 0) {
|
||||
errno = 0;
|
||||
return NULL;
|
||||
}
|
||||
if (count < 0 || count > IOV_MAX) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vec = g_try_new0(struct iovec, count);
|
||||
if (vec == NULL) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
target_vec = lock_user(VERIFY_READ, target_addr,
|
||||
count * sizeof(struct target_iovec), 1);
|
||||
if (target_vec == NULL) {
|
||||
err = EFAULT;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
max_len = 0x7fffffff & MIN(TARGET_PAGE_MASK, PAGE_MASK);
|
||||
total_len = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
abi_ulong base = tswapal(target_vec[i].iov_base);
|
||||
abi_long len = tswapal(target_vec[i].iov_len);
|
||||
|
||||
if (len < 0) {
|
||||
err = EINVAL;
|
||||
goto fail;
|
||||
} else if (len == 0) {
|
||||
/* Zero length pointer is ignored. */
|
||||
vec[i].iov_base = 0;
|
||||
} else {
|
||||
vec[i].iov_base = lock_user(type, base, len, copy);
|
||||
/*
|
||||
* If the first buffer pointer is bad, this is a fault. But
|
||||
* subsequent bad buffers will result in a partial write; this is
|
||||
* realized by filling the vector with null pointers and zero
|
||||
* lengths.
|
||||
*/
|
||||
if (!vec[i].iov_base) {
|
||||
if (i == 0) {
|
||||
err = EFAULT;
|
||||
goto fail;
|
||||
} else {
|
||||
/*
|
||||
* Fail all the subsequent addresses, they are already
|
||||
* zero'd.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (len > max_len - total_len) {
|
||||
len = max_len - total_len;
|
||||
}
|
||||
}
|
||||
vec[i].iov_len = len;
|
||||
total_len += len;
|
||||
}
|
||||
out:
|
||||
unlock_user(target_vec, target_addr, 0);
|
||||
return vec;
|
||||
|
||||
fail:
|
||||
helper_unlock_iovec(target_vec, target_addr, vec, i, copy);
|
||||
fail2:
|
||||
g_free(vec);
|
||||
errno = err;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void unlock_iovec(struct iovec *vec, abi_ulong target_addr,
|
||||
int count, int copy)
|
||||
{
|
||||
struct target_iovec *target_vec;
|
||||
|
||||
target_vec = lock_user(VERIFY_READ, target_addr,
|
||||
count * sizeof(struct target_iovec), 1);
|
||||
if (target_vec) {
|
||||
helper_unlock_iovec(target_vec, target_addr, vec, count, copy);
|
||||
}
|
||||
|
||||
g_free(vec);
|
||||
}
|
||||
|
||||
/*
|
||||
* All errnos that freebsd_syscall() returns must be -TARGET_<errcode>.
|
||||
*/
|
||||
static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4,
|
||||
abi_long arg5, abi_long arg6, abi_long arg7,
|
||||
abi_long arg8)
|
||||
{
|
||||
abi_long ret;
|
||||
|
||||
switch (num) {
|
||||
/*
|
||||
* process system calls
|
||||
*/
|
||||
case TARGET_FREEBSD_NR_exit: /* exit(2) */
|
||||
ret = do_bsd_exit(cpu_env, arg1);
|
||||
break;
|
||||
|
||||
/*
|
||||
* File system calls.
|
||||
*/
|
||||
case TARGET_FREEBSD_NR_read: /* read(2) */
|
||||
ret = do_bsd_read(arg1, arg2, arg3);
|
||||
break;
|
||||
|
||||
case TARGET_FREEBSD_NR_pread: /* pread(2) */
|
||||
ret = do_bsd_pread(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
break;
|
||||
|
||||
case TARGET_FREEBSD_NR_readv: /* readv(2) */
|
||||
ret = do_bsd_readv(arg1, arg2, arg3);
|
||||
break;
|
||||
|
||||
case TARGET_FREEBSD_NR_preadv: /* preadv(2) */
|
||||
ret = do_bsd_preadv(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
|
||||
case TARGET_FREEBSD_NR_write: /* write(2) */
|
||||
ret = do_bsd_write(arg1, arg2, arg3);
|
||||
break;
|
||||
|
||||
case TARGET_FREEBSD_NR_pwrite: /* pwrite(2) */
|
||||
ret = do_bsd_pwrite(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
break;
|
||||
|
||||
case TARGET_FREEBSD_NR_writev: /* writev(2) */
|
||||
ret = do_bsd_writev(arg1, arg2, arg3);
|
||||
break;
|
||||
|
||||
case TARGET_FREEBSD_NR_pwritev: /* pwritev(2) */
|
||||
ret = do_bsd_pwritev(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
|
||||
ret = -TARGET_ENOSYS;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* do_freebsd_syscall() should always have a single exit point at the end so
|
||||
* that actions, such as logging of syscall results, can be performed. This
|
||||
* as a wrapper around freebsd_syscall() so that actually happens. Since
|
||||
* that is a singleton, modern compilers will inline it anyway...
|
||||
*/
|
||||
abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3, abi_long arg4,
|
||||
abi_long arg5, abi_long arg6, abi_long arg7,
|
||||
abi_long arg8)
|
||||
{
|
||||
return 0;
|
||||
CPUState *cpu = env_cpu(cpu_env);
|
||||
int ret;
|
||||
|
||||
trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
|
||||
if (do_strace) {
|
||||
print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
|
||||
}
|
||||
|
||||
ret = freebsd_syscall(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6,
|
||||
arg7, arg8);
|
||||
if (do_strace) {
|
||||
print_freebsd_syscall_ret(num, ret);
|
||||
}
|
||||
trace_guest_user_syscall_ret(cpu, num, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void syscall_init(void)
|
||||
|
22
configure
vendored
22
configure
vendored
@ -315,6 +315,7 @@ meson_args=""
|
||||
ninja=""
|
||||
bindir="bin"
|
||||
skip_meson=no
|
||||
vfio_user_server="disabled"
|
||||
|
||||
# The following Meson options are handled manually (still they
|
||||
# are included in the automatically generated help message)
|
||||
@ -911,6 +912,10 @@ for opt do
|
||||
;;
|
||||
--disable-blobs) meson_option_parse --disable-install-blobs ""
|
||||
;;
|
||||
--enable-vfio-user-server) vfio_user_server="enabled"
|
||||
;;
|
||||
--disable-vfio-user-server) vfio_user_server="disabled"
|
||||
;;
|
||||
--enable-tcmalloc) meson_option_parse --enable-malloc=tcmalloc tcmalloc
|
||||
;;
|
||||
--enable-jemalloc) meson_option_parse --enable-malloc=jemalloc jemalloc
|
||||
@ -2041,7 +2046,6 @@ probe_target_compiler() {
|
||||
container_cross_prefix=x86_64-linux-gnu-
|
||||
;;
|
||||
xtensa*)
|
||||
# FIXME: xtensa-linux-user?
|
||||
container_hosts=x86_64
|
||||
container_image=debian-xtensa-cross
|
||||
|
||||
@ -2150,6 +2154,17 @@ write_container_target_makefile() {
|
||||
|
||||
|
||||
|
||||
##########################################
|
||||
# check for vfio_user_server
|
||||
|
||||
case "$vfio_user_server" in
|
||||
enabled )
|
||||
if test "$git_submodules_action" != "ignore"; then
|
||||
git_submodules="${git_submodules} subprojects/libvfio-user"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
##########################################
|
||||
# End of CC checks
|
||||
# After here, no more $cc or $ld runs
|
||||
@ -2505,6 +2520,10 @@ for target in $target_list; do
|
||||
echo "# Automatically generated by configure - do not modify" > $config_target_mak
|
||||
echo "TARGET_NAME=$arch" >> $config_target_mak
|
||||
case $target in
|
||||
xtensa*-linux-user)
|
||||
# the toolchain is not complete with headers, only build softmmu tests
|
||||
continue
|
||||
;;
|
||||
*-softmmu)
|
||||
test -f $source_path/tests/tcg/$arch/Makefile.softmmu-target || continue
|
||||
qemu="qemu-system-$arch"
|
||||
@ -2693,6 +2712,7 @@ if test "$skip_meson" = no; then
|
||||
test "$slirp" != auto && meson_option_add "-Dslirp=$slirp"
|
||||
test "$smbd" != '' && meson_option_add "-Dsmbd=$smbd"
|
||||
test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg"
|
||||
test "$vfio_user_server" != auto && meson_option_add "-Dvfio_user_server=$vfio_user_server"
|
||||
run_meson() {
|
||||
NINJA=$ninja $meson setup --prefix "$prefix" "$@" $cross_arg "$PWD" "$source_path"
|
||||
}
|
||||
|
@ -213,17 +213,6 @@ MIPS ``Trap-and-Emul`` KVM support (since 6.0)
|
||||
The MIPS ``Trap-and-Emul`` KVM host and guest support has been removed
|
||||
from Linux upstream kernel, declare it deprecated.
|
||||
|
||||
System emulator CPUS
|
||||
--------------------
|
||||
|
||||
MIPS ``I7200`` CPU Model (since 5.2)
|
||||
''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The ``I7200`` guest CPU relies on the nanoMIPS ISA, which is deprecated
|
||||
(the ISA has never been upstreamed to a compiler toolchain). Therefore
|
||||
this CPU is also deprecated.
|
||||
|
||||
|
||||
QEMU API (QAPI) events
|
||||
----------------------
|
||||
|
||||
@ -337,16 +326,6 @@ The above, converted to the current supported format::
|
||||
|
||||
json:{"file.driver":"rbd", "file.pool":"rbd", "file.image":"name"}
|
||||
|
||||
linux-user mode CPUs
|
||||
--------------------
|
||||
|
||||
MIPS ``I7200`` CPU (since 5.2)
|
||||
''''''''''''''''''''''''''''''
|
||||
|
||||
The ``I7200`` guest CPU relies on the nanoMIPS ISA, which is deprecated
|
||||
(the ISA has never been upstreamed to a compiler toolchain). Therefore
|
||||
this CPU is also deprecated.
|
||||
|
||||
Backwards compatibility
|
||||
-----------------------
|
||||
|
||||
@ -376,15 +355,6 @@ versions, aliases will point to newer CPU model versions
|
||||
depending on the machine type, so management software must
|
||||
resolve CPU model aliases before starting a virtual machine.
|
||||
|
||||
Guest Emulator ISAs
|
||||
-------------------
|
||||
|
||||
nanoMIPS ISA
|
||||
''''''''''''
|
||||
|
||||
The ``nanoMIPS`` ISA has never been upstreamed to any compiler toolchain.
|
||||
As it is hard to generate binaries for it, declare it deprecated.
|
||||
|
||||
Tools
|
||||
-----
|
||||
|
||||
|
@ -18,9 +18,9 @@ one-shot fix, the bare minimum we ask is that:
|
||||
<http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/SubmittingPatches?id=f6f94e2ab1b33f0082ac22d71f66385a60d8157f#n297>`__
|
||||
policy.) ``git commit -s`` or ``git format-patch -s`` will add one.
|
||||
- All contributions to QEMU must be **sent as patches** to the
|
||||
qemu-devel `mailing list <MailingLists>`__. Patch contributions
|
||||
should not be posted on the bug tracker, posted on forums, or
|
||||
externally hosted and linked to. (We have other mailing lists too,
|
||||
qemu-devel `mailing list <https://wiki.qemu.org/Contribute/MailingLists>`__.
|
||||
Patch contributions should not be posted on the bug tracker, posted on
|
||||
forums, or externally hosted and linked to. (We have other mailing lists too,
|
||||
but all patches must go to qemu-devel, possibly with a Cc: to another
|
||||
list.) ``git send-email`` (`step-by-step setup
|
||||
guide <https://git-send-email.io/>`__ and `hints and
|
||||
|
@ -92,3 +92,4 @@ Emulated Devices
|
||||
devices/vhost-user.rst
|
||||
devices/virtio-pmem.rst
|
||||
devices/vhost-user-rng.rst
|
||||
devices/canokey.rst
|
||||
|
168
docs/system/devices/canokey.rst
Normal file
168
docs/system/devices/canokey.rst
Normal file
@ -0,0 +1,168 @@
|
||||
.. _canokey:
|
||||
|
||||
CanoKey QEMU
|
||||
------------
|
||||
|
||||
CanoKey [1]_ is an open-source secure key with supports of
|
||||
|
||||
* U2F / FIDO2 with Ed25519 and HMAC-secret
|
||||
* OpenPGP Card V3.4 with RSA4096, Ed25519 and more [2]_
|
||||
* PIV (NIST SP 800-73-4)
|
||||
* HOTP / TOTP
|
||||
* NDEF
|
||||
|
||||
All these platform-independent features are in canokey-core [3]_.
|
||||
|
||||
For different platforms, CanoKey has different implementations,
|
||||
including both hardware implementions and virtual cards:
|
||||
|
||||
* CanoKey STM32 [4]_
|
||||
* CanoKey Pigeon [5]_
|
||||
* (virt-card) CanoKey USB/IP
|
||||
* (virt-card) CanoKey FunctionFS
|
||||
|
||||
In QEMU, yet another CanoKey virt-card is implemented.
|
||||
CanoKey QEMU exposes itself as a USB device to the guest OS.
|
||||
|
||||
With the same software configuration as a hardware key,
|
||||
the guest OS can use all the functionalities of a secure key as if
|
||||
there was actually an hardware key plugged in.
|
||||
|
||||
CanoKey QEMU provides much convenience for debuging:
|
||||
|
||||
* libcanokey-qemu supports debuging output thus developers can
|
||||
inspect what happens inside a secure key
|
||||
* CanoKey QEMU supports trace event thus event
|
||||
* QEMU USB stack supports pcap thus USB packet between the guest
|
||||
and key can be captured and analysed
|
||||
|
||||
Then for developers:
|
||||
|
||||
* For developers on software with secure key support (e.g. FIDO2, OpenPGP),
|
||||
they can see what happens inside the secure key
|
||||
* For secure key developers, USB packets between guest OS and CanoKey
|
||||
can be easily captured and analysed
|
||||
|
||||
Also since this is a virtual card, it can be easily used in CI for testing
|
||||
on code coping with secure key.
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
libcanokey-qemu is required to use CanoKey QEMU.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
git clone https://github.com/canokeys/canokey-qemu
|
||||
mkdir canokey-qemu/build
|
||||
pushd canokey-qemu/build
|
||||
|
||||
If you want to install libcanokey-qemu in a different place,
|
||||
add ``-DCMAKE_INSTALL_PREFIX=/path/to/your/place`` to cmake below.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cmake ..
|
||||
make
|
||||
make install # may need sudo
|
||||
popd
|
||||
|
||||
Then configuring and building:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
# depending on your env, lib/pkgconfig can be lib64/pkgconfig
|
||||
export PKG_CONFIG_PATH=/path/to/your/place/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
./configure --enable-canokey && make
|
||||
|
||||
Using CanoKey QEMU
|
||||
==================
|
||||
|
||||
CanoKey QEMU stores all its data on a file of the host specified by the argument
|
||||
when invoking qemu.
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system| -usb -device canokey,file=$HOME/.canokey-file
|
||||
|
||||
Note: you should keep this file carefully as it may contain your private key!
|
||||
|
||||
The first time when the file is used, it is created and initialized by CanoKey,
|
||||
afterwards CanoKey QEMU would just read this file.
|
||||
|
||||
After the guest OS boots, you can check that there is a USB device.
|
||||
|
||||
For example, If the guest OS is an Linux machine. You may invoke lsusb
|
||||
and find CanoKey QEMU there:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ lsusb
|
||||
Bus 001 Device 002: ID 20a0:42d4 Clay Logic CanoKey QEMU
|
||||
|
||||
You may setup the key as guided in [6]_. The console for the key is at [7]_.
|
||||
|
||||
Debuging
|
||||
========
|
||||
|
||||
CanoKey QEMU consists of two parts, ``libcanokey-qemu.so`` and ``canokey.c``,
|
||||
the latter of which resides in QEMU. The former provides core functionality
|
||||
of a secure key while the latter provides platform-dependent functions:
|
||||
USB packet handling.
|
||||
|
||||
If you want to trace what happens inside the secure key, when compiling
|
||||
libcanokey-qemu, you should add ``-DQEMU_DEBUG_OUTPUT=ON`` in cmake command
|
||||
line:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cmake .. -DQEMU_DEBUG_OUTPUT=ON
|
||||
|
||||
If you want to trace events happened in canokey.c, use
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system| --trace "canokey_*" \\
|
||||
-usb -device canokey,file=$HOME/.canokey-file
|
||||
|
||||
If you want to capture USB packets between the guest and the host, you can:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system| -usb -device canokey,file=$HOME/.canokey-file,pcap=key.pcap
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
Currently libcanokey-qemu.so has dozens of global variables as it was originally
|
||||
designed for embedded systems. Thus one qemu instance can not have
|
||||
multiple CanoKey QEMU running, namely you can not
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system| -usb -device canokey,file=$HOME/.canokey-file \\
|
||||
-device canokey,file=$HOME/.canokey-file2
|
||||
|
||||
Also, there is no lock on canokey-file, thus two CanoKey QEMU instance
|
||||
can not read one canokey-file at the same time.
|
||||
|
||||
Another limitation is that this device is not compatible with ``qemu-xhci``,
|
||||
in that this device would hang when there are FIDO2 packets (traffic on
|
||||
interrupt endpoints). If you do not use FIDO2 then it works as intended,
|
||||
but for full functionality you should use old uhci/ehci bus and attach canokey
|
||||
to it, for example
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system| -device piix3-usb-uhci,id=uhci -device canokey,bus=uhci.0
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] `<https://canokeys.org>`_
|
||||
.. [2] `<https://docs.canokeys.org/userguide/openpgp/#supported-algorithm>`_
|
||||
.. [3] `<https://github.com/canokeys/canokey-core>`_
|
||||
.. [4] `<https://github.com/canokeys/canokey-stm32>`_
|
||||
.. [5] `<https://github.com/canokeys/canokey-pigeon>`_
|
||||
.. [6] `<https://docs.canokeys.org/>`_
|
||||
.. [7] `<https://console.canokeys.org/>`_
|
@ -251,7 +251,7 @@ A very simple setup with just one directly attached CXL Type 3 device::
|
||||
-device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \
|
||||
-device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \
|
||||
-device cxl-type3,bus=root_port13,memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \
|
||||
-cxl-fixed-memory-window targets.0=cxl.1,size=4G
|
||||
-M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G
|
||||
|
||||
A setup suitable for 4 way interleave. Only one fixed window provided, to enable 2 way
|
||||
interleave across 2 CXL host bridges. Each host bridge has 2 CXL Root Ports, with
|
||||
@ -277,7 +277,7 @@ the CXL Type3 device directly attached (no switches).::
|
||||
-device cxl-type3,bus=root_port15,memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem2 \
|
||||
-device cxl-rp,port=1,bus=cxl.2,id=root_port16,chassis=0,slot=6 \
|
||||
-device cxl-type3,bus=root_port16,memdev=cxl-mem4,lsa=cxl-lsa4,id=cxl-pmem3 \
|
||||
-cxl-fixed-memory-window targets.0=cxl.1,targets.1=cxl.2,size=4G,interleave-granularity=8k
|
||||
-M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.targets.1=cxl.2,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=8k
|
||||
|
||||
Kernel Configuration Options
|
||||
----------------------------
|
||||
|
@ -199,6 +199,10 @@ option or the ``device_add`` monitor command. Available devices are:
|
||||
``u2f-{emulated,passthru}``
|
||||
Universal Second Factor device
|
||||
|
||||
``canokey``
|
||||
An Open-source Secure Key implementing FIDO2, OpenPGP, PIV and more.
|
||||
For more information, see :ref:`canokey`.
|
||||
|
||||
Physical port addressing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
14
gdbstub.c
14
gdbstub.c
@ -443,6 +443,15 @@ static int get_char(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return true if there is a GDB currently connected to the stub
|
||||
* and attached to a CPU
|
||||
*/
|
||||
static bool gdb_attached(void)
|
||||
{
|
||||
return gdbserver_state.init && gdbserver_state.c_cpu;
|
||||
}
|
||||
|
||||
static enum {
|
||||
GDB_SYS_UNKNOWN,
|
||||
GDB_SYS_ENABLED,
|
||||
@ -464,8 +473,7 @@ int use_gdb_syscalls(void)
|
||||
/* -semihosting-config target=auto */
|
||||
/* On the first call check if gdb is connected and remember. */
|
||||
if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
|
||||
gdb_syscall_mode = gdbserver_state.init ?
|
||||
GDB_SYS_ENABLED : GDB_SYS_DISABLED;
|
||||
gdb_syscall_mode = gdb_attached() ? GDB_SYS_ENABLED : GDB_SYS_DISABLED;
|
||||
}
|
||||
return gdb_syscall_mode == GDB_SYS_ENABLED;
|
||||
}
|
||||
@ -2950,7 +2958,7 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
|
||||
target_ulong addr;
|
||||
uint64_t i64;
|
||||
|
||||
if (!gdbserver_state.init) {
|
||||
if (!gdb_attached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/acpi/acpi_dev_interface.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
#include "qemu/module.h"
|
||||
|
||||
void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event)
|
||||
@ -18,8 +19,15 @@ static void register_types(void)
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(AcpiDeviceIfClass),
|
||||
};
|
||||
static const TypeInfo acpi_dev_aml_if_info = {
|
||||
.name = TYPE_ACPI_DEV_AML_IF,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(AcpiDevAmlIfClass),
|
||||
};
|
||||
|
||||
|
||||
type_register_static(&acpi_dev_if_info);
|
||||
type_register_static(&acpi_dev_aml_if_info);
|
||||
}
|
||||
|
||||
type_init(register_types)
|
||||
|
@ -65,9 +65,8 @@ static void cedt_build_chbs(GArray *table_data, PXBDev *cxl)
|
||||
* Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory
|
||||
* interleaving.
|
||||
*/
|
||||
static void cedt_build_cfmws(GArray *table_data, MachineState *ms)
|
||||
static void cedt_build_cfmws(GArray *table_data, CXLState *cxls)
|
||||
{
|
||||
CXLState *cxls = ms->cxl_devices_state;
|
||||
GList *it;
|
||||
|
||||
for (it = cxls->fixed_windows; it; it = it->next) {
|
||||
@ -129,9 +128,9 @@ static int cxl_foreach_pxb_hb(Object *obj, void *opaque)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cxl_build_cedt(MachineState *ms, GArray *table_offsets, GArray *table_data,
|
||||
void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
|
||||
BIOSLinker *linker, const char *oem_id,
|
||||
const char *oem_table_id)
|
||||
const char *oem_table_id, CXLState *cxl_state)
|
||||
{
|
||||
Aml *cedt;
|
||||
AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id,
|
||||
@ -144,7 +143,7 @@ void cxl_build_cedt(MachineState *ms, GArray *table_offsets, GArray *table_data,
|
||||
/* reserve space for CEDT header */
|
||||
|
||||
object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt);
|
||||
cedt_build_cfmws(cedt->buf, ms);
|
||||
cedt_build_cfmws(cedt->buf, cxl_state);
|
||||
|
||||
/* copy AML table into ACPI tables blob and patch header there */
|
||||
g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len);
|
||||
|
@ -10,6 +10,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/acpi/ipmi.h"
|
||||
|
||||
void build_acpi_ipmi_devices(Aml *table, BusState *bus, const char *resource)
|
||||
void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/acpi/ipmi.h"
|
||||
|
||||
static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource)
|
||||
static Aml *aml_ipmi_crs(IPMIFwInfo *info)
|
||||
{
|
||||
Aml *crs = aml_resource_template();
|
||||
|
||||
@ -49,7 +49,7 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource)
|
||||
break;
|
||||
case IPMI_MEMSPACE_SMBUS:
|
||||
aml_append(crs, aml_i2c_serial_bus_device(info->base_address,
|
||||
resource));
|
||||
"^"));
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
@ -62,46 +62,27 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info, const char *resource)
|
||||
return crs;
|
||||
}
|
||||
|
||||
static Aml *aml_ipmi_device(IPMIFwInfo *info, const char *resource)
|
||||
void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
Aml *dev;
|
||||
uint16_t version = ((info->ipmi_spec_major_revision << 8)
|
||||
| (info->ipmi_spec_minor_revision << 4));
|
||||
IPMIFwInfo info = {};
|
||||
IPMIInterface *ii = IPMI_INTERFACE(adev);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
uint16_t version;
|
||||
|
||||
assert(info->ipmi_spec_minor_revision <= 15);
|
||||
iic->get_fwinfo(ii, &info);
|
||||
assert(info.ipmi_spec_minor_revision <= 15);
|
||||
version = ((info.ipmi_spec_major_revision << 8)
|
||||
| (info.ipmi_spec_minor_revision << 4));
|
||||
|
||||
dev = aml_device("MI%d", info->uuid);
|
||||
dev = aml_device("MI%d", info.uuid);
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("IPI0001")));
|
||||
aml_append(dev, aml_name_decl("_STR", aml_string("ipmi_%s",
|
||||
info->interface_name)));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(info->uuid)));
|
||||
aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(info, resource)));
|
||||
aml_append(dev, aml_name_decl("_IFT", aml_int(info->interface_type)));
|
||||
info.interface_name)));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(info.uuid)));
|
||||
aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(&info)));
|
||||
aml_append(dev, aml_name_decl("_IFT", aml_int(info.interface_type)));
|
||||
aml_append(dev, aml_name_decl("_SRV", aml_int(version)));
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
void build_acpi_ipmi_devices(Aml *scope, BusState *bus, const char *resource)
|
||||
{
|
||||
|
||||
BusChild *kid;
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
IPMIInterface *ii;
|
||||
IPMIInterfaceClass *iic;
|
||||
IPMIFwInfo info;
|
||||
Object *obj = object_dynamic_cast(OBJECT(kid->child),
|
||||
TYPE_IPMI_INTERFACE);
|
||||
|
||||
if (!obj) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ii = IPMI_INTERFACE(obj);
|
||||
iic = IPMI_INTERFACE_GET_CLASS(obj);
|
||||
memset(&info, 0, sizeof(info));
|
||||
iic->get_fwinfo(ii, &info);
|
||||
aml_append(scope, aml_ipmi_device(&info, resource));
|
||||
}
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c'))
|
||||
if have_tpm
|
||||
acpi_ss.add(files('tpm.c'))
|
||||
endif
|
||||
softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_ACPI', if_false: files('acpi-stub.c', 'aml-build-stub.c', 'ghes-stub.c', 'acpi_interface.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',
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/acpi/pcihp.h"
|
||||
#include "hw/acpi/piix4.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/xen.h"
|
||||
@ -56,47 +58,6 @@ struct pci_status {
|
||||
uint32_t down;
|
||||
};
|
||||
|
||||
struct PIIX4PMState {
|
||||
/*< private >*/
|
||||
PCIDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion io;
|
||||
uint32_t io_base;
|
||||
|
||||
MemoryRegion io_gpe;
|
||||
ACPIREGS ar;
|
||||
|
||||
APMState apm;
|
||||
|
||||
PMSMBus smb;
|
||||
uint32_t smb_io_base;
|
||||
|
||||
qemu_irq irq;
|
||||
qemu_irq smi_irq;
|
||||
int smm_enabled;
|
||||
bool smm_compat;
|
||||
Notifier machine_ready;
|
||||
Notifier powerdown_notifier;
|
||||
|
||||
AcpiPciHpState acpi_pci_hotplug;
|
||||
bool use_acpi_hotplug_bridge;
|
||||
bool use_acpi_root_pci_hotplug;
|
||||
bool not_migrate_acpi_index;
|
||||
|
||||
uint8_t disable_s3;
|
||||
uint8_t disable_s4;
|
||||
uint8_t s4_val;
|
||||
|
||||
bool cpu_hotplug_legacy;
|
||||
AcpiCpuHotplug gpe_cpu;
|
||||
CPUHotplugState cpuhp_state;
|
||||
|
||||
MemHotplugState acpi_memory_hotplug;
|
||||
};
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PIIX4PMState, PIIX4_PM)
|
||||
|
||||
static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
|
||||
PCIBus *bus, PIIX4PMState *s);
|
||||
|
||||
@ -525,6 +486,10 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp)
|
||||
s->machine_ready.notify = piix4_pm_machine_ready;
|
||||
qemu_add_machine_init_done_notifier(&s->machine_ready);
|
||||
|
||||
if (xen_enabled()) {
|
||||
s->use_acpi_hotplug_bridge = false;
|
||||
}
|
||||
|
||||
piix4_acpi_system_hot_add_init(pci_address_space_io(dev),
|
||||
pci_get_bus(dev), s);
|
||||
qbus_set_hotplug_handler(BUS(pci_get_bus(dev)), OBJECT(s));
|
||||
@ -532,32 +497,12 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp)
|
||||
piix4_pm_add_properties(s);
|
||||
}
|
||||
|
||||
I2CBus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
|
||||
qemu_irq sci_irq, qemu_irq smi_irq,
|
||||
int smm_enabled, DeviceState **piix4_pm)
|
||||
static void piix4_pm_init(Object *obj)
|
||||
{
|
||||
PCIDevice *pci_dev;
|
||||
DeviceState *dev;
|
||||
PIIX4PMState *s;
|
||||
PIIX4PMState *s = PIIX4_PM(obj);
|
||||
|
||||
pci_dev = pci_new(devfn, TYPE_PIIX4_PM);
|
||||
dev = DEVICE(pci_dev);
|
||||
qdev_prop_set_uint32(dev, "smb_io_base", smb_io_base);
|
||||
if (piix4_pm) {
|
||||
*piix4_pm = dev;
|
||||
}
|
||||
|
||||
s = PIIX4_PM(dev);
|
||||
s->irq = sci_irq;
|
||||
s->smi_irq = smi_irq;
|
||||
s->smm_enabled = smm_enabled;
|
||||
if (xen_enabled()) {
|
||||
s->use_acpi_hotplug_bridge = false;
|
||||
}
|
||||
|
||||
pci_realize_and_unref(pci_dev, bus, &error_fatal);
|
||||
|
||||
return s->smb.smbus;
|
||||
qdev_init_gpio_out(DEVICE(obj), &s->irq, 1);
|
||||
qdev_init_gpio_out_named(DEVICE(obj), &s->smi_irq, "smi-irq", 1);
|
||||
}
|
||||
|
||||
static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width)
|
||||
@ -663,6 +608,7 @@ static Property piix4_pm_properties[] = {
|
||||
DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState,
|
||||
acpi_memory_hotplug.is_enabled, true),
|
||||
DEFINE_PROP_BOOL("smm-compat", PIIX4PMState, smm_compat, false),
|
||||
DEFINE_PROP_BOOL("smm-enabled", PIIX4PMState, smm_enabled, false),
|
||||
DEFINE_PROP_BOOL("x-not-migrate-acpi-index", PIIX4PMState,
|
||||
not_migrate_acpi_index, false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@ -703,6 +649,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data)
|
||||
static const TypeInfo piix4_pm_info = {
|
||||
.name = TYPE_PIIX4_PM,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_init = piix4_pm_init,
|
||||
.instance_size = sizeof(PIIX4PMState),
|
||||
.class_init = piix4_pm_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
|
109
hw/acpi/viot.c
109
hw/acpi/viot.c
@ -10,17 +10,40 @@
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
|
||||
struct viot_pci_ranges {
|
||||
GArray *blob;
|
||||
size_t count;
|
||||
uint16_t output_node;
|
||||
struct viot_pci_host_range {
|
||||
int min_bus;
|
||||
int max_bus;
|
||||
};
|
||||
|
||||
/* Build PCI range for a given PCI host bridge */
|
||||
static int build_pci_range_node(Object *obj, void *opaque)
|
||||
static void build_pci_host_range(GArray *table_data, int min_bus, int max_bus,
|
||||
uint16_t output_node)
|
||||
{
|
||||
struct viot_pci_ranges *pci_ranges = opaque;
|
||||
GArray *blob = pci_ranges->blob;
|
||||
/* Type */
|
||||
build_append_int_noprefix(table_data, 1 /* PCI range */, 1);
|
||||
/* Reserved */
|
||||
build_append_int_noprefix(table_data, 0, 1);
|
||||
/* Length */
|
||||
build_append_int_noprefix(table_data, 24, 2);
|
||||
/* Endpoint start */
|
||||
build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 4);
|
||||
/* PCI Segment start */
|
||||
build_append_int_noprefix(table_data, 0, 2);
|
||||
/* PCI Segment end */
|
||||
build_append_int_noprefix(table_data, 0, 2);
|
||||
/* PCI BDF start */
|
||||
build_append_int_noprefix(table_data, PCI_BUILD_BDF(min_bus, 0), 2);
|
||||
/* PCI BDF end */
|
||||
build_append_int_noprefix(table_data, PCI_BUILD_BDF(max_bus, 0xff), 2);
|
||||
/* Output node */
|
||||
build_append_int_noprefix(table_data, output_node, 2);
|
||||
/* Reserved */
|
||||
build_append_int_noprefix(table_data, 0, 6);
|
||||
}
|
||||
|
||||
/* Build PCI range for a given PCI host bridge */
|
||||
static int enumerate_pci_host_bridges(Object *obj, void *opaque)
|
||||
{
|
||||
GArray *pci_host_ranges = opaque;
|
||||
|
||||
if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) {
|
||||
PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus;
|
||||
@ -30,34 +53,31 @@ static int build_pci_range_node(Object *obj, void *opaque)
|
||||
|
||||
pci_bus_range(bus, &min_bus, &max_bus);
|
||||
|
||||
/* Type */
|
||||
build_append_int_noprefix(blob, 1 /* PCI range */, 1);
|
||||
/* Reserved */
|
||||
build_append_int_noprefix(blob, 0, 1);
|
||||
/* Length */
|
||||
build_append_int_noprefix(blob, 24, 2);
|
||||
/* Endpoint start */
|
||||
build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 4);
|
||||
/* PCI Segment start */
|
||||
build_append_int_noprefix(blob, 0, 2);
|
||||
/* PCI Segment end */
|
||||
build_append_int_noprefix(blob, 0, 2);
|
||||
/* PCI BDF start */
|
||||
build_append_int_noprefix(blob, PCI_BUILD_BDF(min_bus, 0), 2);
|
||||
/* PCI BDF end */
|
||||
build_append_int_noprefix(blob, PCI_BUILD_BDF(max_bus, 0xff), 2);
|
||||
/* Output node */
|
||||
build_append_int_noprefix(blob, pci_ranges->output_node, 2);
|
||||
/* Reserved */
|
||||
build_append_int_noprefix(blob, 0, 6);
|
||||
|
||||
pci_ranges->count++;
|
||||
const struct viot_pci_host_range pci_host_range = {
|
||||
.min_bus = min_bus,
|
||||
.max_bus = max_bus,
|
||||
};
|
||||
g_array_append_val(pci_host_ranges, pci_host_range);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint pci_host_range_compare(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
struct viot_pci_host_range *range_a = (struct viot_pci_host_range *)a;
|
||||
struct viot_pci_host_range *range_b = (struct viot_pci_host_range *)b;
|
||||
|
||||
if (range_a->min_bus < range_b->min_bus) {
|
||||
return -1;
|
||||
} else if (range_a->min_bus > range_b->min_bus) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a VIOT table with one PCI-based virtio-iommu that manages PCI
|
||||
* endpoints.
|
||||
@ -72,19 +92,22 @@ void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker,
|
||||
int viommu_off = 48;
|
||||
AcpiTable table = { .sig = "VIOT", .rev = 0,
|
||||
.oem_id = oem_id, .oem_table_id = oem_table_id };
|
||||
struct viot_pci_ranges pci_ranges = {
|
||||
.output_node = viommu_off,
|
||||
.blob = g_array_new(false, true /* clear */, 1),
|
||||
};
|
||||
GArray *pci_host_ranges = g_array_new(false, true,
|
||||
sizeof(struct viot_pci_host_range));
|
||||
struct viot_pci_host_range *pci_host_range;
|
||||
int i;
|
||||
|
||||
/* Build the list of PCI ranges that this viommu manages */
|
||||
object_child_foreach_recursive(OBJECT(ms), build_pci_range_node,
|
||||
&pci_ranges);
|
||||
object_child_foreach_recursive(OBJECT(ms), enumerate_pci_host_bridges,
|
||||
pci_host_ranges);
|
||||
|
||||
/* Sort the pci host ranges by min_bus */
|
||||
g_array_sort(pci_host_ranges, pci_host_range_compare);
|
||||
|
||||
/* ACPI table header */
|
||||
acpi_table_begin(&table, table_data);
|
||||
/* Node count */
|
||||
build_append_int_noprefix(table_data, pci_ranges.count + 1, 2);
|
||||
build_append_int_noprefix(table_data, pci_host_ranges->len + 1, 2);
|
||||
/* Node offset */
|
||||
build_append_int_noprefix(table_data, viommu_off, 2);
|
||||
/* Reserved */
|
||||
@ -105,9 +128,15 @@ void build_viot(MachineState *ms, GArray *table_data, BIOSLinker *linker,
|
||||
build_append_int_noprefix(table_data, 0, 8);
|
||||
|
||||
/* PCI ranges found above */
|
||||
g_array_append_vals(table_data, pci_ranges.blob->data,
|
||||
pci_ranges.blob->len);
|
||||
g_array_free(pci_ranges.blob, true);
|
||||
for (i = 0; i < pci_host_ranges->len; i++) {
|
||||
pci_host_range = &g_array_index(pci_host_ranges,
|
||||
struct viot_pci_host_range, i);
|
||||
|
||||
build_pci_host_range(table_data, pci_host_range->min_bus,
|
||||
pci_host_range->max_bus, viommu_off);
|
||||
}
|
||||
|
||||
g_array_free(pci_host_ranges, true);
|
||||
|
||||
acpi_table_end(linker, &table);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ struct CSState {
|
||||
int transferred;
|
||||
int aci_counter;
|
||||
SWVoiceOut *voice;
|
||||
int16_t *tab;
|
||||
const int16_t *tab;
|
||||
};
|
||||
|
||||
#define MODE2 (1 << 6)
|
||||
@ -142,13 +142,13 @@ enum {
|
||||
Capture_Lower_Base_Count
|
||||
};
|
||||
|
||||
static int freqs[2][8] = {
|
||||
static const int freqs[2][8] = {
|
||||
{ 8000, 16000, 27420, 32000, -1, -1, 48000, 9000 },
|
||||
{ 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 }
|
||||
};
|
||||
|
||||
/* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */
|
||||
static int16_t MuLawDecompressTable[256] =
|
||||
static const int16_t MuLawDecompressTable[256] =
|
||||
{
|
||||
-32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
|
||||
-23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
|
||||
@ -184,7 +184,7 @@ static int16_t MuLawDecompressTable[256] =
|
||||
56, 48, 40, 32, 24, 16, 8, 0
|
||||
};
|
||||
|
||||
static int16_t ALawDecompressTable[256] =
|
||||
static const int16_t ALawDecompressTable[256] =
|
||||
{
|
||||
-5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
|
||||
-7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
@ -214,9 +214,9 @@ int cmos_get_fd_drive_type(FloppyDriveType fd0)
|
||||
return val;
|
||||
}
|
||||
|
||||
static void fdc_isa_build_aml(ISADevice *isadev, Aml *scope)
|
||||
static void build_fdc_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
FDCtrlISABus *isa = ISA_FDC(isadev);
|
||||
FDCtrlISABus *isa = ISA_FDC(adev);
|
||||
Aml *dev;
|
||||
Aml *crs;
|
||||
int i;
|
||||
@ -241,7 +241,7 @@ static void fdc_isa_build_aml(ISADevice *isadev, Aml *scope)
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) {
|
||||
FloppyDriveType type = isa_fdc_get_drive_type(isadev, i);
|
||||
FloppyDriveType type = isa_fdc_get_drive_type(ISA_DEVICE(adev), i);
|
||||
|
||||
if (type < FLOPPY_DRIVE_TYPE_NONE) {
|
||||
fde_buf[i] = cpu_to_le32(1); /* drive present */
|
||||
@ -283,14 +283,14 @@ static Property isa_fdc_properties[] = {
|
||||
static void isabus_fdc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ISADeviceClass *isa = ISA_DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
dc->desc = "virtual floppy controller";
|
||||
dc->realize = isabus_fdc_realize;
|
||||
dc->fw_name = "fdc";
|
||||
dc->reset = fdctrl_external_reset_isa;
|
||||
dc->vmsd = &vmstate_isa_fdc;
|
||||
isa->build_aml = fdc_isa_build_aml;
|
||||
adevc->build_dev_aml = build_fdc_aml;
|
||||
device_class_set_props(dc, isa_fdc_properties);
|
||||
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||
}
|
||||
@ -313,6 +313,10 @@ static const TypeInfo isa_fdc_info = {
|
||||
.instance_size = sizeof(FDCtrlISABus),
|
||||
.class_init = isabus_fdc_class_init,
|
||||
.instance_init = isabus_fdc_instance_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void isa_fdc_register_types(void)
|
||||
|
@ -94,18 +94,14 @@ static void fdctrl_handle_tc(void *opaque, int irq, int level)
|
||||
trace_fdctrl_tc_pulse(level);
|
||||
}
|
||||
|
||||
void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
|
||||
hwaddr mmio_base, DriveInfo **fds)
|
||||
void fdctrl_init_sysbus(qemu_irq irq, hwaddr mmio_base, DriveInfo **fds)
|
||||
{
|
||||
FDCtrl *fdctrl;
|
||||
DeviceState *dev;
|
||||
SysBusDevice *sbd;
|
||||
FDCtrlSysBus *sys;
|
||||
|
||||
dev = qdev_new("sysbus-fdc");
|
||||
sys = SYSBUS_FDC(dev);
|
||||
fdctrl = &sys->state;
|
||||
fdctrl->dma_chann = dma_chann; /* FIXME */
|
||||
sbd = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
sysbus_connect_irq(sbd, 0, irq);
|
||||
@ -138,6 +134,16 @@ static void sysbus_fdc_common_instance_init(Object *obj)
|
||||
FDCtrlSysBus *sys = SYSBUS_FDC(obj);
|
||||
FDCtrl *fdctrl = &sys->state;
|
||||
|
||||
/*
|
||||
* DMA is not currently supported for sysbus floppy controllers.
|
||||
* If we wanted to add support then probably the best approach is
|
||||
* to have a QOM link property 'dma-controller' which the board
|
||||
* code can set to an instance of IsaDmaClass, and an integer
|
||||
* property 'dma-channel', so that we can set fdctrl->dma and
|
||||
* fdctrl->dma_chann accordingly.
|
||||
*/
|
||||
fdctrl->dma_chann = -1;
|
||||
|
||||
qdev_set_legacy_instance_id(dev, 0 /* io */, 2); /* FIXME */
|
||||
|
||||
memory_region_init_io(&fdctrl->iomem, obj,
|
||||
|
@ -337,6 +337,7 @@ static int vhost_user_blk_connect(DeviceState *dev, Error **errp)
|
||||
|
||||
vhost_dev_set_config_notifier(&s->dev, &blk_ops);
|
||||
|
||||
s->vhost_user.supports_config = true;
|
||||
ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "qemu/module.h"
|
||||
#include "chardev/char-parallel.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
@ -570,9 +570,9 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp)
|
||||
s, "parallel");
|
||||
}
|
||||
|
||||
static void parallel_isa_build_aml(ISADevice *isadev, Aml *scope)
|
||||
static void parallel_isa_build_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
ISAParallelState *isa = ISA_PARALLEL(isadev);
|
||||
ISAParallelState *isa = ISA_PARALLEL(adev);
|
||||
Aml *dev;
|
||||
Aml *crs;
|
||||
|
||||
@ -645,11 +645,11 @@ static Property parallel_isa_properties[] = {
|
||||
static void parallel_isa_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ISADeviceClass *isa = ISA_DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
dc->realize = parallel_isa_realizefn;
|
||||
dc->vmsd = &vmstate_parallel_isa;
|
||||
isa->build_aml = parallel_isa_build_aml;
|
||||
adevc->build_dev_aml = parallel_isa_build_aml;
|
||||
device_class_set_props(dc, parallel_isa_properties);
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
}
|
||||
@ -659,6 +659,10 @@ static const TypeInfo parallel_isa_info = {
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(ISAParallelState),
|
||||
.class_init = parallel_isa_class_initfn,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void parallel_register_types(void)
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/module.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
@ -83,9 +83,9 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp)
|
||||
isa_register_ioport(isadev, &s->io, isa->iobase);
|
||||
}
|
||||
|
||||
static void serial_isa_build_aml(ISADevice *isadev, Aml *scope)
|
||||
static void serial_isa_build_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
ISASerialState *isa = ISA_SERIAL(isadev);
|
||||
ISASerialState *isa = ISA_SERIAL(adev);
|
||||
Aml *dev;
|
||||
Aml *crs;
|
||||
|
||||
@ -122,11 +122,11 @@ static Property serial_isa_properties[] = {
|
||||
static void serial_isa_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ISADeviceClass *isa = ISA_DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
dc->realize = serial_isa_realizefn;
|
||||
dc->vmsd = &vmstate_isa_serial;
|
||||
isa->build_aml = serial_isa_build_aml;
|
||||
adevc->build_dev_aml = serial_isa_build_aml;
|
||||
device_class_set_props(dc, serial_isa_properties);
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
}
|
||||
@ -146,6 +146,10 @@ static const TypeInfo serial_isa_info = {
|
||||
.instance_size = sizeof(ISASerialState),
|
||||
.instance_init = serial_isa_initfn,
|
||||
.class_init = serial_isa_class_initfn,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void serial_register_types(void)
|
||||
|
@ -33,7 +33,6 @@
|
||||
#include "sysemu/qtest.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "migration/global_state.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "exec/confidential-guest-support.h"
|
||||
@ -629,20 +628,6 @@ static void machine_set_nvdimm_persistence(Object *obj, const char *value,
|
||||
nvdimms_state->persistence_string = g_strdup(value);
|
||||
}
|
||||
|
||||
static bool machine_get_cxl(Object *obj, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(obj);
|
||||
|
||||
return ms->cxl_devices_state->is_enabled;
|
||||
}
|
||||
|
||||
static void machine_set_cxl(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(obj);
|
||||
|
||||
ms->cxl_devices_state->is_enabled = value;
|
||||
}
|
||||
|
||||
void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type)
|
||||
{
|
||||
QAPI_LIST_PREPEND(mc->allowed_dynamic_sysbus_devices, g_strdup(type));
|
||||
@ -929,8 +914,6 @@ static void machine_class_init(ObjectClass *oc, void *data)
|
||||
mc->default_ram_size = 128 * MiB;
|
||||
mc->rom_file_has_mr = true;
|
||||
|
||||
/* Few machines support CXL, so default to off */
|
||||
mc->cxl_supported = false;
|
||||
/* numa node memory size aligned on 8MB by default.
|
||||
* On Linux, each node's border has to be 8MB aligned
|
||||
*/
|
||||
@ -1091,16 +1074,6 @@ static void machine_initfn(Object *obj)
|
||||
"Valid values are cpu, mem-ctrl");
|
||||
}
|
||||
|
||||
if (mc->cxl_supported) {
|
||||
Object *obj = OBJECT(ms);
|
||||
|
||||
ms->cxl_devices_state = g_new0(CXLState, 1);
|
||||
object_property_add_bool(obj, "cxl", machine_get_cxl, machine_set_cxl);
|
||||
object_property_set_description(obj, "cxl",
|
||||
"Set on/off to enable/disable "
|
||||
"CXL instantiation");
|
||||
}
|
||||
|
||||
if (mc->cpu_index_to_instance_props && mc->get_default_cpu_node_id) {
|
||||
ms->numa_state = g_new0(NumaState, 1);
|
||||
object_property_add_bool(obj, "hmat",
|
||||
@ -1138,7 +1111,6 @@ static void machine_finalize(Object *obj)
|
||||
g_free(ms->device_memory);
|
||||
g_free(ms->nvdimms_state);
|
||||
g_free(ms->numa_state);
|
||||
g_free(ms->cxl_devices_state);
|
||||
}
|
||||
|
||||
bool machine_usb(MachineState *machine)
|
||||
|
@ -468,6 +468,28 @@ char *qdev_get_dev_path(DeviceState *dev)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void qdev_add_unplug_blocker(DeviceState *dev, Error *reason)
|
||||
{
|
||||
dev->unplug_blockers = g_slist_prepend(dev->unplug_blockers, reason);
|
||||
}
|
||||
|
||||
void qdev_del_unplug_blocker(DeviceState *dev, Error *reason)
|
||||
{
|
||||
dev->unplug_blockers = g_slist_remove(dev->unplug_blockers, reason);
|
||||
}
|
||||
|
||||
bool qdev_unplug_blocked(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
|
||||
if (dev->unplug_blockers) {
|
||||
error_propagate(errp, error_copy(dev->unplug_blockers->data));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool device_get_realized(Object *obj, Error **errp)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
@ -704,6 +726,8 @@ static void device_finalize(Object *obj)
|
||||
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
|
||||
g_assert(!dev->unplug_blockers);
|
||||
|
||||
QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) {
|
||||
QLIST_REMOVE(ngl, node);
|
||||
qemu_free_irqs(ngl->in, ngl->num_in);
|
||||
|
@ -154,7 +154,8 @@ static void ras_init_common(uint32_t *reg_state, uint32_t *write_msk)
|
||||
reg_state[R_CXL_RAS_ERR_CAP_CTRL] = 0x00;
|
||||
}
|
||||
|
||||
static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk)
|
||||
static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk,
|
||||
enum reg_type type)
|
||||
{
|
||||
int decoder_count = 1;
|
||||
int i;
|
||||
@ -174,6 +175,14 @@ static void hdm_init_common(uint32_t *reg_state, uint32_t *write_msk)
|
||||
write_msk[R_CXL_HDM_DECODER0_SIZE_LO + i * 0x20] = 0xf0000000;
|
||||
write_msk[R_CXL_HDM_DECODER0_SIZE_HI + i * 0x20] = 0xffffffff;
|
||||
write_msk[R_CXL_HDM_DECODER0_CTRL + i * 0x20] = 0x13ff;
|
||||
if (type == CXL2_DEVICE ||
|
||||
type == CXL2_TYPE3_DEVICE ||
|
||||
type == CXL2_LOGICAL_DEVICE) {
|
||||
write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_LO + i * 0x20] = 0xf0000000;
|
||||
} else {
|
||||
write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_LO + i * 0x20] = 0xffffffff;
|
||||
}
|
||||
write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_HI + i * 0x20] = 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,7 +248,7 @@ void cxl_component_register_init_common(uint32_t *reg_state, uint32_t *write_msk
|
||||
}
|
||||
|
||||
init_cap_reg(HDM, 5, 1);
|
||||
hdm_init_common(reg_state, write_msk);
|
||||
hdm_init_common(reg_state, write_msk, type);
|
||||
|
||||
if (caps < 5) {
|
||||
return;
|
||||
|
@ -6,11 +6,10 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "hw/cxl/cxl_host.h"
|
||||
|
||||
void cxl_fixed_memory_window_config(MachineState *ms,
|
||||
CXLFixedMemoryWindowOptions *object,
|
||||
Error **errp) {};
|
||||
|
||||
void cxl_fixed_memory_window_link_targets(Error **errp) {};
|
||||
void cxl_fmws_link_targets(CXLState *stat, Error **errp) {};
|
||||
void cxl_machine_init(Object *obj, CXLState *state) {};
|
||||
void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp) {};
|
||||
|
||||
const MemoryRegionOps cfmws_ops;
|
||||
|
@ -15,14 +15,16 @@
|
||||
|
||||
#include "qapi/qapi-visit-machine.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "hw/cxl/cxl_host.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/pci/pcie_port.h"
|
||||
#include "hw/pci-bridge/pci_expander_bridge.h"
|
||||
|
||||
void cxl_fixed_memory_window_config(MachineState *ms,
|
||||
CXLFixedMemoryWindowOptions *object,
|
||||
Error **errp)
|
||||
static void cxl_fixed_memory_window_config(CXLState *cxl_state,
|
||||
CXLFixedMemoryWindowOptions *object,
|
||||
Error **errp)
|
||||
{
|
||||
CXLFixedWindow *fw = g_malloc0(sizeof(*fw));
|
||||
strList *target;
|
||||
@ -62,20 +64,17 @@ void cxl_fixed_memory_window_config(MachineState *ms,
|
||||
fw->enc_int_gran = 0;
|
||||
}
|
||||
|
||||
ms->cxl_devices_state->fixed_windows =
|
||||
g_list_append(ms->cxl_devices_state->fixed_windows, fw);
|
||||
cxl_state->fixed_windows = g_list_append(cxl_state->fixed_windows, fw);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void cxl_fixed_memory_window_link_targets(Error **errp)
|
||||
void cxl_fmws_link_targets(CXLState *cxl_state, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
|
||||
if (ms->cxl_devices_state && ms->cxl_devices_state->fixed_windows) {
|
||||
if (cxl_state && cxl_state->fixed_windows) {
|
||||
GList *it;
|
||||
|
||||
for (it = ms->cxl_devices_state->fixed_windows; it; it = it->next) {
|
||||
for (it = cxl_state->fixed_windows; it; it = it->next) {
|
||||
CXLFixedWindow *fw = it->data;
|
||||
int i;
|
||||
|
||||
@ -220,3 +219,84 @@ const MemoryRegionOps cfmws_ops = {
|
||||
.unaligned = true,
|
||||
},
|
||||
};
|
||||
|
||||
static void machine_get_cxl(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CXLState *cxl_state = opaque;
|
||||
bool value = cxl_state->is_enabled;
|
||||
|
||||
visit_type_bool(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void machine_set_cxl(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CXLState *cxl_state = opaque;
|
||||
bool value;
|
||||
|
||||
if (!visit_type_bool(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
cxl_state->is_enabled = value;
|
||||
}
|
||||
|
||||
static void machine_get_cfmw(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CXLFixedMemoryWindowOptionsList **list = opaque;
|
||||
|
||||
visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp);
|
||||
}
|
||||
|
||||
static void machine_set_cfmw(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
CXLState *state = opaque;
|
||||
CXLFixedMemoryWindowOptionsList *cfmw_list = NULL;
|
||||
CXLFixedMemoryWindowOptionsList *it;
|
||||
|
||||
visit_type_CXLFixedMemoryWindowOptionsList(v, name, &cfmw_list, errp);
|
||||
if (!cfmw_list) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (it = cfmw_list; it; it = it->next) {
|
||||
cxl_fixed_memory_window_config(state, it->value, errp);
|
||||
}
|
||||
state->cfmw_list = cfmw_list;
|
||||
}
|
||||
|
||||
void cxl_machine_init(Object *obj, CXLState *state)
|
||||
{
|
||||
object_property_add(obj, "cxl", "bool", machine_get_cxl,
|
||||
machine_set_cxl, NULL, state);
|
||||
object_property_set_description(obj, "cxl",
|
||||
"Set on/off to enable/disable "
|
||||
"CXL instantiation");
|
||||
|
||||
object_property_add(obj, "cxl-fmw", "CXLFixedMemoryWindow",
|
||||
machine_get_cfmw, machine_set_cfmw,
|
||||
NULL, state);
|
||||
object_property_set_description(obj, "cxl-fmw",
|
||||
"CXL Fixed Memory Windows (array)");
|
||||
}
|
||||
|
||||
void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp)
|
||||
{
|
||||
/* Walk the pci busses looking for pxb busses to hook up */
|
||||
if (bus) {
|
||||
QLIST_FOREACH(bus, &bus->child, sibling) {
|
||||
if (!pci_bus_is_root(bus)) {
|
||||
continue;
|
||||
}
|
||||
if (pci_bus_is_cxl(bus)) {
|
||||
if (!state->is_enabled) {
|
||||
error_setg(errp, "CXL host bridges present, but cxl=off");
|
||||
return;
|
||||
}
|
||||
pxb_cxl_hook_up_registers(state, bus, errp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,16 +69,17 @@ static void virtio_gpu_notify_event(VirtIOGPUBase *g, uint32_t event_type)
|
||||
virtio_notify_config(&g->parent_obj);
|
||||
}
|
||||
|
||||
static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
|
||||
static void virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
|
||||
{
|
||||
VirtIOGPUBase *g = opaque;
|
||||
|
||||
if (idx >= g->conf.max_outputs) {
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
g->req_state[idx].x = info->xoff;
|
||||
g->req_state[idx].y = info->yoff;
|
||||
g->req_state[idx].refresh_rate = info->refresh_rate;
|
||||
g->req_state[idx].width = info->width;
|
||||
g->req_state[idx].height = info->height;
|
||||
g->req_state[idx].width_mm = info->width_mm;
|
||||
@ -92,7 +93,7 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
|
||||
|
||||
/* send event to guest */
|
||||
virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -217,6 +217,7 @@ virtio_gpu_generate_edid(VirtIOGPU *g, int scanout,
|
||||
.height_mm = b->req_state[scanout].height_mm,
|
||||
.prefx = b->req_state[scanout].width,
|
||||
.prefy = b->req_state[scanout].height,
|
||||
.refresh_rate = b->req_state[scanout].refresh_rate,
|
||||
};
|
||||
|
||||
edid->size = cpu_to_le32(sizeof(edid->edid));
|
||||
@ -514,6 +515,9 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
|
||||
for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
|
||||
scanout = &g->parent_obj.scanout[i];
|
||||
if (scanout->resource_id == res->resource_id &&
|
||||
rf.r.x >= scanout->x && rf.r.y >= scanout->y &&
|
||||
rf.r.x + rf.r.width <= scanout->x + scanout->width &&
|
||||
rf.r.y + rf.r.height <= scanout->y + scanout->height &&
|
||||
console_has_gl(scanout->con)) {
|
||||
dpy_gl_update(scanout->con, 0, 0, scanout->width,
|
||||
scanout->height);
|
||||
|
@ -47,15 +47,14 @@ static void virtio_vga_base_text_update(void *opaque, console_ch_t *chardata)
|
||||
}
|
||||
}
|
||||
|
||||
static int virtio_vga_base_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
|
||||
static void virtio_vga_base_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
|
||||
{
|
||||
VirtIOVGABase *vvga = opaque;
|
||||
VirtIOGPUBase *g = vvga->vgpu;
|
||||
|
||||
if (g->hw_ops->ui_info) {
|
||||
return g->hw_ops->ui_info(g, idx, info);
|
||||
g->hw_ops->ui_info(g, idx, info);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void virtio_vga_base_gl_block(void *opaque, bool block)
|
||||
|
@ -777,16 +777,24 @@ static void xenfb_update(void *opaque)
|
||||
xenfb->up_fullscreen = 0;
|
||||
}
|
||||
|
||||
static void xenfb_update_interval(void *opaque, uint64_t interval)
|
||||
static void xenfb_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
|
||||
{
|
||||
struct XenFB *xenfb = opaque;
|
||||
uint32_t refresh_rate;
|
||||
|
||||
if (xenfb->feature_update) {
|
||||
#ifdef XENFB_TYPE_REFRESH_PERIOD
|
||||
if (xenfb_queue_full(xenfb)) {
|
||||
return;
|
||||
}
|
||||
xenfb_send_refresh_period(xenfb, interval);
|
||||
|
||||
refresh_rate = info->refresh_rate;
|
||||
if (!refresh_rate) {
|
||||
refresh_rate = 75;
|
||||
}
|
||||
|
||||
/* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
|
||||
xenfb_send_refresh_period(xenfb, 1000 * 1000 / refresh_rate);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -983,5 +991,5 @@ struct XenDevOps xen_framebuffer_ops = {
|
||||
static const GraphicHwOps xenfb_ops = {
|
||||
.invalidate = xenfb_invalidate,
|
||||
.gfx_update = xenfb_update,
|
||||
.update_interval = xenfb_update_interval,
|
||||
.ui_info = xenfb_ui_info,
|
||||
};
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "hw/i386/ich9.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(ICH9SMBState, ICH9_SMB_DEVICE)
|
||||
|
||||
@ -94,10 +95,22 @@ static void ich9_smbus_realize(PCIDevice *d, Error **errp)
|
||||
&s->smb.io);
|
||||
}
|
||||
|
||||
static void build_ich9_smb_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
BusChild *kid;
|
||||
ICH9SMBState *s = ICH9_SMB_DEVICE(adev);
|
||||
BusState *bus = BUS(s->smb.smbus);
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
call_dev_aml_func(DEVICE(kid->child), scope);
|
||||
}
|
||||
}
|
||||
|
||||
static void ich9_smb_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_ICH9_6;
|
||||
@ -112,6 +125,7 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data)
|
||||
* pc_q35_init()
|
||||
*/
|
||||
dc->user_creatable = false;
|
||||
adevc->build_dev_aml = build_ich9_smb_aml;
|
||||
}
|
||||
|
||||
static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled)
|
||||
@ -143,6 +157,7 @@ static const TypeInfo ich9_smb_info = {
|
||||
.class_init = ich9_smb_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
@ -31,21 +31,20 @@
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "target/i386/cpu.h"
|
||||
#include "hw/misc/pvpanic.h"
|
||||
#include "hw/timer/hpet.h"
|
||||
#include "hw/acpi/acpi-defs.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/acpi/cpu.h"
|
||||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "hw/acpi/bios-linker-loader.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
#include "hw/input/i8042.h"
|
||||
#include "hw/block/fdc.h"
|
||||
#include "hw/acpi/memory_hotplug.h"
|
||||
#include "sysemu/tpm.h"
|
||||
#include "hw/acpi/tpm.h"
|
||||
#include "hw/acpi/vmgenid.h"
|
||||
#include "hw/acpi/erst.h"
|
||||
#include "hw/acpi/piix4.h"
|
||||
#include "sysemu/tpm_backend.h"
|
||||
#include "hw/rtc/mc146818rtc_regs.h"
|
||||
#include "migration/vmstate.h"
|
||||
@ -74,7 +73,6 @@
|
||||
#include "hw/i386/intel_iommu.h"
|
||||
#include "hw/virtio/virtio-iommu.h"
|
||||
|
||||
#include "hw/acpi/ipmi.h"
|
||||
#include "hw/acpi/hmat.h"
|
||||
#include "hw/acpi/viot.h"
|
||||
#include "hw/acpi/cxl.h"
|
||||
@ -121,8 +119,6 @@ typedef struct AcpiMiscInfo {
|
||||
#endif
|
||||
const unsigned char *dsdt_code;
|
||||
unsigned dsdt_size;
|
||||
uint16_t pvpanic_port;
|
||||
uint16_t applesmc_io_base;
|
||||
} AcpiMiscInfo;
|
||||
|
||||
typedef struct AcpiBuildPciBusHotplugState {
|
||||
@ -307,8 +303,6 @@ static void acpi_get_misc_info(AcpiMiscInfo *info)
|
||||
#ifdef CONFIG_TPM
|
||||
info->tpm_version = tpm_get_version(tpm_find());
|
||||
#endif
|
||||
info->pvpanic_port = pvpanic_port();
|
||||
info->applesmc_io_base = applesmc_port();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -865,21 +859,6 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge)
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void build_isa_devices_aml(Aml *table)
|
||||
{
|
||||
bool ambiguous;
|
||||
Object *obj = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous);
|
||||
Aml *scope;
|
||||
|
||||
assert(obj && !ambiguous);
|
||||
|
||||
scope = aml_scope("_SB.PCI0.ISA");
|
||||
build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA");
|
||||
isa_build_aml(ISA_BUS(obj), scope);
|
||||
|
||||
aml_append(table, scope);
|
||||
}
|
||||
|
||||
static void build_dbg_aml(Aml *table)
|
||||
{
|
||||
Aml *field;
|
||||
@ -1265,15 +1244,22 @@ static void build_q35_isa_bridge(Aml *table)
|
||||
{
|
||||
Aml *dev;
|
||||
Aml *scope;
|
||||
Object *obj;
|
||||
bool ambiguous;
|
||||
|
||||
/*
|
||||
* temporarily fish out isa bridge, build_q35_isa_bridge() will be dropped
|
||||
* once PCI is converted to AcpiDevAmlIf and would be ble to generate
|
||||
* AML for bridge itself
|
||||
*/
|
||||
obj = object_resolve_path_type("", TYPE_ICH9_LPC_DEVICE, &ambiguous);
|
||||
assert(obj && !ambiguous);
|
||||
|
||||
scope = aml_scope("_SB.PCI0");
|
||||
dev = aml_device("ISA");
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(0x001F0000)));
|
||||
|
||||
/* ICH9 PCI to ISA irq remapping */
|
||||
aml_append(dev, aml_operation_region("PIRQ", AML_PCI_CONFIG,
|
||||
aml_int(0x60), 0x0C));
|
||||
|
||||
call_dev_aml_func(DEVICE(obj), dev);
|
||||
aml_append(scope, dev);
|
||||
aml_append(table, scope);
|
||||
}
|
||||
@ -1282,15 +1268,22 @@ static void build_piix4_isa_bridge(Aml *table)
|
||||
{
|
||||
Aml *dev;
|
||||
Aml *scope;
|
||||
Object *obj;
|
||||
bool ambiguous;
|
||||
|
||||
/*
|
||||
* temporarily fish out isa bridge, build_piix4_isa_bridge() will be dropped
|
||||
* once PCI is converted to AcpiDevAmlIf and would be ble to generate
|
||||
* AML for bridge itself
|
||||
*/
|
||||
obj = object_resolve_path_type("", TYPE_PIIX3_PCI_DEVICE, &ambiguous);
|
||||
assert(obj && !ambiguous);
|
||||
|
||||
scope = aml_scope("_SB.PCI0");
|
||||
dev = aml_device("ISA");
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(0x00010000)));
|
||||
|
||||
/* PIIX PCI to ISA irq remapping */
|
||||
aml_append(dev, aml_operation_region("P40C", AML_PCI_CONFIG,
|
||||
aml_int(0x60), 0x04));
|
||||
|
||||
call_dev_aml_func(DEVICE(obj), dev);
|
||||
aml_append(scope, dev);
|
||||
aml_append(table, scope);
|
||||
}
|
||||
@ -1401,13 +1394,21 @@ static Aml *build_q35_osc_method(bool enable_native_pcie_hotplug)
|
||||
return method;
|
||||
}
|
||||
|
||||
static void build_smb0(Aml *table, I2CBus *smbus, int devnr, int func)
|
||||
static void build_smb0(Aml *table, int devnr, int func)
|
||||
{
|
||||
Aml *scope = aml_scope("_SB.PCI0");
|
||||
Aml *dev = aml_device("SMB0");
|
||||
bool ambiguous;
|
||||
Object *obj;
|
||||
/*
|
||||
* temporarily fish out device hosting SMBUS, build_smb0 will be gone once
|
||||
* PCI enumeration will be switched to call_dev_aml_func()
|
||||
*/
|
||||
obj = object_resolve_path_type("", TYPE_ICH9_SMB_DEVICE, &ambiguous);
|
||||
assert(obj && !ambiguous);
|
||||
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(devnr << 16 | func)));
|
||||
build_acpi_ipmi_devices(dev, BUS(smbus), "\\_SB.PCI0.SMB0");
|
||||
call_dev_aml_func(DEVICE(obj), dev);
|
||||
aml_append(scope, dev);
|
||||
aml_append(table, scope);
|
||||
}
|
||||
@ -1470,7 +1471,6 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
build_hpet_aml(dsdt);
|
||||
}
|
||||
build_piix4_isa_bridge(dsdt);
|
||||
build_isa_devices_aml(dsdt);
|
||||
if (pm->pcihp_bridge_en || pm->pcihp_root_en) {
|
||||
build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base);
|
||||
}
|
||||
@ -1519,13 +1519,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
build_hpet_aml(dsdt);
|
||||
}
|
||||
build_q35_isa_bridge(dsdt);
|
||||
build_isa_devices_aml(dsdt);
|
||||
if (pm->pcihp_bridge_en) {
|
||||
build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base);
|
||||
}
|
||||
build_q35_pci0_int(dsdt);
|
||||
if (pcms->smbus && !pcmc->do_not_add_smb_acpi) {
|
||||
build_smb0(dsdt, pcms->smbus, ICH9_SMB_DEV, ICH9_SMB_FUNC);
|
||||
if (pcms->smbus) {
|
||||
build_smb0(dsdt, ICH9_SMB_DEV, ICH9_SMB_FUNC);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1633,7 +1632,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
|
||||
/* Handle the ranges for the PXB expanders */
|
||||
if (pci_bus_is_cxl(bus)) {
|
||||
MemoryRegion *mr = &machine->cxl_devices_state->host_mr;
|
||||
MemoryRegion *mr = &pcms->cxl_devices_state.host_mr;
|
||||
uint64_t base = mr->addr;
|
||||
|
||||
cxl_present = true;
|
||||
@ -1796,110 +1795,15 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
aml_append(dsdt, scope);
|
||||
}
|
||||
|
||||
if (misc->applesmc_io_base) {
|
||||
scope = aml_scope("\\_SB.PCI0.ISA");
|
||||
dev = aml_device("SMC");
|
||||
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0001")));
|
||||
/* device present, functioning, decoding, not shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_io(AML_DECODE16, misc->applesmc_io_base, misc->applesmc_io_base,
|
||||
0x01, APPLESMC_MAX_DATA_LENGTH)
|
||||
);
|
||||
aml_append(crs, aml_irq_no_flags(6));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
aml_append(scope, dev);
|
||||
aml_append(dsdt, scope);
|
||||
}
|
||||
|
||||
if (misc->pvpanic_port) {
|
||||
scope = aml_scope("\\_SB.PCI0.ISA");
|
||||
|
||||
dev = aml_device("PEVT");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0001")));
|
||||
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_io(AML_DECODE16, misc->pvpanic_port, misc->pvpanic_port, 1, 1)
|
||||
);
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
|
||||
aml_int(misc->pvpanic_port), 1));
|
||||
field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PEPT", 8));
|
||||
aml_append(dev, field);
|
||||
|
||||
/* device present, functioning, decoding, shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
|
||||
|
||||
method = aml_method("RDPT", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_store(aml_name("PEPT"), aml_local(0)));
|
||||
aml_append(method, aml_return(aml_local(0)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("WRPT", 1, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_store(aml_arg(0), aml_name("PEPT")));
|
||||
aml_append(dev, method);
|
||||
|
||||
aml_append(scope, dev);
|
||||
aml_append(dsdt, scope);
|
||||
}
|
||||
|
||||
sb_scope = aml_scope("\\_SB");
|
||||
{
|
||||
Object *pci_host;
|
||||
PCIBus *bus = NULL;
|
||||
|
||||
pci_host = acpi_get_i386_pci_host();
|
||||
Object *pci_host = acpi_get_i386_pci_host();
|
||||
|
||||
if (pci_host) {
|
||||
bus = PCI_HOST_BRIDGE(pci_host)->bus;
|
||||
}
|
||||
|
||||
if (bus) {
|
||||
PCIBus *bus = PCI_HOST_BRIDGE(pci_host)->bus;
|
||||
Aml *scope = aml_scope("PCI0");
|
||||
/* Scan all PCI buses. Generate tables to support hotplug. */
|
||||
build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en);
|
||||
|
||||
#ifdef CONFIG_TPM
|
||||
if (TPM_IS_TIS_ISA(tpm)) {
|
||||
if (misc->tpm_version == TPM_VERSION_2_0) {
|
||||
dev = aml_device("TPM");
|
||||
aml_append(dev, aml_name_decl("_HID",
|
||||
aml_string("MSFT0101")));
|
||||
aml_append(dev,
|
||||
aml_name_decl("_STR",
|
||||
aml_string("TPM 2.0 Device")));
|
||||
} else {
|
||||
dev = aml_device("ISA.TPM");
|
||||
aml_append(dev, aml_name_decl("_HID",
|
||||
aml_eisaid("PNP0C31")));
|
||||
}
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(1)));
|
||||
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE,
|
||||
TPM_TIS_ADDR_SIZE, AML_READ_WRITE));
|
||||
/*
|
||||
FIXME: TPM_TIS_IRQ=5 conflicts with PNP0C0F irqs,
|
||||
Rewrite to take IRQ from TPM device model and
|
||||
fix default IRQ value there to use some unused IRQ
|
||||
*/
|
||||
/* aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); */
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
tpm_build_ppi_acpi(tpm, dev);
|
||||
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
aml_append(sb_scope, scope);
|
||||
}
|
||||
}
|
||||
@ -2711,9 +2615,9 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
|
||||
machine->nvdimms_state, machine->ram_slots,
|
||||
x86ms->oem_id, x86ms->oem_table_id);
|
||||
}
|
||||
if (machine->cxl_devices_state->is_enabled) {
|
||||
cxl_build_cedt(machine, table_offsets, tables_blob, tables->linker,
|
||||
x86ms->oem_id, x86ms->oem_table_id);
|
||||
if (pcms->cxl_devices_state.is_enabled) {
|
||||
cxl_build_cedt(table_offsets, tables_blob, tables->linker,
|
||||
x86ms->oem_id, x86ms->oem_table_id, &pcms->cxl_devices_state);
|
||||
}
|
||||
|
||||
acpi_add_table(table_offsets, tables_blob);
|
||||
|
@ -32,6 +32,7 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "hw/i386/fw_cfg.h"
|
||||
@ -187,8 +188,8 @@ static void dt_add_ioapic(MicrovmMachineState *mms, SysBusDevice *dev)
|
||||
static void dt_add_isa_serial(MicrovmMachineState *mms, ISADevice *dev)
|
||||
{
|
||||
const char compat[] = "ns16550";
|
||||
uint32_t irq = object_property_get_int(OBJECT(dev), "irq", NULL);
|
||||
hwaddr base = object_property_get_int(OBJECT(dev), "iobase", NULL);
|
||||
uint32_t irq = object_property_get_int(OBJECT(dev), "irq", &error_fatal);
|
||||
hwaddr base = object_property_get_int(OBJECT(dev), "iobase", &error_fatal);
|
||||
hwaddr size = 8;
|
||||
char *nodename;
|
||||
|
||||
@ -208,8 +209,8 @@ static void dt_add_isa_serial(MicrovmMachineState *mms, ISADevice *dev)
|
||||
static void dt_add_isa_rtc(MicrovmMachineState *mms, ISADevice *dev)
|
||||
{
|
||||
const char compat[] = "motorola,mc146818";
|
||||
uint32_t irq = RTC_ISA_IRQ;
|
||||
hwaddr base = RTC_ISA_BASE;
|
||||
uint32_t irq = object_property_get_uint(OBJECT(dev), "irq", &error_fatal);
|
||||
hwaddr base = object_property_get_uint(OBJECT(dev), "iobase", &error_fatal);
|
||||
hwaddr size = 8;
|
||||
char *nodename;
|
||||
|
||||
|
48
hw/i386/pc.c
48
hw/i386/pc.c
@ -37,6 +37,7 @@
|
||||
#include "hw/ide.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/pci-bridge/pci_expander_bridge.h"
|
||||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "hw/timer/hpet.h"
|
||||
#include "hw/firmware/smbios.h"
|
||||
@ -76,6 +77,7 @@
|
||||
#include "hw/mem/pc-dimm.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "hw/cxl/cxl_host.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-visit-common.h"
|
||||
#include "qapi/qapi-visit-machine.h"
|
||||
@ -96,6 +98,15 @@
|
||||
#include "trace.h"
|
||||
#include CONFIG_DEVICES
|
||||
|
||||
/*
|
||||
* Helper for setting model-id for CPU models that changed model-id
|
||||
* depending on QEMU versions up to QEMU 2.4.
|
||||
*/
|
||||
#define PC_CPU_MODEL_IDS(v) \
|
||||
{ "qemu32-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\
|
||||
{ "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\
|
||||
{ "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },
|
||||
|
||||
GlobalProperty pc_compat_7_0[] = {};
|
||||
const size_t pc_compat_7_0_len = G_N_ELEMENTS(pc_compat_7_0);
|
||||
|
||||
@ -561,7 +572,7 @@ static const char * const fdc_container_path[] = {
|
||||
* Locate the FDC at IO address 0x3f0, in order to configure the CMOS registers
|
||||
* and ACPI objects.
|
||||
*/
|
||||
ISADevice *pc_find_fdc0(void)
|
||||
static ISADevice *pc_find_fdc0(void)
|
||||
{
|
||||
int i;
|
||||
Object *container;
|
||||
@ -705,7 +716,7 @@ static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360,
|
||||
0x280, 0x380 };
|
||||
static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
|
||||
|
||||
void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd)
|
||||
static void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd)
|
||||
{
|
||||
static int nb_ne2k = 0;
|
||||
|
||||
@ -732,6 +743,13 @@ void pc_machine_done(Notifier *notifier, void *data)
|
||||
PCMachineState, machine_done);
|
||||
X86MachineState *x86ms = X86_MACHINE(pcms);
|
||||
|
||||
cxl_hook_up_pxb_registers(pcms->bus, &pcms->cxl_devices_state,
|
||||
&error_fatal);
|
||||
|
||||
if (pcms->cxl_devices_state.is_enabled) {
|
||||
cxl_fmws_link_targets(&pcms->cxl_devices_state, &error_fatal);
|
||||
}
|
||||
|
||||
/* set the number of CPUs */
|
||||
x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus);
|
||||
|
||||
@ -899,8 +917,8 @@ void pc_memory_init(PCMachineState *pcms,
|
||||
&machine->device_memory->mr);
|
||||
}
|
||||
|
||||
if (machine->cxl_devices_state->is_enabled) {
|
||||
MemoryRegion *mr = &machine->cxl_devices_state->host_mr;
|
||||
if (pcms->cxl_devices_state.is_enabled) {
|
||||
MemoryRegion *mr = &pcms->cxl_devices_state.host_mr;
|
||||
hwaddr cxl_size = MiB;
|
||||
|
||||
if (pcmc->has_reserved_memory && machine->device_memory->base) {
|
||||
@ -918,12 +936,12 @@ void pc_memory_init(PCMachineState *pcms,
|
||||
memory_region_init(mr, OBJECT(machine), "cxl_host_reg", cxl_size);
|
||||
memory_region_add_subregion(system_memory, cxl_base, mr);
|
||||
cxl_resv_end = cxl_base + cxl_size;
|
||||
if (machine->cxl_devices_state->fixed_windows) {
|
||||
if (pcms->cxl_devices_state.fixed_windows) {
|
||||
hwaddr cxl_fmw_base;
|
||||
GList *it;
|
||||
|
||||
cxl_fmw_base = ROUND_UP(cxl_base + cxl_size, 256 * MiB);
|
||||
for (it = machine->cxl_devices_state->fixed_windows; it; it = it->next) {
|
||||
for (it = pcms->cxl_devices_state.fixed_windows; it; it = it->next) {
|
||||
CXLFixedWindow *fw = it->data;
|
||||
|
||||
fw->base = cxl_fmw_base;
|
||||
@ -965,7 +983,7 @@ void pc_memory_init(PCMachineState *pcms,
|
||||
res_mem_end += memory_region_size(&machine->device_memory->mr);
|
||||
}
|
||||
|
||||
if (machine->cxl_devices_state->is_enabled) {
|
||||
if (pcms->cxl_devices_state.is_enabled) {
|
||||
res_mem_end = cxl_resv_end;
|
||||
}
|
||||
*val = cpu_to_le64(ROUND_UP(res_mem_end, 1 * GiB));
|
||||
@ -1001,12 +1019,12 @@ uint64_t pc_pci_hole64_start(void)
|
||||
X86MachineState *x86ms = X86_MACHINE(pcms);
|
||||
uint64_t hole64_start = 0;
|
||||
|
||||
if (ms->cxl_devices_state->host_mr.addr) {
|
||||
hole64_start = ms->cxl_devices_state->host_mr.addr +
|
||||
memory_region_size(&ms->cxl_devices_state->host_mr);
|
||||
if (ms->cxl_devices_state->fixed_windows) {
|
||||
if (pcms->cxl_devices_state.host_mr.addr) {
|
||||
hole64_start = pcms->cxl_devices_state.host_mr.addr +
|
||||
memory_region_size(&pcms->cxl_devices_state.host_mr);
|
||||
if (pcms->cxl_devices_state.fixed_windows) {
|
||||
GList *it;
|
||||
for (it = ms->cxl_devices_state->fixed_windows; it; it = it->next) {
|
||||
for (it = pcms->cxl_devices_state.fixed_windows; it; it = it->next) {
|
||||
CXLFixedWindow *fw = it->data;
|
||||
hole64_start = fw->mr.addr + memory_region_size(&fw->mr);
|
||||
}
|
||||
@ -1088,7 +1106,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl,
|
||||
return;
|
||||
}
|
||||
|
||||
i8042 = isa_create_simple(isa_bus, "i8042");
|
||||
i8042 = isa_create_simple(isa_bus, TYPE_I8042);
|
||||
if (!no_vmport) {
|
||||
isa_create_simple(isa_bus, TYPE_VMPORT);
|
||||
vmmouse = isa_try_new("vmmouse");
|
||||
@ -1096,7 +1114,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl,
|
||||
vmmouse = NULL;
|
||||
}
|
||||
if (vmmouse) {
|
||||
object_property_set_link(OBJECT(vmmouse), "i8042", OBJECT(i8042),
|
||||
object_property_set_link(OBJECT(vmmouse), TYPE_I8042, OBJECT(i8042),
|
||||
&error_abort);
|
||||
isa_realize_and_unref(vmmouse, isa_bus, &error_fatal);
|
||||
}
|
||||
@ -1706,6 +1724,7 @@ static void pc_machine_initfn(Object *obj)
|
||||
pcms->pcspk = isa_new(TYPE_PC_SPEAKER);
|
||||
object_property_add_alias(OBJECT(pcms), "pcspk-audiodev",
|
||||
OBJECT(pcms->pcspk), "audiodev");
|
||||
cxl_machine_init(obj, &pcms->cxl_devices_state);
|
||||
}
|
||||
|
||||
static void pc_machine_reset(MachineState *machine)
|
||||
@ -1794,7 +1813,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
|
||||
mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE;
|
||||
mc->nvdimm_supported = true;
|
||||
mc->smp_props.dies_supported = true;
|
||||
mc->cxl_supported = true;
|
||||
mc->default_ram_id = "pc.ram";
|
||||
|
||||
object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "hw/xen/xen-x86.h"
|
||||
#include "exec/memory.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/acpi/piix4.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/xen.h"
|
||||
@ -196,6 +197,9 @@ static void pc_init1(MachineState *machine,
|
||||
|
||||
if (pcmc->pci_enabled) {
|
||||
PIIX3State *piix3;
|
||||
PCIDevice *pci_dev;
|
||||
const char *type = xen_enabled() ? TYPE_PIIX3_XEN_DEVICE
|
||||
: TYPE_PIIX3_DEVICE;
|
||||
|
||||
pci_bus = i440fx_init(host_type,
|
||||
pci_type,
|
||||
@ -206,9 +210,11 @@ static void pc_init1(MachineState *machine,
|
||||
pci_memory, ram_memory);
|
||||
pcms->bus = pci_bus;
|
||||
|
||||
piix3 = piix3_create(pci_bus, &isa_bus);
|
||||
pci_dev = pci_create_simple_multifunction(pci_bus, -1, true, type);
|
||||
piix3 = PIIX3_PCI_DEVICE(pci_dev);
|
||||
piix3->pic = x86ms->gsi;
|
||||
piix3_devfn = piix3->dev.devfn;
|
||||
isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0"));
|
||||
} else {
|
||||
pci_bus = NULL;
|
||||
i440fx_state = NULL;
|
||||
@ -280,14 +286,19 @@ static void pc_init1(MachineState *machine,
|
||||
}
|
||||
|
||||
if (pcmc->pci_enabled && x86_machine_is_acpi_enabled(X86_MACHINE(pcms))) {
|
||||
DeviceState *piix4_pm;
|
||||
PCIDevice *piix4_pm;
|
||||
|
||||
smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0);
|
||||
piix4_pm = pci_new(piix3_devfn + 3, TYPE_PIIX4_PM);
|
||||
qdev_prop_set_uint32(DEVICE(piix4_pm), "smb_io_base", 0xb100);
|
||||
qdev_prop_set_bit(DEVICE(piix4_pm), "smm-enabled",
|
||||
x86_machine_is_smm_enabled(x86ms));
|
||||
pci_realize_and_unref(piix4_pm, pci_bus, &error_fatal);
|
||||
|
||||
qdev_connect_gpio_out(DEVICE(piix4_pm), 0, x86ms->gsi[9]);
|
||||
qdev_connect_gpio_out_named(DEVICE(piix4_pm), "smi-irq", 0, smi_irq);
|
||||
pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(piix4_pm), "i2c"));
|
||||
/* TODO: Populate SPD eeprom data. */
|
||||
pcms->smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
|
||||
x86ms->gsi[9], smi_irq,
|
||||
x86_machine_is_smm_enabled(x86ms),
|
||||
&piix4_pm);
|
||||
smbus_eeprom_init(pcms->smbus, 8, NULL, 0);
|
||||
|
||||
object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP,
|
||||
@ -563,7 +574,6 @@ static void pc_i440fx_3_1_machine_options(MachineClass *m)
|
||||
|
||||
pc_i440fx_4_0_machine_options(m);
|
||||
m->is_default = false;
|
||||
pcmc->do_not_add_smb_acpi = true;
|
||||
m->smbus_no_migration_support = true;
|
||||
m->alias = NULL;
|
||||
pcmc->pvh_enabled = false;
|
||||
|
@ -514,7 +514,6 @@ static void pc_q35_3_1_machine_options(MachineClass *m)
|
||||
|
||||
pc_q35_4_0_machine_options(m);
|
||||
m->default_kernel_irqchip_split = false;
|
||||
pcmc->do_not_add_smb_acpi = true;
|
||||
m->smbus_no_migration_support = true;
|
||||
m->alias = NULL;
|
||||
pcmc->pvh_enabled = false;
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
#include "hw/input/ps2.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/input/i8042.h"
|
||||
@ -767,9 +767,9 @@ static void i8042_realizefn(DeviceState *dev, Error **errp)
|
||||
qemu_register_reset(kbd_reset, s);
|
||||
}
|
||||
|
||||
static void i8042_build_aml(ISADevice *isadev, Aml *scope)
|
||||
static void i8042_build_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
ISAKBDState *isa_s = I8042(isadev);
|
||||
ISAKBDState *isa_s = I8042(adev);
|
||||
Aml *kbd;
|
||||
Aml *mou;
|
||||
Aml *crs;
|
||||
@ -807,12 +807,12 @@ static Property i8042_properties[] = {
|
||||
static void i8042_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ISADeviceClass *isa = ISA_DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
device_class_set_props(dc, i8042_properties);
|
||||
dc->realize = i8042_realizefn;
|
||||
dc->vmsd = &vmstate_kbd_isa;
|
||||
isa->build_aml = i8042_build_aml;
|
||||
adevc->build_dev_aml = i8042_build_aml;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
}
|
||||
|
||||
@ -822,6 +822,10 @@ static const TypeInfo i8042_info = {
|
||||
.instance_size = sizeof(ISAKBDState),
|
||||
.instance_init = i8042_initfn,
|
||||
.class_init = i8042_class_initfn,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void i8042_register_types(void)
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/acpi/ipmi.h"
|
||||
|
||||
#define TYPE_ISA_IPMI_BT "isa-ipmi-bt"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(ISAIPMIBTDevice, ISA_IPMI_BT)
|
||||
@ -144,6 +145,7 @@ static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc);
|
||||
|
||||
dc->realize = isa_ipmi_bt_realize;
|
||||
device_class_set_props(dc, ipmi_isa_properties);
|
||||
@ -151,6 +153,7 @@ static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data)
|
||||
iic->get_backend_data = isa_ipmi_bt_get_backend_data;
|
||||
ipmi_bt_class_init(iic);
|
||||
iic->get_fwinfo = isa_ipmi_bt_get_fwinfo;
|
||||
adevc->build_dev_aml = build_ipmi_dev_aml;
|
||||
}
|
||||
|
||||
static const TypeInfo isa_ipmi_bt_info = {
|
||||
@ -161,6 +164,7 @@ static const TypeInfo isa_ipmi_bt_info = {
|
||||
.class_init = isa_ipmi_bt_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_IPMI_INTERFACE },
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/acpi/ipmi.h"
|
||||
|
||||
#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(ISAIPMIKCSDevice, ISA_IPMI_KCS)
|
||||
@ -151,6 +152,7 @@ static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc);
|
||||
|
||||
dc->realize = ipmi_isa_realize;
|
||||
device_class_set_props(dc, ipmi_isa_properties);
|
||||
@ -158,6 +160,7 @@ static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data)
|
||||
iic->get_backend_data = isa_ipmi_kcs_get_backend_data;
|
||||
ipmi_kcs_class_init(iic);
|
||||
iic->get_fwinfo = isa_ipmi_kcs_get_fwinfo;
|
||||
adevc->build_dev_aml = build_ipmi_dev_aml;
|
||||
}
|
||||
|
||||
static const TypeInfo isa_ipmi_kcs_info = {
|
||||
@ -168,6 +171,7 @@ static const TypeInfo isa_ipmi_kcs_info = {
|
||||
.class_init = isa_ipmi_kcs_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_IPMI_INTERFACE },
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/acpi/ipmi.h"
|
||||
|
||||
#define TYPE_SMBUS_IPMI "smbus-ipmi"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(SMBusIPMIDevice, SMBUS_IPMI)
|
||||
@ -353,6 +354,7 @@ static void smbus_ipmi_class_init(ObjectClass *oc, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
|
||||
SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(oc);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc);
|
||||
|
||||
sc->receive_byte = ipmi_receive_byte;
|
||||
sc->write_data = ipmi_write_data;
|
||||
@ -363,6 +365,7 @@ static void smbus_ipmi_class_init(ObjectClass *oc, void *data)
|
||||
iic->handle_if_event = smbus_ipmi_handle_event;
|
||||
iic->set_irq_enable = smbus_ipmi_set_irq_enable;
|
||||
iic->get_fwinfo = smbus_ipmi_get_fwinfo;
|
||||
adevc->build_dev_aml = build_ipmi_dev_aml;
|
||||
}
|
||||
|
||||
static const TypeInfo smbus_ipmi_info = {
|
||||
@ -373,6 +376,7 @@ static const TypeInfo smbus_ipmi_info = {
|
||||
.class_init = smbus_ipmi_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_IPMI_INTERFACE },
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
|
||||
static ISABus *isabus;
|
||||
|
||||
@ -190,15 +191,9 @@ ISADevice *isa_vga_init(ISABus *bus)
|
||||
void isa_build_aml(ISABus *bus, Aml *scope)
|
||||
{
|
||||
BusChild *kid;
|
||||
ISADevice *dev;
|
||||
ISADeviceClass *dc;
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->parent_obj.children, sibling) {
|
||||
dev = ISA_DEVICE(kid->child);
|
||||
dc = ISA_DEVICE_GET_CLASS(dev);
|
||||
if (dc->build_aml) {
|
||||
dc->build_aml(dev, scope);
|
||||
}
|
||||
call_dev_aml_func(DEVICE(kid->child), scope);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "hw/core/cpu.h"
|
||||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* ICH9 LPC PCI to ISA bridge */
|
||||
@ -803,12 +804,28 @@ static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
|
||||
acpi_send_gpe_event(&s->pm.acpi_regs, s->pm.irq, ev);
|
||||
}
|
||||
|
||||
static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
BusChild *kid;
|
||||
ICH9LPCState *s = ICH9_LPC_DEVICE(adev);
|
||||
BusState *bus = BUS(s->isa_bus);
|
||||
|
||||
/* ICH9 PCI to ISA irq remapping */
|
||||
aml_append(scope, aml_operation_region("PIRQ", AML_PCI_CONFIG,
|
||||
aml_int(0x60), 0x0C));
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
call_dev_aml_func(DEVICE(kid->child), scope);
|
||||
}
|
||||
}
|
||||
|
||||
static void ich9_lpc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||
AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass);
|
||||
AcpiDevAmlIfClass *amldevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
||||
dc->reset = ich9_lpc_reset;
|
||||
@ -833,6 +850,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data)
|
||||
adevc->ospm_status = ich9_pm_ospm_status;
|
||||
adevc->send_event = ich9_send_gpe;
|
||||
adevc->madt_cpu = pc_madt_cpu_entry;
|
||||
amldevc->build_dev_aml = build_ich9_isa_aml;
|
||||
}
|
||||
|
||||
static const TypeInfo ich9_lpc_info = {
|
||||
@ -845,6 +863,7 @@ static const TypeInfo ich9_lpc_info = {
|
||||
{ TYPE_HOTPLUG_HANDLER },
|
||||
{ TYPE_ACPI_DEVICE_IF },
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
115
hw/isa/piix3.c
115
hw/isa/piix3.c
@ -24,6 +24,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/southbridge/piix.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/isa/isa.h"
|
||||
@ -32,12 +33,10 @@
|
||||
#include "sysemu/reset.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
|
||||
#define XEN_PIIX_NUM_PIRQS 128ULL
|
||||
|
||||
#define TYPE_PIIX3_DEVICE "PIIX3"
|
||||
#define TYPE_PIIX3_XEN_DEVICE "PIIX3-xen"
|
||||
|
||||
static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq)
|
||||
{
|
||||
qemu_set_irq(piix3->pic[pic_irq],
|
||||
@ -81,6 +80,17 @@ static void piix3_set_irq(void *opaque, int pirq, int level)
|
||||
piix3_set_irq_level(piix3, pirq, level);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the global irq number corresponding to a given device irq
|
||||
* pin. We could also use the bus number to have a more precise mapping.
|
||||
*/
|
||||
static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx)
|
||||
{
|
||||
int slot_addend;
|
||||
slot_addend = PCI_SLOT(pci_dev->devfn) - 1;
|
||||
return (pci_intx + slot_addend) & 3;
|
||||
}
|
||||
|
||||
static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin)
|
||||
{
|
||||
PIIX3State *piix3 = opaque;
|
||||
@ -269,7 +279,7 @@ static const MemoryRegionOps rcr_ops = {
|
||||
.endianness = DEVICE_LITTLE_ENDIAN
|
||||
};
|
||||
|
||||
static void piix3_realize(PCIDevice *dev, Error **errp)
|
||||
static void pci_piix3_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
PIIX3State *d = PIIX3_PCI_DEVICE(dev);
|
||||
|
||||
@ -286,15 +296,28 @@ static void piix3_realize(PCIDevice *dev, Error **errp)
|
||||
qemu_register_reset(piix3_reset, d);
|
||||
}
|
||||
|
||||
static void build_pci_isa_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
BusChild *kid;
|
||||
BusState *bus = qdev_get_child_bus(DEVICE(adev), "isa.0");
|
||||
|
||||
/* PIIX PCI to ISA irq remapping */
|
||||
aml_append(scope, aml_operation_region("P40C", AML_PCI_CONFIG,
|
||||
aml_int(0x60), 0x04));
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
call_dev_aml_func(DEVICE(kid->child), scope);
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_piix3_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
dc->desc = "ISA bridge";
|
||||
dc->vmsd = &vmstate_piix3;
|
||||
dc->hotpluggable = false;
|
||||
k->realize = piix3_realize;
|
||||
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
/* 82371SB PIIX3 PCI-to-ISA bridge (Step A1) */
|
||||
k->device_id = PCI_DEVICE_ID_INTEL_82371SB_0;
|
||||
@ -304,6 +327,7 @@ static void pci_piix3_class_init(ObjectClass *klass, void *data)
|
||||
* pc_piix.c's pc_init1()
|
||||
*/
|
||||
dc->user_creatable = false;
|
||||
adevc->build_dev_aml = build_pci_isa_aml;
|
||||
}
|
||||
|
||||
static const TypeInfo piix3_pci_type_info = {
|
||||
@ -314,15 +338,33 @@ static const TypeInfo piix3_pci_type_info = {
|
||||
.class_init = pci_piix3_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ INTERFACE_CONVENTIONAL_PCI_DEVICE },
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void piix3_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev);
|
||||
PCIBus *pci_bus = pci_get_bus(dev);
|
||||
|
||||
pci_piix3_realize(dev, errp);
|
||||
if (*errp) {
|
||||
return;
|
||||
}
|
||||
|
||||
pci_bus_irqs(pci_bus, piix3_set_irq, pci_slot_get_pirq,
|
||||
piix3, PIIX_NUM_PIRQS);
|
||||
pci_bus_set_route_irq_fn(pci_bus, piix3_route_intx_pin_to_irq);
|
||||
};
|
||||
|
||||
static void piix3_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->config_write = piix3_write_config;
|
||||
k->realize = piix3_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo piix3_info = {
|
||||
@ -331,11 +373,33 @@ static const TypeInfo piix3_info = {
|
||||
.class_init = piix3_class_init,
|
||||
};
|
||||
|
||||
static void piix3_xen_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev);
|
||||
PCIBus *pci_bus = pci_get_bus(dev);
|
||||
|
||||
pci_piix3_realize(dev, errp);
|
||||
if (*errp) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Xen supports additional interrupt routes from the PCI devices to
|
||||
* the IOAPIC: the four pins of each PCI device on the bus are also
|
||||
* connected to the IOAPIC directly.
|
||||
* These additional routes can be discovered through ACPI.
|
||||
*/
|
||||
pci_bus_irqs(pci_bus, xen_piix3_set_irq, xen_pci_slot_get_pirq,
|
||||
piix3, XEN_PIIX_NUM_PIRQS);
|
||||
};
|
||||
|
||||
static void piix3_xen_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->config_write = piix3_write_config_xen;
|
||||
k->realize = piix3_xen_realize;
|
||||
};
|
||||
|
||||
static const TypeInfo piix3_xen_info = {
|
||||
@ -352,44 +416,3 @@ static void piix3_register_types(void)
|
||||
}
|
||||
|
||||
type_init(piix3_register_types)
|
||||
|
||||
/*
|
||||
* Return the global irq number corresponding to a given device irq
|
||||
* pin. We could also use the bus number to have a more precise mapping.
|
||||
*/
|
||||
static int pci_slot_get_pirq(PCIDevice *pci_dev, int pci_intx)
|
||||
{
|
||||
int slot_addend;
|
||||
slot_addend = PCI_SLOT(pci_dev->devfn) - 1;
|
||||
return (pci_intx + slot_addend) & 3;
|
||||
}
|
||||
|
||||
PIIX3State *piix3_create(PCIBus *pci_bus, ISABus **isa_bus)
|
||||
{
|
||||
PIIX3State *piix3;
|
||||
PCIDevice *pci_dev;
|
||||
|
||||
/*
|
||||
* Xen supports additional interrupt routes from the PCI devices to
|
||||
* the IOAPIC: the four pins of each PCI device on the bus are also
|
||||
* connected to the IOAPIC directly.
|
||||
* These additional routes can be discovered through ACPI.
|
||||
*/
|
||||
if (xen_enabled()) {
|
||||
pci_dev = pci_create_simple_multifunction(pci_bus, -1, true,
|
||||
TYPE_PIIX3_XEN_DEVICE);
|
||||
piix3 = PIIX3_PCI_DEVICE(pci_dev);
|
||||
pci_bus_irqs(pci_bus, xen_piix3_set_irq, xen_pci_slot_get_pirq,
|
||||
piix3, XEN_PIIX_NUM_PIRQS);
|
||||
} else {
|
||||
pci_dev = pci_create_simple_multifunction(pci_bus, -1, true,
|
||||
TYPE_PIIX3_DEVICE);
|
||||
piix3 = PIIX3_PCI_DEVICE(pci_dev);
|
||||
pci_bus_irqs(pci_bus, piix3_set_irq, pci_slot_get_pirq,
|
||||
piix3, PIIX_NUM_PIRQS);
|
||||
pci_bus_set_route_irq_fn(pci_bus, piix3_route_intx_pin_to_irq);
|
||||
}
|
||||
*isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0"));
|
||||
|
||||
return piix3;
|
||||
}
|
||||
|
116
hw/isa/piix4.c
116
hw/isa/piix4.c
@ -34,6 +34,8 @@
|
||||
#include "hw/timer/i8254.h"
|
||||
#include "hw/rtc/mc146818rtc.h"
|
||||
#include "hw/ide/pci.h"
|
||||
#include "hw/acpi/piix4.h"
|
||||
#include "hw/usb/hcd-uhci.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "sysemu/runstate.h"
|
||||
@ -45,6 +47,9 @@ struct PIIX4State {
|
||||
qemu_irq *isa;
|
||||
|
||||
RTCState rtc;
|
||||
PCIIDEState ide;
|
||||
UHCIState uhci;
|
||||
PIIX4PMState pm;
|
||||
/* Reset Control Register */
|
||||
MemoryRegion rcr_mem;
|
||||
uint8_t rcr;
|
||||
@ -73,6 +78,31 @@ static void piix4_set_irq(void *opaque, int irq_num, int level)
|
||||
}
|
||||
}
|
||||
|
||||
static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
|
||||
{
|
||||
int slot;
|
||||
|
||||
slot = PCI_SLOT(pci_dev->devfn);
|
||||
|
||||
switch (slot) {
|
||||
/* PIIX4 USB */
|
||||
case 10:
|
||||
return 3;
|
||||
/* AMD 79C973 Ethernet */
|
||||
case 11:
|
||||
return 1;
|
||||
/* Crystal 4281 Sound */
|
||||
case 12:
|
||||
return 2;
|
||||
/* PCI slot 1 to 4 */
|
||||
case 18 ... 21:
|
||||
return ((slot - 18) + irq_num) & 0x03;
|
||||
/* Unknown device, don't do any translation */
|
||||
default:
|
||||
return irq_num;
|
||||
}
|
||||
}
|
||||
|
||||
static void piix4_isa_reset(DeviceState *dev)
|
||||
{
|
||||
PIIX4State *d = PIIX4_PCI_DEVICE(dev);
|
||||
@ -179,6 +209,7 @@ static const MemoryRegionOps piix4_rcr_ops = {
|
||||
static void piix4_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
PIIX4State *s = PIIX4_PCI_DEVICE(dev);
|
||||
PCIBus *pci_bus = pci_get_bus(dev);
|
||||
ISABus *isa_bus;
|
||||
qemu_irq *i8259_out_irq;
|
||||
|
||||
@ -217,13 +248,41 @@ static void piix4_realize(PCIDevice *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
s->rtc.irq = isa_get_irq(ISA_DEVICE(&s->rtc), s->rtc.isairq);
|
||||
|
||||
/* IDE */
|
||||
qdev_prop_set_int32(DEVICE(&s->ide), "addr", dev->devfn + 1);
|
||||
if (!qdev_realize(DEVICE(&s->ide), BUS(pci_bus), errp)) {
|
||||
return;
|
||||
}
|
||||
pci_ide_create_devs(PCI_DEVICE(&s->ide));
|
||||
|
||||
/* USB */
|
||||
qdev_prop_set_int32(DEVICE(&s->uhci), "addr", dev->devfn + 2);
|
||||
if (!qdev_realize(DEVICE(&s->uhci), BUS(pci_bus), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* ACPI controller */
|
||||
qdev_prop_set_int32(DEVICE(&s->pm), "addr", dev->devfn + 3);
|
||||
if (!qdev_realize(DEVICE(&s->pm), BUS(pci_bus), errp)) {
|
||||
return;
|
||||
}
|
||||
qdev_connect_gpio_out(DEVICE(&s->pm), 0, s->isa[9]);
|
||||
|
||||
pci_bus_irqs(pci_bus, piix4_set_irq, pci_slot_get_pirq, s, PIIX_NUM_PIRQS);
|
||||
}
|
||||
|
||||
static void piix4_init(Object *obj)
|
||||
{
|
||||
PIIX4State *s = PIIX4_PCI_DEVICE(obj);
|
||||
|
||||
object_initialize(&s->rtc, sizeof(s->rtc), TYPE_MC146818_RTC);
|
||||
object_initialize_child(obj, "rtc", &s->rtc, TYPE_MC146818_RTC);
|
||||
object_initialize_child(obj, "ide", &s->ide, "piix4-ide");
|
||||
object_initialize_child(obj, "uhci", &s->uhci, "piix4-usb-uhci");
|
||||
|
||||
object_initialize_child(obj, "pm", &s->pm, TYPE_PIIX4_PM);
|
||||
qdev_prop_set_uint32(DEVICE(&s->pm), "smb_io_base", 0x1100);
|
||||
qdev_prop_set_bit(DEVICE(&s->pm), "smm-enabled", 0);
|
||||
}
|
||||
|
||||
static void piix4_class_init(ObjectClass *klass, void *data)
|
||||
@ -264,58 +323,3 @@ static void piix4_register_types(void)
|
||||
}
|
||||
|
||||
type_init(piix4_register_types)
|
||||
|
||||
static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num)
|
||||
{
|
||||
int slot;
|
||||
|
||||
slot = PCI_SLOT(pci_dev->devfn);
|
||||
|
||||
switch (slot) {
|
||||
/* PIIX4 USB */
|
||||
case 10:
|
||||
return 3;
|
||||
/* AMD 79C973 Ethernet */
|
||||
case 11:
|
||||
return 1;
|
||||
/* Crystal 4281 Sound */
|
||||
case 12:
|
||||
return 2;
|
||||
/* PCI slot 1 to 4 */
|
||||
case 18 ... 21:
|
||||
return ((slot - 18) + irq_num) & 0x03;
|
||||
/* Unknown device, don't do any translation */
|
||||
default:
|
||||
return irq_num;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceState *piix4_create(PCIBus *pci_bus, ISABus **isa_bus, I2CBus **smbus)
|
||||
{
|
||||
PIIX4State *s;
|
||||
PCIDevice *pci;
|
||||
DeviceState *dev;
|
||||
int devfn = PCI_DEVFN(10, 0);
|
||||
|
||||
pci = pci_create_simple_multifunction(pci_bus, devfn, true,
|
||||
TYPE_PIIX4_PCI_DEVICE);
|
||||
dev = DEVICE(pci);
|
||||
s = PIIX4_PCI_DEVICE(pci);
|
||||
if (isa_bus) {
|
||||
*isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0"));
|
||||
}
|
||||
|
||||
pci = pci_create_simple(pci_bus, devfn + 1, "piix4-ide");
|
||||
pci_ide_create_devs(pci);
|
||||
|
||||
pci_create_simple(pci_bus, devfn + 2, "piix4-usb-uhci");
|
||||
if (smbus) {
|
||||
*smbus = piix4_pm_init(pci_bus, devfn + 3, 0x1100,
|
||||
qdev_get_gpio_in_named(dev, "isa", 9),
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
|
||||
pci_bus_irqs(pci_bus, piix4_set_irq, pci_slot_get_pirq, s, PIIX_NUM_PIRQS);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
@ -787,7 +787,8 @@ static void boston_mach_init(MachineState *machine)
|
||||
|
||||
if (kernel_size > 0) {
|
||||
int dt_size;
|
||||
g_autofree const void *dtb_file_data, *dtb_load_data;
|
||||
g_autofree const void *dtb_file_data = NULL;
|
||||
g_autofree const void *dtb_load_data = NULL;
|
||||
hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB);
|
||||
hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr);
|
||||
|
||||
|
@ -353,7 +353,7 @@ static void mips_jazz_init(MachineState *machine,
|
||||
fds[n] = drive_get(IF_FLOPPY, 0, n);
|
||||
}
|
||||
/* FIXME: we should enable DMA with a custom IsaDma device */
|
||||
fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), -1, 0x80003000, fds);
|
||||
fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0x80003000, fds);
|
||||
|
||||
/* Real time clock */
|
||||
mc146818_rtc_init(isa_bus, 1980, NULL);
|
||||
|
@ -1237,7 +1237,9 @@ void mips_malta_init(MachineState *machine)
|
||||
int fl_idx = 0;
|
||||
int be;
|
||||
MaltaState *s;
|
||||
PCIDevice *piix4;
|
||||
DeviceState *dev;
|
||||
DeviceState *pm_dev;
|
||||
|
||||
s = MIPS_MALTA(qdev_new(TYPE_MIPS_MALTA));
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal);
|
||||
@ -1399,7 +1401,12 @@ void mips_malta_init(MachineState *machine)
|
||||
empty_slot_init("GT64120", 0, 0x20000000);
|
||||
|
||||
/* Southbridge */
|
||||
dev = piix4_create(pci_bus, &isa_bus, &smbus);
|
||||
piix4 = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(10, 0), true,
|
||||
TYPE_PIIX4_PCI_DEVICE);
|
||||
dev = DEVICE(piix4);
|
||||
isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0"));
|
||||
pm_dev = DEVICE(object_resolve_path_component(OBJECT(dev), "pm"));
|
||||
smbus = I2C_BUS(qdev_get_child_bus(pm_dev, "i2c"));
|
||||
|
||||
/* Interrupt controller */
|
||||
qdev_connect_gpio_out_named(dev, "intr", 0, i8259_irq);
|
||||
|
@ -37,10 +37,14 @@
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
|
||||
/* #define DEBUG_SMC */
|
||||
|
||||
#define APPLESMC_DEFAULT_IOBASE 0x300
|
||||
#define TYPE_APPLE_SMC "isa-applesmc"
|
||||
#define APPLESMC_MAX_DATA_LENGTH 32
|
||||
#define APPLESMC_PROP_IO_BASE "iobase"
|
||||
|
||||
enum {
|
||||
APPLESMC_DATA_PORT = 0x00,
|
||||
@ -347,14 +351,35 @@ static Property applesmc_isa_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void build_applesmc_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
Aml *crs;
|
||||
AppleSMCState *s = APPLE_SMC(adev);
|
||||
uint32_t iobase = s->iobase;
|
||||
Aml *dev = aml_device("SMC");
|
||||
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0001")));
|
||||
/* device present, functioning, decoding, not shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_io(AML_DECODE16, iobase, iobase, 0x01, APPLESMC_MAX_DATA_LENGTH)
|
||||
);
|
||||
aml_append(crs, aml_irq_no_flags(6));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static void qdev_applesmc_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
dc->realize = applesmc_isa_realize;
|
||||
dc->reset = qdev_applesmc_isa_reset;
|
||||
device_class_set_props(dc, applesmc_isa_properties);
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
adevc->build_dev_aml = build_applesmc_aml;
|
||||
}
|
||||
|
||||
static const TypeInfo applesmc_isa_info = {
|
||||
@ -362,6 +387,10 @@ static const TypeInfo applesmc_isa_info = {
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(AppleSMCState),
|
||||
.class_init = qdev_applesmc_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void applesmc_register_types(void)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "qom/object.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "standard-headers/linux/pvpanic.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PVPanicISAState, PVPANIC_ISA_DEVICE)
|
||||
|
||||
@ -63,6 +64,41 @@ static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp)
|
||||
isa_register_ioport(d, &ps->mr, s->ioport);
|
||||
}
|
||||
|
||||
static void build_pvpanic_isa_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
Aml *crs, *field, *method;
|
||||
PVPanicISAState *s = PVPANIC_ISA_DEVICE(adev);
|
||||
Aml *dev = aml_device("PEVT");
|
||||
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0001")));
|
||||
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_io(AML_DECODE16, s->ioport, s->ioport, 1, 1)
|
||||
);
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
|
||||
aml_int(s->ioport), 1));
|
||||
field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PEPT", 8));
|
||||
aml_append(dev, field);
|
||||
|
||||
/* device present, functioning, decoding, shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
|
||||
|
||||
method = aml_method("RDPT", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_store(aml_name("PEPT"), aml_local(0)));
|
||||
aml_append(method, aml_return(aml_local(0)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("WRPT", 1, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_store(aml_arg(0), aml_name("PEPT")));
|
||||
aml_append(dev, method);
|
||||
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static Property pvpanic_isa_properties[] = {
|
||||
DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505),
|
||||
DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events,
|
||||
@ -73,10 +109,12 @@ static Property pvpanic_isa_properties[] = {
|
||||
static void pvpanic_isa_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
dc->realize = pvpanic_isa_realizefn;
|
||||
device_class_set_props(dc, pvpanic_isa_properties);
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
adevc->build_dev_aml = build_pvpanic_isa_aml;
|
||||
}
|
||||
|
||||
static const TypeInfo pvpanic_isa_info = {
|
||||
@ -85,6 +123,10 @@ static const TypeInfo pvpanic_isa_info = {
|
||||
.instance_size = sizeof(PVPanicISAState),
|
||||
.instance_init = pvpanic_isa_initfn,
|
||||
.class_init = pvpanic_isa_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void pvpanic_register_types(void)
|
||||
|
@ -443,26 +443,3 @@ static void etsec_register_types(void)
|
||||
}
|
||||
|
||||
type_init(etsec_register_types)
|
||||
|
||||
DeviceState *etsec_create(hwaddr base,
|
||||
MemoryRegion * mr,
|
||||
NICInfo * nd,
|
||||
qemu_irq tx_irq,
|
||||
qemu_irq rx_irq,
|
||||
qemu_irq err_irq)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_new("eTSEC");
|
||||
qdev_set_nic_properties(dev, nd);
|
||||
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, tx_irq);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, rx_irq);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, err_irq);
|
||||
|
||||
memory_region_add_subregion(mr, base,
|
||||
SYS_BUS_DEVICE(dev)->mmio[0].memory);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
@ -155,13 +155,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(eTSEC, ETSEC_COMMON)
|
||||
#define eTSEC_TRANSMIT 1
|
||||
#define eTSEC_RECEIVE 2
|
||||
|
||||
DeviceState *etsec_create(hwaddr base,
|
||||
MemoryRegion *mr,
|
||||
NICInfo *nd,
|
||||
qemu_irq tx_irq,
|
||||
qemu_irq rx_irq,
|
||||
qemu_irq err_irq);
|
||||
|
||||
void etsec_update_irq(eTSEC *etsec);
|
||||
|
||||
void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr);
|
||||
|
@ -3,7 +3,8 @@ pci_ss.add(files('pci_bridge_dev.c'))
|
||||
pci_ss.add(when: 'CONFIG_I82801B11', if_true: files('i82801b11.c'))
|
||||
pci_ss.add(when: 'CONFIG_IOH3420', if_true: files('ioh3420.c'))
|
||||
pci_ss.add(when: 'CONFIG_PCIE_PORT', if_true: files('pcie_root_port.c', 'gen_pcie_root_port.c', 'pcie_pci_bridge.c'))
|
||||
pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c'))
|
||||
pci_ss.add(when: 'CONFIG_PXB', if_true: files('pci_expander_bridge.c'),
|
||||
if_false: files('pci_expander_bridge_stubs.c'))
|
||||
pci_ss.add(when: 'CONFIG_XIO3130', if_true: files('xio3130_upstream.c', 'xio3130_downstream.c'))
|
||||
pci_ss.add(when: 'CONFIG_CXL', if_true: files('cxl_root_port.c'))
|
||||
|
||||
@ -13,3 +14,5 @@ pci_ss.add(when: 'CONFIG_DEC_PCI', if_true: files('dec.c'))
|
||||
pci_ss.add(when: 'CONFIG_SIMBA', if_true: files('simba.c'))
|
||||
|
||||
softmmu_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss)
|
||||
|
||||
softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('pci_expander_bridge_stubs.c'))
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci-bridge/pci_expander_bridge.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/error-report.h"
|
||||
@ -186,25 +187,38 @@ static const TypeInfo pxb_host_info = {
|
||||
|
||||
static void pxb_cxl_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
CXLHost *cxl = PXB_CXL_HOST(dev);
|
||||
CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
|
||||
struct MemoryRegion *mr = &cxl_cstate->crb.component_registers;
|
||||
hwaddr offset;
|
||||
|
||||
cxl_component_register_block_init(OBJECT(dev), cxl_cstate,
|
||||
TYPE_PXB_CXL_HOST);
|
||||
sysbus_init_mmio(sbd, mr);
|
||||
}
|
||||
|
||||
offset = memory_region_size(mr) * ms->cxl_devices_state->next_mr_idx;
|
||||
if (offset > memory_region_size(&ms->cxl_devices_state->host_mr)) {
|
||||
/*
|
||||
* Host bridge realization has no means of knowning state associated
|
||||
* with a particular machine. As such, it is nececssary to delay
|
||||
* final setup of the host bridge register space until later in the
|
||||
* machine bring up.
|
||||
*/
|
||||
void pxb_cxl_hook_up_registers(CXLState *cxl_state, PCIBus *bus, Error **errp)
|
||||
{
|
||||
PXBDev *pxb = PXB_CXL_DEV(pci_bridge_get_device(bus));
|
||||
CXLHost *cxl = pxb->cxl.cxl_host_bridge;
|
||||
CXLComponentState *cxl_cstate = &cxl->cxl_cstate;
|
||||
struct MemoryRegion *mr = &cxl_cstate->crb.component_registers;
|
||||
hwaddr offset;
|
||||
|
||||
offset = memory_region_size(mr) * cxl_state->next_mr_idx;
|
||||
if (offset > memory_region_size(&cxl_state->host_mr)) {
|
||||
error_setg(errp, "Insufficient space for pxb cxl host register space");
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_add_subregion(&ms->cxl_devices_state->host_mr, offset, mr);
|
||||
ms->cxl_devices_state->next_mr_idx++;
|
||||
memory_region_add_subregion(&cxl_state->host_mr, offset, mr);
|
||||
cxl_state->next_mr_idx++;
|
||||
}
|
||||
|
||||
static void pxb_cxl_host_class_init(ObjectClass *class, void *data)
|
||||
@ -461,17 +475,11 @@ static const TypeInfo pxb_pcie_dev_info = {
|
||||
|
||||
static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp)
|
||||
{
|
||||
MachineState *ms = MACHINE(qdev_get_machine());
|
||||
|
||||
/* A CXL PXB's parent bus is still PCIe */
|
||||
if (!pci_bus_is_express(pci_get_bus(dev))) {
|
||||
error_setg(errp, "pxb-cxl devices cannot reside on a PCI bus");
|
||||
return;
|
||||
}
|
||||
if (!ms->cxl_devices_state->is_enabled) {
|
||||
error_setg(errp, "Machine does not have cxl=on");
|
||||
return;
|
||||
}
|
||||
|
||||
pxb_dev_realize_common(dev, CXL, errp);
|
||||
pxb_dev_reset(DEVICE(dev));
|
||||
|
14
hw/pci-bridge/pci_expander_bridge_stubs.c
Normal file
14
hw/pci-bridge/pci_expander_bridge_stubs.c
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Stubs for calls made from machines to handle the case where CONFIG_PXB
|
||||
* is not enabled.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/pci-bridge/pci_expander_bridge.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
|
||||
void pxb_cxl_hook_up_registers(CXLState *state, PCIBus *bus, Error **errp) {};
|
49
hw/pci/msi.c
49
hw/pci/msi.c
@ -134,7 +134,7 @@ void msi_set_message(PCIDevice *dev, MSIMessage msg)
|
||||
pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data);
|
||||
}
|
||||
|
||||
MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector)
|
||||
static MSIMessage msi_prepare_message(PCIDevice *dev, unsigned int vector)
|
||||
{
|
||||
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
|
||||
bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
|
||||
@ -159,6 +159,11 @@ MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector)
|
||||
return msg;
|
||||
}
|
||||
|
||||
MSIMessage msi_get_message(PCIDevice *dev, unsigned int vector)
|
||||
{
|
||||
return dev->msi_prepare_message(dev, vector);
|
||||
}
|
||||
|
||||
bool msi_enabled(const PCIDevice *dev)
|
||||
{
|
||||
return msi_present(dev) &&
|
||||
@ -241,6 +246,8 @@ int msi_init(struct PCIDevice *dev, uint8_t offset,
|
||||
0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors));
|
||||
}
|
||||
|
||||
dev->msi_prepare_message = msi_prepare_message;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -256,6 +263,7 @@ void msi_uninit(struct PCIDevice *dev)
|
||||
cap_size = msi_cap_sizeof(flags);
|
||||
pci_del_capability(dev, PCI_CAP_ID_MSI, cap_size);
|
||||
dev->cap_present &= ~QEMU_PCI_CAP_MSI;
|
||||
dev->msi_prepare_message = NULL;
|
||||
|
||||
MSI_DEV_PRINTF(dev, "uninit\n");
|
||||
}
|
||||
@ -307,6 +315,39 @@ bool msi_is_masked(const PCIDevice *dev, unsigned int vector)
|
||||
return mask & (1U << vector);
|
||||
}
|
||||
|
||||
void msi_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
|
||||
bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
|
||||
uint32_t irq_state, vector_mask, pending;
|
||||
|
||||
if (vector > PCI_MSI_VECTORS_MAX) {
|
||||
error_setg(errp, "msi: vector %d not allocated. max vector is %d",
|
||||
vector, PCI_MSI_VECTORS_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
vector_mask = (1U << vector);
|
||||
|
||||
irq_state = pci_get_long(dev->config + msi_mask_off(dev, msi64bit));
|
||||
|
||||
if (mask) {
|
||||
irq_state |= vector_mask;
|
||||
} else {
|
||||
irq_state &= ~vector_mask;
|
||||
}
|
||||
|
||||
pci_set_long(dev->config + msi_mask_off(dev, msi64bit), irq_state);
|
||||
|
||||
pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit));
|
||||
if (!mask && (pending & vector_mask)) {
|
||||
pending &= ~vector_mask;
|
||||
pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending);
|
||||
msi_notify(dev, vector);
|
||||
}
|
||||
}
|
||||
|
||||
void msi_notify(PCIDevice *dev, unsigned int vector)
|
||||
{
|
||||
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
|
||||
@ -334,11 +375,7 @@ void msi_notify(PCIDevice *dev, unsigned int vector)
|
||||
|
||||
void msi_send_message(PCIDevice *dev, MSIMessage msg)
|
||||
{
|
||||
MemTxAttrs attrs = {};
|
||||
|
||||
attrs.requester_id = pci_requester_id(dev);
|
||||
address_space_stl_le(&dev->bus_master_as, msg.address, msg.data,
|
||||
attrs, NULL);
|
||||
dev->msi_trigger(dev, msg);
|
||||
}
|
||||
|
||||
/* Normally called by pci_default_write_config(). */
|
||||
|
@ -31,7 +31,7 @@
|
||||
#define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)
|
||||
#define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8)
|
||||
|
||||
MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
|
||||
static MSIMessage msix_prepare_message(PCIDevice *dev, unsigned vector)
|
||||
{
|
||||
uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
|
||||
MSIMessage msg;
|
||||
@ -41,6 +41,11 @@ MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
|
||||
return msg;
|
||||
}
|
||||
|
||||
MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
|
||||
{
|
||||
return dev->msix_prepare_message(dev, vector);
|
||||
}
|
||||
|
||||
/*
|
||||
* Special API for POWER to configure the vectors through
|
||||
* a side channel. Should never be used by devices.
|
||||
@ -131,6 +136,31 @@ static void msix_handle_mask_update(PCIDevice *dev, int vector, bool was_masked)
|
||||
}
|
||||
}
|
||||
|
||||
void msix_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
unsigned offset;
|
||||
bool was_masked;
|
||||
|
||||
if (vector > dev->msix_entries_nr) {
|
||||
error_setg(errp, "msix: vector %d not allocated. max vector is %d",
|
||||
vector, dev->msix_entries_nr);
|
||||
return;
|
||||
}
|
||||
|
||||
offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
|
||||
|
||||
was_masked = msix_is_masked(dev, vector);
|
||||
|
||||
if (mask) {
|
||||
dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
|
||||
} else {
|
||||
dev->msix_table[offset] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
|
||||
}
|
||||
|
||||
msix_handle_mask_update(dev, vector, was_masked);
|
||||
}
|
||||
|
||||
static bool msix_masked(PCIDevice *dev)
|
||||
{
|
||||
return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK;
|
||||
@ -344,6 +374,8 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries,
|
||||
"msix-pba", pba_size);
|
||||
memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio);
|
||||
|
||||
dev->msix_prepare_message = msix_prepare_message;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -429,6 +461,7 @@ void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar)
|
||||
g_free(dev->msix_entry_used);
|
||||
dev->msix_entry_used = NULL;
|
||||
dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
|
||||
dev->msix_prepare_message = NULL;
|
||||
}
|
||||
|
||||
void msix_uninit_exclusive_bar(PCIDevice *dev)
|
||||
|
29
hw/pci/pci.c
29
hw/pci/pci.c
@ -317,6 +317,15 @@ void pci_device_deassert_intx(PCIDevice *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_msi_trigger(PCIDevice *dev, MSIMessage msg)
|
||||
{
|
||||
MemTxAttrs attrs = {};
|
||||
|
||||
attrs.requester_id = pci_requester_id(dev);
|
||||
address_space_stl_le(&dev->bus_master_as, msg.address, msg.data,
|
||||
attrs, NULL);
|
||||
}
|
||||
|
||||
static void pci_reset_regions(PCIDevice *dev)
|
||||
{
|
||||
int r;
|
||||
@ -1212,6 +1221,8 @@ static void pci_qdev_unrealize(DeviceState *dev)
|
||||
|
||||
pci_device_deassert_intx(pci_dev);
|
||||
do_pci_unregister_device(pci_dev);
|
||||
|
||||
pci_dev->msi_trigger = NULL;
|
||||
}
|
||||
|
||||
void pci_register_bar(PCIDevice *pci_dev, int region_num,
|
||||
@ -2251,6 +2262,8 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
|
||||
}
|
||||
|
||||
pci_set_power(pci_dev, true);
|
||||
|
||||
pci_dev->msi_trigger = pci_msi_trigger;
|
||||
}
|
||||
|
||||
PCIDevice *pci_new_multifunction(int devfn, bool multifunction,
|
||||
@ -2640,15 +2653,15 @@ static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len)
|
||||
static char *pcibus_get_fw_dev_path(DeviceState *dev)
|
||||
{
|
||||
PCIDevice *d = (PCIDevice *)dev;
|
||||
char path[50], name[33];
|
||||
int off;
|
||||
char name[33];
|
||||
int has_func = !!PCI_FUNC(d->devfn);
|
||||
|
||||
off = snprintf(path, sizeof(path), "%s@%x",
|
||||
pci_dev_fw_name(dev, name, sizeof name),
|
||||
PCI_SLOT(d->devfn));
|
||||
if (PCI_FUNC(d->devfn))
|
||||
snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn));
|
||||
return g_strdup(path);
|
||||
return g_strdup_printf("%s@%x%s%.*x",
|
||||
pci_dev_fw_name(dev, name, sizeof(name)),
|
||||
PCI_SLOT(d->devfn),
|
||||
has_func ? "," : "",
|
||||
has_func,
|
||||
PCI_FUNC(d->devfn));
|
||||
}
|
||||
|
||||
static char *pcibus_get_dev_path(DeviceState *dev)
|
||||
|
@ -2,3 +2,7 @@ config MULTIPROCESS
|
||||
bool
|
||||
depends on PCI && PCI_EXPRESS && KVM
|
||||
select REMOTE_PCIHOST
|
||||
|
||||
config VFIO_USER_SERVER
|
||||
bool
|
||||
depends on MULTIPROCESS
|
||||
|
131
hw/remote/iommu.c
Normal file
131
hw/remote/iommu.c
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* IOMMU for remote device
|
||||
*
|
||||
* Copyright © 2022 Oracle and/or its affiliates.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "hw/remote/iommu.h"
|
||||
#include "hw/pci/pci_bus.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "trace.h"
|
||||
|
||||
/**
|
||||
* IOMMU for TYPE_REMOTE_MACHINE - manages DMA address space isolation
|
||||
* for remote machine. It is used by TYPE_VFIO_USER_SERVER.
|
||||
*
|
||||
* - Each TYPE_VFIO_USER_SERVER instance handles one PCIDevice on a PCIBus.
|
||||
* There is one RemoteIommu per PCIBus, so the RemoteIommu tracks multiple
|
||||
* PCIDevices by maintaining a ->elem_by_devfn mapping.
|
||||
*
|
||||
* - memory_region_init_iommu() is not used because vfio-user MemoryRegions
|
||||
* will be added to the elem->mr container instead. This is more natural
|
||||
* than implementing the IOMMUMemoryRegionClass APIs since vfio-user
|
||||
* provides something that is close to a full-fledged MemoryRegion and
|
||||
* not like an IOMMU mapping.
|
||||
*
|
||||
* - When a device is hot unplugged, the elem->mr reference is dropped so
|
||||
* all vfio-user MemoryRegions associated with this vfio-user server are
|
||||
* destroyed.
|
||||
*/
|
||||
|
||||
static AddressSpace *remote_iommu_find_add_as(PCIBus *pci_bus,
|
||||
void *opaque, int devfn)
|
||||
{
|
||||
RemoteIommu *iommu = opaque;
|
||||
RemoteIommuElem *elem = NULL;
|
||||
|
||||
qemu_mutex_lock(&iommu->lock);
|
||||
|
||||
elem = g_hash_table_lookup(iommu->elem_by_devfn, INT2VOIDP(devfn));
|
||||
|
||||
if (!elem) {
|
||||
elem = g_malloc0(sizeof(RemoteIommuElem));
|
||||
g_hash_table_insert(iommu->elem_by_devfn, INT2VOIDP(devfn), elem);
|
||||
}
|
||||
|
||||
if (!elem->mr) {
|
||||
elem->mr = MEMORY_REGION(object_new(TYPE_MEMORY_REGION));
|
||||
memory_region_set_size(elem->mr, UINT64_MAX);
|
||||
address_space_init(&elem->as, elem->mr, NULL);
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&iommu->lock);
|
||||
|
||||
return &elem->as;
|
||||
}
|
||||
|
||||
void remote_iommu_unplug_dev(PCIDevice *pci_dev)
|
||||
{
|
||||
AddressSpace *as = pci_device_iommu_address_space(pci_dev);
|
||||
RemoteIommuElem *elem = NULL;
|
||||
|
||||
if (as == &address_space_memory) {
|
||||
return;
|
||||
}
|
||||
|
||||
elem = container_of(as, RemoteIommuElem, as);
|
||||
|
||||
address_space_destroy(&elem->as);
|
||||
|
||||
object_unref(elem->mr);
|
||||
|
||||
elem->mr = NULL;
|
||||
}
|
||||
|
||||
static void remote_iommu_init(Object *obj)
|
||||
{
|
||||
RemoteIommu *iommu = REMOTE_IOMMU(obj);
|
||||
|
||||
iommu->elem_by_devfn = g_hash_table_new_full(NULL, NULL, NULL, g_free);
|
||||
|
||||
qemu_mutex_init(&iommu->lock);
|
||||
}
|
||||
|
||||
static void remote_iommu_finalize(Object *obj)
|
||||
{
|
||||
RemoteIommu *iommu = REMOTE_IOMMU(obj);
|
||||
|
||||
qemu_mutex_destroy(&iommu->lock);
|
||||
|
||||
g_hash_table_destroy(iommu->elem_by_devfn);
|
||||
|
||||
iommu->elem_by_devfn = NULL;
|
||||
}
|
||||
|
||||
void remote_iommu_setup(PCIBus *pci_bus)
|
||||
{
|
||||
RemoteIommu *iommu = NULL;
|
||||
|
||||
g_assert(pci_bus);
|
||||
|
||||
iommu = REMOTE_IOMMU(object_new(TYPE_REMOTE_IOMMU));
|
||||
|
||||
pci_setup_iommu(pci_bus, remote_iommu_find_add_as, iommu);
|
||||
|
||||
object_property_add_child(OBJECT(pci_bus), "remote-iommu", OBJECT(iommu));
|
||||
|
||||
object_unref(OBJECT(iommu));
|
||||
}
|
||||
|
||||
static const TypeInfo remote_iommu_info = {
|
||||
.name = TYPE_REMOTE_IOMMU,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(RemoteIommu),
|
||||
.instance_init = remote_iommu_init,
|
||||
.instance_finalize = remote_iommu_finalize,
|
||||
};
|
||||
|
||||
static void remote_iommu_register_types(void)
|
||||
{
|
||||
type_register_static(&remote_iommu_info);
|
||||
}
|
||||
|
||||
type_init(remote_iommu_register_types)
|
@ -20,6 +20,11 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "hw/remote/iohub.h"
|
||||
#include "hw/remote/iommu.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/remote/iommu.h"
|
||||
#include "hw/remote/vfio-user-obj.h"
|
||||
#include "hw/pci/msi.h"
|
||||
|
||||
static void remote_machine_init(MachineState *machine)
|
||||
{
|
||||
@ -49,25 +54,102 @@ static void remote_machine_init(MachineState *machine)
|
||||
|
||||
pci_host = PCI_HOST_BRIDGE(rem_host);
|
||||
|
||||
remote_iohub_init(&s->iohub);
|
||||
if (s->vfio_user) {
|
||||
remote_iommu_setup(pci_host->bus);
|
||||
|
||||
pci_bus_irqs(pci_host->bus, remote_iohub_set_irq, remote_iohub_map_irq,
|
||||
&s->iohub, REMOTE_IOHUB_NB_PIRQS);
|
||||
msi_nonbroken = true;
|
||||
|
||||
vfu_object_set_bus_irq(pci_host->bus);
|
||||
} else {
|
||||
remote_iohub_init(&s->iohub);
|
||||
|
||||
pci_bus_irqs(pci_host->bus, remote_iohub_set_irq, remote_iohub_map_irq,
|
||||
&s->iohub, REMOTE_IOHUB_NB_PIRQS);
|
||||
}
|
||||
|
||||
qbus_set_hotplug_handler(BUS(pci_host->bus), OBJECT(s));
|
||||
}
|
||||
|
||||
static bool remote_machine_get_vfio_user(Object *obj, Error **errp)
|
||||
{
|
||||
RemoteMachineState *s = REMOTE_MACHINE(obj);
|
||||
|
||||
return s->vfio_user;
|
||||
}
|
||||
|
||||
static void remote_machine_set_vfio_user(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
RemoteMachineState *s = REMOTE_MACHINE(obj);
|
||||
|
||||
if (phase_check(PHASE_MACHINE_CREATED)) {
|
||||
error_setg(errp, "Error enabling vfio-user - machine already created");
|
||||
return;
|
||||
}
|
||||
|
||||
s->vfio_user = value;
|
||||
}
|
||||
|
||||
static bool remote_machine_get_auto_shutdown(Object *obj, Error **errp)
|
||||
{
|
||||
RemoteMachineState *s = REMOTE_MACHINE(obj);
|
||||
|
||||
return s->auto_shutdown;
|
||||
}
|
||||
|
||||
static void remote_machine_set_auto_shutdown(Object *obj, bool value,
|
||||
Error **errp)
|
||||
{
|
||||
RemoteMachineState *s = REMOTE_MACHINE(obj);
|
||||
|
||||
s->auto_shutdown = value;
|
||||
}
|
||||
|
||||
static void remote_machine_instance_init(Object *obj)
|
||||
{
|
||||
RemoteMachineState *s = REMOTE_MACHINE(obj);
|
||||
|
||||
s->auto_shutdown = true;
|
||||
}
|
||||
|
||||
static void remote_machine_dev_unplug_cb(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
qdev_unrealize(dev);
|
||||
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||
remote_iommu_unplug_dev(PCI_DEVICE(dev));
|
||||
}
|
||||
}
|
||||
|
||||
static void remote_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
|
||||
|
||||
mc->init = remote_machine_init;
|
||||
mc->desc = "Experimental remote machine";
|
||||
|
||||
hc->unplug = remote_machine_dev_unplug_cb;
|
||||
|
||||
object_class_property_add_bool(oc, "vfio-user",
|
||||
remote_machine_get_vfio_user,
|
||||
remote_machine_set_vfio_user);
|
||||
|
||||
object_class_property_add_bool(oc, "auto-shutdown",
|
||||
remote_machine_get_auto_shutdown,
|
||||
remote_machine_set_auto_shutdown);
|
||||
}
|
||||
|
||||
static const TypeInfo remote_machine = {
|
||||
.name = TYPE_REMOTE_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(RemoteMachineState),
|
||||
.instance_init = remote_machine_instance_init,
|
||||
.class_init = remote_machine_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_HOTPLUG_HANDLER },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void remote_machine_register_types(void)
|
||||
|
@ -6,6 +6,10 @@ remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('message.c'))
|
||||
remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('remote-obj.c'))
|
||||
remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('proxy.c'))
|
||||
remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('iohub.c'))
|
||||
remote_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('iommu.c'))
|
||||
remote_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_true: files('vfio-user-obj.c'))
|
||||
|
||||
remote_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_true: libvfio_user_dep)
|
||||
|
||||
specific_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('memory.c'))
|
||||
specific_ss.add(when: 'CONFIG_MULTIPROCESS', if_true: files('proxy-memory-listener.c'))
|
||||
|
@ -2,3 +2,14 @@
|
||||
|
||||
mpqemu_send_io_error(int cmd, int size, int nfds) "send command %d size %d, %d file descriptors to remote process"
|
||||
mpqemu_recv_io_error(int cmd, int size, int nfds) "failed to receive %d size %d, %d file descriptors to remote process"
|
||||
|
||||
# vfio-user-obj.c
|
||||
vfu_prop(const char *prop, const char *val) "vfu: setting %s as %s"
|
||||
vfu_cfg_read(uint32_t offset, uint32_t val) "vfu: cfg: 0x%u -> 0x%x"
|
||||
vfu_cfg_write(uint32_t offset, uint32_t val) "vfu: cfg: 0x%u <- 0x%x"
|
||||
vfu_dma_register(uint64_t gpa, size_t len) "vfu: registering GPA 0x%"PRIx64", %zu bytes"
|
||||
vfu_dma_unregister(uint64_t gpa) "vfu: unregistering GPA 0x%"PRIx64""
|
||||
vfu_bar_register(int i, uint64_t addr, uint64_t size) "vfu: BAR %d: addr 0x%"PRIx64" size 0x%"PRIx64""
|
||||
vfu_bar_rw_enter(const char *op, uint64_t addr) "vfu: %s request for BAR address 0x%"PRIx64""
|
||||
vfu_bar_rw_exit(const char *op, uint64_t addr) "vfu: Finished %s of BAR address 0x%"PRIx64""
|
||||
vfu_interrupt(int pirq) "vfu: sending interrupt to device - PIRQ %d"
|
||||
|
958
hw/remote/vfio-user-obj.c
Normal file
958
hw/remote/vfio-user-obj.c
Normal file
@ -0,0 +1,958 @@
|
||||
/**
|
||||
* QEMU vfio-user-server server object
|
||||
*
|
||||
* Copyright © 2022 Oracle and/or its affiliates.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL-v2, version 2 or later.
|
||||
*
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Usage: add options:
|
||||
* -machine x-remote,vfio-user=on,auto-shutdown=on
|
||||
* -device <PCI-device>,id=<pci-dev-id>
|
||||
* -object x-vfio-user-server,id=<id>,type=unix,path=<socket-path>,
|
||||
* device=<pci-dev-id>
|
||||
*
|
||||
* Note that x-vfio-user-server object must be used with x-remote machine only.
|
||||
* This server could only support PCI devices for now.
|
||||
*
|
||||
* type - SocketAddress type - presently "unix" alone is supported. Required
|
||||
* option
|
||||
*
|
||||
* path - named unix socket, it will be created by the server. It is
|
||||
* a required option
|
||||
*
|
||||
* device - id of a device on the server, a required option. PCI devices
|
||||
* alone are supported presently.
|
||||
*
|
||||
* notes - x-vfio-user-server could block IO and monitor during the
|
||||
* initialization phase.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "qom/object.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "trace.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/remote/machine.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "qapi/qapi-events-misc.h"
|
||||
#include "qemu/notify.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "libvfio-user.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/memory.h"
|
||||
#include "hw/pci/msi.h"
|
||||
#include "hw/pci/msix.h"
|
||||
#include "hw/remote/vfio-user-obj.h"
|
||||
|
||||
#define TYPE_VFU_OBJECT "x-vfio-user-server"
|
||||
OBJECT_DECLARE_TYPE(VfuObject, VfuObjectClass, VFU_OBJECT)
|
||||
|
||||
/**
|
||||
* VFU_OBJECT_ERROR - reports an error message. If auto_shutdown
|
||||
* is set, it aborts the machine on error. Otherwise, it logs an
|
||||
* error message without aborting.
|
||||
*/
|
||||
#define VFU_OBJECT_ERROR(o, fmt, ...) \
|
||||
{ \
|
||||
if (vfu_object_auto_shutdown()) { \
|
||||
error_setg(&error_abort, (fmt), ## __VA_ARGS__); \
|
||||
} else { \
|
||||
error_report((fmt), ## __VA_ARGS__); \
|
||||
} \
|
||||
} \
|
||||
|
||||
struct VfuObjectClass {
|
||||
ObjectClass parent_class;
|
||||
|
||||
unsigned int nr_devs;
|
||||
};
|
||||
|
||||
struct VfuObject {
|
||||
/* private */
|
||||
Object parent;
|
||||
|
||||
SocketAddress *socket;
|
||||
|
||||
char *device;
|
||||
|
||||
Error *err;
|
||||
|
||||
Notifier machine_done;
|
||||
|
||||
vfu_ctx_t *vfu_ctx;
|
||||
|
||||
PCIDevice *pci_dev;
|
||||
|
||||
Error *unplug_blocker;
|
||||
|
||||
int vfu_poll_fd;
|
||||
|
||||
MSITriggerFunc *default_msi_trigger;
|
||||
MSIPrepareMessageFunc *default_msi_prepare_message;
|
||||
MSIxPrepareMessageFunc *default_msix_prepare_message;
|
||||
};
|
||||
|
||||
static void vfu_object_init_ctx(VfuObject *o, Error **errp);
|
||||
|
||||
static bool vfu_object_auto_shutdown(void)
|
||||
{
|
||||
bool auto_shutdown = true;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!current_machine) {
|
||||
return auto_shutdown;
|
||||
}
|
||||
|
||||
auto_shutdown = object_property_get_bool(OBJECT(current_machine),
|
||||
"auto-shutdown",
|
||||
&local_err);
|
||||
|
||||
/*
|
||||
* local_err would be set if no such property exists - safe to ignore.
|
||||
* Unlikely scenario as auto-shutdown is always defined for
|
||||
* TYPE_REMOTE_MACHINE, and TYPE_VFU_OBJECT only works with
|
||||
* TYPE_REMOTE_MACHINE
|
||||
*/
|
||||
if (local_err) {
|
||||
auto_shutdown = true;
|
||||
error_free(local_err);
|
||||
}
|
||||
|
||||
return auto_shutdown;
|
||||
}
|
||||
|
||||
static void vfu_object_set_socket(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
VfuObject *o = VFU_OBJECT(obj);
|
||||
|
||||
if (o->vfu_ctx) {
|
||||
error_setg(errp, "vfu: Unable to set socket property - server busy");
|
||||
return;
|
||||
}
|
||||
|
||||
qapi_free_SocketAddress(o->socket);
|
||||
|
||||
o->socket = NULL;
|
||||
|
||||
visit_type_SocketAddress(v, name, &o->socket, errp);
|
||||
|
||||
if (o->socket->type != SOCKET_ADDRESS_TYPE_UNIX) {
|
||||
error_setg(errp, "vfu: Unsupported socket type - %s",
|
||||
SocketAddressType_str(o->socket->type));
|
||||
qapi_free_SocketAddress(o->socket);
|
||||
o->socket = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
trace_vfu_prop("socket", o->socket->u.q_unix.path);
|
||||
|
||||
vfu_object_init_ctx(o, errp);
|
||||
}
|
||||
|
||||
static void vfu_object_set_device(Object *obj, const char *str, Error **errp)
|
||||
{
|
||||
VfuObject *o = VFU_OBJECT(obj);
|
||||
|
||||
if (o->vfu_ctx) {
|
||||
error_setg(errp, "vfu: Unable to set device property - server busy");
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(o->device);
|
||||
|
||||
o->device = g_strdup(str);
|
||||
|
||||
trace_vfu_prop("device", str);
|
||||
|
||||
vfu_object_init_ctx(o, errp);
|
||||
}
|
||||
|
||||
static void vfu_object_ctx_run(void *opaque)
|
||||
{
|
||||
VfuObject *o = opaque;
|
||||
const char *vfu_id;
|
||||
char *vfu_path, *pci_dev_path;
|
||||
int ret = -1;
|
||||
|
||||
while (ret != 0) {
|
||||
ret = vfu_run_ctx(o->vfu_ctx);
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
} else if (errno == ENOTCONN) {
|
||||
vfu_id = object_get_canonical_path_component(OBJECT(o));
|
||||
vfu_path = object_get_canonical_path(OBJECT(o));
|
||||
g_assert(o->pci_dev);
|
||||
pci_dev_path = object_get_canonical_path(OBJECT(o->pci_dev));
|
||||
/* o->device is a required property and is non-NULL here */
|
||||
g_assert(o->device);
|
||||
qapi_event_send_vfu_client_hangup(vfu_id, vfu_path,
|
||||
o->device, pci_dev_path);
|
||||
qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
|
||||
o->vfu_poll_fd = -1;
|
||||
object_unparent(OBJECT(o));
|
||||
g_free(vfu_path);
|
||||
g_free(pci_dev_path);
|
||||
break;
|
||||
} else {
|
||||
VFU_OBJECT_ERROR(o, "vfu: Failed to run device %s - %s",
|
||||
o->device, strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void vfu_object_attach_ctx(void *opaque)
|
||||
{
|
||||
VfuObject *o = opaque;
|
||||
GPollFD pfds[1];
|
||||
int ret;
|
||||
|
||||
qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
|
||||
|
||||
pfds[0].fd = o->vfu_poll_fd;
|
||||
pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR;
|
||||
|
||||
retry_attach:
|
||||
ret = vfu_attach_ctx(o->vfu_ctx);
|
||||
if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
/**
|
||||
* vfu_object_attach_ctx can block QEMU's main loop
|
||||
* during attach - the monitor and other IO
|
||||
* could be unresponsive during this time.
|
||||
*/
|
||||
(void)qemu_poll_ns(pfds, 1, 500 * (int64_t)SCALE_MS);
|
||||
goto retry_attach;
|
||||
} else if (ret < 0) {
|
||||
VFU_OBJECT_ERROR(o, "vfu: Failed to attach device %s to context - %s",
|
||||
o->device, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
o->vfu_poll_fd = vfu_get_poll_fd(o->vfu_ctx);
|
||||
if (o->vfu_poll_fd < 0) {
|
||||
VFU_OBJECT_ERROR(o, "vfu: Failed to get poll fd %s", o->device);
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_set_fd_handler(o->vfu_poll_fd, vfu_object_ctx_run, NULL, o);
|
||||
}
|
||||
|
||||
static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf,
|
||||
size_t count, loff_t offset,
|
||||
const bool is_write)
|
||||
{
|
||||
VfuObject *o = vfu_get_private(vfu_ctx);
|
||||
uint32_t pci_access_width = sizeof(uint32_t);
|
||||
size_t bytes = count;
|
||||
uint32_t val = 0;
|
||||
char *ptr = buf;
|
||||
int len;
|
||||
|
||||
/*
|
||||
* Writes to the BAR registers would trigger an update to the
|
||||
* global Memory and IO AddressSpaces. But the remote device
|
||||
* never uses the global AddressSpaces, therefore overlapping
|
||||
* memory regions are not a problem
|
||||
*/
|
||||
while (bytes > 0) {
|
||||
len = (bytes > pci_access_width) ? pci_access_width : bytes;
|
||||
if (is_write) {
|
||||
memcpy(&val, ptr, len);
|
||||
pci_host_config_write_common(o->pci_dev, offset,
|
||||
pci_config_size(o->pci_dev),
|
||||
val, len);
|
||||
trace_vfu_cfg_write(offset, val);
|
||||
} else {
|
||||
val = pci_host_config_read_common(o->pci_dev, offset,
|
||||
pci_config_size(o->pci_dev), len);
|
||||
memcpy(ptr, &val, len);
|
||||
trace_vfu_cfg_read(offset, val);
|
||||
}
|
||||
offset += len;
|
||||
ptr += len;
|
||||
bytes -= len;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void dma_register(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
|
||||
{
|
||||
VfuObject *o = vfu_get_private(vfu_ctx);
|
||||
AddressSpace *dma_as = NULL;
|
||||
MemoryRegion *subregion = NULL;
|
||||
g_autofree char *name = NULL;
|
||||
struct iovec *iov = &info->iova;
|
||||
|
||||
if (!info->vaddr) {
|
||||
return;
|
||||
}
|
||||
|
||||
name = g_strdup_printf("mem-%s-%"PRIx64"", o->device,
|
||||
(uint64_t)info->vaddr);
|
||||
|
||||
subregion = g_new0(MemoryRegion, 1);
|
||||
|
||||
memory_region_init_ram_ptr(subregion, NULL, name,
|
||||
iov->iov_len, info->vaddr);
|
||||
|
||||
dma_as = pci_device_iommu_address_space(o->pci_dev);
|
||||
|
||||
memory_region_add_subregion(dma_as->root, (hwaddr)iov->iov_base, subregion);
|
||||
|
||||
trace_vfu_dma_register((uint64_t)iov->iov_base, iov->iov_len);
|
||||
}
|
||||
|
||||
static void dma_unregister(vfu_ctx_t *vfu_ctx, vfu_dma_info_t *info)
|
||||
{
|
||||
VfuObject *o = vfu_get_private(vfu_ctx);
|
||||
AddressSpace *dma_as = NULL;
|
||||
MemoryRegion *mr = NULL;
|
||||
ram_addr_t offset;
|
||||
|
||||
mr = memory_region_from_host(info->vaddr, &offset);
|
||||
if (!mr) {
|
||||
return;
|
||||
}
|
||||
|
||||
dma_as = pci_device_iommu_address_space(o->pci_dev);
|
||||
|
||||
memory_region_del_subregion(dma_as->root, mr);
|
||||
|
||||
object_unparent((OBJECT(mr)));
|
||||
|
||||
trace_vfu_dma_unregister((uint64_t)info->iova.iov_base);
|
||||
}
|
||||
|
||||
static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset,
|
||||
hwaddr size, const bool is_write)
|
||||
{
|
||||
uint8_t *ptr = buf;
|
||||
bool release_lock = false;
|
||||
uint8_t *ram_ptr = NULL;
|
||||
MemTxResult result;
|
||||
int access_size;
|
||||
uint64_t val;
|
||||
|
||||
if (memory_access_is_direct(mr, is_write)) {
|
||||
/**
|
||||
* Some devices expose a PCI expansion ROM, which could be buffer
|
||||
* based as compared to other regions which are primarily based on
|
||||
* MemoryRegionOps. memory_region_find() would already check
|
||||
* for buffer overflow, we don't need to repeat it here.
|
||||
*/
|
||||
ram_ptr = memory_region_get_ram_ptr(mr);
|
||||
|
||||
if (is_write) {
|
||||
memcpy((ram_ptr + offset), buf, size);
|
||||
} else {
|
||||
memcpy(buf, (ram_ptr + offset), size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (size) {
|
||||
/**
|
||||
* The read/write logic used below is similar to the ones in
|
||||
* flatview_read/write_continue()
|
||||
*/
|
||||
release_lock = prepare_mmio_access(mr);
|
||||
|
||||
access_size = memory_access_size(mr, size, offset);
|
||||
|
||||
if (is_write) {
|
||||
val = ldn_he_p(ptr, access_size);
|
||||
|
||||
result = memory_region_dispatch_write(mr, offset, val,
|
||||
size_memop(access_size),
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
} else {
|
||||
result = memory_region_dispatch_read(mr, offset, &val,
|
||||
size_memop(access_size),
|
||||
MEMTXATTRS_UNSPECIFIED);
|
||||
|
||||
stn_he_p(ptr, access_size, val);
|
||||
}
|
||||
|
||||
if (release_lock) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
release_lock = false;
|
||||
}
|
||||
|
||||
if (result != MEMTX_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size -= access_size;
|
||||
ptr += access_size;
|
||||
offset += access_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t vfu_object_bar_rw(PCIDevice *pci_dev, int pci_bar,
|
||||
hwaddr bar_offset, char * const buf,
|
||||
hwaddr len, const bool is_write)
|
||||
{
|
||||
MemoryRegionSection section = { 0 };
|
||||
uint8_t *ptr = (uint8_t *)buf;
|
||||
MemoryRegion *section_mr = NULL;
|
||||
uint64_t section_size;
|
||||
hwaddr section_offset;
|
||||
hwaddr size = 0;
|
||||
|
||||
while (len) {
|
||||
section = memory_region_find(pci_dev->io_regions[pci_bar].memory,
|
||||
bar_offset, len);
|
||||
|
||||
if (!section.mr) {
|
||||
warn_report("vfu: invalid address 0x%"PRIx64"", bar_offset);
|
||||
return size;
|
||||
}
|
||||
|
||||
section_mr = section.mr;
|
||||
section_offset = section.offset_within_region;
|
||||
section_size = int128_get64(section.size);
|
||||
|
||||
if (is_write && section_mr->readonly) {
|
||||
warn_report("vfu: attempting to write to readonly region in "
|
||||
"bar %d - [0x%"PRIx64" - 0x%"PRIx64"]",
|
||||
pci_bar, bar_offset,
|
||||
(bar_offset + section_size));
|
||||
memory_region_unref(section_mr);
|
||||
return size;
|
||||
}
|
||||
|
||||
if (vfu_object_mr_rw(section_mr, ptr, section_offset,
|
||||
section_size, is_write)) {
|
||||
warn_report("vfu: failed to %s "
|
||||
"[0x%"PRIx64" - 0x%"PRIx64"] in bar %d",
|
||||
is_write ? "write to" : "read from", bar_offset,
|
||||
(bar_offset + section_size), pci_bar);
|
||||
memory_region_unref(section_mr);
|
||||
return size;
|
||||
}
|
||||
|
||||
size += section_size;
|
||||
bar_offset += section_size;
|
||||
ptr += section_size;
|
||||
len -= section_size;
|
||||
|
||||
memory_region_unref(section_mr);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* VFU_OBJECT_BAR_HANDLER - macro for defining handlers for PCI BARs.
|
||||
*
|
||||
* To create handler for BAR number 2, VFU_OBJECT_BAR_HANDLER(2) would
|
||||
* define vfu_object_bar2_handler
|
||||
*/
|
||||
#define VFU_OBJECT_BAR_HANDLER(BAR_NO) \
|
||||
static ssize_t vfu_object_bar##BAR_NO##_handler(vfu_ctx_t *vfu_ctx, \
|
||||
char * const buf, size_t count, \
|
||||
loff_t offset, const bool is_write) \
|
||||
{ \
|
||||
VfuObject *o = vfu_get_private(vfu_ctx); \
|
||||
PCIDevice *pci_dev = o->pci_dev; \
|
||||
\
|
||||
return vfu_object_bar_rw(pci_dev, BAR_NO, offset, \
|
||||
buf, count, is_write); \
|
||||
} \
|
||||
|
||||
VFU_OBJECT_BAR_HANDLER(0)
|
||||
VFU_OBJECT_BAR_HANDLER(1)
|
||||
VFU_OBJECT_BAR_HANDLER(2)
|
||||
VFU_OBJECT_BAR_HANDLER(3)
|
||||
VFU_OBJECT_BAR_HANDLER(4)
|
||||
VFU_OBJECT_BAR_HANDLER(5)
|
||||
VFU_OBJECT_BAR_HANDLER(6)
|
||||
|
||||
static vfu_region_access_cb_t *vfu_object_bar_handlers[PCI_NUM_REGIONS] = {
|
||||
&vfu_object_bar0_handler,
|
||||
&vfu_object_bar1_handler,
|
||||
&vfu_object_bar2_handler,
|
||||
&vfu_object_bar3_handler,
|
||||
&vfu_object_bar4_handler,
|
||||
&vfu_object_bar5_handler,
|
||||
&vfu_object_bar6_handler,
|
||||
};
|
||||
|
||||
/**
|
||||
* vfu_object_register_bars - Identify active BAR regions of pdev and setup
|
||||
* callbacks to handle read/write accesses
|
||||
*/
|
||||
static void vfu_object_register_bars(vfu_ctx_t *vfu_ctx, PCIDevice *pdev)
|
||||
{
|
||||
int flags = VFU_REGION_FLAG_RW;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_NUM_REGIONS; i++) {
|
||||
if (!pdev->io_regions[i].size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((i == VFU_PCI_DEV_ROM_REGION_IDX) ||
|
||||
pdev->io_regions[i].memory->readonly) {
|
||||
flags &= ~VFU_REGION_FLAG_WRITE;
|
||||
}
|
||||
|
||||
vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX + i,
|
||||
(size_t)pdev->io_regions[i].size,
|
||||
vfu_object_bar_handlers[i],
|
||||
flags, NULL, 0, -1, 0);
|
||||
|
||||
trace_vfu_bar_register(i, pdev->io_regions[i].addr,
|
||||
pdev->io_regions[i].size);
|
||||
}
|
||||
}
|
||||
|
||||
static int vfu_object_map_irq(PCIDevice *pci_dev, int intx)
|
||||
{
|
||||
int pci_bdf = PCI_BUILD_BDF(pci_bus_num(pci_get_bus(pci_dev)),
|
||||
pci_dev->devfn);
|
||||
|
||||
return pci_bdf;
|
||||
}
|
||||
|
||||
static void vfu_object_set_irq(void *opaque, int pirq, int level)
|
||||
{
|
||||
PCIBus *pci_bus = opaque;
|
||||
PCIDevice *pci_dev = NULL;
|
||||
vfu_ctx_t *vfu_ctx = NULL;
|
||||
int pci_bus_num, devfn;
|
||||
|
||||
if (level) {
|
||||
pci_bus_num = PCI_BUS_NUM(pirq);
|
||||
devfn = PCI_BDF_TO_DEVFN(pirq);
|
||||
|
||||
/*
|
||||
* pci_find_device() performs at O(1) if the device is attached
|
||||
* to the root PCI bus. Whereas, if the device is attached to a
|
||||
* secondary PCI bus (such as when a root port is involved),
|
||||
* finding the parent PCI bus could take O(n)
|
||||
*/
|
||||
pci_dev = pci_find_device(pci_bus, pci_bus_num, devfn);
|
||||
|
||||
vfu_ctx = pci_dev->irq_opaque;
|
||||
|
||||
g_assert(vfu_ctx);
|
||||
|
||||
vfu_irq_trigger(vfu_ctx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static MSIMessage vfu_object_msi_prepare_msg(PCIDevice *pci_dev,
|
||||
unsigned int vector)
|
||||
{
|
||||
MSIMessage msg;
|
||||
|
||||
msg.address = 0;
|
||||
msg.data = vector;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void vfu_object_msi_trigger(PCIDevice *pci_dev, MSIMessage msg)
|
||||
{
|
||||
vfu_ctx_t *vfu_ctx = pci_dev->irq_opaque;
|
||||
|
||||
vfu_irq_trigger(vfu_ctx, msg.data);
|
||||
}
|
||||
|
||||
static void vfu_object_setup_msi_cbs(VfuObject *o)
|
||||
{
|
||||
o->default_msi_trigger = o->pci_dev->msi_trigger;
|
||||
o->default_msi_prepare_message = o->pci_dev->msi_prepare_message;
|
||||
o->default_msix_prepare_message = o->pci_dev->msix_prepare_message;
|
||||
|
||||
o->pci_dev->msi_trigger = vfu_object_msi_trigger;
|
||||
o->pci_dev->msi_prepare_message = vfu_object_msi_prepare_msg;
|
||||
o->pci_dev->msix_prepare_message = vfu_object_msi_prepare_msg;
|
||||
}
|
||||
|
||||
static void vfu_object_restore_msi_cbs(VfuObject *o)
|
||||
{
|
||||
o->pci_dev->msi_trigger = o->default_msi_trigger;
|
||||
o->pci_dev->msi_prepare_message = o->default_msi_prepare_message;
|
||||
o->pci_dev->msix_prepare_message = o->default_msix_prepare_message;
|
||||
}
|
||||
|
||||
static void vfu_msix_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start,
|
||||
uint32_t count, bool mask)
|
||||
{
|
||||
VfuObject *o = vfu_get_private(vfu_ctx);
|
||||
Error *err = NULL;
|
||||
uint32_t vector;
|
||||
|
||||
for (vector = start; vector < count; vector++) {
|
||||
msix_set_mask(o->pci_dev, vector, mask, &err);
|
||||
if (err) {
|
||||
VFU_OBJECT_ERROR(o, "vfu: %s: %s", o->device,
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
err = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void vfu_msi_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start,
|
||||
uint32_t count, bool mask)
|
||||
{
|
||||
VfuObject *o = vfu_get_private(vfu_ctx);
|
||||
Error *err = NULL;
|
||||
uint32_t vector;
|
||||
|
||||
for (vector = start; vector < count; vector++) {
|
||||
msi_set_mask(o->pci_dev, vector, mask, &err);
|
||||
if (err) {
|
||||
VFU_OBJECT_ERROR(o, "vfu: %s: %s", o->device,
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
err = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int vfu_object_setup_irqs(VfuObject *o, PCIDevice *pci_dev)
|
||||
{
|
||||
vfu_ctx_t *vfu_ctx = o->vfu_ctx;
|
||||
int ret;
|
||||
|
||||
ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_INTX_IRQ, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (msix_nr_vectors_allocated(pci_dev)) {
|
||||
ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_MSIX_IRQ,
|
||||
msix_nr_vectors_allocated(pci_dev));
|
||||
vfu_setup_irq_state_callback(vfu_ctx, VFU_DEV_MSIX_IRQ,
|
||||
&vfu_msix_irq_state);
|
||||
} else if (msi_nr_vectors_allocated(pci_dev)) {
|
||||
ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_MSI_IRQ,
|
||||
msi_nr_vectors_allocated(pci_dev));
|
||||
vfu_setup_irq_state_callback(vfu_ctx, VFU_DEV_MSI_IRQ,
|
||||
&vfu_msi_irq_state);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
vfu_object_setup_msi_cbs(o);
|
||||
|
||||
pci_dev->irq_opaque = vfu_ctx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vfu_object_set_bus_irq(PCIBus *pci_bus)
|
||||
{
|
||||
int bus_num = pci_bus_num(pci_bus);
|
||||
int max_bdf = PCI_BUILD_BDF(bus_num, PCI_DEVFN_MAX - 1);
|
||||
|
||||
pci_bus_irqs(pci_bus, vfu_object_set_irq, vfu_object_map_irq, pci_bus,
|
||||
max_bdf);
|
||||
}
|
||||
|
||||
static int vfu_object_device_reset(vfu_ctx_t *vfu_ctx, vfu_reset_type_t type)
|
||||
{
|
||||
VfuObject *o = vfu_get_private(vfu_ctx);
|
||||
|
||||
/* vfu_object_ctx_run() handles lost connection */
|
||||
if (type == VFU_RESET_LOST_CONN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
qdev_reset_all(DEVICE(o->pci_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TYPE_VFU_OBJECT depends on the availability of the 'socket' and 'device'
|
||||
* properties. It also depends on devices instantiated in QEMU. These
|
||||
* dependencies are not available during the instance_init phase of this
|
||||
* object's life-cycle. As such, the server is initialized after the
|
||||
* machine is setup. machine_init_done_notifier notifies TYPE_VFU_OBJECT
|
||||
* when the machine is setup, and the dependencies are available.
|
||||
*/
|
||||
static void vfu_object_machine_done(Notifier *notifier, void *data)
|
||||
{
|
||||
VfuObject *o = container_of(notifier, VfuObject, machine_done);
|
||||
Error *err = NULL;
|
||||
|
||||
vfu_object_init_ctx(o, &err);
|
||||
|
||||
if (err) {
|
||||
error_propagate(&error_abort, err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vfu_object_init_ctx: Create and initialize libvfio-user context. Add
|
||||
* an unplug blocker for the associated PCI device. Setup a FD handler
|
||||
* to process incoming messages in the context's socket.
|
||||
*
|
||||
* The socket and device properties are mandatory, and this function
|
||||
* will not create the context without them - the setters for these
|
||||
* properties should call this function when the property is set. The
|
||||
* machine should also be ready when this function is invoked - it is
|
||||
* because QEMU objects are initialized before devices, and the
|
||||
* associated PCI device wouldn't be available at the object
|
||||
* initialization time. Until these conditions are satisfied, this
|
||||
* function would return early without performing any task.
|
||||
*/
|
||||
static void vfu_object_init_ctx(VfuObject *o, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
DeviceState *dev = NULL;
|
||||
vfu_pci_type_t pci_type = VFU_PCI_TYPE_CONVENTIONAL;
|
||||
int ret;
|
||||
|
||||
if (o->vfu_ctx || !o->socket || !o->device ||
|
||||
!phase_check(PHASE_MACHINE_READY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (o->err) {
|
||||
error_propagate(errp, o->err);
|
||||
o->err = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
o->vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, o->socket->u.q_unix.path,
|
||||
LIBVFIO_USER_FLAG_ATTACH_NB,
|
||||
o, VFU_DEV_TYPE_PCI);
|
||||
if (o->vfu_ctx == NULL) {
|
||||
error_setg(errp, "vfu: Failed to create context - %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
dev = qdev_find_recursive(sysbus_get_default(), o->device);
|
||||
if (dev == NULL) {
|
||||
error_setg(errp, "vfu: Device %s not found", o->device);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
||||
error_setg(errp, "vfu: %s not a PCI device", o->device);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
o->pci_dev = PCI_DEVICE(dev);
|
||||
|
||||
object_ref(OBJECT(o->pci_dev));
|
||||
|
||||
if (pci_is_express(o->pci_dev)) {
|
||||
pci_type = VFU_PCI_TYPE_EXPRESS;
|
||||
}
|
||||
|
||||
ret = vfu_pci_init(o->vfu_ctx, pci_type, PCI_HEADER_TYPE_NORMAL, 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp,
|
||||
"vfu: Failed to attach PCI device %s to context - %s",
|
||||
o->device, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error_setg(&o->unplug_blocker,
|
||||
"vfu: %s for %s must be deleted before unplugging",
|
||||
TYPE_VFU_OBJECT, o->device);
|
||||
qdev_add_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
|
||||
|
||||
ret = vfu_setup_region(o->vfu_ctx, VFU_PCI_DEV_CFG_REGION_IDX,
|
||||
pci_config_size(o->pci_dev), &vfu_object_cfg_access,
|
||||
VFU_REGION_FLAG_RW | VFU_REGION_FLAG_ALWAYS_CB,
|
||||
NULL, 0, -1, 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp,
|
||||
"vfu: Failed to setup config space handlers for %s- %s",
|
||||
o->device, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = vfu_setup_device_dma(o->vfu_ctx, &dma_register, &dma_unregister);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "vfu: Failed to setup DMA handlers for %s",
|
||||
o->device);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vfu_object_register_bars(o->vfu_ctx, o->pci_dev);
|
||||
|
||||
ret = vfu_object_setup_irqs(o, o->pci_dev);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "vfu: Failed to setup interrupts for %s",
|
||||
o->device);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = vfu_setup_device_reset_cb(o->vfu_ctx, &vfu_object_device_reset);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "vfu: Failed to setup reset callback");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = vfu_realize_ctx(o->vfu_ctx);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "vfu: Failed to realize device %s- %s",
|
||||
o->device, strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
o->vfu_poll_fd = vfu_get_poll_fd(o->vfu_ctx);
|
||||
if (o->vfu_poll_fd < 0) {
|
||||
error_setg(errp, "vfu: Failed to get poll fd %s", o->device);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qemu_set_fd_handler(o->vfu_poll_fd, vfu_object_attach_ctx, NULL, o);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
vfu_destroy_ctx(o->vfu_ctx);
|
||||
if (o->unplug_blocker && o->pci_dev) {
|
||||
qdev_del_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
|
||||
error_free(o->unplug_blocker);
|
||||
o->unplug_blocker = NULL;
|
||||
}
|
||||
if (o->pci_dev) {
|
||||
vfu_object_restore_msi_cbs(o);
|
||||
o->pci_dev->irq_opaque = NULL;
|
||||
object_unref(OBJECT(o->pci_dev));
|
||||
o->pci_dev = NULL;
|
||||
}
|
||||
o->vfu_ctx = NULL;
|
||||
}
|
||||
|
||||
static void vfu_object_init(Object *obj)
|
||||
{
|
||||
VfuObjectClass *k = VFU_OBJECT_GET_CLASS(obj);
|
||||
VfuObject *o = VFU_OBJECT(obj);
|
||||
|
||||
k->nr_devs++;
|
||||
|
||||
if (!object_dynamic_cast(OBJECT(current_machine), TYPE_REMOTE_MACHINE)) {
|
||||
error_setg(&o->err, "vfu: %s only compatible with %s machine",
|
||||
TYPE_VFU_OBJECT, TYPE_REMOTE_MACHINE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!phase_check(PHASE_MACHINE_READY)) {
|
||||
o->machine_done.notify = vfu_object_machine_done;
|
||||
qemu_add_machine_init_done_notifier(&o->machine_done);
|
||||
}
|
||||
|
||||
o->vfu_poll_fd = -1;
|
||||
}
|
||||
|
||||
static void vfu_object_finalize(Object *obj)
|
||||
{
|
||||
VfuObjectClass *k = VFU_OBJECT_GET_CLASS(obj);
|
||||
VfuObject *o = VFU_OBJECT(obj);
|
||||
|
||||
k->nr_devs--;
|
||||
|
||||
qapi_free_SocketAddress(o->socket);
|
||||
|
||||
o->socket = NULL;
|
||||
|
||||
if (o->vfu_poll_fd != -1) {
|
||||
qemu_set_fd_handler(o->vfu_poll_fd, NULL, NULL, NULL);
|
||||
o->vfu_poll_fd = -1;
|
||||
}
|
||||
|
||||
if (o->vfu_ctx) {
|
||||
vfu_destroy_ctx(o->vfu_ctx);
|
||||
o->vfu_ctx = NULL;
|
||||
}
|
||||
|
||||
g_free(o->device);
|
||||
|
||||
o->device = NULL;
|
||||
|
||||
if (o->unplug_blocker && o->pci_dev) {
|
||||
qdev_del_unplug_blocker(DEVICE(o->pci_dev), o->unplug_blocker);
|
||||
error_free(o->unplug_blocker);
|
||||
o->unplug_blocker = NULL;
|
||||
}
|
||||
|
||||
if (o->pci_dev) {
|
||||
vfu_object_restore_msi_cbs(o);
|
||||
o->pci_dev->irq_opaque = NULL;
|
||||
object_unref(OBJECT(o->pci_dev));
|
||||
o->pci_dev = NULL;
|
||||
}
|
||||
|
||||
if (!k->nr_devs && vfu_object_auto_shutdown()) {
|
||||
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
||||
}
|
||||
|
||||
if (o->machine_done.notify) {
|
||||
qemu_remove_machine_init_done_notifier(&o->machine_done);
|
||||
o->machine_done.notify = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void vfu_object_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
VfuObjectClass *k = VFU_OBJECT_CLASS(klass);
|
||||
|
||||
k->nr_devs = 0;
|
||||
|
||||
object_class_property_add(klass, "socket", "SocketAddress", NULL,
|
||||
vfu_object_set_socket, NULL, NULL);
|
||||
object_class_property_set_description(klass, "socket",
|
||||
"SocketAddress "
|
||||
"(ex: type=unix,path=/tmp/sock). "
|
||||
"Only UNIX is presently supported");
|
||||
object_class_property_add_str(klass, "device", NULL,
|
||||
vfu_object_set_device);
|
||||
object_class_property_set_description(klass, "device",
|
||||
"device ID - only PCI devices "
|
||||
"are presently supported");
|
||||
}
|
||||
|
||||
static const TypeInfo vfu_object_info = {
|
||||
.name = TYPE_VFU_OBJECT,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(VfuObject),
|
||||
.instance_init = vfu_object_init,
|
||||
.instance_finalize = vfu_object_finalize,
|
||||
.class_size = sizeof(VfuObjectClass),
|
||||
.class_init = vfu_object_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void vfu_register_types(void)
|
||||
{
|
||||
type_register_static(&vfu_object_info);
|
||||
}
|
||||
|
||||
type_init(vfu_register_types);
|
@ -26,7 +26,7 @@
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/bcd.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/qdev-properties-system.h"
|
||||
@ -74,6 +74,8 @@
|
||||
#define RTC_CLOCK_RATE 32768
|
||||
#define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768)
|
||||
|
||||
#define RTC_ISA_BASE 0x70
|
||||
|
||||
static void rtc_set_time(RTCState *s);
|
||||
static void rtc_update_time(RTCState *s);
|
||||
static void rtc_set_cmos(RTCState *s, const struct tm *tm);
|
||||
@ -941,7 +943,7 @@ static void rtc_realizefn(DeviceState *dev, Error **errp)
|
||||
qemu_register_suspend_notifier(&s->suspend_notifier);
|
||||
|
||||
memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 2);
|
||||
isa_register_ioport(isadev, &s->io, RTC_ISA_BASE);
|
||||
isa_register_ioport(isadev, &s->io, s->io_base);
|
||||
|
||||
/* register rtc 0x70 port for coalesced_pio */
|
||||
memory_region_set_flush_coalesced(&s->io);
|
||||
@ -950,7 +952,7 @@ static void rtc_realizefn(DeviceState *dev, Error **errp)
|
||||
memory_region_add_subregion(&s->io, 0, &s->coalesced_io);
|
||||
memory_region_add_coalescing(&s->coalesced_io, 0, 1);
|
||||
|
||||
qdev_set_legacy_instance_id(dev, RTC_ISA_BASE, 3);
|
||||
qdev_set_legacy_instance_id(dev, s->io_base, 3);
|
||||
|
||||
object_property_add_tm(OBJECT(s), "date", rtc_get_date);
|
||||
|
||||
@ -983,6 +985,7 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq)
|
||||
|
||||
static Property mc146818rtc_properties[] = {
|
||||
DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980),
|
||||
DEFINE_PROP_UINT16("iobase", RTCState, io_base, RTC_ISA_BASE),
|
||||
DEFINE_PROP_UINT8("irq", RTCState, isairq, RTC_ISA_IRQ),
|
||||
DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", RTCState,
|
||||
lost_tick_policy, LOST_TICK_POLICY_DISCARD),
|
||||
@ -1017,9 +1020,9 @@ static void rtc_reset_hold(Object *obj)
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
|
||||
static void rtc_build_aml(ISADevice *isadev, Aml *scope)
|
||||
static void rtc_build_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
RTCState *s = MC146818_RTC(isadev);
|
||||
RTCState *s = MC146818_RTC(adev);
|
||||
Aml *dev;
|
||||
Aml *crs;
|
||||
|
||||
@ -1028,7 +1031,7 @@ static void rtc_build_aml(ISADevice *isadev, Aml *scope)
|
||||
* does, even though qemu only responds to the first two ports.
|
||||
*/
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs, aml_io(AML_DECODE16, RTC_ISA_BASE, RTC_ISA_BASE,
|
||||
aml_append(crs, aml_io(AML_DECODE16, s->io_base, s->io_base,
|
||||
0x01, 0x08));
|
||||
aml_append(crs, aml_irq_no_flags(s->isairq));
|
||||
|
||||
@ -1043,13 +1046,13 @@ static void rtc_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
ISADeviceClass *isa = ISA_DEVICE_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
dc->realize = rtc_realizefn;
|
||||
dc->vmsd = &vmstate_rtc;
|
||||
rc->phases.enter = rtc_reset_enter;
|
||||
rc->phases.hold = rtc_reset_hold;
|
||||
isa->build_aml = rtc_build_aml;
|
||||
adevc->build_dev_aml = rtc_build_aml;
|
||||
device_class_set_props(dc, mc146818rtc_properties);
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
}
|
||||
@ -1059,6 +1062,10 @@ static const TypeInfo mc146818rtc_info = {
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(RTCState),
|
||||
.class_init = rtc_class_initfn,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static void mc146818rtc_register_types(void)
|
||||
|
@ -121,7 +121,6 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp)
|
||||
vsc->dev.backend_features = 0;
|
||||
vqs = vsc->dev.vqs;
|
||||
|
||||
s->vhost_user.supports_config = true;
|
||||
ret = vhost_dev_init(&vsc->dev, &s->vhost_user,
|
||||
VHOST_BACKEND_TYPE_USER, 0, errp);
|
||||
if (ret < 0) {
|
||||
|
@ -334,7 +334,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp)
|
||||
parallel_hds_isa_init(s->isa_bus, MAX_PARALLEL_PORTS);
|
||||
|
||||
/* Keyboard */
|
||||
isa_create_simple(s->isa_bus, "i8042");
|
||||
isa_create_simple(s->isa_bus, TYPE_I8042);
|
||||
|
||||
/* Floppy */
|
||||
for (i = 0; i < MAX_FD; i++) {
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "tpm_prop.h"
|
||||
#include "tpm_tis.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
|
||||
struct TPMStateISA {
|
||||
/*< private >*/
|
||||
@ -138,10 +139,39 @@ static void tpm_tis_isa_realizefn(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static void build_tpm_tis_isa_aml(AcpiDevAmlIf *adev, Aml *scope)
|
||||
{
|
||||
Aml *dev, *crs;
|
||||
TPMStateISA *isadev = TPM_TIS_ISA(adev);
|
||||
TPMIf *ti = TPM_IF(isadev);
|
||||
|
||||
dev = aml_device("TPM");
|
||||
if (tpm_tis_isa_get_tpm_version(ti) == TPM_VERSION_2_0) {
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
|
||||
aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device")));
|
||||
} else {
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31")));
|
||||
}
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(1)));
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE,
|
||||
AML_READ_WRITE));
|
||||
/*
|
||||
* FIXME: TPM_TIS_IRQ=5 conflicts with PNP0C0F irqs,
|
||||
* fix default TPM_TIS_IRQ value there to use some unused IRQ
|
||||
*/
|
||||
/* aml_append(crs, aml_irq_no_flags(isadev->state.irq_num)); */
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
tpm_build_ppi_acpi(ti, dev);
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static void tpm_tis_isa_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
TPMIfClass *tc = TPM_IF_CLASS(klass);
|
||||
AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
|
||||
|
||||
device_class_set_props(dc, tpm_tis_isa_properties);
|
||||
dc->vmsd = &vmstate_tpm_tis_isa;
|
||||
@ -151,6 +181,7 @@ static void tpm_tis_isa_class_init(ObjectClass *klass, void *data)
|
||||
tc->request_completed = tpm_tis_isa_request_completed;
|
||||
tc->get_version = tpm_tis_isa_get_tpm_version;
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
adevc->build_dev_aml = build_tpm_tis_isa_aml;
|
||||
}
|
||||
|
||||
static const TypeInfo tpm_tis_isa_info = {
|
||||
@ -161,6 +192,7 @@ static const TypeInfo tpm_tis_isa_info = {
|
||||
.class_init = tpm_tis_isa_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_TPM_IF },
|
||||
{ TYPE_ACPI_DEV_AML_IF },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
@ -119,6 +119,11 @@ config USB_U2F
|
||||
default y
|
||||
depends on USB
|
||||
|
||||
config USB_CANOKEY
|
||||
bool
|
||||
default y
|
||||
depends on USB
|
||||
|
||||
config IMX_USBPHY
|
||||
bool
|
||||
default y
|
||||
|
313
hw/usb/canokey.c
Normal file
313
hw/usb/canokey.c
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* CanoKey QEMU device implementation.
|
||||
*
|
||||
* Copyright (c) 2021-2022 Canokeys.org <contact@canokeys.org>
|
||||
* Written by Hongren (Zenithal) Zheng <i@zenithal.me>
|
||||
*
|
||||
* This code is licensed under the Apache-2.0.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <canokey-qemu.h>
|
||||
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/usb.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "trace.h"
|
||||
#include "desc.h"
|
||||
#include "canokey.h"
|
||||
|
||||
#define CANOKEY_EP_IN(ep) ((ep) & 0x7F)
|
||||
|
||||
#define CANOKEY_VENDOR_NUM 0x20a0
|
||||
#define CANOKEY_PRODUCT_NUM 0x42d2
|
||||
|
||||
/*
|
||||
* placeholder, canokey-qemu implements its own usb desc
|
||||
* Namely we do not use usb_desc_handle_contorl
|
||||
*/
|
||||
enum {
|
||||
STR_MANUFACTURER = 1,
|
||||
STR_PRODUCT,
|
||||
STR_SERIALNUMBER
|
||||
};
|
||||
|
||||
static const USBDescStrings desc_strings = {
|
||||
[STR_MANUFACTURER] = "canokeys.org",
|
||||
[STR_PRODUCT] = "CanoKey QEMU",
|
||||
[STR_SERIALNUMBER] = "0"
|
||||
};
|
||||
|
||||
static const USBDescDevice desc_device_canokey = {
|
||||
.bcdUSB = 0x0,
|
||||
.bMaxPacketSize0 = 16,
|
||||
.bNumConfigurations = 0,
|
||||
.confs = NULL,
|
||||
};
|
||||
|
||||
static const USBDesc desc_canokey = {
|
||||
.id = {
|
||||
.idVendor = CANOKEY_VENDOR_NUM,
|
||||
.idProduct = CANOKEY_PRODUCT_NUM,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = STR_MANUFACTURER,
|
||||
.iProduct = STR_PRODUCT,
|
||||
.iSerialNumber = STR_SERIALNUMBER,
|
||||
},
|
||||
.full = &desc_device_canokey,
|
||||
.high = &desc_device_canokey,
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* libcanokey-qemu.so side functions
|
||||
* All functions are called from canokey_emu_device_loop
|
||||
*/
|
||||
int canokey_emu_stall_ep(void *base, uint8_t ep)
|
||||
{
|
||||
trace_canokey_emu_stall_ep(ep);
|
||||
CanoKeyState *key = base;
|
||||
uint8_t ep_in = CANOKEY_EP_IN(ep); /* INTR IN has ep 129 */
|
||||
key->ep_in_size[ep_in] = 0;
|
||||
key->ep_in_state[ep_in] = CANOKEY_EP_IN_STALL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int canokey_emu_set_address(void *base, uint8_t addr)
|
||||
{
|
||||
trace_canokey_emu_set_address(addr);
|
||||
CanoKeyState *key = base;
|
||||
key->dev.addr = addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int canokey_emu_prepare_receive(
|
||||
void *base, uint8_t ep, uint8_t *pbuf, uint16_t size)
|
||||
{
|
||||
trace_canokey_emu_prepare_receive(ep, size);
|
||||
CanoKeyState *key = base;
|
||||
key->ep_out[ep] = pbuf;
|
||||
key->ep_out_size[ep] = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int canokey_emu_transmit(
|
||||
void *base, uint8_t ep, const uint8_t *pbuf, uint16_t size)
|
||||
{
|
||||
trace_canokey_emu_transmit(ep, size);
|
||||
CanoKeyState *key = base;
|
||||
uint8_t ep_in = CANOKEY_EP_IN(ep); /* INTR IN has ep 129 */
|
||||
memcpy(key->ep_in[ep_in] + key->ep_in_size[ep_in],
|
||||
pbuf, size);
|
||||
key->ep_in_size[ep_in] += size;
|
||||
key->ep_in_state[ep_in] = CANOKEY_EP_IN_READY;
|
||||
/*
|
||||
* ready for more data in device loop
|
||||
*
|
||||
* Note: this is a quirk for CanoKey CTAPHID
|
||||
* because it calls multiple emu_transmit in one device_loop
|
||||
* but w/o data_in it would stuck in device_loop
|
||||
* This has no side effect for CCID as it is strictly
|
||||
* OUT then IN transfer
|
||||
* However it has side effect for Control transfer
|
||||
*/
|
||||
if (ep_in != 0) {
|
||||
canokey_emu_data_in(ep_in);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t canokey_emu_get_rx_data_size(void *base, uint8_t ep)
|
||||
{
|
||||
CanoKeyState *key = base;
|
||||
return key->ep_out_size[ep];
|
||||
}
|
||||
|
||||
/*
|
||||
* QEMU side functions
|
||||
*/
|
||||
static void canokey_handle_reset(USBDevice *dev)
|
||||
{
|
||||
trace_canokey_handle_reset();
|
||||
CanoKeyState *key = CANOKEY(dev);
|
||||
for (int i = 0; i != CANOKEY_EP_NUM; ++i) {
|
||||
key->ep_in_state[i] = CANOKEY_EP_IN_WAIT;
|
||||
key->ep_in_pos[i] = 0;
|
||||
key->ep_in_size[i] = 0;
|
||||
}
|
||||
canokey_emu_reset();
|
||||
}
|
||||
|
||||
static void canokey_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
trace_canokey_handle_control_setup(request, value, index, length);
|
||||
CanoKeyState *key = CANOKEY(dev);
|
||||
|
||||
canokey_emu_setup(request, value, index, length);
|
||||
|
||||
uint32_t dir_in = request & DeviceRequest;
|
||||
if (!dir_in) {
|
||||
/* OUT */
|
||||
trace_canokey_handle_control_out();
|
||||
if (key->ep_out[0] != NULL) {
|
||||
memcpy(key->ep_out[0], data, length);
|
||||
}
|
||||
canokey_emu_data_out(p->ep->nr, data);
|
||||
}
|
||||
|
||||
canokey_emu_device_loop();
|
||||
|
||||
/* IN */
|
||||
switch (key->ep_in_state[0]) {
|
||||
case CANOKEY_EP_IN_WAIT:
|
||||
p->status = USB_RET_NAK;
|
||||
break;
|
||||
case CANOKEY_EP_IN_STALL:
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
case CANOKEY_EP_IN_READY:
|
||||
memcpy(data, key->ep_in[0], key->ep_in_size[0]);
|
||||
p->actual_length = key->ep_in_size[0];
|
||||
trace_canokey_handle_control_in(p->actual_length);
|
||||
/* reset state */
|
||||
key->ep_in_state[0] = CANOKEY_EP_IN_WAIT;
|
||||
key->ep_in_size[0] = 0;
|
||||
key->ep_in_pos[0] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void canokey_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
CanoKeyState *key = CANOKEY(dev);
|
||||
|
||||
uint8_t ep_in = CANOKEY_EP_IN(p->ep->nr);
|
||||
uint8_t ep_out = p->ep->nr;
|
||||
uint32_t in_len;
|
||||
uint32_t out_pos;
|
||||
uint32_t out_len;
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
trace_canokey_handle_data_out(ep_out, p->iov.size);
|
||||
usb_packet_copy(p, key->ep_out_buffer[ep_out], p->iov.size);
|
||||
out_pos = 0;
|
||||
while (out_pos != p->iov.size) {
|
||||
/*
|
||||
* key->ep_out[ep_out] set by prepare_receive
|
||||
* to be a buffer inside libcanokey-qemu.so
|
||||
* key->ep_out_size[ep_out] set by prepare_receive
|
||||
* to be the buffer length
|
||||
*/
|
||||
out_len = MIN(p->iov.size - out_pos, key->ep_out_size[ep_out]);
|
||||
memcpy(key->ep_out[ep_out],
|
||||
key->ep_out_buffer[ep_out] + out_pos, out_len);
|
||||
out_pos += out_len;
|
||||
/* update ep_out_size to actual len */
|
||||
key->ep_out_size[ep_out] = out_len;
|
||||
canokey_emu_data_out(ep_out, NULL);
|
||||
}
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
if (key->ep_in_pos[ep_in] == 0) { /* first time IN */
|
||||
canokey_emu_data_in(ep_in);
|
||||
canokey_emu_device_loop(); /* may call transmit multiple times */
|
||||
}
|
||||
switch (key->ep_in_state[ep_in]) {
|
||||
case CANOKEY_EP_IN_WAIT:
|
||||
/* NAK for early INTR IN */
|
||||
p->status = USB_RET_NAK;
|
||||
break;
|
||||
case CANOKEY_EP_IN_STALL:
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
case CANOKEY_EP_IN_READY:
|
||||
/* submit part of ep_in buffer to USBPacket */
|
||||
in_len = MIN(key->ep_in_size[ep_in] - key->ep_in_pos[ep_in],
|
||||
p->iov.size);
|
||||
usb_packet_copy(p,
|
||||
key->ep_in[ep_in] + key->ep_in_pos[ep_in], in_len);
|
||||
key->ep_in_pos[ep_in] += in_len;
|
||||
/* reset state if all data submitted */
|
||||
if (key->ep_in_pos[ep_in] == key->ep_in_size[ep_in]) {
|
||||
key->ep_in_state[ep_in] = CANOKEY_EP_IN_WAIT;
|
||||
key->ep_in_size[ep_in] = 0;
|
||||
key->ep_in_pos[ep_in] = 0;
|
||||
}
|
||||
trace_canokey_handle_data_in(ep_in, in_len);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void canokey_realize(USBDevice *base, Error **errp)
|
||||
{
|
||||
trace_canokey_realize();
|
||||
CanoKeyState *key = CANOKEY(base);
|
||||
|
||||
if (key->file == NULL) {
|
||||
error_setg(errp, "You must provide file=/path/to/canokey-file");
|
||||
return;
|
||||
}
|
||||
|
||||
usb_desc_init(base);
|
||||
|
||||
for (int i = 0; i != CANOKEY_EP_NUM; ++i) {
|
||||
key->ep_in_state[i] = CANOKEY_EP_IN_WAIT;
|
||||
key->ep_in_size[i] = 0;
|
||||
key->ep_in_pos[i] = 0;
|
||||
}
|
||||
|
||||
if (canokey_emu_init(key, key->file)) {
|
||||
error_setg(errp, "canokey can not create or read %s", key->file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void canokey_unrealize(USBDevice *base)
|
||||
{
|
||||
trace_canokey_unrealize();
|
||||
}
|
||||
|
||||
static Property canokey_properties[] = {
|
||||
DEFINE_PROP_STRING("file", CanoKeyState, file),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void canokey_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
||||
|
||||
uc->product_desc = "CanoKey QEMU";
|
||||
uc->usb_desc = &desc_canokey;
|
||||
uc->handle_reset = canokey_handle_reset;
|
||||
uc->handle_control = canokey_handle_control;
|
||||
uc->handle_data = canokey_handle_data;
|
||||
uc->handle_attach = usb_desc_attach;
|
||||
uc->realize = canokey_realize;
|
||||
uc->unrealize = canokey_unrealize;
|
||||
dc->desc = "CanoKey QEMU";
|
||||
device_class_set_props(dc, canokey_properties);
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
}
|
||||
|
||||
static const TypeInfo canokey_info = {
|
||||
.name = TYPE_CANOKEY,
|
||||
.parent = TYPE_USB_DEVICE,
|
||||
.instance_size = sizeof(CanoKeyState),
|
||||
.class_init = canokey_class_init
|
||||
};
|
||||
|
||||
static void canokey_register_types(void)
|
||||
{
|
||||
type_register_static(&canokey_info);
|
||||
}
|
||||
|
||||
type_init(canokey_register_types)
|
69
hw/usb/canokey.h
Normal file
69
hw/usb/canokey.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* CanoKey QEMU device header.
|
||||
*
|
||||
* Copyright (c) 2021-2022 Canokeys.org <contact@canokeys.org>
|
||||
* Written by Hongren (Zenithal) Zheng <i@zenithal.me>
|
||||
*
|
||||
* This code is licensed under the Apache-2.0.
|
||||
*/
|
||||
|
||||
#ifndef CANOKEY_H
|
||||
#define CANOKEY_H
|
||||
|
||||
#include "hw/qdev-core.h"
|
||||
|
||||
#define TYPE_CANOKEY "canokey"
|
||||
#define CANOKEY(obj) \
|
||||
OBJECT_CHECK(CanoKeyState, (obj), TYPE_CANOKEY)
|
||||
|
||||
/*
|
||||
* State of Canokey (i.e. hw/canokey.c)
|
||||
*/
|
||||
|
||||
/* CTRL INTR BULK */
|
||||
#define CANOKEY_EP_NUM 3
|
||||
/* BULK/INTR IN can be up to 1352 bytes, e.g. get key info */
|
||||
#define CANOKEY_EP_IN_BUFFER_SIZE 2048
|
||||
/* BULK OUT can be up to 270 bytes, e.g. PIV import cert */
|
||||
#define CANOKEY_EP_OUT_BUFFER_SIZE 512
|
||||
|
||||
typedef enum {
|
||||
CANOKEY_EP_IN_WAIT,
|
||||
CANOKEY_EP_IN_READY,
|
||||
CANOKEY_EP_IN_STALL
|
||||
} CanoKeyEPState;
|
||||
|
||||
typedef struct CanoKeyState {
|
||||
USBDevice dev;
|
||||
|
||||
/* IN packets from canokey device loop */
|
||||
uint8_t ep_in[CANOKEY_EP_NUM][CANOKEY_EP_IN_BUFFER_SIZE];
|
||||
/*
|
||||
* See canokey_emu_transmit
|
||||
*
|
||||
* For large INTR IN, receive multiple data from canokey device loop
|
||||
* in this case ep_in_size would increase with every call
|
||||
*/
|
||||
uint32_t ep_in_size[CANOKEY_EP_NUM];
|
||||
/*
|
||||
* Used in canokey_handle_data
|
||||
* for IN larger than p->iov.size, we would do multiple handle_data()
|
||||
*
|
||||
* The difference between ep_in_pos and ep_in_size:
|
||||
* We first increase ep_in_size to fill ep_in buffer in device_loop,
|
||||
* then use ep_in_pos to submit data from ep_in buffer in handle_data
|
||||
*/
|
||||
uint32_t ep_in_pos[CANOKEY_EP_NUM];
|
||||
CanoKeyEPState ep_in_state[CANOKEY_EP_NUM];
|
||||
|
||||
/* OUT pointer to canokey recv buffer */
|
||||
uint8_t *ep_out[CANOKEY_EP_NUM];
|
||||
uint32_t ep_out_size[CANOKEY_EP_NUM];
|
||||
/* For large BULK OUT, multiple write to ep_out is needed */
|
||||
uint8_t ep_out_buffer[CANOKEY_EP_NUM][CANOKEY_EP_OUT_BUFFER_SIZE];
|
||||
|
||||
/* Properties */
|
||||
char *file; /* canokey-file */
|
||||
} CanoKeyState;
|
||||
|
||||
#endif /* CANOKEY_H */
|
@ -2011,7 +2011,10 @@ static int ehci_state_writeback(EHCIQueue *q)
|
||||
ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd);
|
||||
qtd = (uint32_t *) &q->qh.next_qtd;
|
||||
addr = NLPTR_GET(p->qtdaddr);
|
||||
put_dwords(q->ehci, addr + 2 * sizeof(uint32_t), qtd + 2, 2);
|
||||
/* First write back the offset */
|
||||
put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qtd + 3, 1);
|
||||
/* Then write back the token, clearing the 'active' bit */
|
||||
put_dwords(q->ehci, addr + 2 * sizeof(uint32_t), qtd + 2, 1);
|
||||
ehci_free_packet(p);
|
||||
|
||||
/*
|
||||
|
@ -63,6 +63,11 @@ if u2f.found()
|
||||
softmmu_ss.add(when: 'CONFIG_USB_U2F', if_true: [u2f, files('u2f-emulated.c')])
|
||||
endif
|
||||
|
||||
# CanoKey
|
||||
if canokey.found()
|
||||
softmmu_ss.add(when: 'CONFIG_USB_CANOKEY', if_true: [canokey, files('canokey.c')])
|
||||
endif
|
||||
|
||||
# usb redirect
|
||||
if usbredir.found()
|
||||
usbredir_ss = ss.source_set()
|
||||
|
@ -1280,7 +1280,8 @@ static void usbredir_create_parser(USBRedirDevice *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
if (runstate_check(RUN_STATE_INMIGRATE) ||
|
||||
runstate_check(RUN_STATE_PRELAUNCH)) {
|
||||
flags |= usbredirparser_fl_no_hello;
|
||||
}
|
||||
usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE,
|
||||
|
@ -345,3 +345,19 @@ usb_serial_set_baud(int bus, int addr, int baud) "dev %d:%u baud rate %d"
|
||||
usb_serial_set_data(int bus, int addr, int parity, int data, int stop) "dev %d:%u parity %c, data bits %d, stop bits %d"
|
||||
usb_serial_set_flow_control(int bus, int addr, int index) "dev %d:%u flow control %d"
|
||||
usb_serial_set_xonxoff(int bus, int addr, uint8_t xon, uint8_t xoff) "dev %d:%u xon 0x%x xoff 0x%x"
|
||||
|
||||
# canokey.c
|
||||
canokey_emu_stall_ep(uint8_t ep) "ep %d"
|
||||
canokey_emu_set_address(uint8_t addr) "addr %d"
|
||||
canokey_emu_prepare_receive(uint8_t ep, uint16_t size) "ep %d size %d"
|
||||
canokey_emu_transmit(uint8_t ep, uint16_t size) "ep %d size %d"
|
||||
canokey_thread_start(void)
|
||||
canokey_thread_stop(void)
|
||||
canokey_handle_reset(void)
|
||||
canokey_handle_control_setup(int request, int value, int index, int length) "request 0x%04X value 0x%04X index 0x%04X length 0x%04X"
|
||||
canokey_handle_control_out(void)
|
||||
canokey_handle_control_in(int actual_len) "len %d"
|
||||
canokey_handle_data_out(uint8_t ep_out, uint32_t out_len) "ep %d len %d"
|
||||
canokey_handle_data_in(uint8_t ep_in, uint32_t in_len) "ep %d len %d"
|
||||
canokey_realize(void)
|
||||
canokey_unrealize(void)
|
||||
|
@ -106,14 +106,14 @@ err:
|
||||
return;
|
||||
}
|
||||
|
||||
static int vfio_display_edid_ui_info(void *opaque, uint32_t idx,
|
||||
QemuUIInfo *info)
|
||||
static void vfio_display_edid_ui_info(void *opaque, uint32_t idx,
|
||||
QemuUIInfo *info)
|
||||
{
|
||||
VFIOPCIDevice *vdev = opaque;
|
||||
VFIODisplay *dpy = vdev->dpy;
|
||||
|
||||
if (!dpy->edid_regs) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->width && info->height) {
|
||||
@ -121,8 +121,6 @@ static int vfio_display_edid_ui_info(void *opaque, uint32_t idx,
|
||||
} else {
|
||||
vfio_display_edid_update(vdev, false, 0, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vfio_display_edid_init(VFIOPCIDevice *vdev)
|
||||
|
@ -2031,18 +2031,16 @@ static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque,
|
||||
if (supports_f_config) {
|
||||
if (!virtio_has_feature(protocol_features,
|
||||
VHOST_USER_PROTOCOL_F_CONFIG)) {
|
||||
error_setg(errp, "vhost-user device %s expecting "
|
||||
error_setg(errp, "vhost-user device expecting "
|
||||
"VHOST_USER_PROTOCOL_F_CONFIG but the vhost-user backend does "
|
||||
"not support it.", dev->vdev->name);
|
||||
"not support it.");
|
||||
return -EPROTO;
|
||||
}
|
||||
} else {
|
||||
if (virtio_has_feature(protocol_features,
|
||||
VHOST_USER_PROTOCOL_F_CONFIG)) {
|
||||
warn_reportf_err(*errp, "vhost-user backend supports "
|
||||
"VHOST_USER_PROTOCOL_F_CONFIG for "
|
||||
"device %s but QEMU does not.",
|
||||
dev->vdev->name);
|
||||
"VHOST_USER_PROTOCOL_F_CONFIG but QEMU does not.");
|
||||
protocol_features &= ~(1ULL << VHOST_USER_PROTOCOL_F_CONFIG);
|
||||
}
|
||||
}
|
||||
|
@ -419,11 +419,8 @@ static inline bool tlb_hit(target_ulong tlb_addr, target_ulong addr)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
/* accel/tcg/cpu-exec.c */
|
||||
void dump_drift_info(GString *buf);
|
||||
/* accel/tcg/translate-all.c */
|
||||
void dump_exec_info(GString *buf);
|
||||
void dump_opcount_info(GString *buf);
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
|
@ -2810,6 +2810,9 @@ MemTxResult address_space_write_cached_slow(MemoryRegionCache *cache,
|
||||
hwaddr addr, const void *buf,
|
||||
hwaddr len);
|
||||
|
||||
int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr);
|
||||
bool prepare_mmio_access(MemoryRegion *mr);
|
||||
|
||||
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
|
||||
{
|
||||
if (is_write) {
|
||||
|
40
include/hw/acpi/acpi_aml_interface.h
Normal file
40
include/hw/acpi/acpi_aml_interface.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef ACPI_AML_INTERFACE_H
|
||||
#define ACPI_AML_INTERFACE_H
|
||||
|
||||
#include "qom/object.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
|
||||
#define TYPE_ACPI_DEV_AML_IF "acpi-dev-aml-interface"
|
||||
typedef struct AcpiDevAmlIfClass AcpiDevAmlIfClass;
|
||||
DECLARE_CLASS_CHECKERS(AcpiDevAmlIfClass, ACPI_DEV_AML_IF, TYPE_ACPI_DEV_AML_IF)
|
||||
#define ACPI_DEV_AML_IF(obj) \
|
||||
INTERFACE_CHECK(AcpiDevAmlIf, (obj), TYPE_ACPI_DEV_AML_IF)
|
||||
|
||||
typedef struct AcpiDevAmlIf AcpiDevAmlIf;
|
||||
typedef void (*dev_aml_fn)(AcpiDevAmlIf *adev, Aml *scope);
|
||||
|
||||
/**
|
||||
* AcpiDevAmlIfClass:
|
||||
*
|
||||
* build_dev_aml: adds device specific AML blob to provided scope
|
||||
*
|
||||
* Interface is designed for providing generic callback that builds device
|
||||
* specific AML blob.
|
||||
*/
|
||||
struct AcpiDevAmlIfClass {
|
||||
/* <private> */
|
||||
InterfaceClass parent_class;
|
||||
|
||||
/* <public> */
|
||||
dev_aml_fn build_dev_aml;
|
||||
};
|
||||
|
||||
static inline void call_dev_aml_func(DeviceState *dev, Aml *scope)
|
||||
{
|
||||
if (object_dynamic_cast(OBJECT(dev), TYPE_ACPI_DEV_AML_IF)) {
|
||||
AcpiDevAmlIfClass *klass = ACPI_DEV_AML_IF_GET_CLASS(dev);
|
||||
klass->build_dev_aml(ACPI_DEV_AML_IF(dev), scope);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -19,10 +19,11 @@
|
||||
#define HW_ACPI_CXL_H
|
||||
|
||||
#include "hw/acpi/bios-linker-loader.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
|
||||
void cxl_build_cedt(MachineState *ms, GArray *table_offsets, GArray *table_data,
|
||||
void cxl_build_cedt(GArray *table_offsets, GArray *table_data,
|
||||
BIOSLinker *linker, const char *oem_id,
|
||||
const char *oem_table_id);
|
||||
const char *oem_table_id, CXLState *cxl_state);
|
||||
void build_cxl_osc_method(Aml *dev);
|
||||
|
||||
#endif
|
||||
|
@ -9,13 +9,8 @@
|
||||
#ifndef HW_ACPI_IPMI_H
|
||||
#define HW_ACPI_IPMI_H
|
||||
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/acpi/acpi_aml_interface.h"
|
||||
|
||||
/*
|
||||
* Add ACPI IPMI entries for all registered IPMI devices whose parent
|
||||
* bus matches the given bus. The resource is the ACPI resource that
|
||||
* contains the IPMI device, this is required for the I2C CRS.
|
||||
*/
|
||||
void build_acpi_ipmi_devices(Aml *table, BusState *bus, const char *resource);
|
||||
void build_ipmi_dev_aml(AcpiDevAmlIf *adev, Aml *scope);
|
||||
|
||||
#endif /* HW_ACPI_IPMI_H */
|
||||
|
75
include/hw/acpi/piix4.h
Normal file
75
include/hw/acpi/piix4.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* ACPI implementation
|
||||
*
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2.1 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* Contributions after 2012-01-13 are licensed under the terms of the
|
||||
* GNU GPL, version 2 or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef HW_ACPI_PIIX4_H
|
||||
#define HW_ACPI_PIIX4_H
|
||||
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/acpi/cpu_hotplug.h"
|
||||
#include "hw/acpi/memory_hotplug.h"
|
||||
#include "hw/acpi/pcihp.h"
|
||||
#include "hw/i2c/pm_smbus.h"
|
||||
#include "hw/isa/apm.h"
|
||||
|
||||
#define TYPE_PIIX4_PM "PIIX4_PM"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PIIX4PMState, PIIX4_PM)
|
||||
|
||||
struct PIIX4PMState {
|
||||
/*< private >*/
|
||||
PCIDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion io;
|
||||
uint32_t io_base;
|
||||
|
||||
MemoryRegion io_gpe;
|
||||
ACPIREGS ar;
|
||||
|
||||
APMState apm;
|
||||
|
||||
PMSMBus smb;
|
||||
uint32_t smb_io_base;
|
||||
|
||||
qemu_irq irq;
|
||||
qemu_irq smi_irq;
|
||||
bool smm_enabled;
|
||||
bool smm_compat;
|
||||
Notifier machine_ready;
|
||||
Notifier powerdown_notifier;
|
||||
|
||||
AcpiPciHpState acpi_pci_hotplug;
|
||||
bool use_acpi_hotplug_bridge;
|
||||
bool use_acpi_root_pci_hotplug;
|
||||
bool not_migrate_acpi_index;
|
||||
|
||||
uint8_t disable_s3;
|
||||
uint8_t disable_s4;
|
||||
uint8_t s4_val;
|
||||
|
||||
bool cpu_hotplug_legacy;
|
||||
AcpiCpuHotplug gpe_cpu;
|
||||
CPUHotplugState cpuhp_state;
|
||||
|
||||
MemHotplugState acpi_memory_hotplug;
|
||||
};
|
||||
|
||||
#endif
|
@ -10,8 +10,7 @@
|
||||
#define TYPE_ISA_FDC "isa-fdc"
|
||||
|
||||
void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds);
|
||||
void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
|
||||
hwaddr mmio_base, DriveInfo **fds);
|
||||
void fdctrl_init_sysbus(qemu_irq irq, hwaddr mmio_base, DriveInfo **fds);
|
||||
void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base,
|
||||
DriveInfo **fds, qemu_irq *fdc_tc);
|
||||
|
||||
|
@ -269,7 +269,6 @@ struct MachineClass {
|
||||
bool ignore_boot_device_suffixes;
|
||||
bool smbus_no_migration_support;
|
||||
bool nvdimm_supported;
|
||||
bool cxl_supported;
|
||||
bool numa_mem_supported;
|
||||
bool auto_enable_numa;
|
||||
SMPCompatProps smp_props;
|
||||
@ -360,8 +359,8 @@ struct MachineState {
|
||||
CPUArchIdList *possible_cpus;
|
||||
CpuTopology smp;
|
||||
struct NVDIMMState *nvdimms_state;
|
||||
struct CXLState *cxl_devices_state;
|
||||
struct NumaState *numa_state;
|
||||
CXLFixedMemoryWindowOptionsList *cfmws_list;
|
||||
};
|
||||
|
||||
#define DEFINE_MACHINE(namestr, machine_initfn) \
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
|
||||
#include "qapi/qapi-types-machine.h"
|
||||
#include "qapi/qapi-visit-machine.h"
|
||||
#include "hw/pci/pci_bridge.h"
|
||||
#include "hw/pci/pci_host.h"
|
||||
#include "cxl_pci.h"
|
||||
@ -40,6 +41,7 @@ typedef struct CXLState {
|
||||
MemoryRegion host_mr;
|
||||
unsigned int next_mr_idx;
|
||||
GList *fixed_windows;
|
||||
CXLFixedMemoryWindowOptionsList *cfmw_list;
|
||||
} CXLState;
|
||||
|
||||
struct CXLHost {
|
||||
@ -51,11 +53,4 @@ struct CXLHost {
|
||||
#define TYPE_PXB_CXL_HOST "pxb-cxl-host"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(CXLHost, PXB_CXL_HOST)
|
||||
|
||||
void cxl_fixed_memory_window_config(MachineState *ms,
|
||||
CXLFixedMemoryWindowOptions *object,
|
||||
Error **errp);
|
||||
void cxl_fixed_memory_window_link_targets(Error **errp);
|
||||
|
||||
extern const MemoryRegionOps cfmws_ops;
|
||||
|
||||
#endif
|
||||
|
23
include/hw/cxl/cxl_host.h
Normal file
23
include/hw/cxl/cxl_host.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* QEMU CXL Host Setup
|
||||
*
|
||||
* Copyright (c) 2022 Huawei
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2. See the
|
||||
* COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/cxl/cxl.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#ifndef CXL_HOST_H
|
||||
#define CXL_HOST_H
|
||||
|
||||
void cxl_machine_init(Object *obj, CXLState *state);
|
||||
void cxl_fmws_link_targets(CXLState *stat, Error **errp);
|
||||
void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp);
|
||||
|
||||
extern const MemoryRegionOps cfmws_ops;
|
||||
|
||||
#endif
|
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