Compare commits
313 Commits
pull-ui-20
...
pull-ui-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58aa7d8e44 | ||
|
|
a6ccabd676 | ||
|
|
46d921bebe | ||
|
|
e0d2bd5195 | ||
|
|
91ec41dc3f | ||
|
|
1464ad45cd | ||
|
|
48eb62a74f | ||
|
|
b1918fbb1c | ||
|
|
10f759079e | ||
|
|
b5a1b44318 | ||
|
|
0399293e5b | ||
|
|
f194a1ae53 | ||
|
|
9ee86b8526 | ||
|
|
4d91e9115c | ||
|
|
c81200b014 | ||
|
|
14f00c6c49 | ||
|
|
96a1616c85 | ||
|
|
e55250c6cb | ||
|
|
3c0f12df65 | ||
|
|
ba63cf47a9 | ||
|
|
a55c910e0b | ||
|
|
9776f63645 | ||
|
|
7ef295ea5b | ||
|
|
140b7ce5ff | ||
|
|
04ae712a9f | ||
|
|
e334bd3190 | ||
|
|
9886ecdf31 | ||
|
|
91cca2cda9 | ||
|
|
aa6489da4e | ||
|
|
dacf0a2ff7 | ||
|
|
12dcc3217d | ||
|
|
73462dddf6 | ||
|
|
c3ae85fc8f | ||
|
|
9c5a746038 | ||
|
|
b2e62d9a7b | ||
|
|
ed50ff7875 | ||
|
|
f9fd40ebe4 | ||
|
|
49017bd8b4 | ||
|
|
a0e1e6d705 | ||
|
|
eab713941a | ||
|
|
4824a61a6d | ||
|
|
738a5d9fbb | ||
|
|
16f4a8dc5c | ||
|
|
76151cacfe | ||
|
|
83ec1923cd | ||
|
|
8b41c30525 | ||
|
|
0719e71e52 | ||
|
|
71c2768433 | ||
|
|
8c4f0eb94c | ||
|
|
2d3b7c0164 | ||
|
|
f8693c2cd0 | ||
|
|
60253ed1e6 | ||
|
|
9f14b0add1 | ||
|
|
74074e8a7c | ||
|
|
3c52ddcdc5 | ||
|
|
750cf86932 | ||
|
|
ed6128ebbd | ||
|
|
4ade0541de | ||
|
|
3d211d9f4d | ||
|
|
b23197f9cf | ||
|
|
bc9beb47c7 | ||
|
|
5d4e1a1081 | ||
|
|
1bcea73e13 | ||
|
|
56797b1fbc | ||
|
|
3596f524d4 | ||
|
|
62cb4145bb | ||
|
|
4779dc1d19 | ||
|
|
23d92d68e7 | ||
|
|
2c140f5f2c | ||
|
|
6411dd1334 | ||
|
|
9c279bec75 | ||
|
|
646fd16865 | ||
|
|
ce350f32e4 | ||
|
|
d90527178c | ||
|
|
6aaa681c9b | ||
|
|
ce1307e180 | ||
|
|
bc994b74ea | ||
|
|
562f5e0b97 | ||
|
|
8581c115d2 | ||
|
|
a28d8391e3 | ||
|
|
99abd0d6f7 | ||
|
|
fe345a3d5d | ||
|
|
8777f6abdb | ||
|
|
c5b2ee4c7a | ||
|
|
5ab0e547bf | ||
|
|
66fb2d5467 | ||
|
|
0b85d73583 | ||
|
|
d9c7737e57 | ||
|
|
9c74a85304 | ||
|
|
fee5b753ff | ||
|
|
6575ccddf4 | ||
|
|
01df51432e | ||
|
|
f22d0af076 | ||
|
|
b98d26e333 | ||
|
|
f2c1d54c18 | ||
|
|
05fa1c742f | ||
|
|
d2ba7ecb34 | ||
|
|
cc199b16cf | ||
|
|
4c9bca7e39 | ||
|
|
16096a4d47 | ||
|
|
21cd917ff5 | ||
|
|
e5b43573e2 | ||
|
|
04a3615860 | ||
|
|
939901dcd2 | ||
|
|
b189346eb1 | ||
|
|
1bff960642 | ||
|
|
60390a2192 | ||
|
|
eab8eb8db3 | ||
|
|
7725b8bf12 | ||
|
|
071608b519 | ||
|
|
1da90c34c9 | ||
|
|
3ff430aa91 | ||
|
|
35227e6a09 | ||
|
|
e8ce12d9ea | ||
|
|
beded0ff7f | ||
|
|
e220656ce1 | ||
|
|
438528a3e7 | ||
|
|
9f5c6d06ad | ||
|
|
a005b3ef50 | ||
|
|
902c053d83 | ||
|
|
09b5e30da5 | ||
|
|
2d7d06d847 | ||
|
|
cba0e7796b | ||
|
|
ce266b75fe | ||
|
|
d4a63ac8b1 | ||
|
|
3d0db3e74d | ||
|
|
6e378dd214 | ||
|
|
aa53d5bfc3 | ||
|
|
ea6a55bcc0 | ||
|
|
0aa6aefc9c | ||
|
|
e20d84c140 | ||
|
|
d44ec15630 | ||
|
|
0a7ac9f9e7 | ||
|
|
5c1bc9a234 | ||
|
|
ed0db8663a | ||
|
|
a55b53a2f4 | ||
|
|
09aa3bf382 | ||
|
|
1fce1ba985 | ||
|
|
a8d64e7351 | ||
|
|
10eacda787 | ||
|
|
af393ffc6d | ||
|
|
81907a5829 | ||
|
|
58ae2d1f03 | ||
|
|
e6c8fc07b4 | ||
|
|
52ff951b4f | ||
|
|
cb01d3912c | ||
|
|
ae08792301 | ||
|
|
f8c88bbcda | ||
|
|
50866ba5a2 | ||
|
|
235ea1f5c8 | ||
|
|
8da5ef579f | ||
|
|
a609ad8b69 | ||
|
|
4d1e324b22 | ||
|
|
a88a5cd2e8 | ||
|
|
2d4846bd7b | ||
|
|
d8b9d7719c | ||
|
|
bdf46d6478 | ||
|
|
e6915b5f3a | ||
|
|
01bc435b44 | ||
|
|
bee62662a3 | ||
|
|
152db36ae6 | ||
|
|
d319f83fe9 | ||
|
|
0759487b56 | ||
|
|
03cbfd7b5c | ||
|
|
461a1582f0 | ||
|
|
c489e5591f | ||
|
|
67ef811ed1 | ||
|
|
e55eb806db | ||
|
|
0c6940d086 | ||
|
|
8021de1013 | ||
|
|
25d943b957 | ||
|
|
6c6916dac8 | ||
|
|
6771197dff | ||
|
|
02506e2d54 | ||
|
|
a7a173624e | ||
|
|
01fdadde80 | ||
|
|
0b4b49387c | ||
|
|
586fc27e6a | ||
|
|
cfc3b074de | ||
|
|
21a933ea33 | ||
|
|
d61524486c | ||
|
|
3655cb9c73 | ||
|
|
58eaa2174e | ||
|
|
774ae4254d | ||
|
|
df215b59d9 | ||
|
|
828066c78a | ||
|
|
f678f671ba | ||
|
|
3292b4477f | ||
|
|
518d7fd2a0 | ||
|
|
5dc1fbae70 | ||
|
|
723733575b | ||
|
|
8283f6f821 | ||
|
|
533e8bbb55 | ||
|
|
75fb3d286e | ||
|
|
18d6abae3e | ||
|
|
d6b304ba92 | ||
|
|
86165b499e | ||
|
|
5602b39ff3 | ||
|
|
fee089e4e2 | ||
|
|
e24a47c5b7 | ||
|
|
03de2f5274 | ||
|
|
2906cddfec | ||
|
|
adb3feda8d | ||
|
|
a1afb6062e | ||
|
|
8b1fe1cedf | ||
|
|
ef0716df7f | ||
|
|
9ae91bc43f | ||
|
|
3dbc01ae87 | ||
|
|
2b75f84823 | ||
|
|
e8dc06d225 | ||
|
|
d159148b63 | ||
|
|
388e47c75b | ||
|
|
072ed5f260 | ||
|
|
9897e46264 | ||
|
|
3240dd9a69 | ||
|
|
4f7ab0cdbc | ||
|
|
f894efd199 | ||
|
|
93a92d3bd6 | ||
|
|
13756fb008 | ||
|
|
7c73d2a3fa | ||
|
|
b6e17875f2 | ||
|
|
de3f1b9841 | ||
|
|
460c579f3d | ||
|
|
5089c7ce82 | ||
|
|
7bd57b5150 | ||
|
|
c3b7f66800 | ||
|
|
f8e1f5d6a2 | ||
|
|
ce15110981 | ||
|
|
d2dc4069e0 | ||
|
|
1b1624092d | ||
|
|
0ea63844c2 | ||
|
|
91478cefaa | ||
|
|
b3915dbbdc | ||
|
|
869938ae2a | ||
|
|
3174c64bb7 | ||
|
|
90ce6e2644 | ||
|
|
974dc73d77 | ||
|
|
30456d5ba3 | ||
|
|
b1e34d1c3a | ||
|
|
d57106a4b6 | ||
|
|
fd3e39a40c | ||
|
|
e78490c44c | ||
|
|
79f56d82f8 | ||
|
|
1ef26b1f30 | ||
|
|
b82fc321bf | ||
|
|
22672a3798 | ||
|
|
8e388e907b | ||
|
|
474114b730 | ||
|
|
58c7b618f3 | ||
|
|
1e3165980c | ||
|
|
014cb152b8 | ||
|
|
b5e751b51f | ||
|
|
ea6e4981bf | ||
|
|
39de99843e | ||
|
|
ffe42cc14c | ||
|
|
b54ca0c3df | ||
|
|
fa1298c2d6 | ||
|
|
5f77e06baa | ||
|
|
fe3c546c5f | ||
|
|
64c9bc181f | ||
|
|
14ec7b2c5b | ||
|
|
80eecda8e5 | ||
|
|
96c33a4523 | ||
|
|
d42a0d1484 | ||
|
|
4a6b52d67e | ||
|
|
39ba2ea61f | ||
|
|
2264c2c96e | ||
|
|
1fbeff72c2 | ||
|
|
63e27f28f2 | ||
|
|
ca1fc8c97e | ||
|
|
65d64f3623 | ||
|
|
baf28f57e2 | ||
|
|
228de9cf1d | ||
|
|
e3517a5299 | ||
|
|
cf7ea1e60c | ||
|
|
8eb779e422 | ||
|
|
fe243e4881 | ||
|
|
43e15ed4fd | ||
|
|
4b84fc70ce | ||
|
|
f436c94102 | ||
|
|
156abc2f90 | ||
|
|
d310d85bf4 | ||
|
|
1ffad77cde | ||
|
|
f5a845fdb4 | ||
|
|
a90cade023 | ||
|
|
f9d058852c | ||
|
|
eb8a1a1cbd | ||
|
|
398befdf50 | ||
|
|
dce13204a0 | ||
|
|
8a0fc18d88 | ||
|
|
100f8f2608 | ||
|
|
1588ab5d0b | ||
|
|
d5851089a8 | ||
|
|
6f9b6d57ae | ||
|
|
03ba36c83d | ||
|
|
45b2d418e0 | ||
|
|
6921b18095 | ||
|
|
3c9242f5ae | ||
|
|
a02dabe10a | ||
|
|
9bd9c7f5b5 | ||
|
|
a513416ecf | ||
|
|
aa6e546c5a | ||
|
|
fa8b7ce2c6 | ||
|
|
eb769f7420 | ||
|
|
77c9aaefd7 | ||
|
|
499afa2512 | ||
|
|
3babeb153c | ||
|
|
9ba371b634 | ||
|
|
12d5ee3a7e | ||
|
|
bca5a8f462 | ||
|
|
f38738e212 | ||
|
|
2875645b65 | ||
|
|
a40db1b36b |
28
MAINTAINERS
28
MAINTAINERS
@@ -363,6 +363,7 @@ M: Dmitry Solodkiy <d.solodkiy@samsung.com>
|
|||||||
L: qemu-arm@nongnu.org
|
L: qemu-arm@nongnu.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: hw/*/exynos*
|
F: hw/*/exynos*
|
||||||
|
F: include/hw/arm/exynos4210.h
|
||||||
|
|
||||||
Calxeda Highbank
|
Calxeda Highbank
|
||||||
M: Rob Herring <robh@kernel.org>
|
M: Rob Herring <robh@kernel.org>
|
||||||
@@ -390,6 +391,7 @@ L: qemu-arm@nongnu.org
|
|||||||
S: Odd fixes
|
S: Odd fixes
|
||||||
F: hw/*/imx*
|
F: hw/*/imx*
|
||||||
F: hw/arm/kzm.c
|
F: hw/arm/kzm.c
|
||||||
|
F: include/hw/arm/fsl-imx31.h
|
||||||
|
|
||||||
Integrator CP
|
Integrator CP
|
||||||
M: Peter Maydell <peter.maydell@linaro.org>
|
M: Peter Maydell <peter.maydell@linaro.org>
|
||||||
@@ -432,6 +434,7 @@ F: hw/arm/spitz.c
|
|||||||
F: hw/arm/tosa.c
|
F: hw/arm/tosa.c
|
||||||
F: hw/arm/z2.c
|
F: hw/arm/z2.c
|
||||||
F: hw/*/pxa2xx*
|
F: hw/*/pxa2xx*
|
||||||
|
F: include/hw/arm/pxa.h
|
||||||
|
|
||||||
Stellaris
|
Stellaris
|
||||||
M: Peter Maydell <peter.maydell@linaro.org>
|
M: Peter Maydell <peter.maydell@linaro.org>
|
||||||
@@ -653,12 +656,6 @@ F: hw/*/grlib*
|
|||||||
|
|
||||||
S390 Machines
|
S390 Machines
|
||||||
-------------
|
-------------
|
||||||
S390 Virtio
|
|
||||||
M: Alexander Graf <agraf@suse.de>
|
|
||||||
S: Maintained
|
|
||||||
F: hw/s390x/s390-*.c
|
|
||||||
X: hw/s390x/*pci*.[hc]
|
|
||||||
|
|
||||||
S390 Virtio-ccw
|
S390 Virtio-ccw
|
||||||
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
M: Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||||
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
M: Christian Borntraeger <borntraeger@de.ibm.com>
|
||||||
@@ -666,7 +663,6 @@ M: Alexander Graf <agraf@suse.de>
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: hw/char/sclp*.[hc]
|
F: hw/char/sclp*.[hc]
|
||||||
F: hw/s390x/
|
F: hw/s390x/
|
||||||
X: hw/s390x/s390-virtio-bus.[ch]
|
|
||||||
F: include/hw/s390x/
|
F: include/hw/s390x/
|
||||||
F: pc-bios/s390-ccw/
|
F: pc-bios/s390-ccw/
|
||||||
F: hw/watchdog/wdt_diag288.c
|
F: hw/watchdog/wdt_diag288.c
|
||||||
@@ -768,6 +764,7 @@ OMAP
|
|||||||
M: Peter Maydell <peter.maydell@linaro.org>
|
M: Peter Maydell <peter.maydell@linaro.org>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: hw/*/omap*
|
F: hw/*/omap*
|
||||||
|
F: include/hw/arm/omap.h
|
||||||
|
|
||||||
IPack
|
IPack
|
||||||
M: Alberto Garcia <berto@igalia.com>
|
M: Alberto Garcia <berto@igalia.com>
|
||||||
@@ -853,6 +850,10 @@ M: Gerd Hoffmann <kraxel@redhat.com>
|
|||||||
S: Maintained
|
S: Maintained
|
||||||
F: hw/usb/*
|
F: hw/usb/*
|
||||||
F: tests/usb-*-test.c
|
F: tests/usb-*-test.c
|
||||||
|
F: docs/usb2.txt
|
||||||
|
F: docs/usb-storage.txt
|
||||||
|
F: include/hw/usb.h
|
||||||
|
F: include/hw/usb/
|
||||||
|
|
||||||
USB (serial adapter)
|
USB (serial adapter)
|
||||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
@@ -920,6 +921,7 @@ M: Amit Shah <amit.shah@redhat.com>
|
|||||||
S: Supported
|
S: Supported
|
||||||
F: hw/virtio/virtio-rng.c
|
F: hw/virtio/virtio-rng.c
|
||||||
F: include/hw/virtio/virtio-rng.h
|
F: include/hw/virtio/virtio-rng.h
|
||||||
|
F: include/sysemu/rng*.h
|
||||||
F: backends/rng*.c
|
F: backends/rng*.c
|
||||||
|
|
||||||
nvme
|
nvme
|
||||||
@@ -1083,6 +1085,7 @@ SPICE
|
|||||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: include/ui/qemu-spice.h
|
F: include/ui/qemu-spice.h
|
||||||
|
F: include/ui/spice-display.h
|
||||||
F: ui/spice-*.c
|
F: ui/spice-*.c
|
||||||
F: audio/spiceaudio.c
|
F: audio/spiceaudio.c
|
||||||
F: hw/display/qxl*
|
F: hw/display/qxl*
|
||||||
@@ -1091,6 +1094,7 @@ Graphics
|
|||||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
S: Odd Fixes
|
S: Odd Fixes
|
||||||
F: ui/
|
F: ui/
|
||||||
|
F: include/ui/
|
||||||
|
|
||||||
Cocoa graphics
|
Cocoa graphics
|
||||||
M: Andreas Färber <andreas.faerber@web.de>
|
M: Andreas Färber <andreas.faerber@web.de>
|
||||||
@@ -1241,6 +1245,7 @@ F: include/migration/
|
|||||||
F: migration/
|
F: migration/
|
||||||
F: scripts/vmstate-static-checker.py
|
F: scripts/vmstate-static-checker.py
|
||||||
F: tests/vmstate-static-checker-data/
|
F: tests/vmstate-static-checker-data/
|
||||||
|
F: docs/migration.txt
|
||||||
|
|
||||||
Seccomp
|
Seccomp
|
||||||
M: Eduardo Otubo <eduardo.otubo@profitbricks.com>
|
M: Eduardo Otubo <eduardo.otubo@profitbricks.com>
|
||||||
@@ -1283,6 +1288,15 @@ S: Maintained
|
|||||||
F: include/qemu/sockets.h
|
F: include/qemu/sockets.h
|
||||||
F: util/qemu-sockets.c
|
F: util/qemu-sockets.c
|
||||||
|
|
||||||
|
Throttling infrastructure
|
||||||
|
M: Alberto Garcia <berto@igalia.com>
|
||||||
|
S: Supported
|
||||||
|
F: block/throttle-groups.c
|
||||||
|
F: include/block/throttle-groups.h
|
||||||
|
F: include/qemu/throttle.h
|
||||||
|
F: util/throttle.c
|
||||||
|
L: qemu-block@nongnu.org
|
||||||
|
|
||||||
Usermode Emulation
|
Usermode Emulation
|
||||||
------------------
|
------------------
|
||||||
Overall
|
Overall
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -391,7 +391,7 @@ bepo cz
|
|||||||
ifdef INSTALL_BLOBS
|
ifdef INSTALL_BLOBS
|
||||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||||
acpi-dsdt.aml q35-acpi-dsdt.aml \
|
acpi-dsdt.aml \
|
||||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
#ifndef QEMU_AUDIO_H
|
#ifndef QEMU_AUDIO_H
|
||||||
#define QEMU_AUDIO_H
|
#define QEMU_AUDIO_H
|
||||||
|
|
||||||
#include "config-host.h"
|
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
|
|
||||||
typedef void (*audio_callback_fn) (void *opaque, int avail);
|
typedef void (*audio_callback_fn) (void *opaque, int avail);
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ static CharDriverState *chr_baum_init(const char *id,
|
|||||||
ChardevReturn *ret,
|
ChardevReturn *ret,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
ChardevCommon *common = qapi_ChardevDummy_base(backend->u.braille);
|
ChardevCommon *common = backend->u.braille;
|
||||||
BaumDriverState *baum;
|
BaumDriverState *baum;
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
brlapi_handle_t *handle;
|
brlapi_handle_t *handle;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
|
|||||||
ChardevReturn *ret,
|
ChardevReturn *ret,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
ChardevCommon *common = qapi_ChardevDummy_base(backend->u.msmouse);
|
ChardevCommon *common = backend->u.msmouse;
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
|
|
||||||
chr = qemu_chr_alloc(common, errp);
|
chr = qemu_chr_alloc(common, errp);
|
||||||
|
|||||||
@@ -25,33 +25,12 @@ typedef struct RngEgd
|
|||||||
|
|
||||||
CharDriverState *chr;
|
CharDriverState *chr;
|
||||||
char *chr_name;
|
char *chr_name;
|
||||||
|
|
||||||
GSList *requests;
|
|
||||||
} RngEgd;
|
} RngEgd;
|
||||||
|
|
||||||
typedef struct RngRequest
|
static void rng_egd_request_entropy(RngBackend *b, RngRequest *req)
|
||||||
{
|
|
||||||
EntropyReceiveFunc *receive_entropy;
|
|
||||||
uint8_t *data;
|
|
||||||
void *opaque;
|
|
||||||
size_t offset;
|
|
||||||
size_t size;
|
|
||||||
} RngRequest;
|
|
||||||
|
|
||||||
static void rng_egd_request_entropy(RngBackend *b, size_t size,
|
|
||||||
EntropyReceiveFunc *receive_entropy,
|
|
||||||
void *opaque)
|
|
||||||
{
|
{
|
||||||
RngEgd *s = RNG_EGD(b);
|
RngEgd *s = RNG_EGD(b);
|
||||||
RngRequest *req;
|
size_t size = req->size;
|
||||||
|
|
||||||
req = g_malloc(sizeof(*req));
|
|
||||||
|
|
||||||
req->offset = 0;
|
|
||||||
req->size = size;
|
|
||||||
req->receive_entropy = receive_entropy;
|
|
||||||
req->opaque = opaque;
|
|
||||||
req->data = g_malloc(req->size);
|
|
||||||
|
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
uint8_t header[2];
|
uint8_t header[2];
|
||||||
@@ -65,14 +44,6 @@ static void rng_egd_request_entropy(RngBackend *b, size_t size,
|
|||||||
|
|
||||||
size -= len;
|
size -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->requests = g_slist_append(s->requests, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rng_egd_free_request(RngRequest *req)
|
|
||||||
{
|
|
||||||
g_free(req->data);
|
|
||||||
g_free(req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rng_egd_chr_can_read(void *opaque)
|
static int rng_egd_chr_can_read(void *opaque)
|
||||||
@@ -81,7 +52,7 @@ static int rng_egd_chr_can_read(void *opaque)
|
|||||||
GSList *i;
|
GSList *i;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
for (i = s->requests; i; i = i->next) {
|
for (i = s->parent.requests; i; i = i->next) {
|
||||||
RngRequest *req = i->data;
|
RngRequest *req = i->data;
|
||||||
size += req->size - req->offset;
|
size += req->size - req->offset;
|
||||||
}
|
}
|
||||||
@@ -94,8 +65,8 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
RngEgd *s = RNG_EGD(opaque);
|
RngEgd *s = RNG_EGD(opaque);
|
||||||
size_t buf_offset = 0;
|
size_t buf_offset = 0;
|
||||||
|
|
||||||
while (size > 0 && s->requests) {
|
while (size > 0 && s->parent.requests) {
|
||||||
RngRequest *req = s->requests->data;
|
RngRequest *req = s->parent.requests->data;
|
||||||
int len = MIN(size, req->size - req->offset);
|
int len = MIN(size, req->size - req->offset);
|
||||||
|
|
||||||
memcpy(req->data + req->offset, buf + buf_offset, len);
|
memcpy(req->data + req->offset, buf + buf_offset, len);
|
||||||
@@ -104,38 +75,13 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
|
|||||||
size -= len;
|
size -= len;
|
||||||
|
|
||||||
if (req->offset == req->size) {
|
if (req->offset == req->size) {
|
||||||
s->requests = g_slist_remove_link(s->requests, s->requests);
|
|
||||||
|
|
||||||
req->receive_entropy(req->opaque, req->data, req->size);
|
req->receive_entropy(req->opaque, req->data, req->size);
|
||||||
|
|
||||||
rng_egd_free_request(req);
|
rng_backend_finalize_request(&s->parent, req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rng_egd_free_requests(RngEgd *s)
|
|
||||||
{
|
|
||||||
GSList *i;
|
|
||||||
|
|
||||||
for (i = s->requests; i; i = i->next) {
|
|
||||||
rng_egd_free_request(i->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_slist_free(s->requests);
|
|
||||||
s->requests = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rng_egd_cancel_requests(RngBackend *b)
|
|
||||||
{
|
|
||||||
RngEgd *s = RNG_EGD(b);
|
|
||||||
|
|
||||||
/* We simply delete the list of pending requests. If there is data in the
|
|
||||||
* queue waiting to be read, this is okay, because there will always be
|
|
||||||
* more data than we requested originally
|
|
||||||
*/
|
|
||||||
rng_egd_free_requests(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rng_egd_opened(RngBackend *b, Error **errp)
|
static void rng_egd_opened(RngBackend *b, Error **errp)
|
||||||
{
|
{
|
||||||
RngEgd *s = RNG_EGD(b);
|
RngEgd *s = RNG_EGD(b);
|
||||||
@@ -204,8 +150,6 @@ static void rng_egd_finalize(Object *obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_free(s->chr_name);
|
g_free(s->chr_name);
|
||||||
|
|
||||||
rng_egd_free_requests(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rng_egd_class_init(ObjectClass *klass, void *data)
|
static void rng_egd_class_init(ObjectClass *klass, void *data)
|
||||||
@@ -213,7 +157,6 @@ static void rng_egd_class_init(ObjectClass *klass, void *data)
|
|||||||
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
|
||||||
|
|
||||||
rbc->request_entropy = rng_egd_request_entropy;
|
rbc->request_entropy = rng_egd_request_entropy;
|
||||||
rbc->cancel_requests = rng_egd_cancel_requests;
|
|
||||||
rbc->opened = rng_egd_opened;
|
rbc->opened = rng_egd_opened;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,6 @@ struct RndRandom
|
|||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
||||||
EntropyReceiveFunc *receive_func;
|
|
||||||
void *opaque;
|
|
||||||
size_t size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,36 +34,35 @@ struct RndRandom
|
|||||||
static void entropy_available(void *opaque)
|
static void entropy_available(void *opaque)
|
||||||
{
|
{
|
||||||
RndRandom *s = RNG_RANDOM(opaque);
|
RndRandom *s = RNG_RANDOM(opaque);
|
||||||
uint8_t buffer[s->size];
|
|
||||||
ssize_t len;
|
|
||||||
|
|
||||||
len = read(s->fd, buffer, s->size);
|
while (s->parent.requests != NULL) {
|
||||||
if (len < 0 && errno == EAGAIN) {
|
RngRequest *req = s->parent.requests->data;
|
||||||
return;
|
ssize_t len;
|
||||||
|
|
||||||
|
len = read(s->fd, req->data, req->size);
|
||||||
|
if (len < 0 && errno == EAGAIN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_assert(len != -1);
|
||||||
|
|
||||||
|
req->receive_entropy(req->opaque, req->data, len);
|
||||||
|
|
||||||
|
rng_backend_finalize_request(&s->parent, req);
|
||||||
}
|
}
|
||||||
g_assert(len != -1);
|
|
||||||
|
|
||||||
s->receive_func(s->opaque, buffer, len);
|
|
||||||
s->receive_func = NULL;
|
|
||||||
|
|
||||||
|
/* We've drained all requests, the fd handler can be reset. */
|
||||||
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rng_random_request_entropy(RngBackend *b, size_t size,
|
static void rng_random_request_entropy(RngBackend *b, RngRequest *req)
|
||||||
EntropyReceiveFunc *receive_entropy,
|
|
||||||
void *opaque)
|
|
||||||
{
|
{
|
||||||
RndRandom *s = RNG_RANDOM(b);
|
RndRandom *s = RNG_RANDOM(b);
|
||||||
|
|
||||||
if (s->receive_func) {
|
if (s->parent.requests == NULL) {
|
||||||
s->receive_func(s->opaque, NULL, 0);
|
/* If there are no pending requests yet, we need to
|
||||||
|
* install our fd handler. */
|
||||||
|
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
s->receive_func = receive_entropy;
|
|
||||||
s->opaque = opaque;
|
|
||||||
s->size = size;
|
|
||||||
|
|
||||||
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rng_random_opened(RngBackend *b, Error **errp)
|
static void rng_random_opened(RngBackend *b, Error **errp)
|
||||||
|
|||||||
@@ -20,18 +20,20 @@ void rng_backend_request_entropy(RngBackend *s, size_t size,
|
|||||||
void *opaque)
|
void *opaque)
|
||||||
{
|
{
|
||||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
||||||
|
RngRequest *req;
|
||||||
|
|
||||||
if (k->request_entropy) {
|
if (k->request_entropy) {
|
||||||
k->request_entropy(s, size, receive_entropy, opaque);
|
req = g_malloc(sizeof(*req));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void rng_backend_cancel_requests(RngBackend *s)
|
req->offset = 0;
|
||||||
{
|
req->size = size;
|
||||||
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
|
req->receive_entropy = receive_entropy;
|
||||||
|
req->opaque = opaque;
|
||||||
|
req->data = g_malloc(req->size);
|
||||||
|
|
||||||
if (k->cancel_requests) {
|
k->request_entropy(s, req);
|
||||||
k->cancel_requests(s);
|
|
||||||
|
s->requests = g_slist_append(s->requests, req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +75,30 @@ static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
|||||||
s->opened = true;
|
s->opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rng_backend_free_request(RngRequest *req)
|
||||||
|
{
|
||||||
|
g_free(req->data);
|
||||||
|
g_free(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rng_backend_free_requests(RngBackend *s)
|
||||||
|
{
|
||||||
|
GSList *i;
|
||||||
|
|
||||||
|
for (i = s->requests; i; i = i->next) {
|
||||||
|
rng_backend_free_request(i->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_slist_free(s->requests);
|
||||||
|
s->requests = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rng_backend_finalize_request(RngBackend *s, RngRequest *req)
|
||||||
|
{
|
||||||
|
s->requests = g_slist_remove(s->requests, req);
|
||||||
|
rng_backend_free_request(req);
|
||||||
|
}
|
||||||
|
|
||||||
static void rng_backend_init(Object *obj)
|
static void rng_backend_init(Object *obj)
|
||||||
{
|
{
|
||||||
object_property_add_bool(obj, "opened",
|
object_property_add_bool(obj, "opened",
|
||||||
@@ -81,6 +107,13 @@ static void rng_backend_init(Object *obj)
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rng_backend_finalize(Object *obj)
|
||||||
|
{
|
||||||
|
RngBackend *s = RNG_BACKEND(obj);
|
||||||
|
|
||||||
|
rng_backend_free_requests(s);
|
||||||
|
}
|
||||||
|
|
||||||
static void rng_backend_class_init(ObjectClass *oc, void *data)
|
static void rng_backend_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||||
@@ -93,6 +126,7 @@ static const TypeInfo rng_backend_info = {
|
|||||||
.parent = TYPE_OBJECT,
|
.parent = TYPE_OBJECT,
|
||||||
.instance_size = sizeof(RngBackend),
|
.instance_size = sizeof(RngBackend),
|
||||||
.instance_init = rng_backend_init,
|
.instance_init = rng_backend_init,
|
||||||
|
.instance_finalize = rng_backend_finalize,
|
||||||
.class_size = sizeof(RngBackendClass),
|
.class_size = sizeof(RngBackendClass),
|
||||||
.class_init = rng_backend_class_init,
|
.class_init = rng_backend_class_init,
|
||||||
.abstract = true,
|
.abstract = true,
|
||||||
|
|||||||
4
block.c
4
block.c
@@ -1191,10 +1191,6 @@ static int bdrv_fill_options(QDict **options, const char *filename,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
||||||
*flags |= BDRV_O_INACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,7 @@
|
|||||||
#include "qemu/ratelimit.h"
|
#include "qemu/ratelimit.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
|
|
||||||
#define BACKUP_CLUSTER_BITS 16
|
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||||
#define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS)
|
|
||||||
#define BACKUP_SECTORS_PER_CLUSTER (BACKUP_CLUSTER_SIZE / BDRV_SECTOR_SIZE)
|
|
||||||
|
|
||||||
#define SLICE_TIME 100000000ULL /* ns */
|
#define SLICE_TIME 100000000ULL /* ns */
|
||||||
|
|
||||||
typedef struct CowRequest {
|
typedef struct CowRequest {
|
||||||
@@ -46,9 +43,16 @@ typedef struct BackupBlockJob {
|
|||||||
CoRwlock flush_rwlock;
|
CoRwlock flush_rwlock;
|
||||||
uint64_t sectors_read;
|
uint64_t sectors_read;
|
||||||
HBitmap *bitmap;
|
HBitmap *bitmap;
|
||||||
|
int64_t cluster_size;
|
||||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||||
} BackupBlockJob;
|
} BackupBlockJob;
|
||||||
|
|
||||||
|
/* Size of a cluster in sectors, instead of bytes. */
|
||||||
|
static inline int64_t cluster_size_sectors(BackupBlockJob *job)
|
||||||
|
{
|
||||||
|
return job->cluster_size / BDRV_SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
/* See if in-flight requests overlap and wait for them to complete */
|
/* See if in-flight requests overlap and wait for them to complete */
|
||||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||||
int64_t start,
|
int64_t start,
|
||||||
@@ -97,13 +101,14 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
QEMUIOVector bounce_qiov;
|
QEMUIOVector bounce_qiov;
|
||||||
void *bounce_buffer = NULL;
|
void *bounce_buffer = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
int64_t start, end;
|
int64_t start, end;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
||||||
|
|
||||||
start = sector_num / BACKUP_SECTORS_PER_CLUSTER;
|
start = sector_num / sectors_per_cluster;
|
||||||
end = DIV_ROUND_UP(sector_num + nb_sectors, BACKUP_SECTORS_PER_CLUSTER);
|
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster);
|
||||||
|
|
||||||
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
|
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
|
||||||
|
|
||||||
@@ -118,12 +123,12 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
|
|
||||||
trace_backup_do_cow_process(job, start);
|
trace_backup_do_cow_process(job, start);
|
||||||
|
|
||||||
n = MIN(BACKUP_SECTORS_PER_CLUSTER,
|
n = MIN(sectors_per_cluster,
|
||||||
job->common.len / BDRV_SECTOR_SIZE -
|
job->common.len / BDRV_SECTOR_SIZE -
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER);
|
start * sectors_per_cluster);
|
||||||
|
|
||||||
if (!bounce_buffer) {
|
if (!bounce_buffer) {
|
||||||
bounce_buffer = qemu_blockalign(bs, BACKUP_CLUSTER_SIZE);
|
bounce_buffer = qemu_blockalign(bs, job->cluster_size);
|
||||||
}
|
}
|
||||||
iov.iov_base = bounce_buffer;
|
iov.iov_base = bounce_buffer;
|
||||||
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
||||||
@@ -131,10 +136,10 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (is_write_notifier) {
|
if (is_write_notifier) {
|
||||||
ret = bdrv_co_readv_no_serialising(bs,
|
ret = bdrv_co_readv_no_serialising(bs,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
start * sectors_per_cluster,
|
||||||
n, &bounce_qiov);
|
n, &bounce_qiov);
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n,
|
ret = bdrv_co_readv(bs, start * sectors_per_cluster, n,
|
||||||
&bounce_qiov);
|
&bounce_qiov);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -147,11 +152,11 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||||
ret = bdrv_co_write_zeroes(job->target,
|
ret = bdrv_co_write_zeroes(job->target,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
start * sectors_per_cluster,
|
||||||
n, BDRV_REQ_MAY_UNMAP);
|
n, BDRV_REQ_MAY_UNMAP);
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_co_writev(job->target,
|
ret = bdrv_co_writev(job->target,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER, n,
|
start * sectors_per_cluster, n,
|
||||||
&bounce_qiov);
|
&bounce_qiov);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -322,21 +327,22 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
int64_t cluster;
|
int64_t cluster;
|
||||||
int64_t end;
|
int64_t end;
|
||||||
int64_t last_cluster = -1;
|
int64_t last_cluster = -1;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
BlockDriverState *bs = job->common.bs;
|
BlockDriverState *bs = job->common.bs;
|
||||||
HBitmapIter hbi;
|
HBitmapIter hbi;
|
||||||
|
|
||||||
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||||
clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1);
|
clusters_per_iter = MAX((granularity / job->cluster_size), 1);
|
||||||
bdrv_dirty_iter_init(job->sync_bitmap, &hbi);
|
bdrv_dirty_iter_init(job->sync_bitmap, &hbi);
|
||||||
|
|
||||||
/* Find the next dirty sector(s) */
|
/* Find the next dirty sector(s) */
|
||||||
while ((sector = hbitmap_iter_next(&hbi)) != -1) {
|
while ((sector = hbitmap_iter_next(&hbi)) != -1) {
|
||||||
cluster = sector / BACKUP_SECTORS_PER_CLUSTER;
|
cluster = sector / sectors_per_cluster;
|
||||||
|
|
||||||
/* Fake progress updates for any clusters we skipped */
|
/* Fake progress updates for any clusters we skipped */
|
||||||
if (cluster != last_cluster + 1) {
|
if (cluster != last_cluster + 1) {
|
||||||
job->common.offset += ((cluster - last_cluster - 1) *
|
job->common.offset += ((cluster - last_cluster - 1) *
|
||||||
BACKUP_CLUSTER_SIZE);
|
job->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
||||||
@@ -344,8 +350,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
if (yield_and_check(job)) {
|
if (yield_and_check(job)) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER,
|
ret = backup_do_cow(bs, cluster * sectors_per_cluster,
|
||||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read,
|
sectors_per_cluster, &error_is_read,
|
||||||
false);
|
false);
|
||||||
if ((ret < 0) &&
|
if ((ret < 0) &&
|
||||||
backup_error_action(job, error_is_read, -ret) ==
|
backup_error_action(job, error_is_read, -ret) ==
|
||||||
@@ -357,17 +363,17 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
|
|
||||||
/* If the bitmap granularity is smaller than the backup granularity,
|
/* If the bitmap granularity is smaller than the backup granularity,
|
||||||
* we need to advance the iterator pointer to the next cluster. */
|
* we need to advance the iterator pointer to the next cluster. */
|
||||||
if (granularity < BACKUP_CLUSTER_SIZE) {
|
if (granularity < job->cluster_size) {
|
||||||
bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER);
|
bdrv_set_dirty_iter(&hbi, cluster * sectors_per_cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_cluster = cluster - 1;
|
last_cluster = cluster - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Play some final catchup with the progress meter */
|
/* Play some final catchup with the progress meter */
|
||||||
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
|
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||||
if (last_cluster + 1 < end) {
|
if (last_cluster + 1 < end) {
|
||||||
job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE);
|
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -384,13 +390,14 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
.notify = backup_before_write_notify,
|
.notify = backup_before_write_notify,
|
||||||
};
|
};
|
||||||
int64_t start, end;
|
int64_t start, end;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
QLIST_INIT(&job->inflight_reqs);
|
QLIST_INIT(&job->inflight_reqs);
|
||||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||||
|
|
||||||
start = 0;
|
start = 0;
|
||||||
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
|
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||||
|
|
||||||
job->bitmap = hbitmap_alloc(end, 0);
|
job->bitmap = hbitmap_alloc(end, 0);
|
||||||
|
|
||||||
@@ -427,7 +434,7 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
/* Check to see if these blocks are already in the
|
/* Check to see if these blocks are already in the
|
||||||
* backing file. */
|
* backing file. */
|
||||||
|
|
||||||
for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) {
|
for (i = 0; i < sectors_per_cluster;) {
|
||||||
/* bdrv_is_allocated() only returns true/false based
|
/* bdrv_is_allocated() only returns true/false based
|
||||||
* on the first set of sectors it comes across that
|
* on the first set of sectors it comes across that
|
||||||
* are are all in the same state.
|
* are are all in the same state.
|
||||||
@@ -436,8 +443,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
* needed but at some point that is always the case. */
|
* needed but at some point that is always the case. */
|
||||||
alloced =
|
alloced =
|
||||||
bdrv_is_allocated(bs,
|
bdrv_is_allocated(bs,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER + i,
|
start * sectors_per_cluster + i,
|
||||||
BACKUP_SECTORS_PER_CLUSTER - i, &n);
|
sectors_per_cluster - i, &n);
|
||||||
i += n;
|
i += n;
|
||||||
|
|
||||||
if (alloced == 1 || n == 0) {
|
if (alloced == 1 || n == 0) {
|
||||||
@@ -452,8 +459,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* FULL sync mode we copy the whole drive. */
|
/* FULL sync mode we copy the whole drive. */
|
||||||
ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER,
|
ret = backup_do_cow(bs, start * sectors_per_cluster,
|
||||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read, false);
|
sectors_per_cluster, &error_is_read, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Depending on error action, fail now or retry cluster */
|
/* Depending on error action, fail now or retry cluster */
|
||||||
BlockErrorAction action =
|
BlockErrorAction action =
|
||||||
@@ -494,6 +501,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
|||||||
BlockJobTxn *txn, Error **errp)
|
BlockJobTxn *txn, Error **errp)
|
||||||
{
|
{
|
||||||
int64_t len;
|
int64_t len;
|
||||||
|
BlockDriverInfo bdi;
|
||||||
|
int ret;
|
||||||
|
|
||||||
assert(bs);
|
assert(bs);
|
||||||
assert(target);
|
assert(target);
|
||||||
@@ -563,14 +572,32 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_op_block_all(target, job->common.blocker);
|
|
||||||
|
|
||||||
job->on_source_error = on_source_error;
|
job->on_source_error = on_source_error;
|
||||||
job->on_target_error = on_target_error;
|
job->on_target_error = on_target_error;
|
||||||
job->target = target;
|
job->target = target;
|
||||||
job->sync_mode = sync_mode;
|
job->sync_mode = sync_mode;
|
||||||
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
|
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
|
||||||
sync_bitmap : NULL;
|
sync_bitmap : NULL;
|
||||||
|
|
||||||
|
/* If there is no backing file on the target, we cannot rely on COW if our
|
||||||
|
* backup cluster size is smaller than the target cluster size. Even for
|
||||||
|
* targets with a backing file, try to avoid COW if possible. */
|
||||||
|
ret = bdrv_get_info(job->target, &bdi);
|
||||||
|
if (ret < 0 && !target->backing) {
|
||||||
|
error_setg_errno(errp, -ret,
|
||||||
|
"Couldn't determine the cluster size of the target image, "
|
||||||
|
"which has no backing file");
|
||||||
|
error_append_hint(errp,
|
||||||
|
"Aborting, since this may create an unusable destination image\n");
|
||||||
|
goto error;
|
||||||
|
} else if (ret < 0 && target->backing) {
|
||||||
|
/* Not fatal; just trudge on ahead. */
|
||||||
|
job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||||
|
} else {
|
||||||
|
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_op_block_all(target, job->common.blocker);
|
||||||
job->common.len = len;
|
job->common.len = len;
|
||||||
job->common.co = qemu_coroutine_create(backup_run);
|
job->common.co = qemu_coroutine_create(backup_run);
|
||||||
block_job_txn_add_job(txn, &job->common);
|
block_job_txn_add_job(txn, &job->common);
|
||||||
|
|||||||
66
block/curl.c
66
block/curl.c
@@ -27,6 +27,7 @@
|
|||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
#include "qapi/qmp/qbool.h"
|
#include "qapi/qmp/qbool.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
|
#include "crypto/secret.h"
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
|
||||||
// #define DEBUG_CURL
|
// #define DEBUG_CURL
|
||||||
@@ -78,6 +79,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
|||||||
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
|
||||||
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
|
#define CURL_BLOCK_OPT_TIMEOUT "timeout"
|
||||||
#define CURL_BLOCK_OPT_COOKIE "cookie"
|
#define CURL_BLOCK_OPT_COOKIE "cookie"
|
||||||
|
#define CURL_BLOCK_OPT_USERNAME "username"
|
||||||
|
#define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret"
|
||||||
|
#define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
|
||||||
|
#define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
|
||||||
|
|
||||||
struct BDRVCURLState;
|
struct BDRVCURLState;
|
||||||
|
|
||||||
@@ -120,6 +125,10 @@ typedef struct BDRVCURLState {
|
|||||||
char *cookie;
|
char *cookie;
|
||||||
bool accept_range;
|
bool accept_range;
|
||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
|
char *username;
|
||||||
|
char *password;
|
||||||
|
char *proxyusername;
|
||||||
|
char *proxypassword;
|
||||||
} BDRVCURLState;
|
} BDRVCURLState;
|
||||||
|
|
||||||
static void curl_clean_state(CURLState *s);
|
static void curl_clean_state(CURLState *s);
|
||||||
@@ -419,6 +428,21 @@ static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s)
|
|||||||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
||||||
|
|
||||||
|
if (s->username) {
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username);
|
||||||
|
}
|
||||||
|
if (s->password) {
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password);
|
||||||
|
}
|
||||||
|
if (s->proxyusername) {
|
||||||
|
curl_easy_setopt(state->curl,
|
||||||
|
CURLOPT_PROXYUSERNAME, s->proxyusername);
|
||||||
|
}
|
||||||
|
if (s->proxypassword) {
|
||||||
|
curl_easy_setopt(state->curl,
|
||||||
|
CURLOPT_PROXYPASSWORD, s->proxypassword);
|
||||||
|
}
|
||||||
|
|
||||||
/* Restrict supported protocols to avoid security issues in the more
|
/* Restrict supported protocols to avoid security issues in the more
|
||||||
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
|
* obscure protocols. For example, do not allow POP3/SMTP/IMAP see
|
||||||
* CVE-2013-0249.
|
* CVE-2013-0249.
|
||||||
@@ -525,10 +549,31 @@ static QemuOptsList runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "Pass the cookie or list of cookies with each request"
|
.help = "Pass the cookie or list of cookies with each request"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_USERNAME,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "Username for HTTP auth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_PASSWORD_SECRET,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret used as password for HTTP auth",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_PROXY_USERNAME,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "Username for HTTP proxy auth"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret used as password for HTTP proxy auth",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@@ -539,6 +584,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
const char *file;
|
const char *file;
|
||||||
const char *cookie;
|
const char *cookie;
|
||||||
double d;
|
double d;
|
||||||
|
const char *secretid;
|
||||||
|
|
||||||
static int inited = 0;
|
static int inited = 0;
|
||||||
|
|
||||||
@@ -580,6 +626,26 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
goto out_noclean;
|
goto out_noclean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME));
|
||||||
|
secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORD_SECRET);
|
||||||
|
|
||||||
|
if (secretid) {
|
||||||
|
s->password = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||||
|
if (!s->password) {
|
||||||
|
goto out_noclean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s->proxyusername = g_strdup(
|
||||||
|
qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_USERNAME));
|
||||||
|
secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET);
|
||||||
|
if (secretid) {
|
||||||
|
s->proxypassword = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||||
|
if (!s->proxypassword) {
|
||||||
|
goto out_noclean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!inited) {
|
if (!inited) {
|
||||||
curl_global_init(CURL_GLOBAL_ALL);
|
curl_global_init(CURL_GLOBAL_ALL);
|
||||||
inited = 1;
|
inited = 1;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
#include "qapi/qmp/qstring.h"
|
#include "qapi/qmp/qstring.h"
|
||||||
|
#include "crypto/secret.h"
|
||||||
|
|
||||||
#include <iscsi/iscsi.h>
|
#include <iscsi/iscsi.h>
|
||||||
#include <iscsi/scsi-lowlevel.h>
|
#include <iscsi/scsi-lowlevel.h>
|
||||||
@@ -1080,6 +1081,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
|||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
const char *user = NULL;
|
const char *user = NULL;
|
||||||
const char *password = NULL;
|
const char *password = NULL;
|
||||||
|
const char *secretid;
|
||||||
|
char *secret = NULL;
|
||||||
|
|
||||||
list = qemu_find_opts("iscsi");
|
list = qemu_find_opts("iscsi");
|
||||||
if (!list) {
|
if (!list) {
|
||||||
@@ -1099,8 +1102,20 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secretid = qemu_opt_get(opts, "password-secret");
|
||||||
password = qemu_opt_get(opts, "password");
|
password = qemu_opt_get(opts, "password");
|
||||||
if (!password) {
|
if (secretid && password) {
|
||||||
|
error_setg(errp, "'password' and 'password-secret' properties are "
|
||||||
|
"mutually exclusive");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (secretid) {
|
||||||
|
secret = qcrypto_secret_lookup_as_utf8(secretid, errp);
|
||||||
|
if (!secret) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
password = secret;
|
||||||
|
} else if (!password) {
|
||||||
error_setg(errp, "CHAP username specified but no password was given");
|
error_setg(errp, "CHAP username specified but no password was given");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1108,6 +1123,8 @@ static void parse_chap(struct iscsi_context *iscsi, const char *target,
|
|||||||
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
|
||||||
error_setg(errp, "Failed to set initiator username and password");
|
error_setg(errp, "Failed to set initiator username and password");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free(secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
|
static void parse_header_digest(struct iscsi_context *iscsi, const char *target,
|
||||||
@@ -1857,6 +1874,11 @@ static QemuOptsList qemu_iscsi_opts = {
|
|||||||
.name = "password",
|
.name = "password",
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "password for CHAP authentication to target",
|
.help = "password for CHAP authentication to target",
|
||||||
|
},{
|
||||||
|
.name = "password-secret",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of the secret providing password for CHAP "
|
||||||
|
"authentication to target",
|
||||||
},{
|
},{
|
||||||
.name = "header-digest",
|
.name = "header-digest",
|
||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
|
|||||||
353
block/mirror.c
353
block/mirror.c
@@ -47,7 +47,6 @@ typedef struct MirrorBlockJob {
|
|||||||
BlockdevOnError on_source_error, on_target_error;
|
BlockdevOnError on_source_error, on_target_error;
|
||||||
bool synced;
|
bool synced;
|
||||||
bool should_complete;
|
bool should_complete;
|
||||||
int64_t sector_num;
|
|
||||||
int64_t granularity;
|
int64_t granularity;
|
||||||
size_t buf_size;
|
size_t buf_size;
|
||||||
int64_t bdev_length;
|
int64_t bdev_length;
|
||||||
@@ -64,6 +63,8 @@ typedef struct MirrorBlockJob {
|
|||||||
int ret;
|
int ret;
|
||||||
bool unmap;
|
bool unmap;
|
||||||
bool waiting_for_io;
|
bool waiting_for_io;
|
||||||
|
int target_cluster_sectors;
|
||||||
|
int max_iov;
|
||||||
} MirrorBlockJob;
|
} MirrorBlockJob;
|
||||||
|
|
||||||
typedef struct MirrorOp {
|
typedef struct MirrorOp {
|
||||||
@@ -159,115 +160,84 @@ static void mirror_read_complete(void *opaque, int ret)
|
|||||||
mirror_write_complete, op);
|
mirror_write_complete, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
/* Round sector_num and/or nb_sectors to target cluster if COW is needed, and
|
||||||
|
* return the offset of the adjusted tail sector against original. */
|
||||||
|
static int mirror_cow_align(MirrorBlockJob *s,
|
||||||
|
int64_t *sector_num,
|
||||||
|
int *nb_sectors)
|
||||||
|
{
|
||||||
|
bool need_cow;
|
||||||
|
int ret = 0;
|
||||||
|
int chunk_sectors = s->granularity >> BDRV_SECTOR_BITS;
|
||||||
|
int64_t align_sector_num = *sector_num;
|
||||||
|
int align_nb_sectors = *nb_sectors;
|
||||||
|
int max_sectors = chunk_sectors * s->max_iov;
|
||||||
|
|
||||||
|
need_cow = !test_bit(*sector_num / chunk_sectors, s->cow_bitmap);
|
||||||
|
need_cow |= !test_bit((*sector_num + *nb_sectors - 1) / chunk_sectors,
|
||||||
|
s->cow_bitmap);
|
||||||
|
if (need_cow) {
|
||||||
|
bdrv_round_to_clusters(s->target, *sector_num, *nb_sectors,
|
||||||
|
&align_sector_num, &align_nb_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (align_nb_sectors > max_sectors) {
|
||||||
|
align_nb_sectors = max_sectors;
|
||||||
|
if (need_cow) {
|
||||||
|
align_nb_sectors = QEMU_ALIGN_DOWN(align_nb_sectors,
|
||||||
|
s->target_cluster_sectors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = align_sector_num + align_nb_sectors - (*sector_num + *nb_sectors);
|
||||||
|
*sector_num = align_sector_num;
|
||||||
|
*nb_sectors = align_nb_sectors;
|
||||||
|
assert(ret >= 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mirror_wait_for_io(MirrorBlockJob *s)
|
||||||
|
{
|
||||||
|
assert(!s->waiting_for_io);
|
||||||
|
s->waiting_for_io = true;
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
s->waiting_for_io = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Submit async read while handling COW.
|
||||||
|
* Returns: nb_sectors if no alignment is necessary, or
|
||||||
|
* (new_end - sector_num) if tail is rounded up or down due to
|
||||||
|
* alignment or buffer limit.
|
||||||
|
*/
|
||||||
|
static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
||||||
|
int nb_sectors)
|
||||||
{
|
{
|
||||||
BlockDriverState *source = s->common.bs;
|
BlockDriverState *source = s->common.bs;
|
||||||
int nb_sectors, sectors_per_chunk, nb_chunks, max_iov;
|
int sectors_per_chunk, nb_chunks;
|
||||||
int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector;
|
int ret = nb_sectors;
|
||||||
uint64_t delay_ns = 0;
|
|
||||||
MirrorOp *op;
|
MirrorOp *op;
|
||||||
int pnum;
|
|
||||||
int64_t ret;
|
|
||||||
BlockDriverState *file;
|
|
||||||
|
|
||||||
max_iov = MIN(source->bl.max_iov, s->target->bl.max_iov);
|
|
||||||
|
|
||||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
|
||||||
if (s->sector_num < 0) {
|
|
||||||
bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
|
|
||||||
s->sector_num = hbitmap_iter_next(&s->hbi);
|
|
||||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
|
||||||
assert(s->sector_num >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
hbitmap_next_sector = s->sector_num;
|
|
||||||
sector_num = s->sector_num;
|
|
||||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||||
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
|
||||||
|
|
||||||
/* Extend the QEMUIOVector to include all adjacent blocks that will
|
/* We can only handle as much as buf_size at a time. */
|
||||||
* be copied in this operation.
|
nb_sectors = MIN(s->buf_size >> BDRV_SECTOR_BITS, nb_sectors);
|
||||||
*
|
assert(nb_sectors);
|
||||||
* We have to do this if we have no backing file yet in the destination,
|
|
||||||
* and the cluster size is very large. Then we need to do COW ourselves.
|
|
||||||
* The first time a cluster is copied, copy it entirely. Note that,
|
|
||||||
* because both the granularity and the cluster size are powers of two,
|
|
||||||
* the number of sectors to copy cannot exceed one cluster.
|
|
||||||
*
|
|
||||||
* We also want to extend the QEMUIOVector to include more adjacent
|
|
||||||
* dirty blocks if possible, to limit the number of I/O operations and
|
|
||||||
* run efficiently even with a small granularity.
|
|
||||||
*/
|
|
||||||
nb_chunks = 0;
|
|
||||||
nb_sectors = 0;
|
|
||||||
next_sector = sector_num;
|
|
||||||
next_chunk = sector_num / sectors_per_chunk;
|
|
||||||
|
|
||||||
/* Wait for I/O to this cluster (from a previous iteration) to be done. */
|
if (s->cow_bitmap) {
|
||||||
while (test_bit(next_chunk, s->in_flight_bitmap)) {
|
ret += mirror_cow_align(s, §or_num, &nb_sectors);
|
||||||
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
|
||||||
s->waiting_for_io = true;
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
s->waiting_for_io = false;
|
|
||||||
}
|
}
|
||||||
|
assert(nb_sectors << BDRV_SECTOR_BITS <= s->buf_size);
|
||||||
|
/* The sector range must meet granularity because:
|
||||||
|
* 1) Caller passes in aligned values;
|
||||||
|
* 2) mirror_cow_align is used only when target cluster is larger. */
|
||||||
|
assert(!(nb_sectors % sectors_per_chunk));
|
||||||
|
assert(!(sector_num % sectors_per_chunk));
|
||||||
|
nb_chunks = nb_sectors / sectors_per_chunk;
|
||||||
|
|
||||||
do {
|
while (s->buf_free_count < nb_chunks) {
|
||||||
int added_sectors, added_chunks;
|
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
||||||
|
mirror_wait_for_io(s);
|
||||||
if (!bdrv_get_dirty(source, s->dirty_bitmap, next_sector) ||
|
}
|
||||||
test_bit(next_chunk, s->in_flight_bitmap)) {
|
|
||||||
assert(nb_sectors > 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
added_sectors = sectors_per_chunk;
|
|
||||||
if (s->cow_bitmap && !test_bit(next_chunk, s->cow_bitmap)) {
|
|
||||||
bdrv_round_to_clusters(s->target,
|
|
||||||
next_sector, added_sectors,
|
|
||||||
&next_sector, &added_sectors);
|
|
||||||
|
|
||||||
/* On the first iteration, the rounding may make us copy
|
|
||||||
* sectors before the first dirty one.
|
|
||||||
*/
|
|
||||||
if (next_sector < sector_num) {
|
|
||||||
assert(nb_sectors == 0);
|
|
||||||
sector_num = next_sector;
|
|
||||||
next_chunk = next_sector / sectors_per_chunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
added_sectors = MIN(added_sectors, end - (sector_num + nb_sectors));
|
|
||||||
added_chunks = (added_sectors + sectors_per_chunk - 1) / sectors_per_chunk;
|
|
||||||
|
|
||||||
/* When doing COW, it may happen that there is not enough space for
|
|
||||||
* a full cluster. Wait if that is the case.
|
|
||||||
*/
|
|
||||||
while (nb_chunks == 0 && s->buf_free_count < added_chunks) {
|
|
||||||
trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight);
|
|
||||||
s->waiting_for_io = true;
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
s->waiting_for_io = false;
|
|
||||||
}
|
|
||||||
if (s->buf_free_count < nb_chunks + added_chunks) {
|
|
||||||
trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (max_iov < nb_chunks + added_chunks) {
|
|
||||||
trace_mirror_break_iov_max(s, nb_chunks, added_chunks);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We have enough free space to copy these sectors. */
|
|
||||||
bitmap_set(s->in_flight_bitmap, next_chunk, added_chunks);
|
|
||||||
|
|
||||||
nb_sectors += added_sectors;
|
|
||||||
nb_chunks += added_chunks;
|
|
||||||
next_sector += added_sectors;
|
|
||||||
next_chunk += added_chunks;
|
|
||||||
if (!s->synced && s->common.speed) {
|
|
||||||
delay_ns = ratelimit_calculate_delay(&s->limit, added_sectors);
|
|
||||||
}
|
|
||||||
} while (delay_ns == 0 && next_sector < end);
|
|
||||||
|
|
||||||
/* Allocate a MirrorOp that is used as an AIO callback. */
|
/* Allocate a MirrorOp that is used as an AIO callback. */
|
||||||
op = g_new(MirrorOp, 1);
|
op = g_new(MirrorOp, 1);
|
||||||
@@ -279,47 +249,151 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||||||
* from s->buf_free.
|
* from s->buf_free.
|
||||||
*/
|
*/
|
||||||
qemu_iovec_init(&op->qiov, nb_chunks);
|
qemu_iovec_init(&op->qiov, nb_chunks);
|
||||||
next_sector = sector_num;
|
|
||||||
while (nb_chunks-- > 0) {
|
while (nb_chunks-- > 0) {
|
||||||
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
|
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
|
||||||
size_t remaining = (nb_sectors * BDRV_SECTOR_SIZE) - op->qiov.size;
|
size_t remaining = nb_sectors * BDRV_SECTOR_SIZE - op->qiov.size;
|
||||||
|
|
||||||
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
|
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
|
||||||
s->buf_free_count--;
|
s->buf_free_count--;
|
||||||
qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining));
|
qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining));
|
||||||
|
|
||||||
/* Advance the HBitmapIter in parallel, so that we do not examine
|
|
||||||
* the same sector twice.
|
|
||||||
*/
|
|
||||||
if (next_sector > hbitmap_next_sector
|
|
||||||
&& bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
|
||||||
hbitmap_next_sector = hbitmap_iter_next(&s->hbi);
|
|
||||||
}
|
|
||||||
|
|
||||||
next_sector += sectors_per_chunk;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num, nb_sectors);
|
|
||||||
|
|
||||||
/* Copy the dirty cluster. */
|
/* Copy the dirty cluster. */
|
||||||
s->in_flight++;
|
s->in_flight++;
|
||||||
s->sectors_in_flight += nb_sectors;
|
s->sectors_in_flight += nb_sectors;
|
||||||
trace_mirror_one_iteration(s, sector_num, nb_sectors);
|
trace_mirror_one_iteration(s, sector_num, nb_sectors);
|
||||||
|
|
||||||
ret = bdrv_get_block_status_above(source, NULL, sector_num,
|
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
||||||
nb_sectors, &pnum, &file);
|
mirror_read_complete, op);
|
||||||
if (ret < 0 || pnum < nb_sectors ||
|
return ret;
|
||||||
(ret & BDRV_BLOCK_DATA && !(ret & BDRV_BLOCK_ZERO))) {
|
}
|
||||||
bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors,
|
|
||||||
mirror_read_complete, op);
|
static void mirror_do_zero_or_discard(MirrorBlockJob *s,
|
||||||
} else if (ret & BDRV_BLOCK_ZERO) {
|
int64_t sector_num,
|
||||||
|
int nb_sectors,
|
||||||
|
bool is_discard)
|
||||||
|
{
|
||||||
|
MirrorOp *op;
|
||||||
|
|
||||||
|
/* Allocate a MirrorOp that is used as an AIO callback. The qiov is zeroed
|
||||||
|
* so the freeing in mirror_iteration_done is nop. */
|
||||||
|
op = g_new0(MirrorOp, 1);
|
||||||
|
op->s = s;
|
||||||
|
op->sector_num = sector_num;
|
||||||
|
op->nb_sectors = nb_sectors;
|
||||||
|
|
||||||
|
s->in_flight++;
|
||||||
|
s->sectors_in_flight += nb_sectors;
|
||||||
|
if (is_discard) {
|
||||||
|
bdrv_aio_discard(s->target, sector_num, op->nb_sectors,
|
||||||
|
mirror_write_complete, op);
|
||||||
|
} else {
|
||||||
bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors,
|
bdrv_aio_write_zeroes(s->target, sector_num, op->nb_sectors,
|
||||||
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
||||||
mirror_write_complete, op);
|
mirror_write_complete, op);
|
||||||
} else {
|
}
|
||||||
assert(!(ret & BDRV_BLOCK_DATA));
|
}
|
||||||
bdrv_aio_discard(s->target, sector_num, op->nb_sectors,
|
|
||||||
mirror_write_complete, op);
|
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
|
{
|
||||||
|
BlockDriverState *source = s->common.bs;
|
||||||
|
int64_t sector_num;
|
||||||
|
uint64_t delay_ns = 0;
|
||||||
|
/* At least the first dirty chunk is mirrored in one iteration. */
|
||||||
|
int nb_chunks = 1;
|
||||||
|
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||||
|
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||||
|
|
||||||
|
sector_num = hbitmap_iter_next(&s->hbi);
|
||||||
|
if (sector_num < 0) {
|
||||||
|
bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
|
||||||
|
sector_num = hbitmap_iter_next(&s->hbi);
|
||||||
|
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
||||||
|
assert(sector_num >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the number of consective dirty chunks following the first dirty
|
||||||
|
* one, and wait for in flight requests in them. */
|
||||||
|
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
||||||
|
int64_t hbitmap_next;
|
||||||
|
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
|
||||||
|
int64_t next_chunk = next_sector / sectors_per_chunk;
|
||||||
|
if (next_sector >= end ||
|
||||||
|
!bdrv_get_dirty(source, s->dirty_bitmap, next_sector)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||||
|
if (nb_chunks > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
trace_mirror_yield_in_flight(s, next_sector, s->in_flight);
|
||||||
|
mirror_wait_for_io(s);
|
||||||
|
/* Now retry. */
|
||||||
|
} else {
|
||||||
|
hbitmap_next = hbitmap_iter_next(&s->hbi);
|
||||||
|
assert(hbitmap_next == next_sector);
|
||||||
|
nb_chunks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear dirty bits before querying the block status, because
|
||||||
|
* calling bdrv_get_block_status_above could yield - if some blocks are
|
||||||
|
* marked dirty in this window, we need to know.
|
||||||
|
*/
|
||||||
|
bdrv_reset_dirty_bitmap(s->dirty_bitmap, sector_num,
|
||||||
|
nb_chunks * sectors_per_chunk);
|
||||||
|
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
|
||||||
|
while (nb_chunks > 0 && sector_num < end) {
|
||||||
|
int ret;
|
||||||
|
int io_sectors;
|
||||||
|
BlockDriverState *file;
|
||||||
|
enum MirrorMethod {
|
||||||
|
MIRROR_METHOD_COPY,
|
||||||
|
MIRROR_METHOD_ZERO,
|
||||||
|
MIRROR_METHOD_DISCARD
|
||||||
|
} mirror_method = MIRROR_METHOD_COPY;
|
||||||
|
|
||||||
|
assert(!(sector_num % sectors_per_chunk));
|
||||||
|
ret = bdrv_get_block_status_above(source, NULL, sector_num,
|
||||||
|
nb_chunks * sectors_per_chunk,
|
||||||
|
&io_sectors, &file);
|
||||||
|
if (ret < 0) {
|
||||||
|
io_sectors = nb_chunks * sectors_per_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_sectors -= io_sectors % sectors_per_chunk;
|
||||||
|
if (io_sectors < sectors_per_chunk) {
|
||||||
|
io_sectors = sectors_per_chunk;
|
||||||
|
} else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) {
|
||||||
|
int64_t target_sector_num;
|
||||||
|
int target_nb_sectors;
|
||||||
|
bdrv_round_to_clusters(s->target, sector_num, io_sectors,
|
||||||
|
&target_sector_num, &target_nb_sectors);
|
||||||
|
if (target_sector_num == sector_num &&
|
||||||
|
target_nb_sectors == io_sectors) {
|
||||||
|
mirror_method = ret & BDRV_BLOCK_ZERO ?
|
||||||
|
MIRROR_METHOD_ZERO :
|
||||||
|
MIRROR_METHOD_DISCARD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mirror_method) {
|
||||||
|
case MIRROR_METHOD_COPY:
|
||||||
|
io_sectors = mirror_do_read(s, sector_num, io_sectors);
|
||||||
|
break;
|
||||||
|
case MIRROR_METHOD_ZERO:
|
||||||
|
mirror_do_zero_or_discard(s, sector_num, io_sectors, false);
|
||||||
|
break;
|
||||||
|
case MIRROR_METHOD_DISCARD:
|
||||||
|
mirror_do_zero_or_discard(s, sector_num, io_sectors, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
assert(io_sectors);
|
||||||
|
sector_num += io_sectors;
|
||||||
|
nb_chunks -= io_sectors / sectors_per_chunk;
|
||||||
|
delay_ns += ratelimit_calculate_delay(&s->limit, io_sectors);
|
||||||
}
|
}
|
||||||
return delay_ns;
|
return delay_ns;
|
||||||
}
|
}
|
||||||
@@ -344,9 +418,7 @@ static void mirror_free_init(MirrorBlockJob *s)
|
|||||||
static void mirror_drain(MirrorBlockJob *s)
|
static void mirror_drain(MirrorBlockJob *s)
|
||||||
{
|
{
|
||||||
while (s->in_flight > 0) {
|
while (s->in_flight > 0) {
|
||||||
s->waiting_for_io = true;
|
mirror_wait_for_io(s);
|
||||||
qemu_coroutine_yield();
|
|
||||||
s->waiting_for_io = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,6 +492,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
checking for a NULL string */
|
checking for a NULL string */
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int n;
|
int n;
|
||||||
|
int target_cluster_size = BDRV_SECTOR_SIZE;
|
||||||
|
|
||||||
if (block_job_is_cancelled(&s->common)) {
|
if (block_job_is_cancelled(&s->common)) {
|
||||||
goto immediate_exit;
|
goto immediate_exit;
|
||||||
@@ -449,16 +522,16 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
*/
|
*/
|
||||||
bdrv_get_backing_filename(s->target, backing_filename,
|
bdrv_get_backing_filename(s->target, backing_filename,
|
||||||
sizeof(backing_filename));
|
sizeof(backing_filename));
|
||||||
if (backing_filename[0] && !s->target->backing) {
|
if (!bdrv_get_info(s->target, &bdi) && bdi.cluster_size) {
|
||||||
ret = bdrv_get_info(s->target, &bdi);
|
target_cluster_size = bdi.cluster_size;
|
||||||
if (ret < 0) {
|
|
||||||
goto immediate_exit;
|
|
||||||
}
|
|
||||||
if (s->granularity < bdi.cluster_size) {
|
|
||||||
s->buf_size = MAX(s->buf_size, bdi.cluster_size);
|
|
||||||
s->cow_bitmap = bitmap_new(length);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (backing_filename[0] && !s->target->backing
|
||||||
|
&& s->granularity < target_cluster_size) {
|
||||||
|
s->buf_size = MAX(s->buf_size, target_cluster_size);
|
||||||
|
s->cow_bitmap = bitmap_new(length);
|
||||||
|
}
|
||||||
|
s->target_cluster_sectors = target_cluster_size >> BDRV_SECTOR_BITS;
|
||||||
|
s->max_iov = MIN(s->common.bs->bl.max_iov, s->target->bl.max_iov);
|
||||||
|
|
||||||
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||||
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
||||||
@@ -533,9 +606,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
||||||
(cnt == 0 && s->in_flight > 0)) {
|
(cnt == 0 && s->in_flight > 0)) {
|
||||||
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
|
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
|
||||||
s->waiting_for_io = true;
|
mirror_wait_for_io(s);
|
||||||
qemu_coroutine_yield();
|
|
||||||
s->waiting_for_io = false;
|
|
||||||
continue;
|
continue;
|
||||||
} else if (cnt != 0) {
|
} else if (cnt != 0) {
|
||||||
delay_ns = mirror_iteration(s);
|
delay_ns = mirror_iteration(s);
|
||||||
|
|||||||
14
block/nbd.c
14
block/nbd.c
@@ -204,18 +204,20 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, char **export,
|
|||||||
saddr = g_new0(SocketAddress, 1);
|
saddr = g_new0(SocketAddress, 1);
|
||||||
|
|
||||||
if (qdict_haskey(options, "path")) {
|
if (qdict_haskey(options, "path")) {
|
||||||
|
UnixSocketAddress *q_unix;
|
||||||
saddr->type = SOCKET_ADDRESS_KIND_UNIX;
|
saddr->type = SOCKET_ADDRESS_KIND_UNIX;
|
||||||
saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
|
q_unix = saddr->u.q_unix = g_new0(UnixSocketAddress, 1);
|
||||||
saddr->u.q_unix->path = g_strdup(qdict_get_str(options, "path"));
|
q_unix->path = g_strdup(qdict_get_str(options, "path"));
|
||||||
qdict_del(options, "path");
|
qdict_del(options, "path");
|
||||||
} else {
|
} else {
|
||||||
|
InetSocketAddress *inet;
|
||||||
saddr->type = SOCKET_ADDRESS_KIND_INET;
|
saddr->type = SOCKET_ADDRESS_KIND_INET;
|
||||||
saddr->u.inet = g_new0(InetSocketAddress, 1);
|
inet = saddr->u.inet = g_new0(InetSocketAddress, 1);
|
||||||
saddr->u.inet->host = g_strdup(qdict_get_str(options, "host"));
|
inet->host = g_strdup(qdict_get_str(options, "host"));
|
||||||
if (!qdict_get_try_str(options, "port")) {
|
if (!qdict_get_try_str(options, "port")) {
|
||||||
saddr->u.inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
|
inet->port = g_strdup_printf("%d", NBD_DEFAULT_PORT);
|
||||||
} else {
|
} else {
|
||||||
saddr->u.inet->port = g_strdup(qdict_get_str(options, "port"));
|
inet->port = g_strdup(qdict_get_str(options, "port"));
|
||||||
}
|
}
|
||||||
qdict_del(options, "host");
|
qdict_del(options, "host");
|
||||||
qdict_del(options, "port");
|
qdict_del(options, "port");
|
||||||
|
|||||||
12
block/nfs.c
12
block/nfs.c
@@ -36,6 +36,7 @@
|
|||||||
#include <nfsc/libnfs.h>
|
#include <nfsc/libnfs.h>
|
||||||
|
|
||||||
#define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
|
#define QEMU_NFS_MAX_READAHEAD_SIZE 1048576
|
||||||
|
#define QEMU_NFS_MAX_DEBUG_LEVEL 2
|
||||||
|
|
||||||
typedef struct NFSClient {
|
typedef struct NFSClient {
|
||||||
struct nfs_context *context;
|
struct nfs_context *context;
|
||||||
@@ -333,6 +334,17 @@ static int64_t nfs_client_open(NFSClient *client, const char *filename,
|
|||||||
val = QEMU_NFS_MAX_READAHEAD_SIZE;
|
val = QEMU_NFS_MAX_READAHEAD_SIZE;
|
||||||
}
|
}
|
||||||
nfs_set_readahead(client->context, val);
|
nfs_set_readahead(client->context, val);
|
||||||
|
#endif
|
||||||
|
#ifdef LIBNFS_FEATURE_DEBUG
|
||||||
|
} else if (!strcmp(qp->p[i].name, "debug")) {
|
||||||
|
/* limit the maximum debug level to avoid potential flooding
|
||||||
|
* of our log files. */
|
||||||
|
if (val > QEMU_NFS_MAX_DEBUG_LEVEL) {
|
||||||
|
error_report("NFS Warning: Limiting NFS debug level"
|
||||||
|
" to %d", QEMU_NFS_MAX_DEBUG_LEVEL);
|
||||||
|
val = QEMU_NFS_MAX_DEBUG_LEVEL;
|
||||||
|
}
|
||||||
|
nfs_set_debug(client->context, val);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Unknown NFS parameter name: %s",
|
error_setg(errp, "Unknown NFS parameter name: %s",
|
||||||
|
|||||||
20
block/qapi.c
20
block/qapi.c
@@ -92,6 +92,26 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp)
|
|||||||
info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||||
info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||||
|
|
||||||
|
info->has_bps_max_length = info->has_bps_max;
|
||||||
|
info->bps_max_length =
|
||||||
|
cfg.buckets[THROTTLE_BPS_TOTAL].burst_length;
|
||||||
|
info->has_bps_rd_max_length = info->has_bps_rd_max;
|
||||||
|
info->bps_rd_max_length =
|
||||||
|
cfg.buckets[THROTTLE_BPS_READ].burst_length;
|
||||||
|
info->has_bps_wr_max_length = info->has_bps_wr_max;
|
||||||
|
info->bps_wr_max_length =
|
||||||
|
cfg.buckets[THROTTLE_BPS_WRITE].burst_length;
|
||||||
|
|
||||||
|
info->has_iops_max_length = info->has_iops_max;
|
||||||
|
info->iops_max_length =
|
||||||
|
cfg.buckets[THROTTLE_OPS_TOTAL].burst_length;
|
||||||
|
info->has_iops_rd_max_length = info->has_iops_rd_max;
|
||||||
|
info->iops_rd_max_length =
|
||||||
|
cfg.buckets[THROTTLE_OPS_READ].burst_length;
|
||||||
|
info->has_iops_wr_max_length = info->has_iops_wr_max;
|
||||||
|
info->iops_wr_max_length =
|
||||||
|
cfg.buckets[THROTTLE_OPS_WRITE].burst_length;
|
||||||
|
|
||||||
info->has_iops_size = cfg.op_size;
|
info->has_iops_size = cfg.op_size;
|
||||||
info->iops_size = cfg.op_size;
|
info->iops_size = cfg.op_size;
|
||||||
|
|
||||||
|
|||||||
@@ -286,7 +286,8 @@ static void quorum_aio_cb(void *opaque, int ret)
|
|||||||
|
|
||||||
if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) {
|
if (acb->is_read && s->read_pattern == QUORUM_READ_PATTERN_FIFO) {
|
||||||
/* We try to read next child in FIFO order if we fail to read */
|
/* We try to read next child in FIFO order if we fail to read */
|
||||||
if (ret < 0 && ++acb->child_iter < s->num_children) {
|
if (ret < 0 && (acb->child_iter + 1) < s->num_children) {
|
||||||
|
acb->child_iter++;
|
||||||
read_fifo_child(acb);
|
read_fifo_child(acb);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
47
block/rbd.c
47
block/rbd.c
@@ -16,6 +16,7 @@
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
#include "crypto/secret.h"
|
||||||
|
|
||||||
#include <rbd/librbd.h>
|
#include <rbd/librbd.h>
|
||||||
|
|
||||||
@@ -228,6 +229,27 @@ static char *qemu_rbd_parse_clientname(const char *conf, char *clientname)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int qemu_rbd_set_auth(rados_t cluster, const char *secretid,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
if (secretid == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *secret = qcrypto_secret_lookup_as_base64(secretid,
|
||||||
|
errp);
|
||||||
|
if (!secret) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rados_conf_set(cluster, "key", secret);
|
||||||
|
g_free(secret);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
|
static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
|
||||||
bool only_read_conf_file,
|
bool only_read_conf_file,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
@@ -299,10 +321,13 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
char conf[RBD_MAX_CONF_SIZE];
|
char conf[RBD_MAX_CONF_SIZE];
|
||||||
char clientname_buf[RBD_MAX_CONF_SIZE];
|
char clientname_buf[RBD_MAX_CONF_SIZE];
|
||||||
char *clientname;
|
char *clientname;
|
||||||
|
const char *secretid;
|
||||||
rados_t cluster;
|
rados_t cluster;
|
||||||
rados_ioctx_t io_ctx;
|
rados_ioctx_t io_ctx;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
secretid = qemu_opt_get(opts, "password-secret");
|
||||||
|
|
||||||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||||
snap_buf, sizeof(snap_buf),
|
snap_buf, sizeof(snap_buf),
|
||||||
name, sizeof(name),
|
name, sizeof(name),
|
||||||
@@ -350,6 +375,11 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qemu_rbd_set_auth(cluster, secretid, errp) < 0) {
|
||||||
|
rados_shutdown(cluster);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
if (rados_connect(cluster) < 0) {
|
if (rados_connect(cluster) < 0) {
|
||||||
error_setg(errp, "error connecting");
|
error_setg(errp, "error connecting");
|
||||||
rados_shutdown(cluster);
|
rados_shutdown(cluster);
|
||||||
@@ -423,6 +453,11 @@ static QemuOptsList runtime_opts = {
|
|||||||
.type = QEMU_OPT_STRING,
|
.type = QEMU_OPT_STRING,
|
||||||
.help = "Specification of the rbd image",
|
.help = "Specification of the rbd image",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "password-secret",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret providing the password",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -436,6 +471,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
char conf[RBD_MAX_CONF_SIZE];
|
char conf[RBD_MAX_CONF_SIZE];
|
||||||
char clientname_buf[RBD_MAX_CONF_SIZE];
|
char clientname_buf[RBD_MAX_CONF_SIZE];
|
||||||
char *clientname;
|
char *clientname;
|
||||||
|
const char *secretid;
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *filename;
|
const char *filename;
|
||||||
@@ -450,6 +486,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
filename = qemu_opt_get(opts, "filename");
|
filename = qemu_opt_get(opts, "filename");
|
||||||
|
secretid = qemu_opt_get(opts, "password-secret");
|
||||||
|
|
||||||
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
|
||||||
snap_buf, sizeof(snap_buf),
|
snap_buf, sizeof(snap_buf),
|
||||||
@@ -488,6 +525,11 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qemu_rbd_set_auth(s->cluster, secretid, errp) < 0) {
|
||||||
|
r = -EIO;
|
||||||
|
goto failed_shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fallback to more conservative semantics if setting cache
|
* Fallback to more conservative semantics if setting cache
|
||||||
* options fails. Ignore errors from setting rbd_cache because the
|
* options fails. Ignore errors from setting rbd_cache because the
|
||||||
@@ -919,6 +961,11 @@ static QemuOptsList qemu_rbd_create_opts = {
|
|||||||
.type = QEMU_OPT_SIZE,
|
.type = QEMU_OPT_SIZE,
|
||||||
.help = "RBD object size"
|
.help = "RBD object size"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "password-secret",
|
||||||
|
.type = QEMU_OPT_STRING,
|
||||||
|
.help = "ID of secret providing the password",
|
||||||
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
125
block/sheepdog.c
125
block/sheepdog.c
@@ -284,6 +284,12 @@ static inline bool is_snapshot(struct SheepdogInode *inode)
|
|||||||
return !!inode->snap_ctime;
|
return !!inode->snap_ctime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline size_t count_data_objs(const struct SheepdogInode *inode)
|
||||||
|
{
|
||||||
|
return DIV_ROUND_UP(inode->vdi_size,
|
||||||
|
(1UL << inode->block_size_shift));
|
||||||
|
}
|
||||||
|
|
||||||
#undef DPRINTF
|
#undef DPRINTF
|
||||||
#ifdef DEBUG_SDOG
|
#ifdef DEBUG_SDOG
|
||||||
#define DPRINTF(fmt, args...) \
|
#define DPRINTF(fmt, args...) \
|
||||||
@@ -2478,13 +2484,128 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NR_BATCHED_DISCARD 128
|
||||||
|
|
||||||
|
static bool remove_objects(BDRVSheepdogState *s)
|
||||||
|
{
|
||||||
|
int fd, i = 0, nr_objs = 0;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
bool result = true;
|
||||||
|
SheepdogInode *inode = &s->inode;
|
||||||
|
|
||||||
|
fd = connect_to_sdog(s, &local_err);
|
||||||
|
if (fd < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nr_objs = count_data_objs(inode);
|
||||||
|
while (i < nr_objs) {
|
||||||
|
int start_idx, nr_filled_idx;
|
||||||
|
|
||||||
|
while (i < nr_objs && !inode->data_vdi_id[i]) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
start_idx = i;
|
||||||
|
|
||||||
|
nr_filled_idx = 0;
|
||||||
|
while (i < nr_objs && nr_filled_idx < NR_BATCHED_DISCARD) {
|
||||||
|
if (inode->data_vdi_id[i]) {
|
||||||
|
inode->data_vdi_id[i] = 0;
|
||||||
|
nr_filled_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = write_object(fd, s->aio_context,
|
||||||
|
(char *)&inode->data_vdi_id[start_idx],
|
||||||
|
vid_to_vdi_oid(s->inode.vdi_id), inode->nr_copies,
|
||||||
|
(i - start_idx) * sizeof(uint32_t),
|
||||||
|
offsetof(struct SheepdogInode,
|
||||||
|
data_vdi_id[start_idx]),
|
||||||
|
false, s->cache_flags);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("failed to discard snapshot inode.");
|
||||||
|
result = false;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
closesocket(fd);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static int sd_snapshot_delete(BlockDriverState *bs,
|
static int sd_snapshot_delete(BlockDriverState *bs,
|
||||||
const char *snapshot_id,
|
const char *snapshot_id,
|
||||||
const char *name,
|
const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* FIXME: Delete specified snapshot id. */
|
uint32_t snap_id = 0;
|
||||||
return 0;
|
char snap_tag[SD_MAX_VDI_TAG_LEN];
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int fd, ret;
|
||||||
|
char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN];
|
||||||
|
BDRVSheepdogState *s = bs->opaque;
|
||||||
|
unsigned int wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN, rlen = 0;
|
||||||
|
uint32_t vid;
|
||||||
|
SheepdogVdiReq hdr = {
|
||||||
|
.opcode = SD_OP_DEL_VDI,
|
||||||
|
.data_length = wlen,
|
||||||
|
.flags = SD_FLAG_CMD_WRITE,
|
||||||
|
};
|
||||||
|
SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr;
|
||||||
|
|
||||||
|
if (!remove_objects(s)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(buf, 0, sizeof(buf));
|
||||||
|
memset(snap_tag, 0, sizeof(snap_tag));
|
||||||
|
pstrcpy(buf, SD_MAX_VDI_LEN, s->name);
|
||||||
|
if (qemu_strtoul(snapshot_id, NULL, 10, (unsigned long *)&snap_id)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snap_id) {
|
||||||
|
hdr.snapid = snap_id;
|
||||||
|
} else {
|
||||||
|
pstrcpy(snap_tag, sizeof(snap_tag), snapshot_id);
|
||||||
|
pstrcpy(buf + SD_MAX_VDI_LEN, SD_MAX_VDI_TAG_LEN, snap_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = find_vdi_name(s, s->name, snap_id, snap_tag, &vid, true,
|
||||||
|
&local_err);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = connect_to_sdog(s, &local_err);
|
||||||
|
if (fd < 0) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = do_req(fd, s->aio_context, (SheepdogReq *)&hdr,
|
||||||
|
buf, &wlen, &rlen);
|
||||||
|
closesocket(fd);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rsp->result) {
|
||||||
|
case SD_RES_NO_VDI:
|
||||||
|
error_report("%s was already deleted", s->name);
|
||||||
|
case SD_RES_SUCCESS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_report("%s, %s", sd_strerror(rsp->result), s->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||||
|
|||||||
18
block/vhdx.c
18
block/vhdx.c
@@ -264,10 +264,10 @@ static void vhdx_region_unregister_all(BDRVVHDXState *s)
|
|||||||
|
|
||||||
static void vhdx_set_shift_bits(BDRVVHDXState *s)
|
static void vhdx_set_shift_bits(BDRVVHDXState *s)
|
||||||
{
|
{
|
||||||
s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size);
|
s->logical_sector_size_bits = ctz32(s->logical_sector_size);
|
||||||
s->sectors_per_block_bits = 31 - clz32(s->sectors_per_block);
|
s->sectors_per_block_bits = ctz32(s->sectors_per_block);
|
||||||
s->chunk_ratio_bits = 63 - clz64(s->chunk_ratio);
|
s->chunk_ratio_bits = ctz64(s->chunk_ratio);
|
||||||
s->block_size_bits = 31 - clz32(s->block_size);
|
s->block_size_bits = ctz32(s->block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -857,14 +857,8 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s)
|
|||||||
{
|
{
|
||||||
uint32_t data_blocks_cnt, bitmap_blocks_cnt;
|
uint32_t data_blocks_cnt, bitmap_blocks_cnt;
|
||||||
|
|
||||||
data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits;
|
data_blocks_cnt = DIV_ROUND_UP(s->virtual_disk_size, s->block_size);
|
||||||
if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) {
|
bitmap_blocks_cnt = DIV_ROUND_UP(data_blocks_cnt, s->chunk_ratio);
|
||||||
data_blocks_cnt++;
|
|
||||||
}
|
|
||||||
bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits;
|
|
||||||
if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) {
|
|
||||||
bitmap_blocks_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->parent_entries) {
|
if (s->parent_entries) {
|
||||||
s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1);
|
s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1);
|
||||||
|
|||||||
140
blockdev.c
140
blockdev.c
@@ -343,29 +343,6 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
|
|
||||||
{
|
|
||||||
if (throttle_conflicting(cfg)) {
|
|
||||||
error_setg(errp, "bps/iops/max total values and read/write values"
|
|
||||||
" cannot be used at the same time");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!throttle_is_valid(cfg)) {
|
|
||||||
error_setg(errp, "bps/iops/max values must be within [0, %lld]",
|
|
||||||
THROTTLE_VALUE_MAX);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (throttle_max_is_missing_limit(cfg)) {
|
|
||||||
error_setg(errp, "bps_max/iops_max require corresponding"
|
|
||||||
" bps/iops values");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
|
||||||
|
|
||||||
/* All parameters but @opts are optional and may be set to NULL. */
|
/* All parameters but @opts are optional and may be set to NULL. */
|
||||||
@@ -410,7 +387,7 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (throttle_cfg) {
|
if (throttle_cfg) {
|
||||||
memset(throttle_cfg, 0, sizeof(*throttle_cfg));
|
throttle_config_init(throttle_cfg);
|
||||||
throttle_cfg->buckets[THROTTLE_BPS_TOTAL].avg =
|
throttle_cfg->buckets[THROTTLE_BPS_TOTAL].avg =
|
||||||
qemu_opt_get_number(opts, "throttling.bps-total", 0);
|
qemu_opt_get_number(opts, "throttling.bps-total", 0);
|
||||||
throttle_cfg->buckets[THROTTLE_BPS_READ].avg =
|
throttle_cfg->buckets[THROTTLE_BPS_READ].avg =
|
||||||
@@ -437,10 +414,23 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
|||||||
throttle_cfg->buckets[THROTTLE_OPS_WRITE].max =
|
throttle_cfg->buckets[THROTTLE_OPS_WRITE].max =
|
||||||
qemu_opt_get_number(opts, "throttling.iops-write-max", 0);
|
qemu_opt_get_number(opts, "throttling.iops-write-max", 0);
|
||||||
|
|
||||||
|
throttle_cfg->buckets[THROTTLE_BPS_TOTAL].burst_length =
|
||||||
|
qemu_opt_get_number(opts, "throttling.bps-total-max-length", 1);
|
||||||
|
throttle_cfg->buckets[THROTTLE_BPS_READ].burst_length =
|
||||||
|
qemu_opt_get_number(opts, "throttling.bps-read-max-length", 1);
|
||||||
|
throttle_cfg->buckets[THROTTLE_BPS_WRITE].burst_length =
|
||||||
|
qemu_opt_get_number(opts, "throttling.bps-write-max-length", 1);
|
||||||
|
throttle_cfg->buckets[THROTTLE_OPS_TOTAL].burst_length =
|
||||||
|
qemu_opt_get_number(opts, "throttling.iops-total-max-length", 1);
|
||||||
|
throttle_cfg->buckets[THROTTLE_OPS_READ].burst_length =
|
||||||
|
qemu_opt_get_number(opts, "throttling.iops-read-max-length", 1);
|
||||||
|
throttle_cfg->buckets[THROTTLE_OPS_WRITE].burst_length =
|
||||||
|
qemu_opt_get_number(opts, "throttling.iops-write-max-length", 1);
|
||||||
|
|
||||||
throttle_cfg->op_size =
|
throttle_cfg->op_size =
|
||||||
qemu_opt_get_number(opts, "throttling.iops-size", 0);
|
qemu_opt_get_number(opts, "throttling.iops-size", 0);
|
||||||
|
|
||||||
if (!check_throttle_config(throttle_cfg, errp)) {
|
if (!throttle_is_valid(throttle_cfg, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -610,6 +600,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
qdict_put(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, qstring_from_str("on"));
|
qdict_put(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, qstring_from_str("on"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||||
|
bdrv_flags |= BDRV_O_INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags,
|
blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags,
|
||||||
errp);
|
errp);
|
||||||
if (!blk) {
|
if (!blk) {
|
||||||
@@ -688,6 +682,10 @@ static BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||||
|
bdrv_flags |= BDRV_O_INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
bs = NULL;
|
bs = NULL;
|
||||||
ret = bdrv_open(&bs, NULL, NULL, bs_opts, bdrv_flags, errp);
|
ret = bdrv_open(&bs, NULL, NULL, bs_opts, bdrv_flags, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -1204,15 +1202,11 @@ void hmp_commit(Monitor *mon, const QDict *qdict)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blockdev_do_action(TransactionActionKind type, void *data,
|
static void blockdev_do_action(TransactionAction *action, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
TransactionAction action;
|
|
||||||
TransactionActionList list;
|
TransactionActionList list;
|
||||||
|
|
||||||
action.type = type;
|
list.value = action;
|
||||||
action.u.data = data;
|
|
||||||
list.value = &action;
|
|
||||||
list.next = NULL;
|
list.next = NULL;
|
||||||
qmp_transaction(&list, false, NULL, errp);
|
qmp_transaction(&list, false, NULL, errp);
|
||||||
}
|
}
|
||||||
@@ -1238,8 +1232,11 @@ void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
|
|||||||
.has_mode = has_mode,
|
.has_mode = has_mode,
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
};
|
};
|
||||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
|
TransactionAction action = {
|
||||||
&snapshot, errp);
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
|
||||||
|
.u.blockdev_snapshot_sync = &snapshot,
|
||||||
|
};
|
||||||
|
blockdev_do_action(&action, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
||||||
@@ -1249,9 +1246,11 @@ void qmp_blockdev_snapshot(const char *node, const char *overlay,
|
|||||||
.node = (char *) node,
|
.node = (char *) node,
|
||||||
.overlay = (char *) overlay
|
.overlay = (char *) overlay
|
||||||
};
|
};
|
||||||
|
TransactionAction action = {
|
||||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT,
|
||||||
&snapshot_data, errp);
|
.u.blockdev_snapshot = &snapshot_data,
|
||||||
|
};
|
||||||
|
blockdev_do_action(&action, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_blockdev_snapshot_internal_sync(const char *device,
|
void qmp_blockdev_snapshot_internal_sync(const char *device,
|
||||||
@@ -1262,9 +1261,11 @@ void qmp_blockdev_snapshot_internal_sync(const char *device,
|
|||||||
.device = (char *) device,
|
.device = (char *) device,
|
||||||
.name = (char *) name
|
.name = (char *) name
|
||||||
};
|
};
|
||||||
|
TransactionAction action = {
|
||||||
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
|
.type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
|
||||||
&snapshot, errp);
|
.u.blockdev_snapshot_internal_sync = &snapshot,
|
||||||
|
};
|
||||||
|
blockdev_do_action(&action, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
|
||||||
@@ -2515,6 +2516,8 @@ void qmp_blockdev_change_medium(const char *device, const char *filename,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bdrv_flags = blk_get_open_flags_from_root_state(blk);
|
bdrv_flags = blk_get_open_flags_from_root_state(blk);
|
||||||
|
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
|
||||||
|
BDRV_O_PROTOCOL);
|
||||||
|
|
||||||
if (!has_read_only) {
|
if (!has_read_only) {
|
||||||
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
|
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
|
||||||
@@ -2600,6 +2603,18 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
|||||||
int64_t iops_rd_max,
|
int64_t iops_rd_max,
|
||||||
bool has_iops_wr_max,
|
bool has_iops_wr_max,
|
||||||
int64_t iops_wr_max,
|
int64_t iops_wr_max,
|
||||||
|
bool has_bps_max_length,
|
||||||
|
int64_t bps_max_length,
|
||||||
|
bool has_bps_rd_max_length,
|
||||||
|
int64_t bps_rd_max_length,
|
||||||
|
bool has_bps_wr_max_length,
|
||||||
|
int64_t bps_wr_max_length,
|
||||||
|
bool has_iops_max_length,
|
||||||
|
int64_t iops_max_length,
|
||||||
|
bool has_iops_rd_max_length,
|
||||||
|
int64_t iops_rd_max_length,
|
||||||
|
bool has_iops_wr_max_length,
|
||||||
|
int64_t iops_wr_max_length,
|
||||||
bool has_iops_size,
|
bool has_iops_size,
|
||||||
int64_t iops_size,
|
int64_t iops_size,
|
||||||
bool has_group,
|
bool has_group,
|
||||||
@@ -2626,7 +2641,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&cfg, 0, sizeof(cfg));
|
throttle_config_init(&cfg);
|
||||||
cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
|
cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
|
||||||
cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd;
|
cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd;
|
||||||
cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr;
|
cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr;
|
||||||
@@ -2654,11 +2669,30 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
|||||||
cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max;
|
cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (has_bps_max_length) {
|
||||||
|
cfg.buckets[THROTTLE_BPS_TOTAL].burst_length = bps_max_length;
|
||||||
|
}
|
||||||
|
if (has_bps_rd_max_length) {
|
||||||
|
cfg.buckets[THROTTLE_BPS_READ].burst_length = bps_rd_max_length;
|
||||||
|
}
|
||||||
|
if (has_bps_wr_max_length) {
|
||||||
|
cfg.buckets[THROTTLE_BPS_WRITE].burst_length = bps_wr_max_length;
|
||||||
|
}
|
||||||
|
if (has_iops_max_length) {
|
||||||
|
cfg.buckets[THROTTLE_OPS_TOTAL].burst_length = iops_max_length;
|
||||||
|
}
|
||||||
|
if (has_iops_rd_max_length) {
|
||||||
|
cfg.buckets[THROTTLE_OPS_READ].burst_length = iops_rd_max_length;
|
||||||
|
}
|
||||||
|
if (has_iops_wr_max_length) {
|
||||||
|
cfg.buckets[THROTTLE_OPS_WRITE].burst_length = iops_wr_max_length;
|
||||||
|
}
|
||||||
|
|
||||||
if (has_iops_size) {
|
if (has_iops_size) {
|
||||||
cfg.op_size = iops_size;
|
cfg.op_size = iops_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!check_throttle_config(&cfg, errp)) {
|
if (!throttle_is_valid(&cfg, errp)) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4086,6 +4120,30 @@ QemuOptsList qemu_common_drive_opts = {
|
|||||||
.name = "throttling.bps-write-max",
|
.name = "throttling.bps-write-max",
|
||||||
.type = QEMU_OPT_NUMBER,
|
.type = QEMU_OPT_NUMBER,
|
||||||
.help = "total bytes write burst",
|
.help = "total bytes write burst",
|
||||||
|
},{
|
||||||
|
.name = "throttling.iops-total-max-length",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "length of the iops-total-max burst period, in seconds",
|
||||||
|
},{
|
||||||
|
.name = "throttling.iops-read-max-length",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "length of the iops-read-max burst period, in seconds",
|
||||||
|
},{
|
||||||
|
.name = "throttling.iops-write-max-length",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "length of the iops-write-max burst period, in seconds",
|
||||||
|
},{
|
||||||
|
.name = "throttling.bps-total-max-length",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "length of the bps-total-max burst period, in seconds",
|
||||||
|
},{
|
||||||
|
.name = "throttling.bps-read-max-length",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "length of the bps-read-max burst period, in seconds",
|
||||||
|
},{
|
||||||
|
.name = "throttling.bps-write-max-length",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "length of the bps-write-max burst period, in seconds",
|
||||||
},{
|
},{
|
||||||
.name = "throttling.iops-size",
|
.name = "throttling.iops-size",
|
||||||
.type = QEMU_OPT_NUMBER,
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#ifndef TARGET_SYSCALL_H
|
||||||
|
#define TARGET_SYSCALL_H
|
||||||
|
|
||||||
/* default linux values for the selectors */
|
/* default linux values for the selectors */
|
||||||
#define __USER_CS (0x23)
|
#define __USER_CS (0x23)
|
||||||
#define __USER_DS (0x2B)
|
#define __USER_DS (0x2B)
|
||||||
@@ -159,3 +162,4 @@ struct target_vm86plus_struct {
|
|||||||
|
|
||||||
#define UNAME_MACHINE "i386"
|
#define UNAME_MACHINE "i386"
|
||||||
|
|
||||||
|
#endif /* TARGET_SYSCALL_H */
|
||||||
@@ -17,15 +17,12 @@
|
|||||||
#ifndef QEMU_H
|
#ifndef QEMU_H
|
||||||
#define QEMU_H
|
#define QEMU_H
|
||||||
|
|
||||||
#include <signal.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "exec/cpu_ldst.h"
|
#include "exec/cpu_ldst.h"
|
||||||
|
|
||||||
#undef DEBUG_REMAP
|
#undef DEBUG_REMAP
|
||||||
#ifdef DEBUG_REMAP
|
#ifdef DEBUG_REMAP
|
||||||
#include <stdlib.h>
|
|
||||||
#endif /* DEBUG_REMAP */
|
#endif /* DEBUG_REMAP */
|
||||||
|
|
||||||
#include "exec/user/abitypes.h"
|
#include "exec/user/abitypes.h"
|
||||||
@@ -38,7 +35,7 @@ enum BSDType {
|
|||||||
extern enum BSDType bsd_type;
|
extern enum BSDType bsd_type;
|
||||||
|
|
||||||
#include "syscall_defs.h"
|
#include "syscall_defs.h"
|
||||||
#include "syscall.h"
|
#include "target_syscall.h"
|
||||||
#include "target_signal.h"
|
#include "target_signal.h"
|
||||||
#include "exec/gdbstub.h"
|
#include "exec/gdbstub.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#ifndef TARGET_SYSCALL_H
|
||||||
|
#define TARGET_SYSCALL_H
|
||||||
|
|
||||||
struct target_pt_regs {
|
struct target_pt_regs {
|
||||||
abi_ulong psr;
|
abi_ulong psr;
|
||||||
abi_ulong pc;
|
abi_ulong pc;
|
||||||
@@ -7,3 +10,5 @@ struct target_pt_regs {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define UNAME_MACHINE "sun4"
|
#define UNAME_MACHINE "sun4"
|
||||||
|
|
||||||
|
#endif /* TARGET_SYSCALL_H */
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#ifndef TARGET_SYSCALL_H
|
||||||
|
#define TARGET_SYSCALL_H
|
||||||
|
|
||||||
struct target_pt_regs {
|
struct target_pt_regs {
|
||||||
abi_ulong u_regs[16];
|
abi_ulong u_regs[16];
|
||||||
abi_ulong tstate;
|
abi_ulong tstate;
|
||||||
@@ -8,3 +11,5 @@ struct target_pt_regs {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define UNAME_MACHINE "sun4u"
|
#define UNAME_MACHINE "sun4u"
|
||||||
|
|
||||||
|
#endif /* TARGET_SYSCALL_H */
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#ifndef TARGET_SYSCALL_H
|
||||||
|
#define TARGET_SYSCALL_H
|
||||||
|
|
||||||
#define __USER_CS (0x33)
|
#define __USER_CS (0x33)
|
||||||
#define __USER_DS (0x2B)
|
#define __USER_DS (0x2B)
|
||||||
|
|
||||||
@@ -114,3 +117,5 @@ struct target_msqid64_ds {
|
|||||||
#define TARGET_ARCH_SET_FS 0x1002
|
#define TARGET_ARCH_SET_FS 0x1002
|
||||||
#define TARGET_ARCH_GET_FS 0x1003
|
#define TARGET_ARCH_GET_FS 0x1003
|
||||||
#define TARGET_ARCH_GET_GS 0x1004
|
#define TARGET_ARCH_GET_GS 0x1004
|
||||||
|
|
||||||
|
#endif /* TARGET_SYSCALL_H */
|
||||||
20
configure
vendored
20
configure
vendored
@@ -279,6 +279,7 @@ smartcard=""
|
|||||||
libusb=""
|
libusb=""
|
||||||
usb_redir=""
|
usb_redir=""
|
||||||
opengl=""
|
opengl=""
|
||||||
|
opengl_dmabuf="no"
|
||||||
zlib="yes"
|
zlib="yes"
|
||||||
lzo=""
|
lzo=""
|
||||||
snappy=""
|
snappy=""
|
||||||
@@ -3274,7 +3275,7 @@ libs_softmmu="$libs_softmmu $fdt_libs"
|
|||||||
# opengl probe (for sdl2, gtk, milkymist-tmu2)
|
# opengl probe (for sdl2, gtk, milkymist-tmu2)
|
||||||
|
|
||||||
if test "$opengl" != "no" ; then
|
if test "$opengl" != "no" ; then
|
||||||
opengl_pkgs="epoxy"
|
opengl_pkgs="epoxy libdrm gbm"
|
||||||
if $pkg_config $opengl_pkgs x11; then
|
if $pkg_config $opengl_pkgs x11; then
|
||||||
opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags"
|
opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags"
|
||||||
opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs"
|
opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs"
|
||||||
@@ -3292,6 +3293,18 @@ if test "$opengl" != "no" ; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "$opengl" = "yes"; then
|
||||||
|
cat > $TMPC << EOF
|
||||||
|
#include <epoxy/egl.h>
|
||||||
|
#ifndef EGL_MESA_image_dma_buf_export
|
||||||
|
# error mesa/epoxy lacks support for dmabufs (mesa 10.6+)
|
||||||
|
#endif
|
||||||
|
int main(void) { return 0; }
|
||||||
|
EOF
|
||||||
|
if compile_prog "" "" ; then
|
||||||
|
opengl_dmabuf=yes
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# archipelago probe
|
# archipelago probe
|
||||||
@@ -4752,6 +4765,7 @@ echo "smartcard support $smartcard"
|
|||||||
echo "libusb $libusb"
|
echo "libusb $libusb"
|
||||||
echo "usb net redir $usb_redir"
|
echo "usb net redir $usb_redir"
|
||||||
echo "OpenGL support $opengl"
|
echo "OpenGL support $opengl"
|
||||||
|
echo "OpenGL dmabufs $opengl_dmabuf"
|
||||||
echo "libiscsi support $libiscsi"
|
echo "libiscsi support $libiscsi"
|
||||||
echo "libnfs support $libnfs"
|
echo "libnfs support $libnfs"
|
||||||
echo "build guest agent $guest_agent"
|
echo "build guest agent $guest_agent"
|
||||||
@@ -5050,6 +5064,7 @@ if test "$gtk" = "yes" ; then
|
|||||||
echo "CONFIG_GTK=y" >> $config_host_mak
|
echo "CONFIG_GTK=y" >> $config_host_mak
|
||||||
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
|
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
|
||||||
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
|
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
|
||||||
|
echo "GTK_LIBS=$gtk_libs" >> $config_host_mak
|
||||||
if test "$gtk_gl" = "yes" ; then
|
if test "$gtk_gl" = "yes" ; then
|
||||||
echo "CONFIG_GTK_GL=y" >> $config_host_mak
|
echo "CONFIG_GTK_GL=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
@@ -5158,6 +5173,9 @@ if test "$opengl" = "yes" ; then
|
|||||||
echo "CONFIG_OPENGL=y" >> $config_host_mak
|
echo "CONFIG_OPENGL=y" >> $config_host_mak
|
||||||
echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak
|
echo "OPENGL_CFLAGS=$opengl_cflags" >> $config_host_mak
|
||||||
echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak
|
echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak
|
||||||
|
if test "$opengl_dmabuf" = "yes" ; then
|
||||||
|
echo "CONFIG_OPENGL_DMABUF=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "$lzo" = "yes" ; then
|
if test "$lzo" = "yes" ; then
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
* purposes.
|
* purposes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
|
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
|
|||||||
@@ -26,10 +26,7 @@
|
|||||||
* associated to the ivshmem shared memory.
|
* associated to the ivshmem shared memory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include "qemu/event_notifier.h"
|
#include "qemu/event_notifier.h"
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
|
|||||||
@@ -17,12 +17,13 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "vixl/a64/disasm-a64.h"
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include "qemu/osdep.h"
|
||||||
#include "disas/bfd.h"
|
#include "disas/bfd.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "vixl/a64/disasm-a64.h"
|
||||||
|
|
||||||
using namespace vixl;
|
using namespace vixl;
|
||||||
|
|
||||||
static Decoder *vixl_decoder = NULL;
|
static Decoder *vixl_decoder = NULL;
|
||||||
|
|||||||
@@ -1405,6 +1405,10 @@ const struct mips_opcode mips_builtin_opcodes[] =
|
|||||||
{"cmp.sor.d", "D,S,T", 0x46a00019, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6},
|
{"cmp.sor.d", "D,S,T", 0x46a00019, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6},
|
||||||
{"cmp.sune.d", "D,S,T", 0x46a0001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6},
|
{"cmp.sune.d", "D,S,T", 0x46a0001a, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6},
|
||||||
{"cmp.sne.d", "D,S,T", 0x46a0001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6},
|
{"cmp.sne.d", "D,S,T", 0x46a0001b, 0xffe0003f, RD_S|RD_T|WR_D|FP_D, 0, I32R6},
|
||||||
|
{"dvp", "", 0x41600024, 0xffffffff, TRAP, 0, I32R6},
|
||||||
|
{"dvp", "t", 0x41600024, 0xffe0ffff, TRAP|WR_t, 0, I32R6},
|
||||||
|
{"evp", "", 0x41600004, 0xffffffff, TRAP, 0, I32R6},
|
||||||
|
{"evp", "t", 0x41600004, 0xffe0ffff, TRAP|WR_t, 0, I32R6},
|
||||||
|
|
||||||
/* MSA */
|
/* MSA */
|
||||||
{"sll.b", "+d,+e,+f", 0x7800000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA},
|
{"sll.b", "+d,+e,+f", 0x7800000d, 0xffe0003f, WR_VD|RD_VS|RD_VT, 0, MSA},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
= How to use the QAPI code generator =
|
= How to use the QAPI code generator =
|
||||||
|
|
||||||
Copyright IBM Corp. 2011
|
Copyright IBM Corp. 2011
|
||||||
Copyright (C) 2012-2015 Red Hat, Inc.
|
Copyright (C) 2012-2016 Red Hat, Inc.
|
||||||
|
|
||||||
This work is licensed under the terms of the GNU GPL, version 2 or
|
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
later. See the COPYING file in the top-level directory.
|
later. See the COPYING file in the top-level directory.
|
||||||
@@ -52,7 +52,7 @@ schema. The documentation is delimited between two lines of ##, then
|
|||||||
the first line names the expression, an optional overview is provided,
|
the first line names the expression, an optional overview is provided,
|
||||||
then individual documentation about each member of 'data' is provided,
|
then individual documentation about each member of 'data' is provided,
|
||||||
and finally, a 'Since: x.y.z' tag lists the release that introduced
|
and finally, a 'Since: x.y.z' tag lists the release that introduced
|
||||||
the expression. Optional fields are tagged with the phrase
|
the expression. Optional members are tagged with the phrase
|
||||||
'#optional', often with their default value; and extensions added
|
'#optional', often with their default value; and extensions added
|
||||||
after the expression was first released are also given a '(since
|
after the expression was first released are also given a '(since
|
||||||
x.y.z)' comment. For example:
|
x.y.z)' comment. For example:
|
||||||
@@ -108,15 +108,15 @@ user-defined type names, while built-in types are lowercase. Type
|
|||||||
definitions should not end in 'Kind', as this namespace is used for
|
definitions should not end in 'Kind', as this namespace is used for
|
||||||
creating implicit C enums for visiting union types, or in 'List', as
|
creating implicit C enums for visiting union types, or in 'List', as
|
||||||
this namespace is used for creating array types. Command names,
|
this namespace is used for creating array types. Command names,
|
||||||
and field names within a type, should be all lower case with words
|
and member names within a type, should be all lower case with words
|
||||||
separated by a hyphen. However, some existing older commands and
|
separated by a hyphen. However, some existing older commands and
|
||||||
complex types use underscore; when extending such expressions,
|
complex types use underscore; when extending such expressions,
|
||||||
consistency is preferred over blindly avoiding underscore. Event
|
consistency is preferred over blindly avoiding underscore. Event
|
||||||
names should be ALL_CAPS with words separated by underscore. Field
|
names should be ALL_CAPS with words separated by underscore. Member
|
||||||
names cannot start with 'has-' or 'has_', as this is reserved for
|
names cannot start with 'has-' or 'has_', as this is reserved for
|
||||||
tracking optional fields.
|
tracking optional members.
|
||||||
|
|
||||||
Any name (command, event, type, field, or enum value) beginning with
|
Any name (command, event, type, member, or enum value) beginning with
|
||||||
"x-" is marked experimental, and may be withdrawn or changed
|
"x-" is marked experimental, and may be withdrawn or changed
|
||||||
incompatibly in a future release. All names must begin with a letter,
|
incompatibly in a future release. All names must begin with a letter,
|
||||||
and contain only ASCII letters, digits, dash, and underscore. There
|
and contain only ASCII letters, digits, dash, and underscore. There
|
||||||
@@ -127,7 +127,7 @@ the vendor), even if the rest of the name uses dash (example:
|
|||||||
__com.redhat_drive-mirror). Names beginning with 'q_' are reserved
|
__com.redhat_drive-mirror). Names beginning with 'q_' are reserved
|
||||||
for the generator: QMP names that resemble C keywords or other
|
for the generator: QMP names that resemble C keywords or other
|
||||||
problematic strings will be munged in C to use this prefix. For
|
problematic strings will be munged in C to use this prefix. For
|
||||||
example, a field named "default" in qapi becomes "q_default" in the
|
example, a member named "default" in qapi becomes "q_default" in the
|
||||||
generated C code.
|
generated C code.
|
||||||
|
|
||||||
In the rest of this document, usage lines are given for each
|
In the rest of this document, usage lines are given for each
|
||||||
@@ -217,17 +217,18 @@ and must continue to work).
|
|||||||
|
|
||||||
On output structures (only mentioned in the 'returns' side of a command),
|
On output structures (only mentioned in the 'returns' side of a command),
|
||||||
changing from mandatory to optional is in general unsafe (older clients may be
|
changing from mandatory to optional is in general unsafe (older clients may be
|
||||||
expecting the field, and could crash if it is missing), although it can be done
|
expecting the member, and could crash if it is missing), although it
|
||||||
if the only way that the optional argument will be omitted is when it is
|
can be done if the only way that the optional argument will be omitted
|
||||||
triggered by the presence of a new input flag to the command that older clients
|
is when it is triggered by the presence of a new input flag to the
|
||||||
don't know to send. Changing from optional to mandatory is safe.
|
command that older clients don't know to send. Changing from optional
|
||||||
|
to mandatory is safe.
|
||||||
|
|
||||||
A structure that is used in both input and output of various commands
|
A structure that is used in both input and output of various commands
|
||||||
must consider the backwards compatibility constraints of both directions
|
must consider the backwards compatibility constraints of both directions
|
||||||
of use.
|
of use.
|
||||||
|
|
||||||
A struct definition can specify another struct as its base.
|
A struct definition can specify another struct as its base.
|
||||||
In this case, the fields of the base type are included as top-level fields
|
In this case, the members of the base type are included as top-level members
|
||||||
of the new struct's dictionary in the Client JSON Protocol wire
|
of the new struct's dictionary in the Client JSON Protocol wire
|
||||||
format. An example definition is:
|
format. An example definition is:
|
||||||
|
|
||||||
@@ -237,7 +238,7 @@ format. An example definition is:
|
|||||||
'data': { '*backing': 'str' } }
|
'data': { '*backing': 'str' } }
|
||||||
|
|
||||||
An example BlockdevOptionsGenericCOWFormat object on the wire could use
|
An example BlockdevOptionsGenericCOWFormat object on the wire could use
|
||||||
both fields like this:
|
both members like this:
|
||||||
|
|
||||||
{ "file": "/some/place/my-image",
|
{ "file": "/some/place/my-image",
|
||||||
"backing": "/some/place/my-backing-file" }
|
"backing": "/some/place/my-backing-file" }
|
||||||
@@ -262,7 +263,7 @@ The enum constants will be named by using a heuristic to turn the
|
|||||||
type name into a set of underscore separated words. For the example
|
type name into a set of underscore separated words. For the example
|
||||||
above, 'MyEnum' will turn into 'MY_ENUM' giving a constant name
|
above, 'MyEnum' will turn into 'MY_ENUM' giving a constant name
|
||||||
of 'MY_ENUM_VALUE1' for the first value. If the default heuristic
|
of 'MY_ENUM_VALUE1' for the first value. If the default heuristic
|
||||||
does not result in a desirable name, the optional 'prefix' field
|
does not result in a desirable name, the optional 'prefix' member
|
||||||
can be used when defining the enum.
|
can be used when defining the enum.
|
||||||
|
|
||||||
The enumeration values are passed as strings over the Client JSON
|
The enumeration values are passed as strings over the Client JSON
|
||||||
@@ -275,9 +276,9 @@ converting between strings and enum values. Since the wire format
|
|||||||
always passes by name, it is acceptable to reorder or add new
|
always passes by name, it is acceptable to reorder or add new
|
||||||
enumeration members in any location without breaking clients of Client
|
enumeration members in any location without breaking clients of Client
|
||||||
JSON Protocol; however, removing enum values would break
|
JSON Protocol; however, removing enum values would break
|
||||||
compatibility. For any struct that has a field that will only contain
|
compatibility. For any struct that has a member that will only contain
|
||||||
a finite set of string values, using an enum type for that field is
|
a finite set of string values, using an enum type for that member is
|
||||||
better than open-coding the field to be type 'str'.
|
better than open-coding the member to be type 'str'.
|
||||||
|
|
||||||
|
|
||||||
=== Union types ===
|
=== Union types ===
|
||||||
@@ -305,8 +306,8 @@ values to data types like in this example:
|
|||||||
'qcow2': 'Qcow2Options' } }
|
'qcow2': 'Qcow2Options' } }
|
||||||
|
|
||||||
In the Client JSON Protocol, a simple union is represented by a
|
In the Client JSON Protocol, a simple union is represented by a
|
||||||
dictionary that contains the 'type' field as a discriminator, and a
|
dictionary that contains the 'type' member as a discriminator, and a
|
||||||
'data' field that is of the specified data type corresponding to the
|
'data' member that is of the specified data type corresponding to the
|
||||||
discriminator value, as in these examples:
|
discriminator value, as in these examples:
|
||||||
|
|
||||||
{ "type": "file", "data" : { "filename": "/some/place/my-image" } }
|
{ "type": "file", "data" : { "filename": "/some/place/my-image" } }
|
||||||
@@ -321,14 +322,14 @@ enum. The value for each branch can be of any type.
|
|||||||
|
|
||||||
A flat union definition specifies a struct as its base, and
|
A flat union definition specifies a struct as its base, and
|
||||||
avoids nesting on the wire. All branches of the union must be
|
avoids nesting on the wire. All branches of the union must be
|
||||||
complex types, and the top-level fields of the union dictionary on
|
complex types, and the top-level members of the union dictionary on
|
||||||
the wire will be combination of fields from both the base type and the
|
the wire will be combination of members from both the base type and the
|
||||||
appropriate branch type (when merging two dictionaries, there must be
|
appropriate branch type (when merging two dictionaries, there must be
|
||||||
no keys in common). The 'discriminator' field must be the name of an
|
no keys in common). The 'discriminator' member must be the name of an
|
||||||
enum-typed member of the base struct.
|
enum-typed member of the base struct.
|
||||||
|
|
||||||
The following example enhances the above simple union example by
|
The following example enhances the above simple union example by
|
||||||
adding a common field 'readonly', renaming the discriminator to
|
adding a common member 'readonly', renaming the discriminator to
|
||||||
something more applicable, and reducing the number of {} required on
|
something more applicable, and reducing the number of {} required on
|
||||||
the wire:
|
the wire:
|
||||||
|
|
||||||
@@ -353,8 +354,8 @@ the user, but because it must map to a base member with enum type, the
|
|||||||
code generator can ensure that branches exist for all values of the
|
code generator can ensure that branches exist for all values of the
|
||||||
enum (although the order of the keys need not match the declaration of
|
enum (although the order of the keys need not match the declaration of
|
||||||
the enum). In the resulting generated C data types, a flat union is
|
the enum). In the resulting generated C data types, a flat union is
|
||||||
represented as a struct with the base member fields included directly,
|
represented as a struct with the base members included directly, and
|
||||||
and then a union of structures for each branch of the struct.
|
then a union of structures for each branch of the struct.
|
||||||
|
|
||||||
A simple union can always be re-written as a flat union where the base
|
A simple union can always be re-written as a flat union where the base
|
||||||
class has a single member named 'type', and where each branch of the
|
class has a single member named 'type', and where each branch of the
|
||||||
@@ -424,10 +425,10 @@ string name of a complex type, or a dictionary that declares an
|
|||||||
anonymous type with the same semantics as a 'struct' expression, with
|
anonymous type with the same semantics as a 'struct' expression, with
|
||||||
one exception noted below when 'gen' is used.
|
one exception noted below when 'gen' is used.
|
||||||
|
|
||||||
The 'returns' member describes what will appear in the "return" field
|
The 'returns' member describes what will appear in the "return" member
|
||||||
of a Client JSON Protocol reply on successful completion of a command.
|
of a Client JSON Protocol reply on successful completion of a command.
|
||||||
The member is optional from the command declaration; if absent, the
|
The member is optional from the command declaration; if absent, the
|
||||||
"return" field will be an empty dictionary. If 'returns' is present,
|
"return" member will be an empty dictionary. If 'returns' is present,
|
||||||
it must be the string name of a complex or built-in type, a
|
it must be the string name of a complex or built-in type, a
|
||||||
one-element array containing the name of a complex or built-in type,
|
one-element array containing the name of a complex or built-in type,
|
||||||
with one exception noted below when 'gen' is used. Although it is
|
with one exception noted below when 'gen' is used. Although it is
|
||||||
@@ -435,7 +436,7 @@ permitted to have the 'returns' member name a built-in type or an
|
|||||||
array of built-in types, any command that does this cannot be extended
|
array of built-in types, any command that does this cannot be extended
|
||||||
to return additional information in the future; thus, new commands
|
to return additional information in the future; thus, new commands
|
||||||
should strongly consider returning a dictionary-based type or an array
|
should strongly consider returning a dictionary-based type or an array
|
||||||
of dictionaries, even if the dictionary only contains one field at the
|
of dictionaries, even if the dictionary only contains one member at the
|
||||||
present.
|
present.
|
||||||
|
|
||||||
All commands in Client JSON Protocol use a dictionary to report
|
All commands in Client JSON Protocol use a dictionary to report
|
||||||
@@ -478,7 +479,7 @@ response is not possible (although the command will still return a
|
|||||||
normal dictionary error on failure). When a successful reply is not
|
normal dictionary error on failure). When a successful reply is not
|
||||||
possible, the command expression should include the optional key
|
possible, the command expression should include the optional key
|
||||||
'success-response' with boolean value false. So far, only QGA makes
|
'success-response' with boolean value false. So far, only QGA makes
|
||||||
use of this field.
|
use of this member.
|
||||||
|
|
||||||
|
|
||||||
=== Events ===
|
=== Events ===
|
||||||
@@ -656,7 +657,7 @@ Union types
|
|||||||
|
|
||||||
{ "name": "BlockdevOptions", "meta-type": "object",
|
{ "name": "BlockdevOptions", "meta-type": "object",
|
||||||
"members": [
|
"members": [
|
||||||
{ "name": "kind", "type": "BlockdevOptionsKind" } ],
|
{ "name": "type", "type": "BlockdevOptionsKind" } ],
|
||||||
"tag": "type",
|
"tag": "type",
|
||||||
"variants": [
|
"variants": [
|
||||||
{ "case": "file", "type": ":obj-FileOptions-wrapper" },
|
{ "case": "file", "type": ":obj-FileOptions-wrapper" },
|
||||||
@@ -722,33 +723,38 @@ the names of built-in types. Clients should examine member
|
|||||||
|
|
||||||
== Code generation ==
|
== Code generation ==
|
||||||
|
|
||||||
Schemas are fed into four scripts to generate all the code/files that,
|
Schemas are fed into five scripts to generate all the code/files that,
|
||||||
paired with the core QAPI libraries, comprise everything required to
|
paired with the core QAPI libraries, comprise everything required to
|
||||||
take JSON commands read in by a Client JSON Protocol server, unmarshal
|
take JSON commands read in by a Client JSON Protocol server, unmarshal
|
||||||
the arguments into the underlying C types, call into the corresponding
|
the arguments into the underlying C types, call into the corresponding
|
||||||
C function, and map the response back to a Client JSON Protocol
|
C function, map the response back to a Client JSON Protocol response
|
||||||
response to be returned to the user.
|
to be returned to the user, and introspect the commands.
|
||||||
|
|
||||||
As an example, we'll use the following schema, which describes a single
|
As an example, we'll use the following schema, which describes a
|
||||||
complex user-defined type (which will produce a C struct, along with a list
|
single complex user-defined type, along with command which takes a
|
||||||
node structure that can be used to chain together a list of such types in
|
list of that type as a parameter, and returns a single element of that
|
||||||
case we want to accept/return a list of this type with a command), and a
|
type. The user is responsible for writing the implementation of
|
||||||
command which takes that type as a parameter and returns the same type:
|
qmp_my_command(); everything else is produced by the generator.
|
||||||
|
|
||||||
$ cat example-schema.json
|
$ cat example-schema.json
|
||||||
{ 'struct': 'UserDefOne',
|
{ 'struct': 'UserDefOne',
|
||||||
'data': { 'integer': 'int', 'string': 'str' } }
|
'data': { 'integer': 'int', '*string': 'str' } }
|
||||||
|
|
||||||
{ 'command': 'my-command',
|
{ 'command': 'my-command',
|
||||||
'data': {'arg1': 'UserDefOne'},
|
'data': { 'arg1': ['UserDefOne'] },
|
||||||
'returns': 'UserDefOne' }
|
'returns': 'UserDefOne' }
|
||||||
|
|
||||||
{ 'event': 'MY_EVENT' }
|
{ 'event': 'MY_EVENT' }
|
||||||
|
|
||||||
|
For a more thorough look at generated code, the testsuite includes
|
||||||
|
tests/qapi-schema/qapi-schema-tests.json that covers more examples of
|
||||||
|
what the generator will accept, and compiles the resulting C code as
|
||||||
|
part of 'make check-unit'.
|
||||||
|
|
||||||
=== scripts/qapi-types.py ===
|
=== scripts/qapi-types.py ===
|
||||||
|
|
||||||
Used to generate the C types defined by a schema. The following files are
|
Used to generate the C types defined by a schema, along with
|
||||||
created:
|
supporting code. The following files are created:
|
||||||
|
|
||||||
$(prefix)qapi-types.h - C types corresponding to types defined in
|
$(prefix)qapi-types.h - C types corresponding to types defined in
|
||||||
the schema you pass in
|
the schema you pass in
|
||||||
@@ -763,38 +769,6 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-types.py --output-dir="qapi-generated" \
|
$ python scripts/qapi-types.py --output-dir="qapi-generated" \
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
$ cat qapi-generated/example-qapi-types.c
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
void qapi_free_UserDefOne(UserDefOne *obj)
|
|
||||||
{
|
|
||||||
QapiDeallocVisitor *qdv;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qdv = qapi_dealloc_visitor_new();
|
|
||||||
v = qapi_dealloc_get_visitor(qdv);
|
|
||||||
visit_type_UserDefOne(v, &obj, NULL, NULL);
|
|
||||||
qapi_dealloc_visitor_cleanup(qdv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void qapi_free_UserDefOneList(UserDefOneList *obj)
|
|
||||||
{
|
|
||||||
QapiDeallocVisitor *qdv;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
qdv = qapi_dealloc_visitor_new();
|
|
||||||
v = qapi_dealloc_get_visitor(qdv);
|
|
||||||
visit_type_UserDefOneList(v, &obj, NULL, NULL);
|
|
||||||
qapi_dealloc_visitor_cleanup(qdv);
|
|
||||||
}
|
|
||||||
$ cat qapi-generated/example-qapi-types.h
|
$ cat qapi-generated/example-qapi-types.h
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
@@ -809,29 +783,59 @@ Example:
|
|||||||
|
|
||||||
struct UserDefOne {
|
struct UserDefOne {
|
||||||
int64_t integer;
|
int64_t integer;
|
||||||
|
bool has_string;
|
||||||
char *string;
|
char *string;
|
||||||
};
|
};
|
||||||
|
|
||||||
void qapi_free_UserDefOne(UserDefOne *obj);
|
void qapi_free_UserDefOne(UserDefOne *obj);
|
||||||
|
|
||||||
struct UserDefOneList {
|
struct UserDefOneList {
|
||||||
union {
|
|
||||||
UserDefOne *value;
|
|
||||||
uint64_t padding;
|
|
||||||
};
|
|
||||||
UserDefOneList *next;
|
UserDefOneList *next;
|
||||||
|
UserDefOne *value;
|
||||||
};
|
};
|
||||||
|
|
||||||
void qapi_free_UserDefOneList(UserDefOneList *obj);
|
void qapi_free_UserDefOneList(UserDefOneList *obj);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
$ cat qapi-generated/example-qapi-types.c
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
void qapi_free_UserDefOne(UserDefOne *obj)
|
||||||
|
{
|
||||||
|
QapiDeallocVisitor *qdv;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdv = qapi_dealloc_visitor_new();
|
||||||
|
v = qapi_dealloc_get_visitor(qdv);
|
||||||
|
visit_type_UserDefOne(v, NULL, &obj, NULL);
|
||||||
|
qapi_dealloc_visitor_cleanup(qdv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qapi_free_UserDefOneList(UserDefOneList *obj)
|
||||||
|
{
|
||||||
|
QapiDeallocVisitor *qdv;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qdv = qapi_dealloc_visitor_new();
|
||||||
|
v = qapi_dealloc_get_visitor(qdv);
|
||||||
|
visit_type_UserDefOneList(v, NULL, &obj, NULL);
|
||||||
|
qapi_dealloc_visitor_cleanup(qdv);
|
||||||
|
}
|
||||||
|
|
||||||
=== scripts/qapi-visit.py ===
|
=== scripts/qapi-visit.py ===
|
||||||
|
|
||||||
Used to generate the visitor functions used to walk through and convert
|
Used to generate the visitor functions used to walk through and
|
||||||
a QObject (as provided by QMP) to a native C data structure and
|
convert between a native QAPI C data structure and some other format
|
||||||
vice-versa, as well as the visitor function used to dealloc a complex
|
(such as QObject); the generated functions are named visit_type_FOO()
|
||||||
schema-defined C type.
|
and visit_type_FOO_members().
|
||||||
|
|
||||||
The following files are generated:
|
The following files are generated:
|
||||||
|
|
||||||
@@ -848,41 +852,62 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-visit.py --output-dir="qapi-generated"
|
$ python scripts/qapi-visit.py --output-dir="qapi-generated"
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
|
$ cat qapi-generated/example-qapi-visit.h
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
#ifndef EXAMPLE_QAPI_VISIT_H
|
||||||
|
#define EXAMPLE_QAPI_VISIT_H
|
||||||
|
|
||||||
|
[Visitors for built-in types omitted...]
|
||||||
|
|
||||||
|
void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp);
|
||||||
|
void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp);
|
||||||
|
void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp);
|
||||||
|
|
||||||
|
#endif
|
||||||
$ cat qapi-generated/example-qapi-visit.c
|
$ cat qapi-generated/example-qapi-visit.c
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
static void visit_type_UserDefOne_fields(Visitor *v, UserDefOne **obj, Error **errp)
|
void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
visit_type_int(v, &(*obj)->integer, "integer", &err);
|
visit_type_int(v, "integer", &obj->integer, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
visit_type_str(v, &(*obj)->string, "string", &err);
|
if (visit_optional(v, "string", &obj->has_string)) {
|
||||||
if (err) {
|
visit_type_str(v, "string", &obj->string, &err);
|
||||||
goto out;
|
if (err) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_type_UserDefOne(Visitor *v, UserDefOne **obj, const char *name, Error **errp)
|
void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
visit_start_struct(v, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err);
|
visit_start_struct(v, name, (void **)obj, sizeof(UserDefOne), &err);
|
||||||
if (!err) {
|
if (err) {
|
||||||
if (*obj) {
|
goto out;
|
||||||
visit_type_UserDefOne_fields(v, obj, errp);
|
|
||||||
}
|
|
||||||
visit_end_struct(v, &err);
|
|
||||||
}
|
}
|
||||||
|
if (!*obj) {
|
||||||
|
goto out_obj;
|
||||||
|
}
|
||||||
|
visit_type_UserDefOne_members(v, *obj, &err);
|
||||||
|
error_propagate(errp, err);
|
||||||
|
err = NULL;
|
||||||
|
out_obj:
|
||||||
|
visit_end_struct(v, &err);
|
||||||
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_type_UserDefOneList(Visitor *v, UserDefOneList **obj, const char *name, Error **errp)
|
void visit_type_UserDefOneList(Visitor *v, const char *name, UserDefOneList **obj, Error **errp)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
GenericList *i, **prev;
|
GenericList *i, **prev;
|
||||||
@@ -893,35 +918,24 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (prev = (GenericList **)obj;
|
for (prev = (GenericList **)obj;
|
||||||
!err && (i = visit_next_list(v, prev, &err)) != NULL;
|
!err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
|
||||||
prev = &i) {
|
prev = &i) {
|
||||||
UserDefOneList *native_i = (UserDefOneList *)i;
|
UserDefOneList *native_i = (UserDefOneList *)i;
|
||||||
visit_type_UserDefOne(v, &native_i->value, NULL, &err);
|
visit_type_UserDefOne(v, NULL, &native_i->value, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
error_propagate(errp, err);
|
visit_end_list(v);
|
||||||
err = NULL;
|
|
||||||
visit_end_list(v, &err);
|
|
||||||
out:
|
out:
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
$ cat qapi-generated/example-qapi-visit.h
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
#ifndef EXAMPLE_QAPI_VISIT_H
|
|
||||||
#define EXAMPLE_QAPI_VISIT_H
|
|
||||||
|
|
||||||
[Visitors for built-in types omitted...]
|
|
||||||
|
|
||||||
void visit_type_UserDefOne(Visitor *v, UserDefOne **obj, const char *name, Error **errp);
|
|
||||||
void visit_type_UserDefOneList(Visitor *v, UserDefOneList **obj, const char *name, Error **errp);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
=== scripts/qapi-commands.py ===
|
=== scripts/qapi-commands.py ===
|
||||||
|
|
||||||
Used to generate the marshaling/dispatch functions for the commands defined
|
Used to generate the marshaling/dispatch functions for the commands
|
||||||
in the schema. The following files are generated:
|
defined in the schema. The generated code implements
|
||||||
|
qmp_marshal_COMMAND() (mentioned in qmp-commands.hx, and registered
|
||||||
|
automatically), and declares qmp_COMMAND() that the user must
|
||||||
|
implement. The following files are generated:
|
||||||
|
|
||||||
$(prefix)qmp-marshal.c: command marshal/dispatch functions for each
|
$(prefix)qmp-marshal.c: command marshal/dispatch functions for each
|
||||||
QMP command defined in the schema. Functions
|
QMP command defined in the schema. Functions
|
||||||
@@ -939,6 +953,19 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-commands.py --output-dir="qapi-generated"
|
$ python scripts/qapi-commands.py --output-dir="qapi-generated"
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
|
$ cat qapi-generated/example-qmp-commands.h
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
#ifndef EXAMPLE_QMP_COMMANDS_H
|
||||||
|
#define EXAMPLE_QMP_COMMANDS_H
|
||||||
|
|
||||||
|
#include "example-qapi-types.h"
|
||||||
|
#include "qapi/qmp/qdict.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
|
UserDefOne *qmp_my_command(UserDefOneList *arg1, Error **errp);
|
||||||
|
|
||||||
|
#endif
|
||||||
$ cat qapi-generated/example-qmp-marshal.c
|
$ cat qapi-generated/example-qmp-marshal.c
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
@@ -950,7 +977,7 @@ Example:
|
|||||||
Visitor *v;
|
Visitor *v;
|
||||||
|
|
||||||
v = qmp_output_get_visitor(qov);
|
v = qmp_output_get_visitor(qov);
|
||||||
visit_type_UserDefOne(v, &ret_in, "unused", &err);
|
visit_type_UserDefOne(v, "unused", &ret_in, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -961,7 +988,7 @@ Example:
|
|||||||
qmp_output_visitor_cleanup(qov);
|
qmp_output_visitor_cleanup(qov);
|
||||||
qdv = qapi_dealloc_visitor_new();
|
qdv = qapi_dealloc_visitor_new();
|
||||||
v = qapi_dealloc_get_visitor(qdv);
|
v = qapi_dealloc_get_visitor(qdv);
|
||||||
visit_type_UserDefOne(v, &ret_in, "unused", NULL);
|
visit_type_UserDefOne(v, "unused", &ret_in, NULL);
|
||||||
qapi_dealloc_visitor_cleanup(qdv);
|
qapi_dealloc_visitor_cleanup(qdv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,10 +999,10 @@ Example:
|
|||||||
QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
|
QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
|
||||||
QapiDeallocVisitor *qdv;
|
QapiDeallocVisitor *qdv;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
UserDefOne *arg1 = NULL;
|
UserDefOneList *arg1 = NULL;
|
||||||
|
|
||||||
v = qmp_input_get_visitor(qiv);
|
v = qmp_input_get_visitor(qiv);
|
||||||
visit_type_UserDefOne(v, &arg1, "arg1", &err);
|
visit_type_UserDefOneList(v, "arg1", &arg1, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@@ -992,7 +1019,7 @@ Example:
|
|||||||
qmp_input_visitor_cleanup(qiv);
|
qmp_input_visitor_cleanup(qiv);
|
||||||
qdv = qapi_dealloc_visitor_new();
|
qdv = qapi_dealloc_visitor_new();
|
||||||
v = qapi_dealloc_get_visitor(qdv);
|
v = qapi_dealloc_get_visitor(qdv);
|
||||||
visit_type_UserDefOne(v, &arg1, "arg1", NULL);
|
visit_type_UserDefOneList(v, "arg1", &arg1, NULL);
|
||||||
qapi_dealloc_visitor_cleanup(qdv);
|
qapi_dealloc_visitor_cleanup(qdv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1002,24 +1029,12 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
qapi_init(qmp_init_marshal);
|
qapi_init(qmp_init_marshal);
|
||||||
$ cat qapi-generated/example-qmp-commands.h
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
#ifndef EXAMPLE_QMP_COMMANDS_H
|
|
||||||
#define EXAMPLE_QMP_COMMANDS_H
|
|
||||||
|
|
||||||
#include "example-qapi-types.h"
|
|
||||||
#include "qapi/qmp/qdict.h"
|
|
||||||
#include "qapi/error.h"
|
|
||||||
|
|
||||||
UserDefOne *qmp_my_command(UserDefOne *arg1, Error **errp);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
=== scripts/qapi-event.py ===
|
=== scripts/qapi-event.py ===
|
||||||
|
|
||||||
Used to generate the event-related C code defined by a schema. The
|
Used to generate the event-related C code defined by a schema, with
|
||||||
following files are created:
|
implementations for qapi_event_send_FOO(). The following files are
|
||||||
|
created:
|
||||||
|
|
||||||
$(prefix)qapi-event.h - Function prototypes for each event type, plus an
|
$(prefix)qapi-event.h - Function prototypes for each event type, plus an
|
||||||
enumeration of all event names
|
enumeration of all event names
|
||||||
@@ -1029,6 +1044,27 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-event.py --output-dir="qapi-generated"
|
$ python scripts/qapi-event.py --output-dir="qapi-generated"
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
|
$ cat qapi-generated/example-qapi-event.h
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
#ifndef EXAMPLE_QAPI_EVENT_H
|
||||||
|
#define EXAMPLE_QAPI_EVENT_H
|
||||||
|
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/qmp/qdict.h"
|
||||||
|
#include "example-qapi-types.h"
|
||||||
|
|
||||||
|
|
||||||
|
void qapi_event_send_my_event(Error **errp);
|
||||||
|
|
||||||
|
typedef enum example_QAPIEvent {
|
||||||
|
EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
|
||||||
|
EXAMPLE_QAPI_EVENT__MAX = 1,
|
||||||
|
} example_QAPIEvent;
|
||||||
|
|
||||||
|
extern const char *const example_QAPIEvent_lookup[];
|
||||||
|
|
||||||
|
#endif
|
||||||
$ cat qapi-generated/example-qapi-event.c
|
$ cat qapi-generated/example-qapi-event.c
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
@@ -1054,27 +1090,6 @@ Example:
|
|||||||
[EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
|
[EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
|
||||||
[EXAMPLE_QAPI_EVENT__MAX] = NULL,
|
[EXAMPLE_QAPI_EVENT__MAX] = NULL,
|
||||||
};
|
};
|
||||||
$ cat qapi-generated/example-qapi-event.h
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
#ifndef EXAMPLE_QAPI_EVENT_H
|
|
||||||
#define EXAMPLE_QAPI_EVENT_H
|
|
||||||
|
|
||||||
#include "qapi/error.h"
|
|
||||||
#include "qapi/qmp/qdict.h"
|
|
||||||
#include "example-qapi-types.h"
|
|
||||||
|
|
||||||
|
|
||||||
void qapi_event_send_my_event(Error **errp);
|
|
||||||
|
|
||||||
typedef enum example_QAPIEvent {
|
|
||||||
EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
|
|
||||||
EXAMPLE_QAPI_EVENT__MAX = 1,
|
|
||||||
} example_QAPIEvent;
|
|
||||||
|
|
||||||
extern const char *const example_QAPIEvent_lookup[];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
=== scripts/qapi-introspect.py ===
|
=== scripts/qapi-introspect.py ===
|
||||||
|
|
||||||
@@ -1089,17 +1104,6 @@ Example:
|
|||||||
|
|
||||||
$ python scripts/qapi-introspect.py --output-dir="qapi-generated"
|
$ python scripts/qapi-introspect.py --output-dir="qapi-generated"
|
||||||
--prefix="example-" example-schema.json
|
--prefix="example-" example-schema.json
|
||||||
$ cat qapi-generated/example-qmp-introspect.c
|
|
||||||
[Uninteresting stuff omitted...]
|
|
||||||
|
|
||||||
const char example_qmp_schema_json[] = "["
|
|
||||||
"{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
|
|
||||||
"{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
|
|
||||||
"{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
|
|
||||||
"{\"members\": [{\"name\": \"arg1\", \"type\": \"2\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
|
|
||||||
"{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
|
|
||||||
"{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
|
|
||||||
"{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
|
|
||||||
$ cat qapi-generated/example-qmp-introspect.h
|
$ cat qapi-generated/example-qmp-introspect.h
|
||||||
[Uninteresting stuff omitted...]
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
@@ -1109,3 +1113,15 @@ Example:
|
|||||||
extern const char example_qmp_schema_json[];
|
extern const char example_qmp_schema_json[];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
$ cat qapi-generated/example-qmp-introspect.c
|
||||||
|
[Uninteresting stuff omitted...]
|
||||||
|
|
||||||
|
const char example_qmp_schema_json[] = "["
|
||||||
|
"{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, "
|
||||||
|
"{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, "
|
||||||
|
"{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, "
|
||||||
|
"{\"members\": [{\"name\": \"arg1\", \"type\": \"[2]\"}], \"meta-type\": \"object\", \"name\": \"1\"}, "
|
||||||
|
"{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"default\": null, \"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, "
|
||||||
|
"{\"element-type\": \"2\", \"meta-type\": \"array\", \"name\": \"[2]\"}, "
|
||||||
|
"{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, "
|
||||||
|
"{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]";
|
||||||
|
|||||||
@@ -220,6 +220,24 @@ Data:
|
|||||||
},
|
},
|
||||||
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
|
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
|
||||||
|
|
||||||
|
DUMP_COMPLETED
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Emitted when the guest has finished one memory dump.
|
||||||
|
|
||||||
|
Data:
|
||||||
|
|
||||||
|
- "result": DumpQueryResult type described in qapi-schema.json
|
||||||
|
- "error": Error message when dump failed. This is only a
|
||||||
|
human-readable string provided when dump failed. It should not be
|
||||||
|
parsed in any way (json-string, optional)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{ "event": "DUMP_COMPLETED",
|
||||||
|
"data": {"result": {"total": 1090650112, "status": "completed",
|
||||||
|
"completed": 1090650112} } }
|
||||||
|
|
||||||
GUEST_PANICKED
|
GUEST_PANICKED
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
0. About This Document
|
0. About This Document
|
||||||
======================
|
======================
|
||||||
|
|
||||||
Copyright (C) 2009-2015 Red Hat, Inc.
|
Copyright (C) 2009-2016 Red Hat, Inc.
|
||||||
|
|
||||||
This work is licensed under the terms of the GNU GPL, version 2 or
|
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
later. See the COPYING file in the top-level directory.
|
later. See the COPYING file in the top-level directory.
|
||||||
@@ -277,7 +277,7 @@ However, Clients must not assume any particular:
|
|||||||
- Amount of errors generated by a command, that is, new errors can be added
|
- Amount of errors generated by a command, that is, new errors can be added
|
||||||
to any existing command in newer versions of the Server
|
to any existing command in newer versions of the Server
|
||||||
|
|
||||||
Any command or field name beginning with "x-" is deemed experimental,
|
Any command or member name beginning with "x-" is deemed experimental,
|
||||||
and may be withdrawn or changed in an incompatible manner in a future
|
and may be withdrawn or changed in an incompatible manner in a future
|
||||||
release.
|
release.
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,18 @@ in the description of a field.
|
|||||||
write to an image with unknown auto-clear features if it
|
write to an image with unknown auto-clear features if it
|
||||||
clears the respective bits from this field first.
|
clears the respective bits from this field first.
|
||||||
|
|
||||||
Bits 0-63: Reserved (set to 0)
|
Bit 0: Bitmaps extension bit
|
||||||
|
This bit indicates consistency for the bitmaps
|
||||||
|
extension data.
|
||||||
|
|
||||||
|
It is an error if this bit is set without the
|
||||||
|
bitmaps extension present.
|
||||||
|
|
||||||
|
If the bitmaps extension is present but this
|
||||||
|
bit is unset, the bitmaps extension data must be
|
||||||
|
considered inconsistent.
|
||||||
|
|
||||||
|
Bits 1-63: Reserved (set to 0)
|
||||||
|
|
||||||
96 - 99: refcount_order
|
96 - 99: refcount_order
|
||||||
Describes the width of a reference count block entry (width
|
Describes the width of a reference count block entry (width
|
||||||
@@ -123,6 +134,7 @@ be stored. Each extension has a structure like the following:
|
|||||||
0x00000000 - End of the header extension area
|
0x00000000 - End of the header extension area
|
||||||
0xE2792ACA - Backing file format name
|
0xE2792ACA - Backing file format name
|
||||||
0x6803f857 - Feature name table
|
0x6803f857 - Feature name table
|
||||||
|
0x23852875 - Bitmaps extension
|
||||||
other - Unknown header extension, can be safely
|
other - Unknown header extension, can be safely
|
||||||
ignored
|
ignored
|
||||||
|
|
||||||
@@ -166,6 +178,36 @@ the header extension data. Each entry look like this:
|
|||||||
terminated if it has full length)
|
terminated if it has full length)
|
||||||
|
|
||||||
|
|
||||||
|
== Bitmaps extension ==
|
||||||
|
|
||||||
|
The bitmaps extension is an optional header extension. It provides the ability
|
||||||
|
to store bitmaps related to a virtual disk. For now, there is only one bitmap
|
||||||
|
type: the dirty tracking bitmap, which tracks virtual disk changes from some
|
||||||
|
point in time.
|
||||||
|
|
||||||
|
The data of the extension should be considered consistent only if the
|
||||||
|
corresponding auto-clear feature bit is set, see autoclear_features above.
|
||||||
|
|
||||||
|
The fields of the bitmaps extension are:
|
||||||
|
|
||||||
|
Byte 0 - 3: nb_bitmaps
|
||||||
|
The number of bitmaps contained in the image. Must be
|
||||||
|
greater than or equal to 1.
|
||||||
|
|
||||||
|
Note: Qemu currently only supports up to 65535 bitmaps per
|
||||||
|
image.
|
||||||
|
|
||||||
|
4 - 7: Reserved, must be zero.
|
||||||
|
|
||||||
|
8 - 15: bitmap_directory_size
|
||||||
|
Size of the bitmap directory in bytes. It is the cumulative
|
||||||
|
size of all (nb_bitmaps) bitmap headers.
|
||||||
|
|
||||||
|
16 - 23: bitmap_directory_offset
|
||||||
|
Offset into the image file at which the bitmap directory
|
||||||
|
starts. Must be aligned to a cluster boundary.
|
||||||
|
|
||||||
|
|
||||||
== Host cluster management ==
|
== Host cluster management ==
|
||||||
|
|
||||||
qcow2 manages the allocation of host clusters by maintaining a reference count
|
qcow2 manages the allocation of host clusters by maintaining a reference count
|
||||||
@@ -360,3 +402,180 @@ Snapshot table entry:
|
|||||||
|
|
||||||
variable: Padding to round up the snapshot table entry size to the
|
variable: Padding to round up the snapshot table entry size to the
|
||||||
next multiple of 8.
|
next multiple of 8.
|
||||||
|
|
||||||
|
|
||||||
|
== Bitmaps ==
|
||||||
|
|
||||||
|
As mentioned above, the bitmaps extension provides the ability to store bitmaps
|
||||||
|
related to a virtual disk. This section describes how these bitmaps are stored.
|
||||||
|
|
||||||
|
All stored bitmaps are related to the virtual disk stored in the same image, so
|
||||||
|
each bitmap size is equal to the virtual disk size.
|
||||||
|
|
||||||
|
Each bit of the bitmap is responsible for strictly defined range of the virtual
|
||||||
|
disk. For bit number bit_nr the corresponding range (in bytes) will be:
|
||||||
|
|
||||||
|
[bit_nr * bitmap_granularity .. (bit_nr + 1) * bitmap_granularity - 1]
|
||||||
|
|
||||||
|
Granularity is a property of the concrete bitmap, see below.
|
||||||
|
|
||||||
|
|
||||||
|
=== Bitmap directory ===
|
||||||
|
|
||||||
|
Each bitmap saved in the image is described in a bitmap directory entry. The
|
||||||
|
bitmap directory is a contiguous area in the image file, whose starting offset
|
||||||
|
and length are given by the header extension fields bitmap_directory_offset and
|
||||||
|
bitmap_directory_size. The entries of the bitmap directory have variable
|
||||||
|
length, depending on the lengths of the bitmap name and extra data. These
|
||||||
|
entries are also called bitmap headers.
|
||||||
|
|
||||||
|
Structure of a bitmap directory entry:
|
||||||
|
|
||||||
|
Byte 0 - 7: bitmap_table_offset
|
||||||
|
Offset into the image file at which the bitmap table
|
||||||
|
(described below) for the bitmap starts. Must be aligned to
|
||||||
|
a cluster boundary.
|
||||||
|
|
||||||
|
8 - 11: bitmap_table_size
|
||||||
|
Number of entries in the bitmap table of the bitmap.
|
||||||
|
|
||||||
|
12 - 15: flags
|
||||||
|
Bit
|
||||||
|
0: in_use
|
||||||
|
The bitmap was not saved correctly and may be
|
||||||
|
inconsistent.
|
||||||
|
|
||||||
|
1: auto
|
||||||
|
The bitmap must reflect all changes of the virtual
|
||||||
|
disk by any application that would write to this qcow2
|
||||||
|
file (including writes, snapshot switching, etc.). The
|
||||||
|
type of this bitmap must be 'dirty tracking bitmap'.
|
||||||
|
|
||||||
|
2: extra_data_compatible
|
||||||
|
This flags is meaningful when the extra data is
|
||||||
|
unknown to the software (currently any extra data is
|
||||||
|
unknown to Qemu).
|
||||||
|
If it is set, the bitmap may be used as expected, extra
|
||||||
|
data must be left as is.
|
||||||
|
If it is not set, the bitmap must not be used, but
|
||||||
|
both it and its extra data be left as is.
|
||||||
|
|
||||||
|
Bits 3 - 31 are reserved and must be 0.
|
||||||
|
|
||||||
|
16: type
|
||||||
|
This field describes the sort of the bitmap.
|
||||||
|
Values:
|
||||||
|
1: Dirty tracking bitmap
|
||||||
|
|
||||||
|
Values 0, 2 - 255 are reserved.
|
||||||
|
|
||||||
|
17: granularity_bits
|
||||||
|
Granularity bits. Valid values: 0 - 63.
|
||||||
|
|
||||||
|
Note: Qemu currently doesn't support granularity_bits
|
||||||
|
greater than 31.
|
||||||
|
|
||||||
|
Granularity is calculated as
|
||||||
|
granularity = 1 << granularity_bits
|
||||||
|
|
||||||
|
A bitmap's granularity is how many bytes of the image
|
||||||
|
accounts for one bit of the bitmap.
|
||||||
|
|
||||||
|
18 - 19: name_size
|
||||||
|
Size of the bitmap name. Must be non-zero.
|
||||||
|
|
||||||
|
Note: Qemu currently doesn't support values greater than
|
||||||
|
1023.
|
||||||
|
|
||||||
|
20 - 23: extra_data_size
|
||||||
|
Size of type-specific extra data.
|
||||||
|
|
||||||
|
For now, as no extra data is defined, extra_data_size is
|
||||||
|
reserved and should be zero. If it is non-zero the
|
||||||
|
behavior is defined by extra_data_compatible flag.
|
||||||
|
|
||||||
|
variable: extra_data
|
||||||
|
Extra data for the bitmap, occupying extra_data_size bytes.
|
||||||
|
Extra data must never contain references to clusters or in
|
||||||
|
some other way allocate additional clusters.
|
||||||
|
|
||||||
|
variable: name
|
||||||
|
The name of the bitmap (not null terminated), occupying
|
||||||
|
name_size bytes. Must be unique among all bitmap names
|
||||||
|
within the bitmaps extension.
|
||||||
|
|
||||||
|
variable: Padding to round up the bitmap directory entry size to the
|
||||||
|
next multiple of 8. All bytes of the padding must be zero.
|
||||||
|
|
||||||
|
|
||||||
|
=== Bitmap table ===
|
||||||
|
|
||||||
|
Each bitmap is stored using a one-level structure (as opposed to two-level
|
||||||
|
structures like for refcounts and guest clusters mapping) for the mapping of
|
||||||
|
bitmap data to host clusters. This structure is called the bitmap table.
|
||||||
|
|
||||||
|
Each bitmap table has a variable size (stored in the bitmap directory entry)
|
||||||
|
and may use multiple clusters, however, it must be contiguous in the image
|
||||||
|
file.
|
||||||
|
|
||||||
|
Structure of a bitmap table entry:
|
||||||
|
|
||||||
|
Bit 0: Reserved and must be zero if bits 9 - 55 are non-zero.
|
||||||
|
If bits 9 - 55 are zero:
|
||||||
|
0: Cluster should be read as all zeros.
|
||||||
|
1: Cluster should be read as all ones.
|
||||||
|
|
||||||
|
1 - 8: Reserved and must be zero.
|
||||||
|
|
||||||
|
9 - 55: Bits 9 - 55 of the host cluster offset. Must be aligned to
|
||||||
|
a cluster boundary. If the offset is 0, the cluster is
|
||||||
|
unallocated; in that case, bit 0 determines how this
|
||||||
|
cluster should be treated during reads.
|
||||||
|
|
||||||
|
56 - 63: Reserved and must be zero.
|
||||||
|
|
||||||
|
|
||||||
|
=== Bitmap data ===
|
||||||
|
|
||||||
|
As noted above, bitmap data is stored in separate clusters, described by the
|
||||||
|
bitmap table. Given an offset (in bytes) into the bitmap data, the offset into
|
||||||
|
the image file can be obtained as follows:
|
||||||
|
|
||||||
|
image_offset(bitmap_data_offset) =
|
||||||
|
bitmap_table[bitmap_data_offset / cluster_size] +
|
||||||
|
(bitmap_data_offset % cluster_size)
|
||||||
|
|
||||||
|
This offset is not defined if bits 9 - 55 of bitmap table entry are zero (see
|
||||||
|
above).
|
||||||
|
|
||||||
|
Given an offset byte_nr into the virtual disk and the bitmap's granularity, the
|
||||||
|
bit offset into the image file to the corresponding bit of the bitmap can be
|
||||||
|
calculated like this:
|
||||||
|
|
||||||
|
bit_offset(byte_nr) =
|
||||||
|
image_offset(byte_nr / granularity / 8) * 8 +
|
||||||
|
(byte_nr / granularity) % 8
|
||||||
|
|
||||||
|
If the size of the bitmap data is not a multiple of the cluster size then the
|
||||||
|
last cluster of the bitmap data contains some unused tail bits. These bits must
|
||||||
|
be zero.
|
||||||
|
|
||||||
|
|
||||||
|
=== Dirty tracking bitmaps ===
|
||||||
|
|
||||||
|
Bitmaps with 'type' field equal to one are dirty tracking bitmaps.
|
||||||
|
|
||||||
|
When the virtual disk is in use dirty tracking bitmap may be 'enabled' or
|
||||||
|
'disabled'. While the bitmap is 'enabled', all writes to the virtual disk
|
||||||
|
should be reflected in the bitmap. A set bit in the bitmap means that the
|
||||||
|
corresponding range of the virtual disk (see above) was written to while the
|
||||||
|
bitmap was 'enabled'. An unset bit means that this range was not written to.
|
||||||
|
|
||||||
|
The software doesn't have to sync the bitmap in the image file with its
|
||||||
|
representation in RAM after each write. Flag 'in_use' should be set while the
|
||||||
|
bitmap is not synced.
|
||||||
|
|
||||||
|
In the image file the 'enabled' state is reflected by the 'auto' flag. If this
|
||||||
|
flag is set, the software must consider the bitmap as 'enabled' and start
|
||||||
|
tracking virtual disk changes to this bitmap from the first write to the
|
||||||
|
virtual disk. If this flag is not set then the bitmap is disabled.
|
||||||
|
|||||||
252
docs/throttle.txt
Normal file
252
docs/throttle.txt
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
The QEMU throttling infrastructure
|
||||||
|
==================================
|
||||||
|
Copyright (C) 2016 Igalia, S.L.
|
||||||
|
Author: Alberto Garcia <berto@igalia.com>
|
||||||
|
|
||||||
|
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||||
|
later. See the COPYING file in the top-level directory.
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
QEMU includes a throttling module that can be used to set limits to
|
||||||
|
I/O operations. The code itself is generic and independent of the I/O
|
||||||
|
units, but it is currenly used to limit the number of bytes per second
|
||||||
|
and operations per second (IOPS) when performing disk I/O.
|
||||||
|
|
||||||
|
This document explains how to use the throttling code in QEMU, and how
|
||||||
|
it works internally. The implementation is in throttle.c.
|
||||||
|
|
||||||
|
|
||||||
|
Using throttling to limit disk I/O
|
||||||
|
----------------------------------
|
||||||
|
Two aspects of the disk I/O can be limited: the number of bytes per
|
||||||
|
second and the number of operations per second (IOPS). For each one of
|
||||||
|
them the user can set a global limit or separate limits for read and
|
||||||
|
write operations. This gives us a total of six different parameters.
|
||||||
|
|
||||||
|
I/O limits can be set using the throttling.* parameters of -drive, or
|
||||||
|
using the QMP 'block_set_io_throttle' command. These are the names of
|
||||||
|
the parameters for both cases:
|
||||||
|
|
||||||
|
|-----------------------+-----------------------|
|
||||||
|
| -drive | block_set_io_throttle |
|
||||||
|
|-----------------------+-----------------------|
|
||||||
|
| throttling.iops-total | iops |
|
||||||
|
| throttling.iops-read | iops_rd |
|
||||||
|
| throttling.iops-write | iops_wr |
|
||||||
|
| throttling.bps-total | bps |
|
||||||
|
| throttling.bps-read | bps_rd |
|
||||||
|
| throttling.bps-write | bps_wr |
|
||||||
|
|-----------------------+-----------------------|
|
||||||
|
|
||||||
|
It is possible to set limits for both IOPS and bps and the same time,
|
||||||
|
and for each case we can decide whether to have separate read and
|
||||||
|
write limits or not, but note that if iops-total is set then neither
|
||||||
|
iops-read nor iops-write can be set. The same applies to bps-total and
|
||||||
|
bps-read/write.
|
||||||
|
|
||||||
|
The default value of these parameters is 0, and it means 'unlimited'.
|
||||||
|
|
||||||
|
In its most basic usage, the user can add a drive to QEMU with a limit
|
||||||
|
of 100 IOPS with the following -drive line:
|
||||||
|
|
||||||
|
-drive file=hd0.qcow2,throttling.iops-total=100
|
||||||
|
|
||||||
|
We can do the same using QMP. In this case all these parameters are
|
||||||
|
mandatory, so we must set to 0 the ones that we don't want to limit:
|
||||||
|
|
||||||
|
{ "execute": "block_set_io_throttle",
|
||||||
|
"arguments": {
|
||||||
|
"device": "virtio0",
|
||||||
|
"iops": 100,
|
||||||
|
"iops_rd": 0,
|
||||||
|
"iops_wr": 0,
|
||||||
|
"bps": 0,
|
||||||
|
"bps_rd": 0,
|
||||||
|
"bps_wr": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
I/O bursts
|
||||||
|
----------
|
||||||
|
In addition to the basic limits we have just seen, QEMU allows the
|
||||||
|
user to do bursts of I/O for a configurable amount of time. A burst is
|
||||||
|
an amount of I/O that can exceed the basic limit. Bursts are useful to
|
||||||
|
allow better performance when there are peaks of activity (the OS
|
||||||
|
boots, a service needs to be restarted) while keeping the average
|
||||||
|
limits lower the rest of the time.
|
||||||
|
|
||||||
|
Two parameters control bursts: their length and the maximum amount of
|
||||||
|
I/O they allow. These two can be configured separately for each one of
|
||||||
|
the six basic parameters described in the previous section, but in
|
||||||
|
this section we'll use 'iops-total' as an example.
|
||||||
|
|
||||||
|
The I/O limit during bursts is set using 'iops-total-max', and the
|
||||||
|
maximum length (in seconds) is set with 'iops-total-max-length'. So if
|
||||||
|
we want to configure a drive with a basic limit of 100 IOPS and allow
|
||||||
|
bursts of 2000 IOPS for 60 seconds, we would do it like this (the line
|
||||||
|
is split for clarity):
|
||||||
|
|
||||||
|
-drive file=hd0.qcow2,
|
||||||
|
throttling.iops-total=100,
|
||||||
|
throttling.iops-total-max=2000,
|
||||||
|
throttling.iops-total-max-length=60
|
||||||
|
|
||||||
|
Or, with QMP:
|
||||||
|
|
||||||
|
{ "execute": "block_set_io_throttle",
|
||||||
|
"arguments": {
|
||||||
|
"device": "virtio0",
|
||||||
|
"iops": 100,
|
||||||
|
"iops_rd": 0,
|
||||||
|
"iops_wr": 0,
|
||||||
|
"bps": 0,
|
||||||
|
"bps_rd": 0,
|
||||||
|
"bps_wr": 0,
|
||||||
|
"iops_max": 2000,
|
||||||
|
"iops_max_length": 60,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
With this, the user can perform I/O on hd0.qcow2 at a rate of 2000
|
||||||
|
IOPS for 1 minute before it's throttled down to 100 IOPS.
|
||||||
|
|
||||||
|
The user will be able to do bursts again if there's a sufficiently
|
||||||
|
long period of time with unused I/O (see below for details).
|
||||||
|
|
||||||
|
The default value for 'iops-total-max' is 0 and it means that bursts
|
||||||
|
are not allowed. 'iops-total-max-length' can only be set if
|
||||||
|
'iops-total-max' is set as well, and its default value is 1 second.
|
||||||
|
|
||||||
|
Here's the complete list of parameters for configuring bursts:
|
||||||
|
|
||||||
|
|----------------------------------+-----------------------|
|
||||||
|
| -drive | block_set_io_throttle |
|
||||||
|
|----------------------------------+-----------------------|
|
||||||
|
| throttling.iops-total-max | iops_max |
|
||||||
|
| throttling.iops-total-max-length | iops_max_length |
|
||||||
|
| throttling.iops-read-max | iops_rd_max |
|
||||||
|
| throttling.iops-read-max-length | iops_rd_max_length |
|
||||||
|
| throttling.iops-write-max | iops_wr_max |
|
||||||
|
| throttling.iops-write-max-length | iops_wr_max_length |
|
||||||
|
| throttling.bps-total-max | bps_max |
|
||||||
|
| throttling.bps-total-max-length | bps_max_length |
|
||||||
|
| throttling.bps-read-max | bps_rd_max |
|
||||||
|
| throttling.bps-read-max-length | bps_rd_max_length |
|
||||||
|
| throttling.bps-write-max | bps_wr_max |
|
||||||
|
| throttling.bps-write-max-length | bps_wr_max_length |
|
||||||
|
|----------------------------------+-----------------------|
|
||||||
|
|
||||||
|
|
||||||
|
Controlling the size of I/O operations
|
||||||
|
--------------------------------------
|
||||||
|
When applying IOPS limits all I/O operations are treated equally
|
||||||
|
regardless of their size. This means that the user can take advantage
|
||||||
|
of this in order to circumvent the limits and submit one huge I/O
|
||||||
|
request instead of several smaller ones.
|
||||||
|
|
||||||
|
QEMU provides a setting called throttling.iops-size to prevent this
|
||||||
|
from happening. This setting specifies the size (in bytes) of an I/O
|
||||||
|
request for accounting purposes. Larger requests will be counted
|
||||||
|
proportionally to this size.
|
||||||
|
|
||||||
|
For example, if iops-size is set to 4096 then an 8KB request will be
|
||||||
|
counted as two, and a 6KB request will be counted as one and a
|
||||||
|
half. This only applies to requests larger than iops-size: smaller
|
||||||
|
requests will be always counted as one, no matter their size.
|
||||||
|
|
||||||
|
The default value of iops-size is 0 and it means that the size of the
|
||||||
|
requests is never taken into account when applying IOPS limits.
|
||||||
|
|
||||||
|
|
||||||
|
Applying I/O limits to groups of disks
|
||||||
|
--------------------------------------
|
||||||
|
In all the examples so far we have seen how to apply limits to the I/O
|
||||||
|
performed on individual drives, but QEMU allows grouping drives so
|
||||||
|
they all share the same limits.
|
||||||
|
|
||||||
|
The way it works is that each drive with I/O limits is assigned to a
|
||||||
|
group named using the throttling.group parameter. If this parameter is
|
||||||
|
not specified, then the device name (i.e. 'virtio0', 'ide0-hd0') will
|
||||||
|
be used as the group name.
|
||||||
|
|
||||||
|
Limits set using the throttling.* parameters discussed earlier in this
|
||||||
|
document apply to the combined I/O of all members of a group.
|
||||||
|
|
||||||
|
Consider this example:
|
||||||
|
|
||||||
|
-drive file=hd1.qcow2,throttling.iops-total=6000,throttling.group=foo
|
||||||
|
-drive file=hd2.qcow2,throttling.iops-total=6000,throttling.group=foo
|
||||||
|
-drive file=hd3.qcow2,throttling.iops-total=3000,throttling.group=bar
|
||||||
|
-drive file=hd4.qcow2,throttling.iops-total=6000,throttling.group=foo
|
||||||
|
-drive file=hd5.qcow2,throttling.iops-total=3000,throttling.group=bar
|
||||||
|
-drive file=hd6.qcow2,throttling.iops-total=5000
|
||||||
|
|
||||||
|
Here hd1, hd2 and hd4 are all members of a group named 'foo' with a
|
||||||
|
combined IOPS limit of 6000, and hd3 and hd5 are members of 'bar'. hd6
|
||||||
|
is left alone (technically it is part of a 1-member group).
|
||||||
|
|
||||||
|
Limits are applied in a round-robin fashion so if there are concurrent
|
||||||
|
I/O requests on several drives of the same group they will be
|
||||||
|
distributed evenly.
|
||||||
|
|
||||||
|
When I/O limits are applied to an existing drive using the QMP command
|
||||||
|
'block_set_io_throttle', the following things need to be taken into
|
||||||
|
account:
|
||||||
|
|
||||||
|
- I/O limits are shared within the same group, so new values will
|
||||||
|
affect all members and overwrite the previous settings. In other
|
||||||
|
words: if different limits are applied to members of the same
|
||||||
|
group, the last one wins.
|
||||||
|
|
||||||
|
- If 'group' is unset it is assumed to be the current group of that
|
||||||
|
drive. If the drive is not in a group yet, it will be added to a
|
||||||
|
group named after the device name.
|
||||||
|
|
||||||
|
- If 'group' is set then the drive will be moved to that group if
|
||||||
|
it was member of a different one. In this case the limits
|
||||||
|
specified in the parameters will be applied to the new group
|
||||||
|
only.
|
||||||
|
|
||||||
|
- I/O limits can be disabled by setting all of them to 0. In this
|
||||||
|
case the device will be removed from its group and the rest of
|
||||||
|
its members will not be affected. The 'group' parameter is
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
|
||||||
|
The Leaky Bucket algorithm
|
||||||
|
--------------------------
|
||||||
|
I/O limits in QEMU are implemented using the leaky bucket algorithm
|
||||||
|
(specifically the "Leaky bucket as a meter" variant).
|
||||||
|
|
||||||
|
This algorithm uses the analogy of a bucket that leaks water
|
||||||
|
constantly. The water that gets into the bucket represents the I/O
|
||||||
|
that has been performed, and no more I/O is allowed once the bucket is
|
||||||
|
full.
|
||||||
|
|
||||||
|
To see the way this corresponds to the throttling parameters in QEMU,
|
||||||
|
consider the following values:
|
||||||
|
|
||||||
|
iops-total=100
|
||||||
|
iops-total-max=2000
|
||||||
|
iops-total-max-length=60
|
||||||
|
|
||||||
|
- Water leaks from the bucket at a rate of 100 IOPS.
|
||||||
|
- Water can be added to the bucket at a rate of 2000 IOPS.
|
||||||
|
- The size of the bucket is 2000 x 60 = 120000
|
||||||
|
- If 'iops-total-max-length' is unset then the bucket size is 100.
|
||||||
|
|
||||||
|
The bucket is initially empty, therefore water can be added until it's
|
||||||
|
full at a rate of 2000 IOPS (the burst rate). Once the bucket is full
|
||||||
|
we can only add as much water as it leaks, therefore the I/O rate is
|
||||||
|
reduced to 100 IOPS. If we add less water than it leaks then the
|
||||||
|
bucket will start to empty, allowing for bursts again.
|
||||||
|
|
||||||
|
Note that since water is leaking from the bucket even during bursts,
|
||||||
|
it will take a bit more than 60 seconds at 2000 IOPS to fill it
|
||||||
|
up. After those 60 seconds the bucket will have leaked 60 x 100 =
|
||||||
|
6000, allowing for 3 more seconds of I/O at 2000 IOPS.
|
||||||
|
|
||||||
|
Also, due to the way the algorithm works, longer burst can be done at
|
||||||
|
a lower I/O rate, e.g. 1000 IOPS during 120 seconds.
|
||||||
@@ -172,9 +172,6 @@ source tree. It may not be as powerful as platform-specific or third-party
|
|||||||
trace backends but it is portable. This is the recommended trace backend
|
trace backends but it is portable. This is the recommended trace backend
|
||||||
unless you have specific needs for more advanced backends.
|
unless you have specific needs for more advanced backends.
|
||||||
|
|
||||||
The "simple" backend currently does not capture string arguments, it simply
|
|
||||||
records the char* pointer value instead of the string that is pointed to.
|
|
||||||
|
|
||||||
=== Ftrace ===
|
=== Ftrace ===
|
||||||
|
|
||||||
The "ftrace" backend writes trace data to ftrace marker. This effectively
|
The "ftrace" backend writes trace data to ftrace marker. This effectively
|
||||||
@@ -347,3 +344,44 @@ This will immediately call:
|
|||||||
and will generate the TCG code to call:
|
and will generate the TCG code to call:
|
||||||
|
|
||||||
void trace_foo(uint8_t a1, uint32_t a2);
|
void trace_foo(uint8_t a1, uint32_t a2);
|
||||||
|
|
||||||
|
=== "vcpu" ===
|
||||||
|
|
||||||
|
Identifies events that trace vCPU-specific information. It implicitly adds a
|
||||||
|
"CPUState*" argument, and extends the tracing print format to show the vCPU
|
||||||
|
information. If used together with the "tcg" property, it adds a second
|
||||||
|
"TCGv_env" argument that must point to the per-target global TCG register that
|
||||||
|
points to the vCPU when guest code is executed (usually the "cpu_env" variable).
|
||||||
|
|
||||||
|
The following example events:
|
||||||
|
|
||||||
|
foo(uint32_t a) "a=%x"
|
||||||
|
vcpu bar(uint32_t a) "a=%x"
|
||||||
|
tcg vcpu baz(uint32_t a) "a=%x", "a=%x"
|
||||||
|
|
||||||
|
Can be used as:
|
||||||
|
|
||||||
|
#include "trace-tcg.h"
|
||||||
|
|
||||||
|
CPUArchState *env;
|
||||||
|
TCGv_ptr cpu_env;
|
||||||
|
|
||||||
|
void some_disassembly_func(...)
|
||||||
|
{
|
||||||
|
/* trace emitted at this point */
|
||||||
|
trace_foo(0xd1);
|
||||||
|
/* trace emitted at this point */
|
||||||
|
trace_bar(ENV_GET_CPU(env), 0xd2);
|
||||||
|
/* trace emitted at this point (env) and when guest code is executed (cpu_env) */
|
||||||
|
trace_baz_tcg(ENV_GET_CPU(env), cpu_env, 0xd3);
|
||||||
|
}
|
||||||
|
|
||||||
|
If the translating vCPU has address 0xc1 and code is later executed by vCPU
|
||||||
|
0xc2, this would be an example output:
|
||||||
|
|
||||||
|
// at guest code translation
|
||||||
|
foo a=0xd1
|
||||||
|
bar cpu=0xc1 a=0xd2
|
||||||
|
baz_trans cpu=0xc1 a=0xd3
|
||||||
|
// at guest code execution
|
||||||
|
baz_exec cpu=0xc2 a=0xd3
|
||||||
|
|||||||
215
dump.c
215
dump.c
@@ -25,6 +25,7 @@
|
|||||||
#include "sysemu/cpus.h"
|
#include "sysemu/cpus.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
#include "qmp-commands.h"
|
#include "qmp-commands.h"
|
||||||
|
#include "qapi-event.h"
|
||||||
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#ifdef CONFIG_LZO
|
#ifdef CONFIG_LZO
|
||||||
@@ -82,12 +83,6 @@ static int dump_cleanup(DumpState *s)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_error(DumpState *s, const char *reason, Error **errp)
|
|
||||||
{
|
|
||||||
dump_cleanup(s);
|
|
||||||
error_setg(errp, "%s", reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fd_write_vmcore(const void *buf, size_t size, void *opaque)
|
static int fd_write_vmcore(const void *buf, size_t size, void *opaque)
|
||||||
{
|
{
|
||||||
DumpState *s = opaque;
|
DumpState *s = opaque;
|
||||||
@@ -128,7 +123,7 @@ static void write_elf64_header(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
|
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write elf header", errp);
|
error_setg(errp, "dump: failed to write elf header");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +154,7 @@ static void write_elf32_header(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
|
ret = fd_write_vmcore(&elf_header, sizeof(elf_header), s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write elf header", errp);
|
error_setg(errp, "dump: failed to write elf header");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +177,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping,
|
|||||||
|
|
||||||
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
|
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write program header table", errp);
|
error_setg(errp, "dump: failed to write program header table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +200,7 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping,
|
|||||||
|
|
||||||
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
|
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write program header table", errp);
|
error_setg(errp, "dump: failed to write program header table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +220,7 @@ static void write_elf64_note(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
|
ret = fd_write_vmcore(&phdr, sizeof(Elf64_Phdr), s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write program header table", errp);
|
error_setg(errp, "dump: failed to write program header table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +240,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
|
|||||||
id = cpu_index(cpu);
|
id = cpu_index(cpu);
|
||||||
ret = cpu_write_elf64_note(f, cpu, id, s);
|
ret = cpu_write_elf64_note(f, cpu, id, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write elf notes", errp);
|
error_setg(errp, "dump: failed to write elf notes");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,7 +248,7 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s,
|
|||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
ret = cpu_write_elf64_qemunote(f, cpu, s);
|
ret = cpu_write_elf64_qemunote(f, cpu, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write CPU status", errp);
|
error_setg(errp, "dump: failed to write CPU status");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,7 +270,7 @@ static void write_elf32_note(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
|
ret = fd_write_vmcore(&phdr, sizeof(Elf32_Phdr), s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write program header table", errp);
|
error_setg(errp, "dump: failed to write program header table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +285,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
|
|||||||
id = cpu_index(cpu);
|
id = cpu_index(cpu);
|
||||||
ret = cpu_write_elf32_note(f, cpu, id, s);
|
ret = cpu_write_elf32_note(f, cpu, id, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write elf notes", errp);
|
error_setg(errp, "dump: failed to write elf notes");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -298,7 +293,7 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s,
|
|||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
ret = cpu_write_elf32_qemunote(f, cpu, s);
|
ret = cpu_write_elf32_qemunote(f, cpu, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write CPU status", errp);
|
error_setg(errp, "dump: failed to write CPU status");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,7 +321,7 @@ static void write_elf_section(DumpState *s, int type, Error **errp)
|
|||||||
|
|
||||||
ret = fd_write_vmcore(&shdr, shdr_size, s);
|
ret = fd_write_vmcore(&shdr, shdr_size, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write section header table", errp);
|
error_setg(errp, "dump: failed to write section header table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,7 +331,9 @@ static void write_data(DumpState *s, void *buf, int length, Error **errp)
|
|||||||
|
|
||||||
ret = fd_write_vmcore(buf, length, s);
|
ret = fd_write_vmcore(buf, length, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to save memory", errp);
|
error_setg(errp, "dump: failed to save memory");
|
||||||
|
} else {
|
||||||
|
s->written_size += length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,11 +565,6 @@ static void dump_begin(DumpState *s, Error **errp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_completed(DumpState *s)
|
|
||||||
{
|
|
||||||
dump_cleanup(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_next_block(DumpState *s, GuestPhysBlock *block)
|
static int get_next_block(DumpState *s, GuestPhysBlock *block)
|
||||||
{
|
{
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -624,8 +616,6 @@ static void dump_iterate(DumpState *s, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} while (!get_next_block(s, block));
|
} while (!get_next_block(s, block));
|
||||||
|
|
||||||
dump_completed(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_vmcore(DumpState *s, Error **errp)
|
static void create_vmcore(DumpState *s, Error **errp)
|
||||||
@@ -765,7 +755,7 @@ static void create_header32(DumpState *s, Error **errp)
|
|||||||
dh->status = cpu_to_dump32(s, status);
|
dh->status = cpu_to_dump32(s, status);
|
||||||
|
|
||||||
if (write_buffer(s->fd, 0, dh, size) < 0) {
|
if (write_buffer(s->fd, 0, dh, size) < 0) {
|
||||||
dump_error(s, "dump: failed to write disk dump header", errp);
|
error_setg(errp, "dump: failed to write disk dump header");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,7 +774,7 @@ static void create_header32(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
|
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
|
||||||
block_size, kh, size) < 0) {
|
block_size, kh, size) < 0) {
|
||||||
dump_error(s, "dump: failed to write kdump sub header", errp);
|
error_setg(errp, "dump: failed to write kdump sub header");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -800,7 +790,7 @@ static void create_header32(DumpState *s, Error **errp)
|
|||||||
}
|
}
|
||||||
if (write_buffer(s->fd, offset_note, s->note_buf,
|
if (write_buffer(s->fd, offset_note, s->note_buf,
|
||||||
s->note_size) < 0) {
|
s->note_size) < 0) {
|
||||||
dump_error(s, "dump: failed to write notes", errp);
|
error_setg(errp, "dump: failed to write notes");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -865,7 +855,7 @@ static void create_header64(DumpState *s, Error **errp)
|
|||||||
dh->status = cpu_to_dump32(s, status);
|
dh->status = cpu_to_dump32(s, status);
|
||||||
|
|
||||||
if (write_buffer(s->fd, 0, dh, size) < 0) {
|
if (write_buffer(s->fd, 0, dh, size) < 0) {
|
||||||
dump_error(s, "dump: failed to write disk dump header", errp);
|
error_setg(errp, "dump: failed to write disk dump header");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -884,7 +874,7 @@ static void create_header64(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
|
if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
|
||||||
block_size, kh, size) < 0) {
|
block_size, kh, size) < 0) {
|
||||||
dump_error(s, "dump: failed to write kdump sub header", errp);
|
error_setg(errp, "dump: failed to write kdump sub header");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -901,7 +891,7 @@ static void create_header64(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
if (write_buffer(s->fd, offset_note, s->note_buf,
|
if (write_buffer(s->fd, offset_note, s->note_buf,
|
||||||
s->note_size) < 0) {
|
s->note_size) < 0) {
|
||||||
dump_error(s, "dump: failed to write notes", errp);
|
error_setg(errp, "dump: failed to write notes");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1087,7 +1077,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
|
|||||||
while (get_next_page(&block_iter, &pfn, NULL, s)) {
|
while (get_next_page(&block_iter, &pfn, NULL, s)) {
|
||||||
ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s);
|
ret = set_dump_bitmap(last_pfn, pfn, true, dump_bitmap_buf, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to set dump_bitmap", errp);
|
error_setg(errp, "dump: failed to set dump_bitmap");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1104,7 +1094,7 @@ static void write_dump_bitmap(DumpState *s, Error **errp)
|
|||||||
ret = set_dump_bitmap(last_pfn, last_pfn + bits_per_buf, false,
|
ret = set_dump_bitmap(last_pfn, last_pfn + bits_per_buf, false,
|
||||||
dump_bitmap_buf, s);
|
dump_bitmap_buf, s);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to sync dump_bitmap", errp);
|
error_setg(errp, "dump: failed to sync dump_bitmap");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1237,7 +1227,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||||||
ret = write_cache(&page_data, buf, s->dump_info.page_size, false);
|
ret = write_cache(&page_data, buf, s->dump_info.page_size, false);
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write page data (zero page)", errp);
|
error_setg(errp, "dump: failed to write page data (zero page)");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1253,7 +1243,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||||||
ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor),
|
ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor),
|
||||||
false);
|
false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write page desc", errp);
|
error_setg(errp, "dump: failed to write page desc");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1278,7 +1268,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = write_cache(&page_data, buf_out, size_out, false);
|
ret = write_cache(&page_data, buf_out, size_out, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write page data", errp);
|
error_setg(errp, "dump: failed to write page data");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_LZO
|
#ifdef CONFIG_LZO
|
||||||
@@ -1291,7 +1281,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = write_cache(&page_data, buf_out, size_out, false);
|
ret = write_cache(&page_data, buf_out, size_out, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write page data", errp);
|
error_setg(errp, "dump: failed to write page data");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1305,7 +1295,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = write_cache(&page_data, buf_out, size_out, false);
|
ret = write_cache(&page_data, buf_out, size_out, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write page data", errp);
|
error_setg(errp, "dump: failed to write page data");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1321,7 +1311,7 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||||||
ret = write_cache(&page_data, buf,
|
ret = write_cache(&page_data, buf,
|
||||||
s->dump_info.page_size, false);
|
s->dump_info.page_size, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write page data", errp);
|
error_setg(errp, "dump: failed to write page data");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1333,20 +1323,21 @@ static void write_dump_pages(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false);
|
ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write page desc", errp);
|
error_setg(errp, "dump: failed to write page desc");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s->written_size += s->dump_info.page_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = write_cache(&page_desc, NULL, 0, true);
|
ret = write_cache(&page_desc, NULL, 0, true);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to sync cache for page_desc", errp);
|
error_setg(errp, "dump: failed to sync cache for page_desc");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = write_cache(&page_data, NULL, 0, true);
|
ret = write_cache(&page_data, NULL, 0, true);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to sync cache for page_data", errp);
|
error_setg(errp, "dump: failed to sync cache for page_data");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1390,7 +1381,7 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = write_start_flat_header(s->fd);
|
ret = write_start_flat_header(s->fd);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write start flat header", errp);
|
error_setg(errp, "dump: failed to write start flat header");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1414,11 +1405,9 @@ static void create_kdump_vmcore(DumpState *s, Error **errp)
|
|||||||
|
|
||||||
ret = write_end_flat_header(s->fd);
|
ret = write_end_flat_header(s->fd);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dump_error(s, "dump: failed to write end flat header", errp);
|
error_setg(errp, "dump: failed to write end flat header");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_completed(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ram_addr_t get_start_block(DumpState *s)
|
static ram_addr_t get_start_block(DumpState *s)
|
||||||
@@ -1457,6 +1446,44 @@ static void get_max_mapnr(DumpState *s)
|
|||||||
s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end);
|
s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DumpState dump_state_global = { .status = DUMP_STATUS_NONE };
|
||||||
|
|
||||||
|
static void dump_state_prepare(DumpState *s)
|
||||||
|
{
|
||||||
|
/* zero the struct, setting status to active */
|
||||||
|
*s = (DumpState) { .status = DUMP_STATUS_ACTIVE };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dump_in_progress(void)
|
||||||
|
{
|
||||||
|
DumpState *state = &dump_state_global;
|
||||||
|
return (atomic_read(&state->status) == DUMP_STATUS_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate total size of memory to be dumped (taking filter into
|
||||||
|
* acoount.) */
|
||||||
|
static int64_t dump_calculate_size(DumpState *s)
|
||||||
|
{
|
||||||
|
GuestPhysBlock *block;
|
||||||
|
int64_t size = 0, total = 0, left = 0, right = 0;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) {
|
||||||
|
if (s->has_filter) {
|
||||||
|
/* calculate the overlapped region. */
|
||||||
|
left = MAX(s->begin, block->target_start);
|
||||||
|
right = MIN(s->begin + s->length, block->target_end);
|
||||||
|
size = right - left;
|
||||||
|
size = size > 0 ? size : 0;
|
||||||
|
} else {
|
||||||
|
/* count the whole region in */
|
||||||
|
size = (block->target_end - block->target_start);
|
||||||
|
}
|
||||||
|
total += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
static void dump_init(DumpState *s, int fd, bool has_format,
|
static void dump_init(DumpState *s, int fd, bool has_format,
|
||||||
DumpGuestMemoryFormat format, bool paging, bool has_filter,
|
DumpGuestMemoryFormat format, bool paging, bool has_filter,
|
||||||
int64_t begin, int64_t length, Error **errp)
|
int64_t begin, int64_t length, Error **errp)
|
||||||
@@ -1466,6 +1493,10 @@ static void dump_init(DumpState *s, int fd, bool has_format,
|
|||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
s->has_format = has_format;
|
||||||
|
s->format = format;
|
||||||
|
s->written_size = 0;
|
||||||
|
|
||||||
/* kdump-compressed is conflict with paging and filter */
|
/* kdump-compressed is conflict with paging and filter */
|
||||||
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
|
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
|
||||||
assert(!paging && !has_filter);
|
assert(!paging && !has_filter);
|
||||||
@@ -1496,6 +1527,10 @@ static void dump_init(DumpState *s, int fd, bool has_format,
|
|||||||
|
|
||||||
guest_phys_blocks_init(&s->guest_phys_blocks);
|
guest_phys_blocks_init(&s->guest_phys_blocks);
|
||||||
guest_phys_blocks_append(&s->guest_phys_blocks);
|
guest_phys_blocks_append(&s->guest_phys_blocks);
|
||||||
|
s->total_size = dump_calculate_size(s);
|
||||||
|
#ifdef DEBUG_DUMP_GUEST_MEMORY
|
||||||
|
fprintf(stderr, "DUMP: total memory to dump: %lu\n", s->total_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
s->start = get_start_block(s);
|
s->start = get_start_block(s);
|
||||||
if (s->start == -1) {
|
if (s->start == -1) {
|
||||||
@@ -1624,8 +1659,60 @@ cleanup:
|
|||||||
dump_cleanup(s);
|
dump_cleanup(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
/* this operation might be time consuming. */
|
||||||
int64_t begin, bool has_length,
|
static void dump_process(DumpState *s, Error **errp)
|
||||||
|
{
|
||||||
|
Error *local_err = NULL;
|
||||||
|
DumpQueryResult *result = NULL;
|
||||||
|
|
||||||
|
if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
|
||||||
|
create_kdump_vmcore(s, &local_err);
|
||||||
|
} else {
|
||||||
|
create_vmcore(s, &local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure status is written after written_size updates */
|
||||||
|
smp_wmb();
|
||||||
|
atomic_set(&s->status,
|
||||||
|
(local_err ? DUMP_STATUS_FAILED : DUMP_STATUS_COMPLETED));
|
||||||
|
|
||||||
|
/* send DUMP_COMPLETED message (unconditionally) */
|
||||||
|
result = qmp_query_dump(NULL);
|
||||||
|
/* should never fail */
|
||||||
|
assert(result);
|
||||||
|
qapi_event_send_dump_completed(result, !!local_err, (local_err ? \
|
||||||
|
error_get_pretty(local_err) : NULL),
|
||||||
|
&error_abort);
|
||||||
|
qapi_free_DumpQueryResult(result);
|
||||||
|
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
dump_cleanup(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *dump_thread(void *data)
|
||||||
|
{
|
||||||
|
Error *err = NULL;
|
||||||
|
DumpState *s = (DumpState *)data;
|
||||||
|
dump_process(s, &err);
|
||||||
|
error_free(err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DumpQueryResult *qmp_query_dump(Error **errp)
|
||||||
|
{
|
||||||
|
DumpQueryResult *result = g_new(DumpQueryResult, 1);
|
||||||
|
DumpState *state = &dump_state_global;
|
||||||
|
result->status = atomic_read(&state->status);
|
||||||
|
/* make sure we are reading status and written_size in order */
|
||||||
|
smp_rmb();
|
||||||
|
result->completed = state->written_size;
|
||||||
|
result->total = state->total_size;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qmp_dump_guest_memory(bool paging, const char *file,
|
||||||
|
bool has_detach, bool detach,
|
||||||
|
bool has_begin, int64_t begin, bool has_length,
|
||||||
int64_t length, bool has_format,
|
int64_t length, bool has_format,
|
||||||
DumpGuestMemoryFormat format, Error **errp)
|
DumpGuestMemoryFormat format, Error **errp)
|
||||||
{
|
{
|
||||||
@@ -1633,6 +1720,19 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
|||||||
int fd = -1;
|
int fd = -1;
|
||||||
DumpState *s;
|
DumpState *s;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
bool detach_p = false;
|
||||||
|
|
||||||
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||||
|
error_setg(errp, "Dump not allowed during incoming migration.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if there is a dump in background, we should wait until the dump
|
||||||
|
* finished */
|
||||||
|
if (dump_in_progress()) {
|
||||||
|
error_setg(errp, "There is a dump in process, please wait.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* kdump-compressed format need the whole memory dumped, so paging or
|
* kdump-compressed format need the whole memory dumped, so paging or
|
||||||
@@ -1652,6 +1752,9 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
|||||||
error_setg(errp, QERR_MISSING_PARAMETER, "begin");
|
error_setg(errp, QERR_MISSING_PARAMETER, "begin");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (has_detach) {
|
||||||
|
detach_p = detach;
|
||||||
|
}
|
||||||
|
|
||||||
/* check whether lzo/snappy is supported */
|
/* check whether lzo/snappy is supported */
|
||||||
#ifndef CONFIG_LZO
|
#ifndef CONFIG_LZO
|
||||||
@@ -1690,23 +1793,25 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s = g_malloc0(sizeof(DumpState));
|
s = &dump_state_global;
|
||||||
|
dump_state_prepare(s);
|
||||||
|
|
||||||
dump_init(s, fd, has_format, format, paging, has_begin,
|
dump_init(s, fd, has_format, format, paging, has_begin,
|
||||||
begin, length, &local_err);
|
begin, length, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
g_free(s);
|
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
atomic_set(&s->status, DUMP_STATUS_FAILED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
|
if (detach_p) {
|
||||||
create_kdump_vmcore(s, errp);
|
/* detached dump */
|
||||||
|
qemu_thread_create(&s->dump_thread, "dump_thread", dump_thread,
|
||||||
|
s, QEMU_THREAD_DETACHED);
|
||||||
} else {
|
} else {
|
||||||
create_vmcore(s, errp);
|
/* sync dump */
|
||||||
|
dump_process(s, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp)
|
DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp)
|
||||||
|
|||||||
48
exec.c
48
exec.c
@@ -1717,6 +1717,8 @@ ram_addr_t qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
|
|||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mr->ram_block = new_block;
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1866,9 +1868,13 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
|
|||||||
*
|
*
|
||||||
* Called within RCU critical section.
|
* Called within RCU critical section.
|
||||||
*/
|
*/
|
||||||
void *qemu_get_ram_ptr(ram_addr_t addr)
|
void *qemu_get_ram_ptr(RAMBlock *ram_block, ram_addr_t addr)
|
||||||
{
|
{
|
||||||
RAMBlock *block = qemu_get_ram_block(addr);
|
RAMBlock *block = ram_block;
|
||||||
|
|
||||||
|
if (block == NULL) {
|
||||||
|
block = qemu_get_ram_block(addr);
|
||||||
|
}
|
||||||
|
|
||||||
if (xen_enabled() && block->host == NULL) {
|
if (xen_enabled() && block->host == NULL) {
|
||||||
/* We need to check if the requested address is in the RAM
|
/* We need to check if the requested address is in the RAM
|
||||||
@@ -1889,15 +1895,18 @@ void *qemu_get_ram_ptr(ram_addr_t addr)
|
|||||||
*
|
*
|
||||||
* Called within RCU critical section.
|
* Called within RCU critical section.
|
||||||
*/
|
*/
|
||||||
static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size)
|
static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr,
|
||||||
|
hwaddr *size)
|
||||||
{
|
{
|
||||||
RAMBlock *block;
|
RAMBlock *block = ram_block;
|
||||||
ram_addr_t offset_inside_block;
|
ram_addr_t offset_inside_block;
|
||||||
if (*size == 0) {
|
if (*size == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
block = qemu_get_ram_block(addr);
|
if (block == NULL) {
|
||||||
|
block = qemu_get_ram_block(addr);
|
||||||
|
}
|
||||||
offset_inside_block = addr - block->offset;
|
offset_inside_block = addr - block->offset;
|
||||||
*size = MIN(*size, block->max_length - offset_inside_block);
|
*size = MIN(*size, block->max_length - offset_inside_block);
|
||||||
|
|
||||||
@@ -2025,13 +2034,13 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
|
|||||||
}
|
}
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
stb_p(qemu_get_ram_ptr(ram_addr), val);
|
stb_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
stw_p(qemu_get_ram_ptr(ram_addr), val);
|
stw_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
stl_p(qemu_get_ram_ptr(ram_addr), val);
|
stl_p(qemu_get_ram_ptr(NULL, ram_addr), val);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
@@ -2607,7 +2616,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
|
|||||||
} else {
|
} else {
|
||||||
addr1 += memory_region_get_ram_addr(mr);
|
addr1 += memory_region_get_ram_addr(mr);
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr(addr1);
|
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
||||||
memcpy(ptr, buf, l);
|
memcpy(ptr, buf, l);
|
||||||
invalidate_and_set_dirty(mr, addr1, l);
|
invalidate_and_set_dirty(mr, addr1, l);
|
||||||
}
|
}
|
||||||
@@ -2698,7 +2707,7 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr(mr->ram_addr + addr1);
|
ptr = qemu_get_ram_ptr(mr->ram_block, mr->ram_addr + addr1);
|
||||||
memcpy(buf, ptr, l);
|
memcpy(buf, ptr, l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2783,7 +2792,7 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
|
|||||||
} else {
|
} else {
|
||||||
addr1 += memory_region_get_ram_addr(mr);
|
addr1 += memory_region_get_ram_addr(mr);
|
||||||
/* ROM/RAM case */
|
/* ROM/RAM case */
|
||||||
ptr = qemu_get_ram_ptr(addr1);
|
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WRITE_DATA:
|
case WRITE_DATA:
|
||||||
memcpy(ptr, buf, l);
|
memcpy(ptr, buf, l);
|
||||||
@@ -2995,7 +3004,7 @@ void *address_space_map(AddressSpace *as,
|
|||||||
|
|
||||||
memory_region_ref(mr);
|
memory_region_ref(mr);
|
||||||
*plen = done;
|
*plen = done;
|
||||||
ptr = qemu_ram_ptr_length(raddr + base, plen);
|
ptr = qemu_ram_ptr_length(mr->ram_block, raddr + base, plen);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
@@ -3079,7 +3088,8 @@ static inline uint32_t address_space_ldl_internal(AddressSpace *as, hwaddr addr,
|
|||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr)
|
ptr = qemu_get_ram_ptr(mr->ram_block,
|
||||||
|
(memory_region_get_ram_addr(mr)
|
||||||
& TARGET_PAGE_MASK)
|
& TARGET_PAGE_MASK)
|
||||||
+ addr1);
|
+ addr1);
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
@@ -3174,7 +3184,8 @@ static inline uint64_t address_space_ldq_internal(AddressSpace *as, hwaddr addr,
|
|||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr)
|
ptr = qemu_get_ram_ptr(mr->ram_block,
|
||||||
|
(memory_region_get_ram_addr(mr)
|
||||||
& TARGET_PAGE_MASK)
|
& TARGET_PAGE_MASK)
|
||||||
+ addr1);
|
+ addr1);
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
@@ -3289,7 +3300,8 @@ static inline uint32_t address_space_lduw_internal(AddressSpace *as,
|
|||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
ptr = qemu_get_ram_ptr((memory_region_get_ram_addr(mr)
|
ptr = qemu_get_ram_ptr(mr->ram_block,
|
||||||
|
(memory_region_get_ram_addr(mr)
|
||||||
& TARGET_PAGE_MASK)
|
& TARGET_PAGE_MASK)
|
||||||
+ addr1);
|
+ addr1);
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
@@ -3374,7 +3386,7 @@ void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val,
|
|||||||
r = memory_region_dispatch_write(mr, addr1, val, 4, attrs);
|
r = memory_region_dispatch_write(mr, addr1, val, 4, attrs);
|
||||||
} else {
|
} else {
|
||||||
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
|
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
|
||||||
ptr = qemu_get_ram_ptr(addr1);
|
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
||||||
stl_p(ptr, val);
|
stl_p(ptr, val);
|
||||||
|
|
||||||
dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
dirty_log_mask = memory_region_get_dirty_log_mask(mr);
|
||||||
@@ -3429,7 +3441,7 @@ static inline void address_space_stl_internal(AddressSpace *as,
|
|||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
|
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
|
||||||
ptr = qemu_get_ram_ptr(addr1);
|
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
case DEVICE_LITTLE_ENDIAN:
|
case DEVICE_LITTLE_ENDIAN:
|
||||||
stl_le_p(ptr, val);
|
stl_le_p(ptr, val);
|
||||||
@@ -3539,7 +3551,7 @@ static inline void address_space_stw_internal(AddressSpace *as,
|
|||||||
} else {
|
} else {
|
||||||
/* RAM case */
|
/* RAM case */
|
||||||
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
|
addr1 += memory_region_get_ram_addr(mr) & TARGET_PAGE_MASK;
|
||||||
ptr = qemu_get_ram_ptr(addr1);
|
ptr = qemu_get_ram_ptr(mr->ram_block, addr1);
|
||||||
switch (endian) {
|
switch (endian) {
|
||||||
case DEVICE_LITTLE_ENDIAN:
|
case DEVICE_LITTLE_ENDIAN:
|
||||||
stw_le_p(ptr, val);
|
stw_le_p(ptr, val);
|
||||||
|
|||||||
@@ -12,11 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
#ifndef _FILEOP_H
|
#ifndef _FILEOP_H
|
||||||
#define _FILEOP_H
|
#define _FILEOP_H
|
||||||
#include <sys/types.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <sys/time.h>
|
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <sys/vfs.h>
|
#include <sys/vfs.h>
|
||||||
|
|
||||||
|
|||||||
@@ -784,6 +784,20 @@ STEXI
|
|||||||
@item info skeys @var{address}
|
@item info skeys @var{address}
|
||||||
@findex skeys
|
@findex skeys
|
||||||
Display the value of a storage key (s390 only)
|
Display the value of a storage key (s390 only)
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "dump",
|
||||||
|
.args_type = "",
|
||||||
|
.params = "",
|
||||||
|
.help = "Display the latest dump status",
|
||||||
|
.mhandler.cmd = hmp_info_dump,
|
||||||
|
},
|
||||||
|
|
||||||
|
STEXI
|
||||||
|
@item info dump
|
||||||
|
@findex dump
|
||||||
|
Display the latest dump status.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
STEXI
|
STEXI
|
||||||
|
|||||||
@@ -1056,10 +1056,11 @@ ETEXI
|
|||||||
|
|
||||||
{
|
{
|
||||||
.name = "dump-guest-memory",
|
.name = "dump-guest-memory",
|
||||||
.args_type = "paging:-p,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?",
|
.args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?",
|
||||||
.params = "[-p] [-z|-l|-s] filename [begin length]",
|
.params = "[-p] [-d] [-z|-l|-s] filename [begin length]",
|
||||||
.help = "dump guest memory into file 'filename'.\n\t\t\t"
|
.help = "dump guest memory into file 'filename'.\n\t\t\t"
|
||||||
"-p: do paging to get guest's memory mapping.\n\t\t\t"
|
"-p: do paging to get guest's memory mapping.\n\t\t\t"
|
||||||
|
"-d: return immediately (do not wait for completion).\n\t\t\t"
|
||||||
"-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t"
|
"-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t"
|
||||||
"-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t"
|
"-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t"
|
||||||
"-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t"
|
"-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t"
|
||||||
|
|||||||
38
hmp.c
38
hmp.c
@@ -1414,6 +1414,18 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
|
|||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
0,
|
0,
|
||||||
|
false, /* no burst length via HMP */
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
false, /* No default I/O size */
|
false, /* No default I/O size */
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
@@ -1587,8 +1599,10 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
|||||||
const char *file = qdict_get_str(qdict, "filename");
|
const char *file = qdict_get_str(qdict, "filename");
|
||||||
bool has_begin = qdict_haskey(qdict, "begin");
|
bool has_begin = qdict_haskey(qdict, "begin");
|
||||||
bool has_length = qdict_haskey(qdict, "length");
|
bool has_length = qdict_haskey(qdict, "length");
|
||||||
|
bool has_detach = qdict_haskey(qdict, "detach");
|
||||||
int64_t begin = 0;
|
int64_t begin = 0;
|
||||||
int64_t length = 0;
|
int64_t length = 0;
|
||||||
|
bool detach = false;
|
||||||
enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
|
enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF;
|
||||||
char *prot;
|
char *prot;
|
||||||
|
|
||||||
@@ -1616,11 +1630,14 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict)
|
|||||||
if (has_length) {
|
if (has_length) {
|
||||||
length = qdict_get_int(qdict, "length");
|
length = qdict_get_int(qdict, "length");
|
||||||
}
|
}
|
||||||
|
if (has_detach) {
|
||||||
|
detach = qdict_get_bool(qdict, "detach");
|
||||||
|
}
|
||||||
|
|
||||||
prot = g_strconcat("file:", file, NULL);
|
prot = g_strconcat("file:", file, NULL);
|
||||||
|
|
||||||
qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length,
|
qmp_dump_guest_memory(paging, prot, true, detach, has_begin, begin,
|
||||||
true, dump_format, &err);
|
has_length, length, true, dump_format, &err);
|
||||||
hmp_handle_error(mon, &err);
|
hmp_handle_error(mon, &err);
|
||||||
g_free(prot);
|
g_free(prot);
|
||||||
}
|
}
|
||||||
@@ -2346,3 +2363,20 @@ void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict)
|
|||||||
|
|
||||||
qapi_free_RockerOfDpaGroupList(list);
|
qapi_free_RockerOfDpaGroupList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hmp_info_dump(Monitor *mon, const QDict *qdict)
|
||||||
|
{
|
||||||
|
DumpQueryResult *result = qmp_query_dump(NULL);
|
||||||
|
|
||||||
|
assert(result && result->status < DUMP_STATUS__MAX);
|
||||||
|
monitor_printf(mon, "Status: %s\n", DumpStatus_lookup[result->status]);
|
||||||
|
|
||||||
|
if (result->status == DUMP_STATUS_ACTIVE) {
|
||||||
|
float percent = 0;
|
||||||
|
assert(result->total != 0);
|
||||||
|
percent = 100.0 * result->completed / result->total;
|
||||||
|
monitor_printf(mon, "Finished: %.2f %%\n", percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
qapi_free_DumpQueryResult(result);
|
||||||
|
}
|
||||||
|
|||||||
1
hmp.h
1
hmp.h
@@ -131,5 +131,6 @@ void hmp_rocker(Monitor *mon, const QDict *qdict);
|
|||||||
void hmp_rocker_ports(Monitor *mon, const QDict *qdict);
|
void hmp_rocker_ports(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
|
void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
|
void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict);
|
||||||
|
void hmp_info_dump(Monitor *mon, const QDict *qdict);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,9 +13,6 @@
|
|||||||
#ifndef HW_9PFS_SYNTH_H
|
#ifndef HW_9PFS_SYNTH_H
|
||||||
#define HW_9PFS_SYNTH_H 1
|
#define HW_9PFS_SYNTH_H 1
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
typedef struct V9fsSynthNode V9fsSynthNode;
|
typedef struct V9fsSynthNode V9fsSynthNode;
|
||||||
typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset,
|
typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset,
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#ifndef _QEMU_9P_H
|
#ifndef _QEMU_9P_H
|
||||||
#define _QEMU_9P_H
|
#define _QEMU_9P_H
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <sys/time.h>
|
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|||||||
@@ -1451,7 +1451,7 @@ build_header(GArray *linker, GArray *table_data,
|
|||||||
h->checksum = 0;
|
h->checksum = 0;
|
||||||
/* Checksum to be filled in by Guest linker */
|
/* Checksum to be filled in by Guest linker */
|
||||||
bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE,
|
bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE,
|
||||||
table_data->data, h, len, &h->checksum);
|
table_data, h, len, &h->checksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *acpi_data_push(GArray *table_data, unsigned size)
|
void *acpi_data_push(GArray *table_data, unsigned size)
|
||||||
|
|||||||
@@ -25,6 +25,13 @@
|
|||||||
|
|
||||||
#include "qemu/bswap.h"
|
#include "qemu/bswap.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Linker/loader is a paravirtualized interface that passes commands to guest.
|
||||||
|
* The commands can be used to request guest to
|
||||||
|
* - allocate memory chunks and initialize them from QEMU FW CFG files
|
||||||
|
* - link allocated chunks by storing pointer to one chunk into another
|
||||||
|
* - calculate ACPI checksum of part of the chunk and store into same chunk
|
||||||
|
*/
|
||||||
#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH
|
#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH
|
||||||
|
|
||||||
struct BiosLinkerLoaderEntry {
|
struct BiosLinkerLoaderEntry {
|
||||||
@@ -88,6 +95,12 @@ enum {
|
|||||||
BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
|
BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bios_linker_loader_init: allocate a new linker file blob array.
|
||||||
|
*
|
||||||
|
* After initialization, linker commands can be added, and will
|
||||||
|
* be stored in the array.
|
||||||
|
*/
|
||||||
GArray *bios_linker_loader_init(void)
|
GArray *bios_linker_loader_init(void)
|
||||||
{
|
{
|
||||||
return g_array_new(false, true /* clear */, 1);
|
return g_array_new(false, true /* clear */, 1);
|
||||||
@@ -99,6 +112,16 @@ void *bios_linker_loader_cleanup(GArray *linker)
|
|||||||
return g_array_free(linker, false);
|
return g_array_free(linker, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bios_linker_loader_alloc: ask guest to load file into guest memory.
|
||||||
|
*
|
||||||
|
* @linker: linker file blob array
|
||||||
|
* @file: file to be loaded
|
||||||
|
* @alloc_align: required minimal alignment in bytes. Must be a power of 2.
|
||||||
|
* @alloc_fseg: request allocation in FSEG zone (useful for the RSDP ACPI table)
|
||||||
|
*
|
||||||
|
* Note: this command must precede any other linker command using this file.
|
||||||
|
*/
|
||||||
void bios_linker_loader_alloc(GArray *linker,
|
void bios_linker_loader_alloc(GArray *linker,
|
||||||
const char *file,
|
const char *file,
|
||||||
uint32_t alloc_align,
|
uint32_t alloc_align,
|
||||||
@@ -106,6 +129,8 @@ void bios_linker_loader_alloc(GArray *linker,
|
|||||||
{
|
{
|
||||||
BiosLinkerLoaderEntry entry;
|
BiosLinkerLoaderEntry entry;
|
||||||
|
|
||||||
|
assert(!(alloc_align & (alloc_align - 1)));
|
||||||
|
|
||||||
memset(&entry, 0, sizeof entry);
|
memset(&entry, 0, sizeof entry);
|
||||||
strncpy(entry.alloc.file, file, sizeof entry.alloc.file - 1);
|
strncpy(entry.alloc.file, file, sizeof entry.alloc.file - 1);
|
||||||
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE);
|
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE);
|
||||||
@@ -118,23 +143,77 @@ void bios_linker_loader_alloc(GArray *linker,
|
|||||||
g_array_prepend_vals(linker, &entry, sizeof entry);
|
g_array_prepend_vals(linker, &entry, sizeof entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bios_linker_loader_add_checksum: ask guest to add checksum of file data
|
||||||
|
* into (same) file at the specified pointer.
|
||||||
|
*
|
||||||
|
* Checksum calculation simply sums -X for each byte X in the range
|
||||||
|
* using 8-bit math (i.e. ACPI checksum).
|
||||||
|
*
|
||||||
|
* @linker: linker file blob array
|
||||||
|
* @file: file that includes the checksum to be calculated
|
||||||
|
* and the data to be checksummed
|
||||||
|
* @table: @file blob contents
|
||||||
|
* @start, @size: range of data to checksum
|
||||||
|
* @checksum: location of the checksum to be patched within file blob
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* - checksum byte initial value must have been pushed into @table
|
||||||
|
* and reside at address @checksum.
|
||||||
|
* - @size bytes must have been pushed into @table and reside at address
|
||||||
|
* @start.
|
||||||
|
* - Guest calculates checksum of specified range of data, result is added to
|
||||||
|
* initial value at @checksum into copy of @file in Guest memory.
|
||||||
|
* - Range might include the checksum itself.
|
||||||
|
* - To avoid confusion, caller must always put 0x0 at @checksum.
|
||||||
|
* - @file must be loaded into Guest memory using bios_linker_loader_alloc
|
||||||
|
*/
|
||||||
void bios_linker_loader_add_checksum(GArray *linker, const char *file,
|
void bios_linker_loader_add_checksum(GArray *linker, const char *file,
|
||||||
void *table,
|
GArray *table,
|
||||||
void *start, unsigned size,
|
void *start, unsigned size,
|
||||||
uint8_t *checksum)
|
uint8_t *checksum)
|
||||||
{
|
{
|
||||||
BiosLinkerLoaderEntry entry;
|
BiosLinkerLoaderEntry entry;
|
||||||
|
ptrdiff_t checksum_offset = (gchar *)checksum - table->data;
|
||||||
|
ptrdiff_t start_offset = (gchar *)start - table->data;
|
||||||
|
|
||||||
|
assert(checksum_offset >= 0);
|
||||||
|
assert(start_offset >= 0);
|
||||||
|
assert(checksum_offset + 1 <= table->len);
|
||||||
|
assert(start_offset + size <= table->len);
|
||||||
|
assert(*checksum == 0x0);
|
||||||
|
|
||||||
memset(&entry, 0, sizeof entry);
|
memset(&entry, 0, sizeof entry);
|
||||||
strncpy(entry.cksum.file, file, sizeof entry.cksum.file - 1);
|
strncpy(entry.cksum.file, file, sizeof entry.cksum.file - 1);
|
||||||
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM);
|
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM);
|
||||||
entry.cksum.offset = cpu_to_le32(checksum - (uint8_t *)table);
|
entry.cksum.offset = cpu_to_le32(checksum_offset);
|
||||||
entry.cksum.start = cpu_to_le32((uint8_t *)start - (uint8_t *)table);
|
entry.cksum.start = cpu_to_le32(start_offset);
|
||||||
entry.cksum.length = cpu_to_le32(size);
|
entry.cksum.length = cpu_to_le32(size);
|
||||||
|
|
||||||
g_array_append_vals(linker, &entry, sizeof entry);
|
g_array_append_vals(linker, &entry, sizeof entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bios_linker_loader_add_pointer: ask guest to add address of source file
|
||||||
|
* into destination file at the specified pointer.
|
||||||
|
*
|
||||||
|
* @linker: linker file blob array
|
||||||
|
* @dest_file: destination file that must be changed
|
||||||
|
* @src_file: source file who's address must be taken
|
||||||
|
* @table: @dest_file blob contents array
|
||||||
|
* @pointer: location of the pointer to be patched within destination file blob
|
||||||
|
* @pointer_size: size of pointer to be patched, in bytes
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* - @pointer_size bytes must have been pushed into @table
|
||||||
|
* and reside at address @pointer.
|
||||||
|
* - Guest address is added to initial value at @pointer
|
||||||
|
* into copy of @dest_file in Guest memory.
|
||||||
|
* e.g. to get start of src_file in guest memory, put 0x0 there
|
||||||
|
* to get address of a field at offset 0x10 in src_file, put 0x10 there
|
||||||
|
* - Both @dest_file and @src_file must be
|
||||||
|
* loaded into Guest memory using bios_linker_loader_alloc
|
||||||
|
*/
|
||||||
void bios_linker_loader_add_pointer(GArray *linker,
|
void bios_linker_loader_add_pointer(GArray *linker,
|
||||||
const char *dest_file,
|
const char *dest_file,
|
||||||
const char *src_file,
|
const char *src_file,
|
||||||
@@ -142,7 +221,10 @@ void bios_linker_loader_add_pointer(GArray *linker,
|
|||||||
uint8_t pointer_size)
|
uint8_t pointer_size)
|
||||||
{
|
{
|
||||||
BiosLinkerLoaderEntry entry;
|
BiosLinkerLoaderEntry entry;
|
||||||
size_t offset = (gchar *)pointer - table->data;
|
ptrdiff_t offset = (gchar *)pointer - table->data;
|
||||||
|
|
||||||
|
assert(offset >= 0);
|
||||||
|
assert(offset + pointer_size <= table->len);
|
||||||
|
|
||||||
memset(&entry, 0, sizeof entry);
|
memset(&entry, 0, sizeof entry);
|
||||||
strncpy(entry.pointer.dest_file, dest_file,
|
strncpy(entry.pointer.dest_file, dest_file,
|
||||||
@@ -150,7 +232,6 @@ void bios_linker_loader_add_pointer(GArray *linker,
|
|||||||
strncpy(entry.pointer.src_file, src_file,
|
strncpy(entry.pointer.src_file, src_file,
|
||||||
sizeof entry.pointer.src_file - 1);
|
sizeof entry.pointer.src_file - 1);
|
||||||
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER);
|
entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER);
|
||||||
assert(table->len >= offset + pointer_size);
|
|
||||||
entry.pointer.offset = cpu_to_le32(offset);
|
entry.pointer.offset = cpu_to_le32(offset);
|
||||||
entry.pointer.size = pointer_size;
|
entry.pointer.size = pointer_size;
|
||||||
assert(pointer_size == 1 || pointer_size == 2 ||
|
assert(pointer_size == 1 || pointer_size == 2 ||
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#include "hw/nvram/fw_cfg.h"
|
#include "hw/nvram/fw_cfg.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
#include "qapi/opts-visitor.h"
|
#include "qapi/opts-visitor.h"
|
||||||
#include "qapi/dealloc-visitor.h"
|
|
||||||
#include "qapi-visit.h"
|
#include "qapi-visit.h"
|
||||||
#include "qapi-event.h"
|
#include "qapi-event.h"
|
||||||
|
|
||||||
@@ -297,15 +296,7 @@ void acpi_table_add(const QemuOpts *opts, Error **errp)
|
|||||||
out:
|
out:
|
||||||
g_free(blob);
|
g_free(blob);
|
||||||
g_strfreev(pathnames);
|
g_strfreev(pathnames);
|
||||||
|
qapi_free_AcpiTableOptions(hdrs);
|
||||||
if (hdrs != NULL) {
|
|
||||||
QapiDeallocVisitor *dv;
|
|
||||||
|
|
||||||
dv = qapi_dealloc_visitor_new();
|
|
||||||
visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), NULL, &hdrs,
|
|
||||||
NULL);
|
|
||||||
qapi_dealloc_visitor_cleanup(dv);
|
|
||||||
}
|
|
||||||
|
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ static void pm_powerdown_req(Notifier *n, void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
|
void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
|
||||||
bool smm_enabled, bool enable_tco,
|
bool smm_enabled,
|
||||||
qemu_irq sci_irq)
|
qemu_irq sci_irq)
|
||||||
{
|
{
|
||||||
memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
|
memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE);
|
||||||
@@ -264,10 +264,8 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
|
|||||||
|
|
||||||
pm->smm_enabled = smm_enabled;
|
pm->smm_enabled = smm_enabled;
|
||||||
|
|
||||||
pm->enable_tco = enable_tco;
|
pm->enable_tco = true;
|
||||||
if (pm->enable_tco) {
|
acpi_pm_tco_init(&pm->tco_regs, &pm->io);
|
||||||
acpi_pm_tco_init(&pm->tco_regs, &pm->io);
|
|
||||||
}
|
|
||||||
|
|
||||||
pm->irq = sci_irq;
|
pm->irq = sci_irq;
|
||||||
qemu_register_reset(pm_reset, pm);
|
qemu_register_reset(pm_reset, pm);
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ static void clipper_init(MachineState *machine)
|
|||||||
}
|
}
|
||||||
size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
|
size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys,
|
||||||
NULL, &palcode_entry, &palcode_low, &palcode_high,
|
NULL, &palcode_entry, &palcode_low, &palcode_high,
|
||||||
0, EM_ALPHA, 0);
|
0, EM_ALPHA, 0, 0);
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
error_report("could not load palcode '%s'", palcode_filename);
|
error_report("could not load palcode '%s'", palcode_filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -131,7 +131,7 @@ static void clipper_init(MachineState *machine)
|
|||||||
|
|
||||||
size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
|
size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys,
|
||||||
NULL, &kernel_entry, &kernel_low, &kernel_high,
|
NULL, &kernel_entry, &kernel_low, &kernel_high,
|
||||||
0, EM_ALPHA, 0);
|
0, EM_ALPHA, 0, 0);
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
error_report("could not load kernel '%s'", kernel_filename);
|
error_report("could not load kernel '%s'", kernel_filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
|
|||||||
|
|
||||||
if (kernel_filename) {
|
if (kernel_filename) {
|
||||||
image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
|
image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
|
||||||
NULL, big_endian, EM_ARM, 1);
|
NULL, big_endian, EM_ARM, 1, 0);
|
||||||
if (image_size < 0) {
|
if (image_size < 0) {
|
||||||
image_size = load_image_targphys(kernel_filename, 0, mem_size);
|
image_size = load_image_targphys(kernel_filename, 0, mem_size);
|
||||||
lowaddr = 0;
|
lowaddr = 0;
|
||||||
|
|||||||
@@ -182,6 +182,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
|||||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
|
||||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
||||||
INTERRUPT_ARASANSDIO));
|
INTERRUPT_ARASANSDIO));
|
||||||
|
object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->sdhci), "sd-bus",
|
||||||
|
&err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
|
static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
|
||||||
|
|||||||
@@ -73,6 +73,13 @@ static void bcm2836_realize(DeviceState *dev, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->peripherals),
|
||||||
|
"sd-bus", &err);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0,
|
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->peripherals), 0,
|
||||||
BCM2836_PERI_BASE, 1);
|
BCM2836_PERI_BASE, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -518,9 +518,34 @@ static void do_cpu_reset(void *opaque)
|
|||||||
cpu_reset(cs);
|
cpu_reset(cs);
|
||||||
if (info) {
|
if (info) {
|
||||||
if (!info->is_linux) {
|
if (!info->is_linux) {
|
||||||
|
int i;
|
||||||
/* Jump to the entry point. */
|
/* Jump to the entry point. */
|
||||||
uint64_t entry = info->entry;
|
uint64_t entry = info->entry;
|
||||||
|
|
||||||
|
switch (info->endianness) {
|
||||||
|
case ARM_ENDIANNESS_LE:
|
||||||
|
env->cp15.sctlr_el[1] &= ~SCTLR_E0E;
|
||||||
|
for (i = 1; i < 4; ++i) {
|
||||||
|
env->cp15.sctlr_el[i] &= ~SCTLR_EE;
|
||||||
|
}
|
||||||
|
env->uncached_cpsr &= ~CPSR_E;
|
||||||
|
break;
|
||||||
|
case ARM_ENDIANNESS_BE8:
|
||||||
|
env->cp15.sctlr_el[1] |= SCTLR_E0E;
|
||||||
|
for (i = 1; i < 4; ++i) {
|
||||||
|
env->cp15.sctlr_el[i] |= SCTLR_EE;
|
||||||
|
}
|
||||||
|
env->uncached_cpsr |= CPSR_E;
|
||||||
|
break;
|
||||||
|
case ARM_ENDIANNESS_BE32:
|
||||||
|
env->cp15.sctlr_el[1] |= SCTLR_B;
|
||||||
|
break;
|
||||||
|
case ARM_ENDIANNESS_UNKNOWN:
|
||||||
|
break; /* Board's decision */
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
if (!env->aarch64) {
|
if (!env->aarch64) {
|
||||||
env->thumb = info->entry & 1;
|
env->thumb = info->entry & 1;
|
||||||
entry &= 0xfffffffe;
|
entry &= 0xfffffffe;
|
||||||
@@ -638,6 +663,62 @@ static int do_arm_linux_init(Object *obj, void *opaque)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
|
||||||
|
uint64_t *lowaddr, uint64_t *highaddr,
|
||||||
|
int elf_machine)
|
||||||
|
{
|
||||||
|
bool elf_is64;
|
||||||
|
union {
|
||||||
|
Elf32_Ehdr h32;
|
||||||
|
Elf64_Ehdr h64;
|
||||||
|
} elf_header;
|
||||||
|
int data_swab = 0;
|
||||||
|
bool big_endian;
|
||||||
|
uint64_t ret = -1;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
|
||||||
|
if (err) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elf_is64) {
|
||||||
|
big_endian = elf_header.h64.e_ident[EI_DATA] == ELFDATA2MSB;
|
||||||
|
info->endianness = big_endian ? ARM_ENDIANNESS_BE8
|
||||||
|
: ARM_ENDIANNESS_LE;
|
||||||
|
} else {
|
||||||
|
big_endian = elf_header.h32.e_ident[EI_DATA] == ELFDATA2MSB;
|
||||||
|
if (big_endian) {
|
||||||
|
if (bswap32(elf_header.h32.e_flags) & EF_ARM_BE8) {
|
||||||
|
info->endianness = ARM_ENDIANNESS_BE8;
|
||||||
|
} else {
|
||||||
|
info->endianness = ARM_ENDIANNESS_BE32;
|
||||||
|
/* In BE32, the CPU has a different view of the per-byte
|
||||||
|
* address map than the rest of the system. BE32 ELF files
|
||||||
|
* are organised such that they can be programmed through
|
||||||
|
* the CPU's per-word byte-reversed view of the world. QEMU
|
||||||
|
* however loads ELF files independently of the CPU. So
|
||||||
|
* tell the ELF loader to byte reverse the data for us.
|
||||||
|
*/
|
||||||
|
data_swab = 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info->endianness = ARM_ENDIANNESS_LE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = load_elf(info->kernel_filename, NULL, NULL,
|
||||||
|
pentry, lowaddr, highaddr, big_endian, elf_machine,
|
||||||
|
1, data_swab);
|
||||||
|
if (ret <= 0) {
|
||||||
|
/* The header loaded but the image didn't */
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
||||||
{
|
{
|
||||||
CPUState *cs;
|
CPUState *cs;
|
||||||
@@ -647,7 +728,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
uint64_t elf_entry, elf_low_addr, elf_high_addr;
|
uint64_t elf_entry, elf_low_addr, elf_high_addr;
|
||||||
int elf_machine;
|
int elf_machine;
|
||||||
hwaddr entry, kernel_load_offset;
|
hwaddr entry, kernel_load_offset;
|
||||||
int big_endian;
|
|
||||||
static const ARMInsnFixup *primary_loader;
|
static const ARMInsnFixup *primary_loader;
|
||||||
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
||||||
notifier, notifier);
|
notifier, notifier);
|
||||||
@@ -733,12 +813,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
if (info->nb_cpus == 0)
|
if (info->nb_cpus == 0)
|
||||||
info->nb_cpus = 1;
|
info->nb_cpus = 1;
|
||||||
|
|
||||||
#ifdef TARGET_WORDS_BIGENDIAN
|
|
||||||
big_endian = 1;
|
|
||||||
#else
|
|
||||||
big_endian = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* We want to put the initrd far enough into RAM that when the
|
/* We want to put the initrd far enough into RAM that when the
|
||||||
* kernel is uncompressed it will not clobber the initrd. However
|
* kernel is uncompressed it will not clobber the initrd. However
|
||||||
* on boards without much RAM we must ensure that we still leave
|
* on boards without much RAM we must ensure that we still leave
|
||||||
@@ -753,9 +827,8 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
MIN(info->ram_size / 2, 128 * 1024 * 1024);
|
MIN(info->ram_size / 2, 128 * 1024 * 1024);
|
||||||
|
|
||||||
/* Assume that raw images are linux kernels, and ELF images are not. */
|
/* Assume that raw images are linux kernels, and ELF images are not. */
|
||||||
kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
|
kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr,
|
||||||
&elf_low_addr, &elf_high_addr, big_endian,
|
&elf_high_addr, elf_machine);
|
||||||
elf_machine, 1);
|
|
||||||
if (kernel_size > 0 && have_dtb(info)) {
|
if (kernel_size > 0 && have_dtb(info)) {
|
||||||
/* If there is still some room left at the base of RAM, try and put
|
/* If there is still some room left at the base of RAM, try and put
|
||||||
* the DTB there like we do for images loaded with -bios or -pflash.
|
* the DTB there like we do for images loaded with -bios or -pflash.
|
||||||
|
|||||||
@@ -113,6 +113,10 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size)
|
|||||||
static void raspi2_init(MachineState *machine)
|
static void raspi2_init(MachineState *machine)
|
||||||
{
|
{
|
||||||
RasPiState *s = g_new0(RasPiState, 1);
|
RasPiState *s = g_new0(RasPiState, 1);
|
||||||
|
DriveInfo *di;
|
||||||
|
BlockBackend *blk;
|
||||||
|
BusState *bus;
|
||||||
|
DeviceState *carddev;
|
||||||
|
|
||||||
object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
|
object_initialize(&s->soc, sizeof(s->soc), TYPE_BCM2836);
|
||||||
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc),
|
||||||
@@ -133,6 +137,18 @@ static void raspi2_init(MachineState *machine)
|
|||||||
&error_abort);
|
&error_abort);
|
||||||
object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort);
|
object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort);
|
||||||
|
|
||||||
|
/* Create and plug in the SD cards */
|
||||||
|
di = drive_get_next(IF_SD);
|
||||||
|
blk = di ? blk_by_legacy_dinfo(di) : NULL;
|
||||||
|
bus = qdev_get_child_bus(DEVICE(&s->soc), "sd-bus");
|
||||||
|
if (bus == NULL) {
|
||||||
|
error_report("No SD bus found in SOC object");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
carddev = qdev_create(bus, TYPE_SD_CARD);
|
||||||
|
qdev_prop_set_drive(carddev, "drive", blk, &error_fatal);
|
||||||
|
object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal);
|
||||||
|
|
||||||
setup_boot(machine, 2, machine->ram_size);
|
setup_boot(machine, 2, machine->ram_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -359,7 +359,8 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
|
|||||||
rsdp->checksum = 0;
|
rsdp->checksum = 0;
|
||||||
/* Checksum to be filled by Guest linker */
|
/* Checksum to be filled by Guest linker */
|
||||||
bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
|
bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
|
||||||
rsdp, rsdp, sizeof *rsdp, &rsdp->checksum);
|
rsdp_table, rsdp, sizeof *rsdp,
|
||||||
|
&rsdp->checksum);
|
||||||
|
|
||||||
return rsdp_table;
|
return rsdp_table;
|
||||||
}
|
}
|
||||||
|
|||||||
168
hw/arm/virt.c
168
hw/arm/virt.c
@@ -73,6 +73,7 @@ typedef struct VirtBoardInfo {
|
|||||||
uint32_t clock_phandle;
|
uint32_t clock_phandle;
|
||||||
uint32_t gic_phandle;
|
uint32_t gic_phandle;
|
||||||
uint32_t v2m_phandle;
|
uint32_t v2m_phandle;
|
||||||
|
bool using_psci;
|
||||||
} VirtBoardInfo;
|
} VirtBoardInfo;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -95,6 +96,23 @@ typedef struct {
|
|||||||
#define VIRT_MACHINE_CLASS(klass) \
|
#define VIRT_MACHINE_CLASS(klass) \
|
||||||
OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE)
|
OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE)
|
||||||
|
|
||||||
|
/* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means
|
||||||
|
* RAM can go up to the 256GB mark, leaving 256GB of the physical
|
||||||
|
* address space unallocated and free for future use between 256G and 512G.
|
||||||
|
* If we need to provide more RAM to VMs in the future then we need to:
|
||||||
|
* * allocate a second bank of RAM starting at 2TB and working up
|
||||||
|
* * fix the DT and ACPI table generation code in QEMU to correctly
|
||||||
|
* report two split lumps of RAM to the guest
|
||||||
|
* * fix KVM in the host kernel to allow guests with >40 bit address spaces
|
||||||
|
* (We don't want to fill all the way up to 512GB with RAM because
|
||||||
|
* we might want it for non-RAM purposes later. Conversely it seems
|
||||||
|
* reasonable to assume that anybody configuring a VM with a quarter
|
||||||
|
* of a terabyte of RAM will be doing it on a host with more than a
|
||||||
|
* terabyte of physical address space.)
|
||||||
|
*/
|
||||||
|
#define RAMLIMIT_GB 255
|
||||||
|
#define RAMLIMIT_BYTES (RAMLIMIT_GB * 1024ULL * 1024 * 1024)
|
||||||
|
|
||||||
/* Addresses and sizes of our components.
|
/* Addresses and sizes of our components.
|
||||||
* 0..128MB is space for a flash device so we can run bootrom code such as UEFI.
|
* 0..128MB is space for a flash device so we can run bootrom code such as UEFI.
|
||||||
* 128MB..256MB is used for miscellaneous device I/O.
|
* 128MB..256MB is used for miscellaneous device I/O.
|
||||||
@@ -127,10 +145,11 @@ static const MemMapEntry a15memmap[] = {
|
|||||||
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
||||||
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
||||||
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
||||||
|
[VIRT_SECURE_MEM] = { 0x0e000000, 0x01000000 },
|
||||||
[VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
|
[VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 },
|
||||||
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
|
[VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 },
|
||||||
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
|
[VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 },
|
||||||
[VIRT_MEM] = { 0x40000000, 30ULL * 1024 * 1024 * 1024 },
|
[VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES },
|
||||||
/* Second PCIe window, 512GB wide at the 512GB boundary */
|
/* Second PCIe window, 512GB wide at the 512GB boundary */
|
||||||
[VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL },
|
[VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL },
|
||||||
};
|
};
|
||||||
@@ -230,6 +249,10 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi)
|
|||||||
void *fdt = vbi->fdt;
|
void *fdt = vbi->fdt;
|
||||||
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
|
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
|
||||||
|
|
||||||
|
if (!vbi->using_psci) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qemu_fdt_add_subnode(fdt, "/psci");
|
qemu_fdt_add_subnode(fdt, "/psci");
|
||||||
if (armcpu->psci_version == 2) {
|
if (armcpu->psci_version == 2) {
|
||||||
const char comp[] = "arm,psci-0.2\0arm,psci";
|
const char comp[] = "arm,psci-0.2\0arm,psci";
|
||||||
@@ -341,7 +364,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi)
|
|||||||
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible",
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible",
|
||||||
armcpu->dtb_compatible);
|
armcpu->dtb_compatible);
|
||||||
|
|
||||||
if (vbi->smp_cpus > 1) {
|
if (vbi->using_psci && vbi->smp_cpus > 1) {
|
||||||
qemu_fdt_setprop_string(vbi->fdt, nodename,
|
qemu_fdt_setprop_string(vbi->fdt, nodename,
|
||||||
"enable-method", "psci");
|
"enable-method", "psci");
|
||||||
}
|
}
|
||||||
@@ -678,13 +701,15 @@ static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void create_one_flash(const char *name, hwaddr flashbase,
|
static void create_one_flash(const char *name, hwaddr flashbase,
|
||||||
hwaddr flashsize)
|
hwaddr flashsize, const char *file,
|
||||||
|
MemoryRegion *sysmem)
|
||||||
{
|
{
|
||||||
/* Create and map a single flash device. We use the same
|
/* Create and map a single flash device. We use the same
|
||||||
* parameters as the flash devices on the Versatile Express board.
|
* parameters as the flash devices on the Versatile Express board.
|
||||||
*/
|
*/
|
||||||
DriveInfo *dinfo = drive_get_next(IF_PFLASH);
|
DriveInfo *dinfo = drive_get_next(IF_PFLASH);
|
||||||
DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
|
DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
|
||||||
|
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||||
const uint64_t sectorlength = 256 * 1024;
|
const uint64_t sectorlength = 256 * 1024;
|
||||||
|
|
||||||
if (dinfo) {
|
if (dinfo) {
|
||||||
@@ -704,19 +729,10 @@ static void create_one_flash(const char *name, hwaddr flashbase,
|
|||||||
qdev_prop_set_string(dev, "name", name);
|
qdev_prop_set_string(dev, "name", name);
|
||||||
qdev_init_nofail(dev);
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, flashbase);
|
memory_region_add_subregion(sysmem, flashbase,
|
||||||
}
|
sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0));
|
||||||
|
|
||||||
static void create_flash(const VirtBoardInfo *vbi)
|
if (file) {
|
||||||
{
|
|
||||||
/* Create two flash devices to fill the VIRT_FLASH space in the memmap.
|
|
||||||
* Any file passed via -bios goes in the first of these.
|
|
||||||
*/
|
|
||||||
hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2;
|
|
||||||
hwaddr flashbase = vbi->memmap[VIRT_FLASH].base;
|
|
||||||
char *nodename;
|
|
||||||
|
|
||||||
if (bios_name) {
|
|
||||||
char *fn;
|
char *fn;
|
||||||
int image_size;
|
int image_size;
|
||||||
|
|
||||||
@@ -726,30 +742,73 @@ static void create_flash(const VirtBoardInfo *vbi)
|
|||||||
"but you cannot use both options at once");
|
"but you cannot use both options at once");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, file);
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
error_report("Could not find ROM image '%s'", bios_name);
|
error_report("Could not find ROM image '%s'", file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
image_size = load_image_targphys(fn, flashbase, flashsize);
|
image_size = load_image_mr(fn, sysbus_mmio_get_region(sbd, 0));
|
||||||
g_free(fn);
|
g_free(fn);
|
||||||
if (image_size < 0) {
|
if (image_size < 0) {
|
||||||
error_report("Could not load ROM image '%s'", bios_name);
|
error_report("Could not load ROM image '%s'", file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
create_one_flash("virt.flash0", flashbase, flashsize);
|
static void create_flash(const VirtBoardInfo *vbi,
|
||||||
create_one_flash("virt.flash1", flashbase + flashsize, flashsize);
|
MemoryRegion *sysmem,
|
||||||
|
MemoryRegion *secure_sysmem)
|
||||||
|
{
|
||||||
|
/* Create two flash devices to fill the VIRT_FLASH space in the memmap.
|
||||||
|
* Any file passed via -bios goes in the first of these.
|
||||||
|
* sysmem is the system memory space. secure_sysmem is the secure view
|
||||||
|
* of the system, and the first flash device should be made visible only
|
||||||
|
* there. The second flash device is visible to both secure and nonsecure.
|
||||||
|
* If sysmem == secure_sysmem this means there is no separate Secure
|
||||||
|
* address space and both flash devices are generally visible.
|
||||||
|
*/
|
||||||
|
hwaddr flashsize = vbi->memmap[VIRT_FLASH].size / 2;
|
||||||
|
hwaddr flashbase = vbi->memmap[VIRT_FLASH].base;
|
||||||
|
char *nodename;
|
||||||
|
|
||||||
nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
|
create_one_flash("virt.flash0", flashbase, flashsize,
|
||||||
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
bios_name, secure_sysmem);
|
||||||
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
|
create_one_flash("virt.flash1", flashbase + flashsize, flashsize,
|
||||||
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
NULL, sysmem);
|
||||||
2, flashbase, 2, flashsize,
|
|
||||||
2, flashbase + flashsize, 2, flashsize);
|
if (sysmem == secure_sysmem) {
|
||||||
qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
|
/* Report both flash devices as a single node in the DT */
|
||||||
g_free(nodename);
|
nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
|
||||||
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
|
||||||
|
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
||||||
|
2, flashbase, 2, flashsize,
|
||||||
|
2, flashbase + flashsize, 2, flashsize);
|
||||||
|
qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
|
||||||
|
g_free(nodename);
|
||||||
|
} else {
|
||||||
|
/* Report the devices as separate nodes so we can mark one as
|
||||||
|
* only visible to the secure world.
|
||||||
|
*/
|
||||||
|
nodename = g_strdup_printf("/secflash@%" PRIx64, flashbase);
|
||||||
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
|
||||||
|
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
||||||
|
2, flashbase, 2, flashsize);
|
||||||
|
qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled");
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay");
|
||||||
|
g_free(nodename);
|
||||||
|
|
||||||
|
nodename = g_strdup_printf("/flash@%" PRIx64, flashbase);
|
||||||
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "compatible", "cfi-flash");
|
||||||
|
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
||||||
|
2, flashbase + flashsize, 2, flashsize);
|
||||||
|
qemu_fdt_setprop_cell(vbi->fdt, nodename, "bank-width", 4);
|
||||||
|
g_free(nodename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as)
|
static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as)
|
||||||
@@ -960,6 +1019,27 @@ static void create_platform_bus(VirtBoardInfo *vbi, qemu_irq *pic)
|
|||||||
sysbus_mmio_get_region(s, 0));
|
sysbus_mmio_get_region(s, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void create_secure_ram(VirtBoardInfo *vbi, MemoryRegion *secure_sysmem)
|
||||||
|
{
|
||||||
|
MemoryRegion *secram = g_new(MemoryRegion, 1);
|
||||||
|
char *nodename;
|
||||||
|
hwaddr base = vbi->memmap[VIRT_SECURE_MEM].base;
|
||||||
|
hwaddr size = vbi->memmap[VIRT_SECURE_MEM].size;
|
||||||
|
|
||||||
|
memory_region_init_ram(secram, NULL, "virt.secure-ram", size, &error_fatal);
|
||||||
|
vmstate_register_ram_global(secram);
|
||||||
|
memory_region_add_subregion(secure_sysmem, base, secram);
|
||||||
|
|
||||||
|
nodename = g_strdup_printf("/secram@%" PRIx64, base);
|
||||||
|
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "device_type", "memory");
|
||||||
|
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", 2, base, 2, size);
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "status", "disabled");
|
||||||
|
qemu_fdt_setprop_string(vbi->fdt, nodename, "secure-status", "okay");
|
||||||
|
|
||||||
|
g_free(nodename);
|
||||||
|
}
|
||||||
|
|
||||||
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
|
static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size)
|
||||||
{
|
{
|
||||||
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
|
const VirtBoardInfo *board = (const VirtBoardInfo *)binfo;
|
||||||
@@ -1020,6 +1100,7 @@ static void machvirt_init(MachineState *machine)
|
|||||||
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
|
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
|
||||||
VirtGuestInfo *guest_info = &guest_info_state->info;
|
VirtGuestInfo *guest_info = &guest_info_state->info;
|
||||||
char **cpustr;
|
char **cpustr;
|
||||||
|
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
||||||
|
|
||||||
if (!cpu_model) {
|
if (!cpu_model) {
|
||||||
cpu_model = "cortex-a15";
|
cpu_model = "cortex-a15";
|
||||||
@@ -1047,6 +1128,15 @@ static void machvirt_init(MachineState *machine)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we have an EL3 boot ROM then the assumption is that it will
|
||||||
|
* implement PSCI itself, so disable QEMU's internal implementation
|
||||||
|
* so it doesn't get in the way. Instead of starting secondary
|
||||||
|
* CPUs in PSCI powerdown state we will start them all running and
|
||||||
|
* let the boot ROM sort them out.
|
||||||
|
* The usual case is that we do use QEMU's PSCI implementation.
|
||||||
|
*/
|
||||||
|
vbi->using_psci = !(vms->secure && firmware_loaded);
|
||||||
|
|
||||||
/* The maximum number of CPUs depends on the GIC version, or on how
|
/* The maximum number of CPUs depends on the GIC version, or on how
|
||||||
* many redistributors we can fit into the memory map.
|
* many redistributors we can fit into the memory map.
|
||||||
*/
|
*/
|
||||||
@@ -1066,7 +1156,7 @@ static void machvirt_init(MachineState *machine)
|
|||||||
vbi->smp_cpus = smp_cpus;
|
vbi->smp_cpus = smp_cpus;
|
||||||
|
|
||||||
if (machine->ram_size > vbi->memmap[VIRT_MEM].size) {
|
if (machine->ram_size > vbi->memmap[VIRT_MEM].size) {
|
||||||
error_report("mach-virt: cannot model more than 30GB RAM");
|
error_report("mach-virt: cannot model more than %dGB RAM", RAMLIMIT_GB);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1114,12 +1204,15 @@ static void machvirt_init(MachineState *machine)
|
|||||||
object_property_set_bool(cpuobj, false, "has_el3", NULL);
|
object_property_set_bool(cpuobj, false, "has_el3", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC, "psci-conduit",
|
if (vbi->using_psci) {
|
||||||
NULL);
|
object_property_set_int(cpuobj, QEMU_PSCI_CONDUIT_HVC,
|
||||||
|
"psci-conduit", NULL);
|
||||||
|
|
||||||
/* Secondary CPUs start in PSCI powered-down state */
|
/* Secondary CPUs start in PSCI powered-down state */
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
object_property_set_bool(cpuobj, true, "start-powered-off", NULL);
|
object_property_set_bool(cpuobj, true,
|
||||||
|
"start-powered-off", NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
|
if (object_property_find(cpuobj, "reset-cbar", NULL)) {
|
||||||
@@ -1145,13 +1238,14 @@ static void machvirt_init(MachineState *machine)
|
|||||||
machine->ram_size);
|
machine->ram_size);
|
||||||
memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram);
|
memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram);
|
||||||
|
|
||||||
create_flash(vbi);
|
create_flash(vbi, sysmem, secure_sysmem ? secure_sysmem : sysmem);
|
||||||
|
|
||||||
create_gic(vbi, pic, gic_version, vms->secure);
|
create_gic(vbi, pic, gic_version, vms->secure);
|
||||||
|
|
||||||
create_uart(vbi, pic, VIRT_UART, sysmem);
|
create_uart(vbi, pic, VIRT_UART, sysmem);
|
||||||
|
|
||||||
if (vms->secure) {
|
if (vms->secure) {
|
||||||
|
create_secure_ram(vbi, secure_sysmem);
|
||||||
create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem);
|
create_uart(vbi, pic, VIRT_SECURE_UART, secure_sysmem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1187,7 +1281,7 @@ static void machvirt_init(MachineState *machine)
|
|||||||
vbi->bootinfo.board_id = -1;
|
vbi->bootinfo.board_id = -1;
|
||||||
vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base;
|
vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base;
|
||||||
vbi->bootinfo.get_dtb = machvirt_dtb;
|
vbi->bootinfo.get_dtb = machvirt_dtb;
|
||||||
vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
vbi->bootinfo.firmware_loaded = firmware_loaded;
|
||||||
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo);
|
arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -34,7 +34,6 @@
|
|||||||
typedef signed char GUSchar;
|
typedef signed char GUSchar;
|
||||||
typedef signed short GUSsample;
|
typedef signed short GUSsample;
|
||||||
#else
|
#else
|
||||||
#include <stdint.h>
|
|
||||||
typedef int8_t GUSchar;
|
typedef int8_t GUSchar;
|
||||||
typedef uint8_t GUSbyte;
|
typedef uint8_t GUSbyte;
|
||||||
typedef uint16_t GUSword;
|
typedef uint16_t GUSword;
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#include "hw/virtio/virtio-access.h"
|
#include "hw/virtio/virtio-access.h"
|
||||||
#include "hw/virtio/dataplane/vring.h"
|
|
||||||
#include "hw/virtio/dataplane/vring-accessors.h"
|
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "hw/virtio/virtio-blk.h"
|
#include "hw/virtio/virtio-blk.h"
|
||||||
#include "virtio-blk.h"
|
#include "virtio-blk.h"
|
||||||
@@ -28,7 +26,6 @@
|
|||||||
#include "qom/object_interfaces.h"
|
#include "qom/object_interfaces.h"
|
||||||
|
|
||||||
struct VirtIOBlockDataPlane {
|
struct VirtIOBlockDataPlane {
|
||||||
bool started;
|
|
||||||
bool starting;
|
bool starting;
|
||||||
bool stopping;
|
bool stopping;
|
||||||
bool disabled;
|
bool disabled;
|
||||||
@@ -36,7 +33,7 @@ struct VirtIOBlockDataPlane {
|
|||||||
VirtIOBlkConf *conf;
|
VirtIOBlkConf *conf;
|
||||||
|
|
||||||
VirtIODevice *vdev;
|
VirtIODevice *vdev;
|
||||||
Vring vring; /* virtqueue vring */
|
VirtQueue *vq; /* virtqueue vring */
|
||||||
EventNotifier *guest_notifier; /* irq */
|
EventNotifier *guest_notifier; /* irq */
|
||||||
QEMUBH *bh; /* bh for guest notification */
|
QEMUBH *bh; /* bh for guest notification */
|
||||||
|
|
||||||
@@ -49,93 +46,26 @@ struct VirtIOBlockDataPlane {
|
|||||||
*/
|
*/
|
||||||
IOThread *iothread;
|
IOThread *iothread;
|
||||||
AioContext *ctx;
|
AioContext *ctx;
|
||||||
EventNotifier host_notifier; /* doorbell */
|
|
||||||
|
|
||||||
/* Operation blocker on BDS */
|
/* Operation blocker on BDS */
|
||||||
Error *blocker;
|
Error *blocker;
|
||||||
void (*saved_complete_request)(struct VirtIOBlockReq *req,
|
|
||||||
unsigned char status);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Raise an interrupt to signal guest, if necessary */
|
/* Raise an interrupt to signal guest, if necessary */
|
||||||
static void notify_guest(VirtIOBlockDataPlane *s)
|
void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s)
|
||||||
{
|
{
|
||||||
if (!vring_should_notify(s->vdev, &s->vring)) {
|
qemu_bh_schedule(s->bh);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event_notifier_set(s->guest_notifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void notify_guest_bh(void *opaque)
|
static void notify_guest_bh(void *opaque)
|
||||||
{
|
{
|
||||||
VirtIOBlockDataPlane *s = opaque;
|
VirtIOBlockDataPlane *s = opaque;
|
||||||
|
|
||||||
notify_guest(s);
|
if (!virtio_should_notify(s->vdev, s->vq)) {
|
||||||
}
|
return;
|
||||||
|
|
||||||
static void complete_request_vring(VirtIOBlockReq *req, unsigned char status)
|
|
||||||
{
|
|
||||||
VirtIOBlockDataPlane *s = req->dev->dataplane;
|
|
||||||
stb_p(&req->in->status, status);
|
|
||||||
|
|
||||||
vring_push(s->vdev, &req->dev->dataplane->vring, &req->elem, req->in_len);
|
|
||||||
|
|
||||||
/* Suppress notification to guest by BH and its scheduled
|
|
||||||
* flag because requests are completed as a batch after io
|
|
||||||
* plug & unplug is introduced, and the BH can still be
|
|
||||||
* executed in dataplane aio context even after it is
|
|
||||||
* stopped, so needn't worry about notification loss with BH.
|
|
||||||
*/
|
|
||||||
qemu_bh_schedule(s->bh);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_notify(EventNotifier *e)
|
|
||||||
{
|
|
||||||
VirtIOBlockDataPlane *s = container_of(e, VirtIOBlockDataPlane,
|
|
||||||
host_notifier);
|
|
||||||
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
|
|
||||||
|
|
||||||
event_notifier_test_and_clear(&s->host_notifier);
|
|
||||||
blk_io_plug(s->conf->conf.blk);
|
|
||||||
for (;;) {
|
|
||||||
MultiReqBuffer mrb = {};
|
|
||||||
|
|
||||||
/* Disable guest->host notifies to avoid unnecessary vmexits */
|
|
||||||
vring_disable_notification(s->vdev, &s->vring);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
VirtIOBlockReq *req = vring_pop(s->vdev, &s->vring,
|
|
||||||
sizeof(VirtIOBlockReq));
|
|
||||||
|
|
||||||
if (req == NULL) {
|
|
||||||
break; /* no more requests */
|
|
||||||
}
|
|
||||||
|
|
||||||
virtio_blk_init_request(vblk, req);
|
|
||||||
trace_virtio_blk_data_plane_process_request(s, req->elem.out_num,
|
|
||||||
req->elem.in_num,
|
|
||||||
req->elem.index);
|
|
||||||
|
|
||||||
virtio_blk_handle_request(req, &mrb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mrb.num_reqs) {
|
|
||||||
virtio_blk_submit_multireq(s->conf->conf.blk, &mrb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (likely(!vring_more_avail(s->vdev, &s->vring))) { /* vring emptied */
|
|
||||||
/* Re-enable guest->host notifies and stop processing the vring.
|
|
||||||
* But if the guest has snuck in more descriptors, keep processing.
|
|
||||||
*/
|
|
||||||
if (vring_enable_notification(s->vdev, &s->vring)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else { /* fatal error */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
blk_io_unplug(s->conf->conf.blk);
|
|
||||||
|
event_notifier_set(s->guest_notifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s)
|
static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s)
|
||||||
@@ -260,23 +190,14 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
|||||||
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
|
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
|
||||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||||
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
|
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
|
||||||
VirtQueue *vq;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (s->started || s->disabled) {
|
if (vblk->dataplane_started || s->starting) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->starting) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->starting = true;
|
s->starting = true;
|
||||||
|
s->vq = virtio_get_queue(s->vdev, 0);
|
||||||
vq = virtio_get_queue(s->vdev, 0);
|
|
||||||
if (!vring_setup(&s->vring, s->vdev, 0)) {
|
|
||||||
goto fail_vring;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set up guest notifier (irq) */
|
/* Set up guest notifier (irq) */
|
||||||
r = k->set_guest_notifiers(qbus->parent, 1, true);
|
r = k->set_guest_notifiers(qbus->parent, 1, true);
|
||||||
@@ -285,7 +206,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
|||||||
"ensure -enable-kvm is set\n", r);
|
"ensure -enable-kvm is set\n", r);
|
||||||
goto fail_guest_notifiers;
|
goto fail_guest_notifiers;
|
||||||
}
|
}
|
||||||
s->guest_notifier = virtio_queue_get_guest_notifier(vq);
|
s->guest_notifier = virtio_queue_get_guest_notifier(s->vq);
|
||||||
|
|
||||||
/* Set up virtqueue notify */
|
/* Set up virtqueue notify */
|
||||||
r = k->set_host_notifier(qbus->parent, 0, true);
|
r = k->set_host_notifier(qbus->parent, 0, true);
|
||||||
@@ -293,34 +214,28 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
|||||||
fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
|
fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
|
||||||
goto fail_host_notifier;
|
goto fail_host_notifier;
|
||||||
}
|
}
|
||||||
s->host_notifier = *virtio_queue_get_host_notifier(vq);
|
|
||||||
|
|
||||||
s->saved_complete_request = vblk->complete_request;
|
|
||||||
vblk->complete_request = complete_request_vring;
|
|
||||||
|
|
||||||
s->starting = false;
|
s->starting = false;
|
||||||
s->started = true;
|
vblk->dataplane_started = true;
|
||||||
trace_virtio_blk_data_plane_start(s);
|
trace_virtio_blk_data_plane_start(s);
|
||||||
|
|
||||||
blk_set_aio_context(s->conf->conf.blk, s->ctx);
|
blk_set_aio_context(s->conf->conf.blk, s->ctx);
|
||||||
|
|
||||||
/* Kick right away to begin processing requests already in vring */
|
/* Kick right away to begin processing requests already in vring */
|
||||||
event_notifier_set(virtio_queue_get_host_notifier(vq));
|
event_notifier_set(virtio_queue_get_host_notifier(s->vq));
|
||||||
|
|
||||||
/* Get this show started by hooking up our callbacks */
|
/* Get this show started by hooking up our callbacks */
|
||||||
aio_context_acquire(s->ctx);
|
aio_context_acquire(s->ctx);
|
||||||
aio_set_event_notifier(s->ctx, &s->host_notifier, true,
|
virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, true, true);
|
||||||
handle_notify);
|
|
||||||
aio_context_release(s->ctx);
|
aio_context_release(s->ctx);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail_host_notifier:
|
fail_host_notifier:
|
||||||
k->set_guest_notifiers(qbus->parent, 1, false);
|
k->set_guest_notifiers(qbus->parent, 1, false);
|
||||||
fail_guest_notifiers:
|
fail_guest_notifiers:
|
||||||
vring_teardown(&s->vring, s->vdev, 0);
|
|
||||||
s->disabled = true;
|
s->disabled = true;
|
||||||
fail_vring:
|
|
||||||
s->starting = false;
|
s->starting = false;
|
||||||
|
vblk->dataplane_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Context: QEMU global mutex held */
|
/* Context: QEMU global mutex held */
|
||||||
@@ -330,39 +245,34 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
|
|||||||
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
||||||
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
|
VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
|
||||||
|
|
||||||
|
if (!vblk->dataplane_started || s->stopping) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Better luck next time. */
|
/* Better luck next time. */
|
||||||
if (s->disabled) {
|
if (s->disabled) {
|
||||||
s->disabled = false;
|
s->disabled = false;
|
||||||
return;
|
vblk->dataplane_started = false;
|
||||||
}
|
|
||||||
if (!s->started || s->stopping) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s->stopping = true;
|
s->stopping = true;
|
||||||
vblk->complete_request = s->saved_complete_request;
|
|
||||||
trace_virtio_blk_data_plane_stop(s);
|
trace_virtio_blk_data_plane_stop(s);
|
||||||
|
|
||||||
aio_context_acquire(s->ctx);
|
aio_context_acquire(s->ctx);
|
||||||
|
|
||||||
/* Stop notifications for new requests from guest */
|
/* Stop notifications for new requests from guest */
|
||||||
aio_set_event_notifier(s->ctx, &s->host_notifier, true, NULL);
|
virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, false, false);
|
||||||
|
|
||||||
/* Drain and switch bs back to the QEMU main loop */
|
/* Drain and switch bs back to the QEMU main loop */
|
||||||
blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
|
blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
|
||||||
|
|
||||||
aio_context_release(s->ctx);
|
aio_context_release(s->ctx);
|
||||||
|
|
||||||
/* Sync vring state back to virtqueue so that non-dataplane request
|
|
||||||
* processing can continue when we disable the host notifier below.
|
|
||||||
*/
|
|
||||||
vring_teardown(&s->vring, s->vdev, 0);
|
|
||||||
|
|
||||||
k->set_host_notifier(qbus->parent, 0, false);
|
k->set_host_notifier(qbus->parent, 0, false);
|
||||||
|
|
||||||
/* Clean up guest notifier (irq) */
|
/* Clean up guest notifier (irq) */
|
||||||
k->set_guest_notifiers(qbus->parent, 1, false);
|
k->set_guest_notifiers(qbus->parent, 1, false);
|
||||||
|
|
||||||
s->started = false;
|
vblk->dataplane_started = false;
|
||||||
s->stopping = false;
|
s->stopping = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,5 +26,6 @@ void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);
|
|||||||
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s);
|
void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s);
|
||||||
void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s);
|
void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s);
|
||||||
void virtio_blk_data_plane_drain(VirtIOBlockDataPlane *s);
|
void virtio_blk_data_plane_drain(VirtIOBlockDataPlane *s);
|
||||||
|
void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s);
|
||||||
|
|
||||||
#endif /* HW_DATAPLANE_VIRTIO_BLK_H */
|
#endif /* HW_DATAPLANE_VIRTIO_BLK_H */
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#include "sysemu/blockdev.h"
|
#include "sysemu/blockdev.h"
|
||||||
#include "hw/virtio/virtio-blk.h"
|
#include "hw/virtio/virtio-blk.h"
|
||||||
#include "dataplane/virtio-blk.h"
|
#include "dataplane/virtio-blk.h"
|
||||||
#include "migration/migration.h"
|
|
||||||
#include "block/scsi.h"
|
#include "block/scsi.h"
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
# include <scsi/sg.h>
|
# include <scsi/sg.h>
|
||||||
@@ -45,8 +44,7 @@ void virtio_blk_free_request(VirtIOBlockReq *req)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtio_blk_complete_request(VirtIOBlockReq *req,
|
static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
|
||||||
unsigned char status)
|
|
||||||
{
|
{
|
||||||
VirtIOBlock *s = req->dev;
|
VirtIOBlock *s = req->dev;
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
VirtIODevice *vdev = VIRTIO_DEVICE(s);
|
||||||
@@ -55,12 +53,11 @@ static void virtio_blk_complete_request(VirtIOBlockReq *req,
|
|||||||
|
|
||||||
stb_p(&req->in->status, status);
|
stb_p(&req->in->status, status);
|
||||||
virtqueue_push(s->vq, &req->elem, req->in_len);
|
virtqueue_push(s->vq, &req->elem, req->in_len);
|
||||||
virtio_notify(vdev, s->vq);
|
if (s->dataplane) {
|
||||||
}
|
virtio_blk_data_plane_notify(s->dataplane);
|
||||||
|
} else {
|
||||||
static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status)
|
virtio_notify(vdev, s->vq);
|
||||||
{
|
}
|
||||||
req->dev->complete_request(req, status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
|
static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
|
||||||
@@ -589,7 +586,7 @@ static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
|||||||
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
|
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
|
||||||
* dataplane here instead of waiting for .set_status().
|
* dataplane here instead of waiting for .set_status().
|
||||||
*/
|
*/
|
||||||
if (s->dataplane) {
|
if (s->dataplane && !s->dataplane_started) {
|
||||||
virtio_blk_data_plane_start(s->dataplane);
|
virtio_blk_data_plane_start(s->dataplane);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -852,36 +849,6 @@ static const BlockDevOps virtio_block_ops = {
|
|||||||
.resize_cb = virtio_blk_resize,
|
.resize_cb = virtio_blk_resize,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Disable dataplane thread during live migration since it does not
|
|
||||||
* update the dirty memory bitmap yet.
|
|
||||||
*/
|
|
||||||
static void virtio_blk_migration_state_changed(Notifier *notifier, void *data)
|
|
||||||
{
|
|
||||||
VirtIOBlock *s = container_of(notifier, VirtIOBlock,
|
|
||||||
migration_state_notifier);
|
|
||||||
MigrationState *mig = data;
|
|
||||||
Error *err = NULL;
|
|
||||||
|
|
||||||
if (migration_in_setup(mig)) {
|
|
||||||
if (!s->dataplane) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
virtio_blk_data_plane_destroy(s->dataplane);
|
|
||||||
s->dataplane = NULL;
|
|
||||||
} else if (migration_has_finished(mig) ||
|
|
||||||
migration_has_failed(mig)) {
|
|
||||||
if (s->dataplane) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
blk_drain_all(); /* complete in-flight non-dataplane requests */
|
|
||||||
virtio_blk_data_plane_create(VIRTIO_DEVICE(s), &s->conf,
|
|
||||||
&s->dataplane, &err);
|
|
||||||
if (err != NULL) {
|
|
||||||
error_report_err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
@@ -916,15 +883,12 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
|||||||
s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
|
s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
|
||||||
|
|
||||||
s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output);
|
s->vq = virtio_add_queue(vdev, 128, virtio_blk_handle_output);
|
||||||
s->complete_request = virtio_blk_complete_request;
|
|
||||||
virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
|
virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
virtio_cleanup(vdev);
|
virtio_cleanup(vdev);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s->migration_state_notifier.notify = virtio_blk_migration_state_changed;
|
|
||||||
add_migration_state_change_notifier(&s->migration_state_notifier);
|
|
||||||
|
|
||||||
s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
|
s->change = qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s);
|
||||||
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
|
register_savevm(dev, "virtio-blk", virtio_blk_id++, 2,
|
||||||
@@ -940,7 +904,6 @@ static void virtio_blk_device_unrealize(DeviceState *dev, Error **errp)
|
|||||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
VirtIOBlock *s = VIRTIO_BLK(dev);
|
VirtIOBlock *s = VIRTIO_BLK(dev);
|
||||||
|
|
||||||
remove_migration_state_change_notifier(&s->migration_state_notifier);
|
|
||||||
virtio_blk_data_plane_destroy(s->dataplane);
|
virtio_blk_data_plane_destroy(s->dataplane);
|
||||||
s->dataplane = NULL;
|
s->dataplane = NULL;
|
||||||
qemu_del_vm_change_state_handler(s->change);
|
qemu_del_vm_change_state_handler(s->change);
|
||||||
|
|||||||
@@ -842,14 +842,16 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
|
|||||||
{
|
{
|
||||||
ChannelState *s = (ChannelState *)dev;
|
ChannelState *s = (ChannelState *)dev;
|
||||||
int qcode, keycode;
|
int qcode, keycode;
|
||||||
|
InputKeyEvent *key;
|
||||||
|
|
||||||
assert(evt->type == INPUT_EVENT_KIND_KEY);
|
assert(evt->type == INPUT_EVENT_KIND_KEY);
|
||||||
qcode = qemu_input_key_value_to_qcode(evt->u.key->key);
|
key = evt->u.key;
|
||||||
|
qcode = qemu_input_key_value_to_qcode(key->key);
|
||||||
trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
|
trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
|
||||||
evt->u.key->down);
|
key->down);
|
||||||
|
|
||||||
if (qcode == Q_KEY_CODE_CAPS_LOCK) {
|
if (qcode == Q_KEY_CODE_CAPS_LOCK) {
|
||||||
if (evt->u.key->down) {
|
if (key->down) {
|
||||||
s->caps_lock_mode ^= 1;
|
s->caps_lock_mode ^= 1;
|
||||||
if (s->caps_lock_mode == 2) {
|
if (s->caps_lock_mode == 2) {
|
||||||
return; /* Drop second press */
|
return; /* Drop second press */
|
||||||
@@ -863,7 +865,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (qcode == Q_KEY_CODE_NUM_LOCK) {
|
if (qcode == Q_KEY_CODE_NUM_LOCK) {
|
||||||
if (evt->u.key->down) {
|
if (key->down) {
|
||||||
s->num_lock_mode ^= 1;
|
s->num_lock_mode ^= 1;
|
||||||
if (s->num_lock_mode == 2) {
|
if (s->num_lock_mode == 2) {
|
||||||
return; /* Drop second press */
|
return; /* Drop second press */
|
||||||
@@ -877,7 +879,7 @@ static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
|
|||||||
}
|
}
|
||||||
|
|
||||||
keycode = qcode_to_keycode[qcode];
|
keycode = qcode_to_keycode[qcode];
|
||||||
if (!evt->u.key->down) {
|
if (!key->down) {
|
||||||
keycode |= 0x80;
|
keycode |= 0x80;
|
||||||
}
|
}
|
||||||
trace_escc_sunkbd_event_out(keycode);
|
trace_escc_sunkbd_event_out(keycode);
|
||||||
|
|||||||
@@ -147,6 +147,28 @@ int load_image_targphys(const char *filename,
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int load_image_mr(const char *filename, MemoryRegion *mr)
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (!memory_access_is_direct(mr, false)) {
|
||||||
|
/* Can only load an image into RAM or ROM */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = get_image_size(filename);
|
||||||
|
|
||||||
|
if (size > memory_region_size(mr)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (size > 0) {
|
||||||
|
if (rom_add_file_mr(filename, mr, -1) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size,
|
void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size,
|
||||||
const char *source)
|
const char *source)
|
||||||
{
|
{
|
||||||
@@ -332,10 +354,66 @@ const char *load_elf_strerror(int error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
uint8_t e_ident_local[EI_NIDENT];
|
||||||
|
uint8_t *e_ident;
|
||||||
|
size_t hdr_size, off;
|
||||||
|
bool is64l;
|
||||||
|
|
||||||
|
if (!hdr) {
|
||||||
|
hdr = e_ident_local;
|
||||||
|
}
|
||||||
|
e_ident = hdr;
|
||||||
|
|
||||||
|
fd = open(filename, O_RDONLY | O_BINARY);
|
||||||
|
if (fd < 0) {
|
||||||
|
error_setg_errno(errp, errno, "Failed to open file: %s", filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (read(fd, hdr, EI_NIDENT) != EI_NIDENT) {
|
||||||
|
error_setg_errno(errp, errno, "Failed to read file: %s", filename);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (e_ident[0] != ELFMAG0 ||
|
||||||
|
e_ident[1] != ELFMAG1 ||
|
||||||
|
e_ident[2] != ELFMAG2 ||
|
||||||
|
e_ident[3] != ELFMAG3) {
|
||||||
|
error_setg(errp, "Bad ELF magic");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
is64l = e_ident[EI_CLASS] == ELFCLASS64;
|
||||||
|
hdr_size = is64l ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr);
|
||||||
|
if (is64) {
|
||||||
|
*is64 = is64l;
|
||||||
|
}
|
||||||
|
|
||||||
|
off = EI_NIDENT;
|
||||||
|
while (hdr != e_ident_local && off < hdr_size) {
|
||||||
|
size_t br = read(fd, hdr + off, hdr_size - off);
|
||||||
|
switch (br) {
|
||||||
|
case 0:
|
||||||
|
error_setg(errp, "File too short: %s", filename);
|
||||||
|
goto fail;
|
||||||
|
case -1:
|
||||||
|
error_setg_errno(errp, errno, "Failed to read file: %s",
|
||||||
|
filename);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
off += br;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
/* return < 0 if error, otherwise the number of bytes loaded in memory */
|
/* return < 0 if error, otherwise the number of bytes loaded in memory */
|
||||||
int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
||||||
void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
|
void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
|
||||||
uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
|
uint64_t *highaddr, int big_endian, int elf_machine,
|
||||||
|
int clear_lsb, int data_swab)
|
||||||
{
|
{
|
||||||
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
|
int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
|
||||||
uint8_t e_ident[EI_NIDENT];
|
uint8_t e_ident[EI_NIDENT];
|
||||||
@@ -374,10 +452,12 @@ int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
|
|||||||
lseek(fd, 0, SEEK_SET);
|
lseek(fd, 0, SEEK_SET);
|
||||||
if (e_ident[EI_CLASS] == ELFCLASS64) {
|
if (e_ident[EI_CLASS] == ELFCLASS64) {
|
||||||
ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
|
ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
|
||||||
pentry, lowaddr, highaddr, elf_machine, clear_lsb);
|
pentry, lowaddr, highaddr, elf_machine, clear_lsb,
|
||||||
|
data_swab);
|
||||||
} else {
|
} else {
|
||||||
ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
|
ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
|
||||||
pentry, lowaddr, highaddr, elf_machine, clear_lsb);
|
pentry, lowaddr, highaddr, elf_machine, clear_lsb,
|
||||||
|
data_swab);
|
||||||
}
|
}
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@@ -751,7 +831,7 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name)
|
|||||||
|
|
||||||
int rom_add_file(const char *file, const char *fw_dir,
|
int rom_add_file(const char *file, const char *fw_dir,
|
||||||
hwaddr addr, int32_t bootindex,
|
hwaddr addr, int32_t bootindex,
|
||||||
bool option_rom)
|
bool option_rom, MemoryRegion *mr)
|
||||||
{
|
{
|
||||||
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
||||||
Rom *rom;
|
Rom *rom;
|
||||||
@@ -818,7 +898,12 @@ int rom_add_file(const char *file, const char *fw_dir,
|
|||||||
|
|
||||||
fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize);
|
fw_cfg_add_file(fw_cfg, fw_file_name, data, rom->romsize);
|
||||||
} else {
|
} else {
|
||||||
snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
|
if (mr) {
|
||||||
|
rom->mr = mr;
|
||||||
|
snprintf(devpath, sizeof(devpath), "/rom@%s", file);
|
||||||
|
} else {
|
||||||
|
snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add_boot_device_path(bootindex, NULL, devpath);
|
add_boot_device_path(bootindex, NULL, devpath);
|
||||||
@@ -892,12 +977,12 @@ int rom_add_elf_program(const char *name, void *data, size_t datasize,
|
|||||||
|
|
||||||
int rom_add_vga(const char *file)
|
int rom_add_vga(const char *file)
|
||||||
{
|
{
|
||||||
return rom_add_file(file, "vgaroms", 0, -1, true);
|
return rom_add_file(file, "vgaroms", 0, -1, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int rom_add_option(const char *file, int32_t bootindex)
|
int rom_add_option(const char *file, int32_t bootindex)
|
||||||
{
|
{
|
||||||
return rom_add_file(file, "genroms", 0, bootindex, true);
|
return rom_add_file(file, "genroms", 0, bootindex, true, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rom_reset(void *unused)
|
static void rom_reset(void *unused)
|
||||||
|
|||||||
@@ -312,6 +312,21 @@ static bool machine_get_suppress_vmdesc(Object *obj, Error **errp)
|
|||||||
return ms->suppress_vmdesc;
|
return ms->suppress_vmdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void machine_set_enforce_config_section(Object *obj, bool value,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
MachineState *ms = MACHINE(obj);
|
||||||
|
|
||||||
|
ms->enforce_config_section = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool machine_get_enforce_config_section(Object *obj, Error **errp)
|
||||||
|
{
|
||||||
|
MachineState *ms = MACHINE(obj);
|
||||||
|
|
||||||
|
return ms->enforce_config_section;
|
||||||
|
}
|
||||||
|
|
||||||
static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque)
|
static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque)
|
||||||
{
|
{
|
||||||
error_report("Option '-device %s' cannot be handled by this machine",
|
error_report("Option '-device %s' cannot be handled by this machine",
|
||||||
@@ -467,6 +482,12 @@ static void machine_initfn(Object *obj)
|
|||||||
object_property_set_description(obj, "suppress-vmdesc",
|
object_property_set_description(obj, "suppress-vmdesc",
|
||||||
"Set on to disable self-describing migration",
|
"Set on to disable self-describing migration",
|
||||||
NULL);
|
NULL);
|
||||||
|
object_property_add_bool(obj, "enforce-config-section",
|
||||||
|
machine_get_enforce_config_section,
|
||||||
|
machine_set_enforce_config_section, NULL);
|
||||||
|
object_property_set_description(obj, "enforce-config-section",
|
||||||
|
"Set on to enforce configuration section migration",
|
||||||
|
NULL);
|
||||||
|
|
||||||
/* Register notifier when init is done for sysbus sanity checks */
|
/* Register notifier when init is done for sysbus sanity checks */
|
||||||
ms->sysbus_notifier.notify = machine_init_notify;
|
ms->sysbus_notifier.notify = machine_init_notify;
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp)
|
|||||||
/* Memory map (addresses are offsets from PERIPHBASE):
|
/* Memory map (addresses are offsets from PERIPHBASE):
|
||||||
* 0x0000-0x0fff -- reserved
|
* 0x0000-0x0fff -- reserved
|
||||||
* 0x1000-0x1fff -- GIC Distributor
|
* 0x1000-0x1fff -- GIC Distributor
|
||||||
* 0x2000-0x2fff -- GIC CPU interface
|
* 0x2000-0x3fff -- GIC CPU interface
|
||||||
* 0x4000-0x4fff -- GIC virtual interface control (not modelled)
|
* 0x4000-0x4fff -- GIC virtual interface control (not modelled)
|
||||||
* 0x5000-0x5fff -- GIC virtual interface control (not modelled)
|
* 0x5000-0x5fff -- GIC virtual interface control (not modelled)
|
||||||
* 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
|
* 0x6000-0x7fff -- GIC virtual CPU interface (not modelled)
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ void cris_load_image(CRISCPU *cpu, struct cris_load_info *li)
|
|||||||
/* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis
|
/* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis
|
||||||
devboard SDK. */
|
devboard SDK. */
|
||||||
image_size = load_elf(li->image_filename, translate_kernel_address, NULL,
|
image_size = load_elf(li->image_filename, translate_kernel_address, NULL,
|
||||||
&entry, NULL, &high, 0, EM_CRIS, 0);
|
&entry, NULL, &high, 0, EM_CRIS, 0, 0);
|
||||||
li->entry = entry;
|
li->entry = entry;
|
||||||
if (image_size < 0) {
|
if (image_size < 0) {
|
||||||
/* Takes a kimage from the axis devboard SDK. */
|
/* Takes a kimage from the axis devboard SDK. */
|
||||||
|
|||||||
@@ -276,14 +276,14 @@ static bool blit_region_is_unsafe(struct CirrusVGAState *s,
|
|||||||
+ ((int64_t)s->cirrus_blt_height-1) * pitch;
|
+ ((int64_t)s->cirrus_blt_height-1) * pitch;
|
||||||
int32_t max = addr
|
int32_t max = addr
|
||||||
+ s->cirrus_blt_width;
|
+ s->cirrus_blt_width;
|
||||||
if (min < 0 || max >= s->vga.vram_size) {
|
if (min < 0 || max > s->vga.vram_size) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int64_t max = addr
|
int64_t max = addr
|
||||||
+ ((int64_t)s->cirrus_blt_height-1) * pitch
|
+ ((int64_t)s->cirrus_blt_height-1) * pitch
|
||||||
+ s->cirrus_blt_width;
|
+ s->cirrus_blt_width;
|
||||||
if (max >= s->vga.vram_size) {
|
if (max > s->vga.vram_size) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1156,7 +1156,9 @@ static void qxl_soft_reset(PCIQXLDevice *d)
|
|||||||
trace_qxl_soft_reset(d->id);
|
trace_qxl_soft_reset(d->id);
|
||||||
qxl_check_state(d);
|
qxl_check_state(d);
|
||||||
qxl_clear_guest_bug(d);
|
qxl_clear_guest_bug(d);
|
||||||
|
qemu_mutex_lock(&d->async_lock);
|
||||||
d->current_async = QXL_UNDEFINED_IO;
|
d->current_async = QXL_UNDEFINED_IO;
|
||||||
|
qemu_mutex_unlock(&d->async_lock);
|
||||||
|
|
||||||
if (d->id == 0) {
|
if (d->id == 0) {
|
||||||
qxl_enter_vga_mode(d);
|
qxl_enter_vga_mode(d);
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
#define HW_VGA_INT_H 1
|
#define HW_VGA_INT_H 1
|
||||||
|
|
||||||
#include <hw/hw.h>
|
#include <hw/hw.h>
|
||||||
#include "qapi/error.h"
|
|
||||||
#include "exec/memory.h"
|
#include "exec/memory.h"
|
||||||
|
|
||||||
#define ST01_V_RETRACE 0x08
|
#define ST01_V_RETRACE 0x08
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ typedef struct PL061State {
|
|||||||
qemu_irq irq;
|
qemu_irq irq;
|
||||||
qemu_irq out[8];
|
qemu_irq out[8];
|
||||||
const unsigned char *id;
|
const unsigned char *id;
|
||||||
|
uint32_t rsvd_start; /* reserved area: [rsvd_start, 0xfcc] */
|
||||||
} PL061State;
|
} PL061State;
|
||||||
|
|
||||||
static const VMStateDescription vmstate_pl061 = {
|
static const VMStateDescription vmstate_pl061 = {
|
||||||
@@ -152,12 +153,15 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
|
|||||||
{
|
{
|
||||||
PL061State *s = (PL061State *)opaque;
|
PL061State *s = (PL061State *)opaque;
|
||||||
|
|
||||||
if (offset >= 0xfd0 && offset < 0x1000) {
|
|
||||||
return s->id[(offset - 0xfd0) >> 2];
|
|
||||||
}
|
|
||||||
if (offset < 0x400) {
|
if (offset < 0x400) {
|
||||||
return s->data & (offset >> 2);
|
return s->data & (offset >> 2);
|
||||||
}
|
}
|
||||||
|
if (offset >= s->rsvd_start && offset <= 0xfcc) {
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (offset >= 0xfd0 && offset < 0x1000) {
|
||||||
|
return s->id[(offset - 0xfd0) >> 2];
|
||||||
|
}
|
||||||
switch (offset) {
|
switch (offset) {
|
||||||
case 0x400: /* Direction */
|
case 0x400: /* Direction */
|
||||||
return s->dir;
|
return s->dir;
|
||||||
@@ -198,10 +202,12 @@ static uint64_t pl061_read(void *opaque, hwaddr offset,
|
|||||||
case 0x528: /* Analog mode select */
|
case 0x528: /* Analog mode select */
|
||||||
return s->amsel;
|
return s->amsel;
|
||||||
default:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
break;
|
||||||
"pl061_read: Bad offset %x\n", (int)offset);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
err_out:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"pl061_read: Bad offset %x\n", (int)offset);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pl061_write(void *opaque, hwaddr offset,
|
static void pl061_write(void *opaque, hwaddr offset,
|
||||||
@@ -216,6 +222,9 @@ static void pl061_write(void *opaque, hwaddr offset,
|
|||||||
pl061_update(s);
|
pl061_update(s);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (offset >= s->rsvd_start) {
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
switch (offset) {
|
switch (offset) {
|
||||||
case 0x400: /* Direction */
|
case 0x400: /* Direction */
|
||||||
s->dir = value & 0xff;
|
s->dir = value & 0xff;
|
||||||
@@ -274,10 +283,13 @@ static void pl061_write(void *opaque, hwaddr offset,
|
|||||||
s->amsel = value & 0xff;
|
s->amsel = value & 0xff;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
goto err_out;
|
||||||
"pl061_write: Bad offset %x\n", (int)offset);
|
|
||||||
}
|
}
|
||||||
pl061_update(s);
|
pl061_update(s);
|
||||||
|
return;
|
||||||
|
err_out:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"pl061_write: Bad offset %x\n", (int)offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pl061_reset(DeviceState *dev)
|
static void pl061_reset(DeviceState *dev)
|
||||||
@@ -347,6 +359,7 @@ static void pl061_luminary_init(Object *obj)
|
|||||||
PL061State *s = PL061(obj);
|
PL061State *s = PL061(obj);
|
||||||
|
|
||||||
s->id = pl061_id_luminary;
|
s->id = pl061_id_luminary;
|
||||||
|
s->rsvd_start = 0x52c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pl061_init(Object *obj)
|
static void pl061_init(Object *obj)
|
||||||
@@ -354,6 +367,7 @@ static void pl061_init(Object *obj)
|
|||||||
PL061State *s = PL061(obj);
|
PL061State *s = PL061(obj);
|
||||||
|
|
||||||
s->id = pl061_id;
|
s->id = pl061_id;
|
||||||
|
s->rsvd_start = 0x424;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pl061_class_init(ObjectClass *klass, void *data)
|
static void pl061_class_init(ObjectClass *klass, void *data)
|
||||||
|
|||||||
@@ -2532,7 +2532,8 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
|
|||||||
rsdp->checksum = 0;
|
rsdp->checksum = 0;
|
||||||
/* Checksum to be filled by Guest linker */
|
/* Checksum to be filled by Guest linker */
|
||||||
bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
|
bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE,
|
||||||
rsdp, rsdp, sizeof *rsdp, &rsdp->checksum);
|
rsdp_table, rsdp, sizeof *rsdp,
|
||||||
|
&rsdp->checksum);
|
||||||
|
|
||||||
return rsdp_table;
|
return rsdp_table;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,7 +196,8 @@ int load_multiboot(FWCfgState *fw_cfg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
||||||
&elf_low, &elf_high, 0, I386_ELF_MACHINE, 0);
|
&elf_low, &elf_high, 0, I386_ELF_MACHINE,
|
||||||
|
0, 0);
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
fprintf(stderr, "Error while loading elf kernel\n");
|
fprintf(stderr, "Error while loading elf kernel\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
176
hw/i386/pc_q35.c
176
hw/i386/pc_q35.c
@@ -81,11 +81,9 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
* If it doesn't, we need to split it in chunks below and above 4G.
|
* If it doesn't, we need to split it in chunks below and above 4G.
|
||||||
* In any case, try to make sure that guest addresses aligned at
|
* In any case, try to make sure that guest addresses aligned at
|
||||||
* 1G boundaries get mapped to host addresses aligned at 1G boundaries.
|
* 1G boundaries get mapped to host addresses aligned at 1G boundaries.
|
||||||
* For old machine types, use whatever split we used historically to avoid
|
|
||||||
* breaking migration.
|
|
||||||
*/
|
*/
|
||||||
if (machine->ram_size >= 0xb0000000) {
|
if (machine->ram_size >= 0xb0000000) {
|
||||||
lowmem = pcmc->gigabyte_align ? 0x80000000 : 0xb0000000;
|
lowmem = 0x80000000;
|
||||||
} else {
|
} else {
|
||||||
lowmem = 0xb0000000;
|
lowmem = 0xb0000000;
|
||||||
}
|
}
|
||||||
@@ -116,10 +114,6 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pc_cpus_init(pcms);
|
pc_cpus_init(pcms);
|
||||||
if (!pcmc->has_acpi_build) {
|
|
||||||
/* only machine types 1.7 & older need this */
|
|
||||||
pc_acpi_init("q35-acpi-dsdt.aml");
|
|
||||||
}
|
|
||||||
|
|
||||||
kvmclock_create();
|
kvmclock_create();
|
||||||
|
|
||||||
@@ -225,7 +219,7 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
(pcms->vmport != ON_OFF_AUTO_ON), 0xff0104);
|
(pcms->vmport != ON_OFF_AUTO_ON), 0xff0104);
|
||||||
|
|
||||||
/* connect pm stuff to lpc */
|
/* connect pm stuff to lpc */
|
||||||
ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pcms), !mc->no_tco);
|
ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pcms));
|
||||||
|
|
||||||
/* ahci and SATA device, for q35 1 ahci controller is built-in */
|
/* ahci and SATA device, for q35 1 ahci controller is built-in */
|
||||||
ahci = pci_create_simple_multifunction(host_bus,
|
ahci = pci_create_simple_multifunction(host_bus,
|
||||||
@@ -259,62 +253,6 @@ static void pc_q35_init(MachineState *machine)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Looking for a pc_compat_2_4() function? It doesn't exist.
|
|
||||||
* pc_compat_*() functions that run on machine-init time and
|
|
||||||
* change global QEMU state are deprecated. Please don't create
|
|
||||||
* one, and implement any pc-*-2.4 (and newer) compat code in
|
|
||||||
* HW_COMPAT_*, PC_COMPAT_*, or * pc_*_machine_options().
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void pc_compat_2_3(MachineState *machine)
|
|
||||||
{
|
|
||||||
PCMachineState *pcms = PC_MACHINE(machine);
|
|
||||||
savevm_skip_section_footers();
|
|
||||||
if (kvm_enabled()) {
|
|
||||||
pcms->smm = ON_OFF_AUTO_OFF;
|
|
||||||
}
|
|
||||||
global_state_set_optional();
|
|
||||||
savevm_skip_configuration();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pc_compat_2_2(MachineState *machine)
|
|
||||||
{
|
|
||||||
pc_compat_2_3(machine);
|
|
||||||
machine->suppress_vmdesc = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pc_compat_2_1(MachineState *machine)
|
|
||||||
{
|
|
||||||
pc_compat_2_2(machine);
|
|
||||||
x86_cpu_change_kvm_default("svm", NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pc_compat_2_0(MachineState *machine)
|
|
||||||
{
|
|
||||||
pc_compat_2_1(machine);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pc_compat_1_7(MachineState *machine)
|
|
||||||
{
|
|
||||||
pc_compat_2_0(machine);
|
|
||||||
x86_cpu_change_kvm_default("x2apic", NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pc_compat_1_6(MachineState *machine)
|
|
||||||
{
|
|
||||||
pc_compat_1_7(machine);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pc_compat_1_5(MachineState *machine)
|
|
||||||
{
|
|
||||||
pc_compat_1_6(machine);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pc_compat_1_4(MachineState *machine)
|
|
||||||
{
|
|
||||||
pc_compat_1_5(machine);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \
|
#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \
|
||||||
static void pc_init_##suffix(MachineState *machine) \
|
static void pc_init_##suffix(MachineState *machine) \
|
||||||
{ \
|
{ \
|
||||||
@@ -336,7 +274,6 @@ static void pc_q35_machine_options(MachineClass *m)
|
|||||||
m->default_machine_opts = "firmware=bios-256k.bin";
|
m->default_machine_opts = "firmware=bios-256k.bin";
|
||||||
m->default_display = "std";
|
m->default_display = "std";
|
||||||
m->no_floppy = 1;
|
m->no_floppy = 1;
|
||||||
m->no_tco = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pc_q35_2_6_machine_options(MachineClass *m)
|
static void pc_q35_2_6_machine_options(MachineClass *m)
|
||||||
@@ -371,112 +308,3 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
|
|||||||
|
|
||||||
DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL,
|
DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL,
|
||||||
pc_q35_2_4_machine_options);
|
pc_q35_2_4_machine_options);
|
||||||
|
|
||||||
|
|
||||||
static void pc_q35_2_3_machine_options(MachineClass *m)
|
|
||||||
{
|
|
||||||
pc_q35_2_4_machine_options(m);
|
|
||||||
m->hw_version = "2.3.0";
|
|
||||||
m->no_floppy = 0;
|
|
||||||
m->no_tco = 1;
|
|
||||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3,
|
|
||||||
pc_q35_2_3_machine_options);
|
|
||||||
|
|
||||||
|
|
||||||
static void pc_q35_2_2_machine_options(MachineClass *m)
|
|
||||||
{
|
|
||||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
|
||||||
pc_q35_2_3_machine_options(m);
|
|
||||||
m->hw_version = "2.2.0";
|
|
||||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
|
|
||||||
pcmc->rsdp_in_ram = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
|
|
||||||
pc_q35_2_2_machine_options);
|
|
||||||
|
|
||||||
|
|
||||||
static void pc_q35_2_1_machine_options(MachineClass *m)
|
|
||||||
{
|
|
||||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
|
||||||
pc_q35_2_2_machine_options(m);
|
|
||||||
m->hw_version = "2.1.0";
|
|
||||||
m->default_display = NULL;
|
|
||||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
|
|
||||||
pcmc->smbios_uuid_encoded = false;
|
|
||||||
pcmc->enforce_aligned_dimm = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
|
|
||||||
pc_q35_2_1_machine_options);
|
|
||||||
|
|
||||||
|
|
||||||
static void pc_q35_2_0_machine_options(MachineClass *m)
|
|
||||||
{
|
|
||||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
|
||||||
pc_q35_2_1_machine_options(m);
|
|
||||||
m->hw_version = "2.0.0";
|
|
||||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
|
|
||||||
pcmc->has_reserved_memory = false;
|
|
||||||
pcmc->smbios_legacy_mode = true;
|
|
||||||
pcmc->acpi_data_size = 0x10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
|
|
||||||
pc_q35_2_0_machine_options);
|
|
||||||
|
|
||||||
|
|
||||||
static void pc_q35_1_7_machine_options(MachineClass *m)
|
|
||||||
{
|
|
||||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
|
||||||
pc_q35_2_0_machine_options(m);
|
|
||||||
m->hw_version = "1.7.0";
|
|
||||||
m->default_machine_opts = NULL;
|
|
||||||
m->option_rom_has_mr = true;
|
|
||||||
SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
|
|
||||||
pcmc->smbios_defaults = false;
|
|
||||||
pcmc->gigabyte_align = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
|
|
||||||
pc_q35_1_7_machine_options);
|
|
||||||
|
|
||||||
|
|
||||||
static void pc_q35_1_6_machine_options(MachineClass *m)
|
|
||||||
{
|
|
||||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
|
||||||
pc_q35_machine_options(m);
|
|
||||||
m->hw_version = "1.6.0";
|
|
||||||
m->rom_file_has_mr = false;
|
|
||||||
SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
|
|
||||||
pcmc->has_acpi_build = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_Q35_MACHINE(v1_6, "pc-q35-1.6", pc_compat_1_6,
|
|
||||||
pc_q35_1_6_machine_options);
|
|
||||||
|
|
||||||
|
|
||||||
static void pc_q35_1_5_machine_options(MachineClass *m)
|
|
||||||
{
|
|
||||||
pc_q35_1_6_machine_options(m);
|
|
||||||
m->hw_version = "1.5.0";
|
|
||||||
SET_MACHINE_COMPAT(m, PC_COMPAT_1_5);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_Q35_MACHINE(v1_5, "pc-q35-1.5", pc_compat_1_5,
|
|
||||||
pc_q35_1_5_machine_options);
|
|
||||||
|
|
||||||
|
|
||||||
static void pc_q35_1_4_machine_options(MachineClass *m)
|
|
||||||
{
|
|
||||||
pc_q35_1_5_machine_options(m);
|
|
||||||
m->hw_version = "1.4.0";
|
|
||||||
m->hot_add_cpu = NULL;
|
|
||||||
SET_MACHINE_COMPAT(m, PC_COMPAT_1_4);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_Q35_MACHINE(v1_4, "pc-q35-1.4", pc_compat_1_4,
|
|
||||||
pc_q35_1_4_machine_options);
|
|
||||||
|
|||||||
@@ -116,37 +116,42 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
|
|||||||
};
|
};
|
||||||
HIDState *hs = (HIDState *)dev;
|
HIDState *hs = (HIDState *)dev;
|
||||||
HIDPointerEvent *e;
|
HIDPointerEvent *e;
|
||||||
|
InputMoveEvent *move;
|
||||||
|
InputBtnEvent *btn;
|
||||||
|
|
||||||
assert(hs->n < QUEUE_LENGTH);
|
assert(hs->n < QUEUE_LENGTH);
|
||||||
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
|
e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
|
||||||
|
|
||||||
switch (evt->type) {
|
switch (evt->type) {
|
||||||
case INPUT_EVENT_KIND_REL:
|
case INPUT_EVENT_KIND_REL:
|
||||||
if (evt->u.rel->axis == INPUT_AXIS_X) {
|
move = evt->u.rel;
|
||||||
e->xdx += evt->u.rel->value;
|
if (move->axis == INPUT_AXIS_X) {
|
||||||
} else if (evt->u.rel->axis == INPUT_AXIS_Y) {
|
e->xdx += move->value;
|
||||||
e->ydy += evt->u.rel->value;
|
} else if (move->axis == INPUT_AXIS_Y) {
|
||||||
|
e->ydy += move->value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INPUT_EVENT_KIND_ABS:
|
case INPUT_EVENT_KIND_ABS:
|
||||||
if (evt->u.rel->axis == INPUT_AXIS_X) {
|
move = evt->u.abs;
|
||||||
e->xdx = evt->u.rel->value;
|
if (move->axis == INPUT_AXIS_X) {
|
||||||
} else if (evt->u.rel->axis == INPUT_AXIS_Y) {
|
e->xdx = move->value;
|
||||||
e->ydy = evt->u.rel->value;
|
} else if (move->axis == INPUT_AXIS_Y) {
|
||||||
|
e->ydy = move->value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INPUT_EVENT_KIND_BTN:
|
case INPUT_EVENT_KIND_BTN:
|
||||||
if (evt->u.btn->down) {
|
btn = evt->u.btn;
|
||||||
e->buttons_state |= bmap[evt->u.btn->button];
|
if (btn->down) {
|
||||||
if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
|
e->buttons_state |= bmap[btn->button];
|
||||||
|
if (btn->button == INPUT_BUTTON_WHEEL_UP) {
|
||||||
e->dz--;
|
e->dz--;
|
||||||
} else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
|
} else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
|
||||||
e->dz++;
|
e->dz++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
e->buttons_state &= ~bmap[evt->u.btn->button];
|
e->buttons_state &= ~bmap[btn->button];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -223,9 +228,10 @@ static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
|
|||||||
HIDState *hs = (HIDState *)dev;
|
HIDState *hs = (HIDState *)dev;
|
||||||
int scancodes[3], i, count;
|
int scancodes[3], i, count;
|
||||||
int slot;
|
int slot;
|
||||||
|
InputKeyEvent *key = evt->u.key;
|
||||||
|
|
||||||
count = qemu_input_key_value_to_scancode(evt->u.key->key,
|
count = qemu_input_key_value_to_scancode(key->key,
|
||||||
evt->u.key->down,
|
key->down,
|
||||||
scancodes);
|
scancodes);
|
||||||
if (hs->n + count > QUEUE_LENGTH) {
|
if (hs->n + count > QUEUE_LENGTH) {
|
||||||
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
fprintf(stderr, "usb-kbd: warning: key event queue full\n");
|
||||||
|
|||||||
@@ -182,10 +182,11 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
|
|||||||
{
|
{
|
||||||
PS2KbdState *s = (PS2KbdState *)dev;
|
PS2KbdState *s = (PS2KbdState *)dev;
|
||||||
int scancodes[3], i, count;
|
int scancodes[3], i, count;
|
||||||
|
InputKeyEvent *key = evt->u.key;
|
||||||
|
|
||||||
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
|
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
|
||||||
count = qemu_input_key_value_to_scancode(evt->u.key->key,
|
count = qemu_input_key_value_to_scancode(key->key,
|
||||||
evt->u.key->down,
|
key->down,
|
||||||
scancodes);
|
scancodes);
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
ps2_put_keycode(s, scancodes[i]);
|
ps2_put_keycode(s, scancodes[i]);
|
||||||
@@ -389,6 +390,8 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
|
|||||||
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
|
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
|
||||||
};
|
};
|
||||||
PS2MouseState *s = (PS2MouseState *)dev;
|
PS2MouseState *s = (PS2MouseState *)dev;
|
||||||
|
InputMoveEvent *move;
|
||||||
|
InputBtnEvent *btn;
|
||||||
|
|
||||||
/* check if deltas are recorded when disabled */
|
/* check if deltas are recorded when disabled */
|
||||||
if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
|
if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
|
||||||
@@ -396,23 +399,25 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
|
|||||||
|
|
||||||
switch (evt->type) {
|
switch (evt->type) {
|
||||||
case INPUT_EVENT_KIND_REL:
|
case INPUT_EVENT_KIND_REL:
|
||||||
if (evt->u.rel->axis == INPUT_AXIS_X) {
|
move = evt->u.rel;
|
||||||
s->mouse_dx += evt->u.rel->value;
|
if (move->axis == INPUT_AXIS_X) {
|
||||||
} else if (evt->u.rel->axis == INPUT_AXIS_Y) {
|
s->mouse_dx += move->value;
|
||||||
s->mouse_dy -= evt->u.rel->value;
|
} else if (move->axis == INPUT_AXIS_Y) {
|
||||||
|
s->mouse_dy -= move->value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INPUT_EVENT_KIND_BTN:
|
case INPUT_EVENT_KIND_BTN:
|
||||||
if (evt->u.btn->down) {
|
btn = evt->u.btn;
|
||||||
s->mouse_buttons |= bmap[evt->u.btn->button];
|
if (btn->down) {
|
||||||
if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
|
s->mouse_buttons |= bmap[btn->button];
|
||||||
|
if (btn->button == INPUT_BUTTON_WHEEL_UP) {
|
||||||
s->mouse_dz--;
|
s->mouse_dz--;
|
||||||
} else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
|
} else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
|
||||||
s->mouse_dz++;
|
s->mouse_dz++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s->mouse_buttons &= ~bmap[evt->u.btn->button];
|
s->mouse_buttons &= ~bmap[btn->button];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -143,8 +143,8 @@ static const unsigned int keymap_button[INPUT_BUTTON__MAX] = {
|
|||||||
[INPUT_BUTTON_LEFT] = BTN_LEFT,
|
[INPUT_BUTTON_LEFT] = BTN_LEFT,
|
||||||
[INPUT_BUTTON_RIGHT] = BTN_RIGHT,
|
[INPUT_BUTTON_RIGHT] = BTN_RIGHT,
|
||||||
[INPUT_BUTTON_MIDDLE] = BTN_MIDDLE,
|
[INPUT_BUTTON_MIDDLE] = BTN_MIDDLE,
|
||||||
[INPUT_BUTTON_WHEELUP] = BTN_GEAR_UP,
|
[INPUT_BUTTON_WHEEL_UP] = BTN_GEAR_UP,
|
||||||
[INPUT_BUTTON_WHEELDOWN] = BTN_GEAR_DOWN,
|
[INPUT_BUTTON_WHEEL_DOWN] = BTN_GEAR_DOWN,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned int axismap_rel[INPUT_AXIS__MAX] = {
|
static const unsigned int axismap_rel[INPUT_AXIS__MAX] = {
|
||||||
@@ -191,46 +191,53 @@ static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
|
|||||||
VirtIOInput *vinput = VIRTIO_INPUT(dev);
|
VirtIOInput *vinput = VIRTIO_INPUT(dev);
|
||||||
virtio_input_event event;
|
virtio_input_event event;
|
||||||
int qcode;
|
int qcode;
|
||||||
|
InputKeyEvent *key;
|
||||||
|
InputMoveEvent *move;
|
||||||
|
InputBtnEvent *btn;
|
||||||
|
|
||||||
switch (evt->type) {
|
switch (evt->type) {
|
||||||
case INPUT_EVENT_KIND_KEY:
|
case INPUT_EVENT_KIND_KEY:
|
||||||
qcode = qemu_input_key_value_to_qcode(evt->u.key->key);
|
key = evt->u.key;
|
||||||
|
qcode = qemu_input_key_value_to_qcode(key->key);
|
||||||
if (qcode && keymap_qcode[qcode]) {
|
if (qcode && keymap_qcode[qcode]) {
|
||||||
event.type = cpu_to_le16(EV_KEY);
|
event.type = cpu_to_le16(EV_KEY);
|
||||||
event.code = cpu_to_le16(keymap_qcode[qcode]);
|
event.code = cpu_to_le16(keymap_qcode[qcode]);
|
||||||
event.value = cpu_to_le32(evt->u.key->down ? 1 : 0);
|
event.value = cpu_to_le32(key->down ? 1 : 0);
|
||||||
virtio_input_send(vinput, &event);
|
virtio_input_send(vinput, &event);
|
||||||
} else {
|
} else {
|
||||||
if (evt->u.key->down) {
|
if (key->down) {
|
||||||
fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__,
|
fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__,
|
||||||
qcode, QKeyCode_lookup[qcode]);
|
qcode, QKeyCode_lookup[qcode]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case INPUT_EVENT_KIND_BTN:
|
case INPUT_EVENT_KIND_BTN:
|
||||||
if (keymap_button[evt->u.btn->button]) {
|
btn = evt->u.btn;
|
||||||
|
if (keymap_button[btn->button]) {
|
||||||
event.type = cpu_to_le16(EV_KEY);
|
event.type = cpu_to_le16(EV_KEY);
|
||||||
event.code = cpu_to_le16(keymap_button[evt->u.btn->button]);
|
event.code = cpu_to_le16(keymap_button[btn->button]);
|
||||||
event.value = cpu_to_le32(evt->u.btn->down ? 1 : 0);
|
event.value = cpu_to_le32(btn->down ? 1 : 0);
|
||||||
virtio_input_send(vinput, &event);
|
virtio_input_send(vinput, &event);
|
||||||
} else {
|
} else {
|
||||||
if (evt->u.btn->down) {
|
if (btn->down) {
|
||||||
fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
|
fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
|
||||||
evt->u.btn->button,
|
btn->button,
|
||||||
InputButton_lookup[evt->u.btn->button]);
|
InputButton_lookup[btn->button]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case INPUT_EVENT_KIND_REL:
|
case INPUT_EVENT_KIND_REL:
|
||||||
|
move = evt->u.rel;
|
||||||
event.type = cpu_to_le16(EV_REL);
|
event.type = cpu_to_le16(EV_REL);
|
||||||
event.code = cpu_to_le16(axismap_rel[evt->u.rel->axis]);
|
event.code = cpu_to_le16(axismap_rel[move->axis]);
|
||||||
event.value = cpu_to_le32(evt->u.rel->value);
|
event.value = cpu_to_le32(move->value);
|
||||||
virtio_input_send(vinput, &event);
|
virtio_input_send(vinput, &event);
|
||||||
break;
|
break;
|
||||||
case INPUT_EVENT_KIND_ABS:
|
case INPUT_EVENT_KIND_ABS:
|
||||||
|
move = evt->u.abs;
|
||||||
event.type = cpu_to_le16(EV_ABS);
|
event.type = cpu_to_le16(EV_ABS);
|
||||||
event.code = cpu_to_le16(axismap_abs[evt->u.abs->axis]);
|
event.code = cpu_to_le16(axismap_abs[move->axis]);
|
||||||
event.value = cpu_to_le32(evt->u.abs->value);
|
event.value = cpu_to_le32(move->value);
|
||||||
virtio_input_send(vinput, &event);
|
virtio_input_send(vinput, &event);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -500,6 +500,41 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true if we should split priority drop and interrupt deactivation,
|
||||||
|
* ie whether the relevant EOIMode bit is set.
|
||||||
|
*/
|
||||||
|
static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
if (s->revision != 2) {
|
||||||
|
/* Before GICv2 prio-drop and deactivate are not separable */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (s->security_extn && !attrs.secure) {
|
||||||
|
return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE_NS;
|
||||||
|
}
|
||||||
|
return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
int cm = 1 << cpu;
|
||||||
|
int group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
|
||||||
|
|
||||||
|
if (!gic_eoi_split(s, cpu, attrs)) {
|
||||||
|
/* This is UNPREDICTABLE; we choose to ignore it */
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"gic_deactivate_irq: GICC_DIR write when EOIMode clear");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->security_extn && !attrs.secure && !group) {
|
||||||
|
DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GIC_CLEAR_ACTIVE(irq, cm);
|
||||||
|
}
|
||||||
|
|
||||||
void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
|
void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
|
||||||
{
|
{
|
||||||
int cm = 1 << cpu;
|
int cm = 1 << cpu;
|
||||||
@@ -544,7 +579,11 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
gic_drop_prio(s, cpu, group);
|
gic_drop_prio(s, cpu, group);
|
||||||
GIC_CLEAR_ACTIVE(irq, cm);
|
|
||||||
|
/* In GICv2 the guest can choose to split priority-drop and deactivate */
|
||||||
|
if (!gic_eoi_split(s, cpu, attrs)) {
|
||||||
|
GIC_CLEAR_ACTIVE(irq, cm);
|
||||||
|
}
|
||||||
gic_update(s);
|
gic_update(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1210,6 +1249,10 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset,
|
|||||||
s->nsapr[regno][cpu] = value;
|
s->nsapr[regno][cpu] = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 0x1000:
|
||||||
|
/* GICC_DIR */
|
||||||
|
gic_deactivate_irq(s, cpu, value & 0x3ff, attrs);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
qemu_log_mask(LOG_GUEST_ERROR,
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
"gic_cpu_write: Bad offset %x\n", (int)offset);
|
"gic_cpu_write: Bad offset %x\n", (int)offset);
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
|
|||||||
* neither it can use KVM.
|
* neither it can use KVM.
|
||||||
*/
|
*/
|
||||||
memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
|
memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
|
||||||
s, "gic_cpu", s->revision == 2 ? 0x1000 : 0x100);
|
s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100);
|
||||||
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
|
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -712,7 +712,7 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
|
int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi, Error **errp)
|
||||||
{
|
{
|
||||||
ICSState *ics = &icp->ics[src];
|
ICSState *ics = &icp->ics[src];
|
||||||
int irq;
|
int irq;
|
||||||
@@ -720,14 +720,14 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
|
|||||||
if (irq_hint) {
|
if (irq_hint) {
|
||||||
assert(src == xics_find_source(icp, irq_hint));
|
assert(src == xics_find_source(icp, irq_hint));
|
||||||
if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
|
if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) {
|
||||||
trace_xics_alloc_failed_hint(src, irq_hint);
|
error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
irq = irq_hint;
|
irq = irq_hint;
|
||||||
} else {
|
} else {
|
||||||
irq = ics_find_free_block(ics, 1, 1);
|
irq = ics_find_free_block(ics, 1, 1);
|
||||||
if (irq < 0) {
|
if (irq < 0) {
|
||||||
trace_xics_alloc_failed_no_left(src);
|
error_setg(errp, "can't allocate IRQ: no IRQ left");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
irq += ics->offset;
|
irq += ics->offset;
|
||||||
@@ -743,7 +743,8 @@ int xics_alloc(XICSState *icp, int src, int irq_hint, bool lsi)
|
|||||||
* Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
|
* Allocate block of consecutive IRQs, and return the number of the first IRQ in the block.
|
||||||
* If align==true, aligns the first IRQ number to num.
|
* If align==true, aligns the first IRQ number to num.
|
||||||
*/
|
*/
|
||||||
int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
|
int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
int i, first = -1;
|
int i, first = -1;
|
||||||
ICSState *ics = &icp->ics[src];
|
ICSState *ics = &icp->ics[src];
|
||||||
@@ -763,6 +764,10 @@ int xics_alloc_block(XICSState *icp, int src, int num, bool lsi, bool align)
|
|||||||
} else {
|
} else {
|
||||||
first = ics_find_free_block(ics, num, 1);
|
first = ics_find_free_block(ics, num, 1);
|
||||||
}
|
}
|
||||||
|
if (first < 0) {
|
||||||
|
error_setg(errp, "can't find a free %d-IRQ block", num);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (first >= 0) {
|
if (first >= 0) {
|
||||||
for (i = first; i < first + num; ++i) {
|
for (i = first; i < first + num; ++i) {
|
||||||
|
|||||||
@@ -369,13 +369,13 @@ static void ich9_set_sci(void *opaque, int irq_num, int level)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled, bool enable_tco)
|
void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled)
|
||||||
{
|
{
|
||||||
ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
|
ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci);
|
||||||
qemu_irq sci_irq;
|
qemu_irq sci_irq;
|
||||||
|
|
||||||
sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0);
|
sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0);
|
||||||
ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, enable_tco, sci_irq);
|
ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq);
|
||||||
ich9_lpc_reset(&lpc->d.qdev);
|
ich9_lpc_reset(&lpc->d.qdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ static void lm32_evr_init(MachineState *machine)
|
|||||||
int kernel_size;
|
int kernel_size;
|
||||||
|
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
|
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
|
||||||
1, EM_LATTICEMICO32, 0);
|
1, EM_LATTICEMICO32, 0, 0);
|
||||||
reset_info->bootstrap_pc = entry;
|
reset_info->bootstrap_pc = entry;
|
||||||
|
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
@@ -245,7 +245,7 @@ static void lm32_uclinux_init(MachineState *machine)
|
|||||||
int kernel_size;
|
int kernel_size;
|
||||||
|
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
|
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
|
||||||
1, EM_LATTICEMICO32, 0);
|
1, EM_LATTICEMICO32, 0, 0);
|
||||||
reset_info->bootstrap_pc = entry;
|
reset_info->bootstrap_pc = entry;
|
||||||
|
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ milkymist_init(MachineState *machine)
|
|||||||
|
|
||||||
/* Boots a kernel elf binary. */
|
/* Boots a kernel elf binary. */
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
|
kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL,
|
||||||
1, EM_LATTICEMICO32, 0);
|
1, EM_LATTICEMICO32, 0, 0);
|
||||||
reset_info->bootstrap_pc = entry;
|
reset_info->bootstrap_pc = entry;
|
||||||
|
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ static void an5206_init(MachineState *machine)
|
|||||||
}
|
}
|
||||||
|
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
||||||
NULL, NULL, 1, EM_68K, 0);
|
NULL, NULL, 1, EM_68K, 0, 0);
|
||||||
entry = elf_entry;
|
entry = elf_entry;
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
|
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ static void dummy_m68k_init(MachineState *machine)
|
|||||||
/* Load kernel. */
|
/* Load kernel. */
|
||||||
if (kernel_filename) {
|
if (kernel_filename) {
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
||||||
NULL, NULL, 1, EM_68K, 0);
|
NULL, NULL, 1, EM_68K, 0, 0);
|
||||||
entry = elf_entry;
|
entry = elf_entry;
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
|
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ static void mcf5208evb_init(MachineState *machine)
|
|||||||
}
|
}
|
||||||
|
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
||||||
NULL, NULL, 1, EM_68K, 0);
|
NULL, NULL, 1, EM_68K, 0, 0);
|
||||||
entry = elf_entry;
|
entry = elf_entry;
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
|
kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL,
|
||||||
|
|||||||
@@ -192,32 +192,6 @@ int qmp_pc_dimm_device_list(Object *obj, void *opaque)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ram_addr_t get_current_ram_size(void)
|
|
||||||
{
|
|
||||||
MemoryDeviceInfoList *info_list = NULL;
|
|
||||||
MemoryDeviceInfoList **prev = &info_list;
|
|
||||||
MemoryDeviceInfoList *info;
|
|
||||||
ram_addr_t size = ram_size;
|
|
||||||
|
|
||||||
qmp_pc_dimm_device_list(qdev_get_machine(), &prev);
|
|
||||||
for (info = info_list; info; info = info->next) {
|
|
||||||
MemoryDeviceInfo *value = info->value;
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
switch (value->type) {
|
|
||||||
case MEMORY_DEVICE_INFO_KIND_DIMM:
|
|
||||||
size += value->u.dimm->size;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qapi_free_MemoryDeviceInfoList(info_list);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
|
static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
|
||||||
{
|
{
|
||||||
unsigned long *bitmap = opaque;
|
unsigned long *bitmap = opaque;
|
||||||
|
|||||||
@@ -142,12 +142,12 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base,
|
|||||||
/* Boots a kernel elf binary. */
|
/* Boots a kernel elf binary. */
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL,
|
kernel_size = load_elf(kernel_filename, NULL, NULL,
|
||||||
&entry, &low, &high,
|
&entry, &low, &high,
|
||||||
big_endian, EM_MICROBLAZE, 0);
|
big_endian, EM_MICROBLAZE, 0, 0);
|
||||||
base32 = entry;
|
base32 = entry;
|
||||||
if (base32 == 0xc0000000) {
|
if (base32 == 0xc0000000) {
|
||||||
kernel_size = load_elf(kernel_filename, translate_kernel_address,
|
kernel_size = load_elf(kernel_filename, translate_kernel_address,
|
||||||
NULL, &entry, NULL, NULL,
|
NULL, &entry, NULL, NULL,
|
||||||
big_endian, EM_MICROBLAZE, 0);
|
big_endian, EM_MICROBLAZE, 0, 0);
|
||||||
}
|
}
|
||||||
/* Always boot into physical ram. */
|
/* Always boot into physical ram. */
|
||||||
boot_info.bootstrap_pc = (uint32_t)entry;
|
boot_info.bootstrap_pc = (uint32_t)entry;
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ static int64_t load_kernel (CPUMIPSState *env)
|
|||||||
|
|
||||||
if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
|
if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
|
||||||
(uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
|
(uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
|
||||||
(uint64_t *)&kernel_high, 0, EM_MIPS, 1) < 0) {
|
(uint64_t *)&kernel_high, 0, EM_MIPS, 1, 0) < 0) {
|
||||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||||
loaderparams.kernel_filename);
|
loaderparams.kernel_filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -796,7 +796,7 @@ static int64_t load_kernel (void)
|
|||||||
|
|
||||||
if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
|
if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL,
|
||||||
(uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high,
|
(uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high,
|
||||||
big_endian, EM_MIPS, 1) < 0) {
|
big_endian, EM_MIPS, 1, 0) < 0) {
|
||||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||||
loaderparams.kernel_filename);
|
loaderparams.kernel_filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ static int64_t load_kernel(void)
|
|||||||
kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
|
kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
|
||||||
NULL, (uint64_t *)&entry, NULL,
|
NULL, (uint64_t *)&entry, NULL,
|
||||||
(uint64_t *)&kernel_high, big_endian,
|
(uint64_t *)&kernel_high, big_endian,
|
||||||
EM_MIPS, 1);
|
EM_MIPS, 1, 0);
|
||||||
if (kernel_size >= 0) {
|
if (kernel_size >= 0) {
|
||||||
if ((entry & ~0x7fffffffULL) == 0x80000000)
|
if ((entry & ~0x7fffffffULL) == 0x80000000)
|
||||||
entry = (int32_t)entry;
|
entry = (int32_t)entry;
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ static int64_t load_kernel(void)
|
|||||||
kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
|
kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys,
|
||||||
NULL, (uint64_t *)&entry, NULL,
|
NULL, (uint64_t *)&entry, NULL,
|
||||||
(uint64_t *)&kernel_high, big_endian,
|
(uint64_t *)&kernel_high, big_endian,
|
||||||
EM_MIPS, 1);
|
EM_MIPS, 1, 0);
|
||||||
if (kernel_size >= 0) {
|
if (kernel_size >= 0) {
|
||||||
if ((entry & ~0x7fffffffULL) == 0x80000000)
|
if ((entry & ~0x7fffffffULL) == 0x80000000)
|
||||||
entry = (int32_t)entry;
|
entry = (int32_t)entry;
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ static void bcm2835_mbox_update(BCM2835MboxState *s)
|
|||||||
*/
|
*/
|
||||||
for (n = 0; n < MBOX_CHAN_COUNT; n++) {
|
for (n = 0; n < MBOX_CHAN_COUNT; n++) {
|
||||||
while (s->available[n] && !(s->mbox[0].status & ARM_MS_FULL)) {
|
while (s->available[n] && !(s->mbox[0].status & ARM_MS_FULL)) {
|
||||||
value = ldl_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT);
|
value = ldl_le_phys(&s->mbox_as, n << MBOX_AS_CHAN_SHIFT);
|
||||||
assert(value != MBOX_INVALID_DATA); /* Pending interrupt but no data */
|
assert(value != MBOX_INVALID_DATA); /* Pending interrupt but no data */
|
||||||
mbox_push(&s->mbox[0], value);
|
mbox_push(&s->mbox[0], value);
|
||||||
}
|
}
|
||||||
@@ -207,12 +207,12 @@ static void bcm2835_mbox_write(void *opaque, hwaddr offset,
|
|||||||
ch = value & 0xf;
|
ch = value & 0xf;
|
||||||
if (ch < MBOX_CHAN_COUNT) {
|
if (ch < MBOX_CHAN_COUNT) {
|
||||||
childaddr = ch << MBOX_AS_CHAN_SHIFT;
|
childaddr = ch << MBOX_AS_CHAN_SHIFT;
|
||||||
if (ldl_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) {
|
if (ldl_le_phys(&s->mbox_as, childaddr + MBOX_AS_PENDING)) {
|
||||||
/* Child busy, push delayed. Push it in the arm->vc mbox */
|
/* Child busy, push delayed. Push it in the arm->vc mbox */
|
||||||
mbox_push(&s->mbox[1], value);
|
mbox_push(&s->mbox[1], value);
|
||||||
} else {
|
} else {
|
||||||
/* Push it directly to the child device */
|
/* Push it directly to the child device */
|
||||||
stl_phys(&s->mbox_as, childaddr, value);
|
stl_le_phys(&s->mbox_as, childaddr, value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Invalid channel number */
|
/* Invalid channel number */
|
||||||
|
|||||||
@@ -22,20 +22,20 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
|||||||
|
|
||||||
s->addr = value;
|
s->addr = value;
|
||||||
|
|
||||||
tot_len = ldl_phys(&s->dma_as, value);
|
tot_len = ldl_le_phys(&s->dma_as, value);
|
||||||
|
|
||||||
/* @(addr + 4) : Buffer response code */
|
/* @(addr + 4) : Buffer response code */
|
||||||
value = s->addr + 8;
|
value = s->addr + 8;
|
||||||
while (value + 8 <= s->addr + tot_len) {
|
while (value + 8 <= s->addr + tot_len) {
|
||||||
tag = ldl_phys(&s->dma_as, value);
|
tag = ldl_le_phys(&s->dma_as, value);
|
||||||
bufsize = ldl_phys(&s->dma_as, value + 4);
|
bufsize = ldl_le_phys(&s->dma_as, value + 4);
|
||||||
/* @(value + 8) : Request/response indicator */
|
/* @(value + 8) : Request/response indicator */
|
||||||
resplen = 0;
|
resplen = 0;
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case 0x00000000: /* End tag */
|
case 0x00000000: /* End tag */
|
||||||
break;
|
break;
|
||||||
case 0x00000001: /* Get firmware revision */
|
case 0x00000001: /* Get firmware revision */
|
||||||
stl_phys(&s->dma_as, value + 12, 346337);
|
stl_le_phys(&s->dma_as, value + 12, 346337);
|
||||||
resplen = 4;
|
resplen = 4;
|
||||||
break;
|
break;
|
||||||
case 0x00010001: /* Get board model */
|
case 0x00010001: /* Get board model */
|
||||||
@@ -44,7 +44,7 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
|||||||
resplen = 4;
|
resplen = 4;
|
||||||
break;
|
break;
|
||||||
case 0x00010002: /* Get board revision */
|
case 0x00010002: /* Get board revision */
|
||||||
stl_phys(&s->dma_as, value + 12, s->board_rev);
|
stl_le_phys(&s->dma_as, value + 12, s->board_rev);
|
||||||
resplen = 4;
|
resplen = 4;
|
||||||
break;
|
break;
|
||||||
case 0x00010003: /* Get board MAC address */
|
case 0x00010003: /* Get board MAC address */
|
||||||
@@ -58,24 +58,24 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
|||||||
break;
|
break;
|
||||||
case 0x00010005: /* Get ARM memory */
|
case 0x00010005: /* Get ARM memory */
|
||||||
/* base */
|
/* base */
|
||||||
stl_phys(&s->dma_as, value + 12, 0);
|
stl_le_phys(&s->dma_as, value + 12, 0);
|
||||||
/* size */
|
/* size */
|
||||||
stl_phys(&s->dma_as, value + 16, s->ram_size);
|
stl_le_phys(&s->dma_as, value + 16, s->ram_size);
|
||||||
resplen = 8;
|
resplen = 8;
|
||||||
break;
|
break;
|
||||||
case 0x00028001: /* Set power state */
|
case 0x00028001: /* Set power state */
|
||||||
/* Assume that whatever device they asked for exists,
|
/* Assume that whatever device they asked for exists,
|
||||||
* and we'll just claim we set it to the desired state
|
* and we'll just claim we set it to the desired state
|
||||||
*/
|
*/
|
||||||
tmp = ldl_phys(&s->dma_as, value + 16);
|
tmp = ldl_le_phys(&s->dma_as, value + 16);
|
||||||
stl_phys(&s->dma_as, value + 16, (tmp & 1));
|
stl_le_phys(&s->dma_as, value + 16, (tmp & 1));
|
||||||
resplen = 8;
|
resplen = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Clocks */
|
/* Clocks */
|
||||||
|
|
||||||
case 0x00030001: /* Get clock state */
|
case 0x00030001: /* Get clock state */
|
||||||
stl_phys(&s->dma_as, value + 16, 0x1);
|
stl_le_phys(&s->dma_as, value + 16, 0x1);
|
||||||
resplen = 8;
|
resplen = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -88,15 +88,15 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
|||||||
case 0x00030002: /* Get clock rate */
|
case 0x00030002: /* Get clock rate */
|
||||||
case 0x00030004: /* Get max clock rate */
|
case 0x00030004: /* Get max clock rate */
|
||||||
case 0x00030007: /* Get min clock rate */
|
case 0x00030007: /* Get min clock rate */
|
||||||
switch (ldl_phys(&s->dma_as, value + 12)) {
|
switch (ldl_le_phys(&s->dma_as, value + 12)) {
|
||||||
case 1: /* EMMC */
|
case 1: /* EMMC */
|
||||||
stl_phys(&s->dma_as, value + 16, 50000000);
|
stl_le_phys(&s->dma_as, value + 16, 50000000);
|
||||||
break;
|
break;
|
||||||
case 2: /* UART */
|
case 2: /* UART */
|
||||||
stl_phys(&s->dma_as, value + 16, 3000000);
|
stl_le_phys(&s->dma_as, value + 16, 3000000);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
stl_phys(&s->dma_as, value + 16, 700000000);
|
stl_le_phys(&s->dma_as, value + 16, 700000000);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
resplen = 8;
|
resplen = 8;
|
||||||
@@ -113,19 +113,19 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
|||||||
/* Temperature */
|
/* Temperature */
|
||||||
|
|
||||||
case 0x00030006: /* Get temperature */
|
case 0x00030006: /* Get temperature */
|
||||||
stl_phys(&s->dma_as, value + 16, 25000);
|
stl_le_phys(&s->dma_as, value + 16, 25000);
|
||||||
resplen = 8;
|
resplen = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0003000A: /* Get max temperature */
|
case 0x0003000A: /* Get max temperature */
|
||||||
stl_phys(&s->dma_as, value + 16, 99000);
|
stl_le_phys(&s->dma_as, value + 16, 99000);
|
||||||
resplen = 8;
|
resplen = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case 0x00060001: /* Get DMA channels */
|
case 0x00060001: /* Get DMA channels */
|
||||||
/* channels 2-5 */
|
/* channels 2-5 */
|
||||||
stl_phys(&s->dma_as, value + 12, 0x003C);
|
stl_le_phys(&s->dma_as, value + 12, 0x003C);
|
||||||
resplen = 4;
|
resplen = 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -143,12 +143,12 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
stl_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
|
stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
|
||||||
value += bufsize + 12;
|
value += bufsize + 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Buffer response code */
|
/* Buffer response code */
|
||||||
stl_phys(&s->dma_as, s->addr + 4, (1 << 31));
|
stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
|
static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
|
||||||
|
|||||||
@@ -557,11 +557,13 @@ void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
|
|||||||
|
|
||||||
DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
|
DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
|
||||||
|
|
||||||
|
assert(rw);
|
||||||
|
assert(flush);
|
||||||
|
|
||||||
ch->irq = irq;
|
ch->irq = irq;
|
||||||
ch->rw = rw;
|
ch->rw = rw;
|
||||||
ch->flush = flush;
|
ch->flush = flush;
|
||||||
ch->io.opaque = opaque;
|
ch->io.opaque = opaque;
|
||||||
ch->io.channel = ch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -775,6 +777,20 @@ static void dbdma_reset(void *opaque)
|
|||||||
memset(s->channels[i].regs, 0, DBDMA_SIZE);
|
memset(s->channels[i].regs, 0, DBDMA_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dbdma_unassigned_rw(DBDMA_io *io)
|
||||||
|
{
|
||||||
|
DBDMA_channel *ch = io->channel;
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
|
||||||
|
__func__, ch->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dbdma_unassigned_flush(DBDMA_io *io)
|
||||||
|
{
|
||||||
|
DBDMA_channel *ch = io->channel;
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR, "%s: use of unassigned channel %d\n",
|
||||||
|
__func__, ch->channel);
|
||||||
|
}
|
||||||
|
|
||||||
void* DBDMA_init (MemoryRegion **dbdma_mem)
|
void* DBDMA_init (MemoryRegion **dbdma_mem)
|
||||||
{
|
{
|
||||||
DBDMAState *s;
|
DBDMAState *s;
|
||||||
@@ -784,8 +800,13 @@ void* DBDMA_init (MemoryRegion **dbdma_mem)
|
|||||||
|
|
||||||
for (i = 0; i < DBDMA_CHANNELS; i++) {
|
for (i = 0; i < DBDMA_CHANNELS; i++) {
|
||||||
DBDMA_io *io = &s->channels[i].io;
|
DBDMA_io *io = &s->channels[i].io;
|
||||||
|
DBDMA_channel *ch = &s->channels[i];
|
||||||
qemu_iovec_init(&io->iov, 1);
|
qemu_iovec_init(&io->iov, 1);
|
||||||
s->channels[i].channel = i;
|
|
||||||
|
ch->rw = dbdma_unassigned_rw;
|
||||||
|
ch->flush = dbdma_unassigned_flush;
|
||||||
|
ch->channel = i;
|
||||||
|
ch->io.channel = ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_region_init_io(&s->mem, NULL, &dbdma_ops, s, "dbdma", 0x1000);
|
memory_region_init_io(&s->mem, NULL, &dbdma_ops, s, "dbdma", 0x1000);
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params)
|
|||||||
ram_addr_t initrd_offset;
|
ram_addr_t initrd_offset;
|
||||||
|
|
||||||
kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL,
|
kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL,
|
||||||
&entry, &kernel_low, &kernel_high, 1, EM_MOXIE, 0);
|
&entry, &kernel_low, &kernel_high, 1, EM_MOXIE,
|
||||||
|
0, 0);
|
||||||
|
|
||||||
if (kernel_size <= 0) {
|
if (kernel_size <= 0) {
|
||||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
#ifndef _ETSEC_REGISTERS_H_
|
#ifndef _ETSEC_REGISTERS_H_
|
||||||
#define _ETSEC_REGISTERS_H_
|
#define _ETSEC_REGISTERS_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
enum eTSEC_Register_Access_Type {
|
enum eTSEC_Register_Access_Type {
|
||||||
ACC_RW = 1, /* Read/Write */
|
ACC_RW = 1, /* Read/Write */
|
||||||
|
|||||||
@@ -778,17 +778,19 @@ FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase,
|
|||||||
DeviceState *dev;
|
DeviceState *dev;
|
||||||
FWCfgState *s;
|
FWCfgState *s;
|
||||||
uint32_t version = FW_CFG_VERSION;
|
uint32_t version = FW_CFG_VERSION;
|
||||||
bool dma_enabled = dma_iobase && dma_as;
|
bool dma_requested = dma_iobase && dma_as;
|
||||||
|
|
||||||
dev = qdev_create(NULL, TYPE_FW_CFG_IO);
|
dev = qdev_create(NULL, TYPE_FW_CFG_IO);
|
||||||
qdev_prop_set_uint32(dev, "iobase", iobase);
|
qdev_prop_set_uint32(dev, "iobase", iobase);
|
||||||
qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase);
|
qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase);
|
||||||
qdev_prop_set_bit(dev, "dma_enabled", dma_enabled);
|
if (!dma_requested) {
|
||||||
|
qdev_prop_set_bit(dev, "dma_enabled", false);
|
||||||
|
}
|
||||||
|
|
||||||
fw_cfg_init1(dev);
|
fw_cfg_init1(dev);
|
||||||
s = FW_CFG(dev);
|
s = FW_CFG(dev);
|
||||||
|
|
||||||
if (dma_enabled) {
|
if (s->dma_enabled) {
|
||||||
/* 64 bits for the address field */
|
/* 64 bits for the address field */
|
||||||
s->dma_as = dma_as;
|
s->dma_as = dma_as;
|
||||||
s->dma_addr = 0;
|
s->dma_addr = 0;
|
||||||
@@ -814,11 +816,13 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr,
|
|||||||
SysBusDevice *sbd;
|
SysBusDevice *sbd;
|
||||||
FWCfgState *s;
|
FWCfgState *s;
|
||||||
uint32_t version = FW_CFG_VERSION;
|
uint32_t version = FW_CFG_VERSION;
|
||||||
bool dma_enabled = dma_addr && dma_as;
|
bool dma_requested = dma_addr && dma_as;
|
||||||
|
|
||||||
dev = qdev_create(NULL, TYPE_FW_CFG_MEM);
|
dev = qdev_create(NULL, TYPE_FW_CFG_MEM);
|
||||||
qdev_prop_set_uint32(dev, "data_width", data_width);
|
qdev_prop_set_uint32(dev, "data_width", data_width);
|
||||||
qdev_prop_set_bit(dev, "dma_enabled", dma_enabled);
|
if (!dma_requested) {
|
||||||
|
qdev_prop_set_bit(dev, "dma_enabled", false);
|
||||||
|
}
|
||||||
|
|
||||||
fw_cfg_init1(dev);
|
fw_cfg_init1(dev);
|
||||||
|
|
||||||
@@ -828,7 +832,7 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr,
|
|||||||
|
|
||||||
s = FW_CFG(dev);
|
s = FW_CFG(dev);
|
||||||
|
|
||||||
if (dma_enabled) {
|
if (s->dma_enabled) {
|
||||||
s->dma_as = dma_as;
|
s->dma_as = dma_as;
|
||||||
s->dma_addr = 0;
|
s->dma_addr = 0;
|
||||||
sysbus_mmio_map(sbd, 2, dma_addr);
|
sysbus_mmio_map(sbd, 2, dma_addr);
|
||||||
@@ -873,7 +877,7 @@ static Property fw_cfg_io_properties[] = {
|
|||||||
DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1),
|
DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1),
|
||||||
DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1),
|
DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1),
|
||||||
DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled,
|
DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled,
|
||||||
false),
|
true),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -913,7 +917,7 @@ static const TypeInfo fw_cfg_io_info = {
|
|||||||
static Property fw_cfg_mem_properties[] = {
|
static Property fw_cfg_mem_properties[] = {
|
||||||
DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1),
|
DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1),
|
||||||
DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled,
|
DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled,
|
||||||
false),
|
true),
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ static void cpu_openrisc_load_kernel(ram_addr_t ram_size,
|
|||||||
|
|
||||||
if (kernel_filename && !qtest_enabled()) {
|
if (kernel_filename && !qtest_enabled()) {
|
||||||
kernel_size = load_elf(kernel_filename, NULL, NULL,
|
kernel_size = load_elf(kernel_filename, NULL, NULL,
|
||||||
&elf_entry, NULL, NULL, 1, EM_OPENRISC, 1);
|
&elf_entry, NULL, NULL, 1, EM_OPENRISC,
|
||||||
|
1, 0);
|
||||||
entry = elf_entry;
|
entry = elf_entry;
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_uimage(kernel_filename,
|
kernel_size = load_uimage(kernel_filename,
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ static void raven_realize(PCIDevice *d, Error **errp)
|
|||||||
if (filename) {
|
if (filename) {
|
||||||
if (s->elf_machine != EM_NONE) {
|
if (s->elf_machine != EM_NONE) {
|
||||||
bios_size = load_elf(filename, NULL, NULL, NULL,
|
bios_size = load_elf(filename, NULL, NULL, NULL,
|
||||||
NULL, NULL, 1, s->elf_machine, 0);
|
NULL, NULL, 1, s->elf_machine, 0, 0);
|
||||||
}
|
}
|
||||||
if (bios_size < 0) {
|
if (bios_size < 0) {
|
||||||
bios_size = get_image_size(filename);
|
bios_size = get_image_size(filename);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user