Compare commits
313 Commits
v2.5.1.1
...
pull-ui-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb47dc9ab9 | ||
|
|
4083733db5 | ||
|
|
a7e00e2536 | ||
|
|
ad24f947be | ||
|
|
263699432c | ||
|
|
c9a464420d | ||
|
|
bf43330aa4 | ||
|
|
e913cac71b | ||
|
|
ac93a06786 | ||
|
|
4101dfe0cd | ||
|
|
cef517ca4b | ||
|
|
9967e4fec0 | ||
|
|
c6daed8654 | ||
|
|
67a9faccef | ||
|
|
4b9294c00e | ||
|
|
bf864863f6 | ||
|
|
cae7e84eb7 | ||
|
|
437b8d8c59 | ||
|
|
ad30c0b0d8 | ||
|
|
38a762fec6 | ||
|
|
8b4f90316a | ||
|
|
e833dfddb4 | ||
|
|
5fbba56073 | ||
|
|
05bec7eb0e | ||
|
|
50de626151 | ||
|
|
48befbc344 | ||
|
|
d8c02bcc94 | ||
|
|
d84b79d358 | ||
|
|
7b36064c90 | ||
|
|
dd2bf9eb95 | ||
|
|
7b3c618ad0 | ||
|
|
4b3a4f2d45 | ||
|
|
bead59946a | ||
|
|
e4d2edc9d0 | ||
|
|
5530427f0c | ||
|
|
ca3df95df8 | ||
|
|
a23b887281 | ||
|
|
25c1432ebe | ||
|
|
6d5ea945ce | ||
|
|
36de884a13 | ||
|
|
dabad78b0d | ||
|
|
df241999b6 | ||
|
|
0073518dd7 | ||
|
|
67a5c0faa6 | ||
|
|
c360639aee | ||
|
|
f411199de7 | ||
|
|
7b38ba9cb6 | ||
|
|
7059eb4262 | ||
|
|
20ca520884 | ||
|
|
439e2a6e10 | ||
|
|
7e192a383b | ||
|
|
6e1db3f263 | ||
|
|
2d3f667dc6 | ||
|
|
52483d147b | ||
|
|
95cb066190 | ||
|
|
70d1fb9c0b | ||
|
|
772863954c | ||
|
|
87252e1b61 | ||
|
|
8870ca0e94 | ||
|
|
5c42eef243 | ||
|
|
40c520418b | ||
|
|
c9c0afbb19 | ||
|
|
276a65ba4b | ||
|
|
240240d5da | ||
|
|
1e37b7149a | ||
|
|
aa8abbed00 | ||
|
|
ad2a807718 | ||
|
|
44d3db0d96 | ||
|
|
90b6180500 | ||
|
|
bd66bcfca5 | ||
|
|
f8490451ac | ||
|
|
24f976d30a | ||
|
|
a9b74079cb | ||
|
|
0719029c47 | ||
|
|
67aa56fc03 | ||
|
|
8bfffbccad | ||
|
|
23076bb34b | ||
|
|
13fc834308 | ||
|
|
34be1e7c92 | ||
|
|
81ed6482a3 | ||
|
|
02b07434be | ||
|
|
d7fd0e6914 | ||
|
|
71ae9e94d9 | ||
|
|
cdedce0564 | ||
|
|
16a9e8a5bc | ||
|
|
cd4040ec18 | ||
|
|
2b0ddf6612 | ||
|
|
7102fa7073 | ||
|
|
097a50d0d8 | ||
|
|
5dc42c186d | ||
|
|
723697551a | ||
|
|
62d32ec817 | ||
|
|
6890a695d9 | ||
|
|
d6b6913276 | ||
|
|
b4a9e25b7b | ||
|
|
3515727f31 | ||
|
|
222565f65c | ||
|
|
648296e067 | ||
|
|
bd44feb754 | ||
|
|
49cffbc607 | ||
|
|
c595b21888 | ||
|
|
2f6f826e03 | ||
|
|
c688084506 | ||
|
|
de532ff1df | ||
|
|
1d7b5b4afd | ||
|
|
ac1d887849 | ||
|
|
920639cab0 | ||
|
|
e9cf2fe07f | ||
|
|
89bc0b6cae | ||
|
|
b06f904f2e | ||
|
|
6126bc5522 | ||
|
|
7ea11bf376 | ||
|
|
f9e98e5d7a | ||
|
|
d5f042232c | ||
|
|
2cc452281e | ||
|
|
04d2529da2 | ||
|
|
9d4a6cf0ea | ||
|
|
a5002d5302 | ||
|
|
92d617abc5 | ||
|
|
5c9d9ca597 | ||
|
|
12dcb1c018 | ||
|
|
548e1ff379 | ||
|
|
a41aa71c15 | ||
|
|
289f3ebae8 | ||
|
|
8a7607c2e2 | ||
|
|
ba88944495 | ||
|
|
27a7649a48 | ||
|
|
8382ba6153 | ||
|
|
bbe1ef2686 | ||
|
|
d657c0c289 | ||
|
|
b1fc8f934b | ||
|
|
e9dbdc5e46 | ||
|
|
03bb78ed25 | ||
|
|
61ce55fc02 | ||
|
|
791c9a004e | ||
|
|
c293a80927 | ||
|
|
1038bbb803 | ||
|
|
164e0f89cc | ||
|
|
29d72431ef | ||
|
|
8b13976d3f | ||
|
|
bd5072d756 | ||
|
|
0a8111e0fb | ||
|
|
8f7acbe6ea | ||
|
|
768ee459f5 | ||
|
|
c5e8bfb7cd | ||
|
|
91a097e747 | ||
|
|
ccf9dc07b5 | ||
|
|
fc17c25931 | ||
|
|
39c4ae941e | ||
|
|
145f598e4a | ||
|
|
de3b53f007 | ||
|
|
8e2160e2c7 | ||
|
|
2851810223 | ||
|
|
4c9dfe5d8a | ||
|
|
62392ebb09 | ||
|
|
4cdd01d32e | ||
|
|
260fecf13b | ||
|
|
9e700c1ac6 | ||
|
|
d9b7b05703 | ||
|
|
40365552c2 | ||
|
|
cddff5bae1 | ||
|
|
5365f44dfa | ||
|
|
35cea22373 | ||
|
|
ce2cbc4910 | ||
|
|
1b19bb9d17 | ||
|
|
18f49881cf | ||
|
|
67a7084062 | ||
|
|
d98e4eb7de | ||
|
|
195e14d026 | ||
|
|
2d1d0e70cf | ||
|
|
ed8ee42c40 | ||
|
|
d6e48869a4 | ||
|
|
559607ea17 | ||
|
|
b02db2d920 | ||
|
|
1c809fa01d | ||
|
|
666a3af9c8 | ||
|
|
6a6533213d | ||
|
|
29cd81ffe3 | ||
|
|
1e819697c9 | ||
|
|
36896bffd1 | ||
|
|
8aad35f678 | ||
|
|
4cae9c9796 | ||
|
|
f6d153f1bf | ||
|
|
3cc8f88499 | ||
|
|
1619d1fe73 | ||
|
|
a203ac702e | ||
|
|
eb7eeb8862 | ||
|
|
612263cf33 | ||
|
|
a676854f34 | ||
|
|
e81bcda529 | ||
|
|
49b24afcb1 | ||
|
|
c8ee0a445a | ||
|
|
120a9848c2 | ||
|
|
1382902055 | ||
|
|
c30f0d182f | ||
|
|
b81b971c7a | ||
|
|
48880da696 | ||
|
|
aafcf80e22 | ||
|
|
1d512a65ac | ||
|
|
79e8ed3597 | ||
|
|
c6ce9f176f | ||
|
|
013a29424c | ||
|
|
31e38a22a0 | ||
|
|
15eafc2e60 | ||
|
|
32c18a2dba | ||
|
|
ff99aa64b1 | ||
|
|
b67dbb7070 | ||
|
|
50efe82c3c | ||
|
|
977a8d9c0d | ||
|
|
866eea9a13 | ||
|
|
fff02bc00b | ||
|
|
d5da3ef2e2 | ||
|
|
1dd1305e66 | ||
|
|
e2d4f3f75b | ||
|
|
952970ba56 | ||
|
|
836fc48cbc | ||
|
|
d29d4ff8ef | ||
|
|
8c56c1a592 | ||
|
|
bc92e4e97e | ||
|
|
2f3a2bb15e | ||
|
|
fc3e7665d7 | ||
|
|
a29ac16632 | ||
|
|
e5fbe28e54 | ||
|
|
92eccc6e13 | ||
|
|
cb54d868c6 | ||
|
|
aaa9ec3b4d | ||
|
|
9de46a0aa3 | ||
|
|
261f4d6d3e | ||
|
|
34c45d5302 | ||
|
|
e4482ab7e3 | ||
|
|
26ae593485 | ||
|
|
2ecb2027bc | ||
|
|
29eb3d9a91 | ||
|
|
3e6ebb64a3 | ||
|
|
4bedd8495b | ||
|
|
c1a158b7ed | ||
|
|
37d0e98006 | ||
|
|
4ecdc746e9 | ||
|
|
ac6aa59a21 | ||
|
|
aeb1a36d65 | ||
|
|
b0a3721e44 | ||
|
|
45fcf53940 | ||
|
|
4dbfc88149 | ||
|
|
7999a5c8f6 | ||
|
|
30901475b9 | ||
|
|
580106df5f | ||
|
|
58f6d82fc4 | ||
|
|
f1f7e4bf76 | ||
|
|
98557acf92 | ||
|
|
c1a5f950cd | ||
|
|
fc77eb20d7 | ||
|
|
dc337c6e26 | ||
|
|
71f3ef0836 | ||
|
|
bac5429ccb | ||
|
|
01cfbaa4c3 | ||
|
|
893e1f2c51 | ||
|
|
93bda4dd46 | ||
|
|
d44f9ac80c | ||
|
|
29637a6ee9 | ||
|
|
5cdc8831a7 | ||
|
|
d00341af38 | ||
|
|
9d3f3494c5 | ||
|
|
0b2e84ba77 | ||
|
|
0426d53c65 | ||
|
|
7264f5c50c | ||
|
|
1310a3d3bd | ||
|
|
55e1819c50 | ||
|
|
d20a580bc0 | ||
|
|
f22a28b898 | ||
|
|
86f4b6871c | ||
|
|
04e0639d4e | ||
|
|
7fb1cf1606 | ||
|
|
59a92feedc | ||
|
|
5be5b7764f | ||
|
|
a31939e6c8 | ||
|
|
7549457200 | ||
|
|
c43567c120 | ||
|
|
27b60ab93b | ||
|
|
88d4ef8b5c | ||
|
|
61a946611b | ||
|
|
10565ca92a | ||
|
|
c2183d2e62 | ||
|
|
b807a1e1e3 | ||
|
|
14ff84619c | ||
|
|
577de12d22 | ||
|
|
23a4b2c6f1 | ||
|
|
08683353fc | ||
|
|
cdc5fa37ed | ||
|
|
e564e2dd59 | ||
|
|
fff5f231d5 | ||
|
|
7d9586f900 | ||
|
|
570cd8d119 | ||
|
|
da34a9bd99 | ||
|
|
6c8d56a2e9 | ||
|
|
38bf20931a | ||
|
|
66f8fd9dda | ||
|
|
3f8752b4e5 | ||
|
|
3bef7e8aab | ||
|
|
9c4a5c55f5 | ||
|
|
2f79a18fdd | ||
|
|
2d99f6299b | ||
|
|
95a860f62e | ||
|
|
624d1fc30f | ||
|
|
88a0f8300b | ||
|
|
156a2e4dbf | ||
|
|
93d592e3d1 | ||
|
|
8e3e3897ce | ||
|
|
b3c4d4250f | ||
|
|
4c7a67f5cd | ||
|
|
fc3e493bc8 | ||
|
|
55c8672c2e | ||
|
|
bdfe5159cb | ||
|
|
f0ada3608a |
15
MAINTAINERS
15
MAINTAINERS
@@ -941,6 +941,13 @@ M: Jiri Pirko <jiri@resnulli.us>
|
||||
S: Maintained
|
||||
F: hw/net/rocker/
|
||||
|
||||
NVDIMM
|
||||
M: Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||
S: Maintained
|
||||
F: hw/acpi/nvdimm.c
|
||||
F: hw/mem/nvdimm.c
|
||||
F: include/hw/mem/nvdimm.h
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
@@ -1243,6 +1250,13 @@ S: Odd fixes
|
||||
F: util/buffer.c
|
||||
F: include/qemu/buffer.h
|
||||
|
||||
I/O Channels
|
||||
M: Daniel P. Berrange <berrange@redhat.com>
|
||||
S: Maintained
|
||||
F: io/
|
||||
F: include/io/
|
||||
F: tests/test-io-*
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
Overall
|
||||
@@ -1483,6 +1497,7 @@ M: Denis V. Lunev <den@openvz.org>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/parallels.c
|
||||
F: docs/specs/parallels.txt
|
||||
|
||||
qed
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
|
||||
5
Makefile
5
Makefile
@@ -159,6 +159,7 @@ dummy := $(call unnest-vars,, \
|
||||
crypto-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
|
||||
@@ -178,6 +179,7 @@ SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
|
||||
|
||||
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
|
||||
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
|
||||
|
||||
subdir-%:
|
||||
@@ -269,7 +271,8 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
|
||||
|
||||
qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
|
||||
$(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
|
||||
$(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json
|
||||
$(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
|
||||
$(SRC_PATH)/qapi/crypto.json
|
||||
|
||||
qapi-types.c qapi-types.h :\
|
||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
|
||||
|
||||
@@ -28,6 +28,11 @@ crypto-aes-obj-y = crypto/
|
||||
|
||||
qom-obj-y = qom/
|
||||
|
||||
#######################################################################
|
||||
# io-obj-y is code used by both qemu system emulation and qemu-img
|
||||
|
||||
io-obj-y = io/
|
||||
|
||||
######################################################################
|
||||
# Target independent part of system emulation. The long term path is to
|
||||
# suppress *all* target specific code in case of system emulation, i.e. a
|
||||
|
||||
@@ -176,6 +176,7 @@ dummy := $(call unnest-vars,.., \
|
||||
crypto-obj-y \
|
||||
crypto-aes-obj-y \
|
||||
qom-obj-y \
|
||||
io-obj-y \
|
||||
common-obj-y \
|
||||
common-obj-m)
|
||||
target-obj-y := $(target-obj-y-save)
|
||||
@@ -185,6 +186,7 @@ all-obj-y += $(qom-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
|
||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
|
||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
|
||||
|
||||
$(QEMU_PROG_BUILD): config-devices.mak
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
#define AUDIO_CAP "coreaudio"
|
||||
#include "audio_int.h"
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_6
|
||||
#define MAC_OS_X_VERSION_10_6 1060
|
||||
#endif
|
||||
|
||||
static int isAtexit;
|
||||
|
||||
typedef struct {
|
||||
@@ -45,11 +49,233 @@ typedef struct coreaudioVoiceOut {
|
||||
AudioDeviceID outputDeviceID;
|
||||
UInt32 audioDevicePropertyBufferFrameSize;
|
||||
AudioStreamBasicDescription outputStreamBasicDescription;
|
||||
AudioDeviceIOProcID ioprocid;
|
||||
int live;
|
||||
int decr;
|
||||
int rpos;
|
||||
} coreaudioVoiceOut;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
|
||||
/* The APIs used here only become available from 10.6 */
|
||||
|
||||
static OSStatus coreaudio_get_voice(AudioDeviceID *id)
|
||||
{
|
||||
UInt32 size = sizeof(*id);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
id);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
|
||||
AudioValueRange *framerange)
|
||||
{
|
||||
UInt32 size = sizeof(*framerange);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
framerange);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
{
|
||||
UInt32 size = sizeof(*framesize);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
{
|
||||
UInt32 size = sizeof(*framesize);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectSetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
size,
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectSetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
|
||||
{
|
||||
UInt32 size = sizeof(*result);
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioDevicePropertyDeviceIsRunning,
|
||||
kAudioDevicePropertyScopeOutput,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
return AudioObjectGetPropertyData(id,
|
||||
&addr,
|
||||
0,
|
||||
NULL,
|
||||
&size,
|
||||
result);
|
||||
}
|
||||
#else
|
||||
/* Legacy versions of functions using deprecated APIs */
|
||||
|
||||
static OSStatus coreaudio_get_voice(AudioDeviceID *id)
|
||||
{
|
||||
UInt32 size = sizeof(*id);
|
||||
|
||||
return AudioHardwareGetProperty(
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
&size,
|
||||
id);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
|
||||
AudioValueRange *framerange)
|
||||
{
|
||||
UInt32 size = sizeof(*framerange);
|
||||
|
||||
return AudioDeviceGetProperty(
|
||||
id,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&size,
|
||||
framerange);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
{
|
||||
UInt32 size = sizeof(*framesize);
|
||||
|
||||
return AudioDeviceGetProperty(
|
||||
id,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
&size,
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
|
||||
{
|
||||
UInt32 size = sizeof(*framesize);
|
||||
|
||||
return AudioDeviceSetProperty(
|
||||
id,
|
||||
NULL,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
size,
|
||||
framesize);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
|
||||
return AudioDeviceGetProperty(
|
||||
id,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
&size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
|
||||
AudioStreamBasicDescription *d)
|
||||
{
|
||||
UInt32 size = sizeof(*d);
|
||||
|
||||
return AudioDeviceSetProperty(
|
||||
id,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
size,
|
||||
d);
|
||||
}
|
||||
|
||||
static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
|
||||
{
|
||||
UInt32 size = sizeof(*result);
|
||||
|
||||
return AudioDeviceGetProperty(
|
||||
id,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyDeviceIsRunning,
|
||||
&size,
|
||||
result);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void coreaudio_logstatus (OSStatus status)
|
||||
{
|
||||
const char *str = "BUG";
|
||||
@@ -144,10 +370,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
|
||||
{
|
||||
OSStatus status;
|
||||
UInt32 result = 0;
|
||||
UInt32 propertySize = sizeof(outputDeviceID);
|
||||
status = AudioDeviceGetProperty(
|
||||
outputDeviceID, 0, 0,
|
||||
kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
|
||||
status = coreaudio_get_isrunning(outputDeviceID, &result);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr(status,
|
||||
"Could not determine whether Device is playing\n");
|
||||
@@ -288,7 +511,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
{
|
||||
OSStatus status;
|
||||
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
|
||||
UInt32 propertySize;
|
||||
int err;
|
||||
const char *typ = "playback";
|
||||
AudioValueRange frameRange;
|
||||
@@ -303,12 +525,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
audio_pcm_init_info (&hw->info, as);
|
||||
|
||||
/* open default output device */
|
||||
propertySize = sizeof(core->outputDeviceID);
|
||||
status = AudioHardwareGetProperty(
|
||||
kAudioHardwarePropertyDefaultOutputDevice,
|
||||
&propertySize,
|
||||
&core->outputDeviceID);
|
||||
status = coreaudio_get_voice(&core->outputDeviceID);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get default output Device\n");
|
||||
@@ -320,14 +537,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
/* get minimum and maximum buffer frame sizes */
|
||||
propertySize = sizeof(frameRange);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyBufferFrameSizeRange,
|
||||
&propertySize,
|
||||
&frameRange);
|
||||
status = coreaudio_get_framesizerange(core->outputDeviceID,
|
||||
&frameRange);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get device buffer frame range\n");
|
||||
@@ -347,15 +558,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
/* set Buffer Frame Size */
|
||||
propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
|
||||
status = AudioDeviceSetProperty(
|
||||
core->outputDeviceID,
|
||||
NULL,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
propertySize,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
status = coreaudio_set_framesize(core->outputDeviceID,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not set device buffer frame size %" PRIu32 "\n",
|
||||
@@ -364,14 +568,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
/* get Buffer Frame Size */
|
||||
propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
&propertySize,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
status = coreaudio_get_framesize(core->outputDeviceID,
|
||||
&core->audioDevicePropertyBufferFrameSize);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get device buffer frame size\n");
|
||||
@@ -380,14 +578,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
|
||||
|
||||
/* get StreamFormat */
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
status = AudioDeviceGetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
false,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
&propertySize,
|
||||
&core->outputStreamBasicDescription);
|
||||
status = coreaudio_get_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ,
|
||||
"Could not get Device Stream properties\n");
|
||||
@@ -397,15 +589,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
/* set Samplerate */
|
||||
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
|
||||
propertySize = sizeof(core->outputStreamBasicDescription);
|
||||
status = AudioDeviceSetProperty(
|
||||
core->outputDeviceID,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
kAudioDevicePropertyStreamFormat,
|
||||
propertySize,
|
||||
&core->outputStreamBasicDescription);
|
||||
status = coreaudio_set_streamformat(core->outputDeviceID,
|
||||
&core->outputStreamBasicDescription);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
|
||||
as->freq);
|
||||
@@ -414,8 +599,12 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
}
|
||||
|
||||
/* set Callback */
|
||||
status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
core->ioprocid = NULL;
|
||||
status = AudioDeviceCreateIOProcID(core->outputDeviceID,
|
||||
audioDeviceIOProc,
|
||||
hw,
|
||||
&core->ioprocid);
|
||||
if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
|
||||
coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
@@ -423,10 +612,10 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||
|
||||
/* start Playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
|
||||
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr2 (status, typ, "Could not start playback\n");
|
||||
AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
|
||||
AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid);
|
||||
core->outputDeviceID = kAudioDeviceUnknown;
|
||||
return -1;
|
||||
}
|
||||
@@ -444,15 +633,15 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
|
||||
if (!isAtexit) {
|
||||
/* stop playback */
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not stop playback\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* remove callback */
|
||||
status = AudioDeviceRemoveIOProc(core->outputDeviceID,
|
||||
audioDeviceIOProc);
|
||||
status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
|
||||
core->ioprocid);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not remove IOProc\n");
|
||||
}
|
||||
@@ -475,7 +664,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
case VOICE_ENABLE:
|
||||
/* start playback */
|
||||
if (!isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
|
||||
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not resume playback\n");
|
||||
}
|
||||
@@ -486,7 +675,8 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
/* stop playback */
|
||||
if (!isAtexit) {
|
||||
if (isPlaying(core->outputDeviceID)) {
|
||||
status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
|
||||
status = AudioDeviceStop(core->outputDeviceID,
|
||||
core->ioprocid);
|
||||
if (status != kAudioHardwareNoError) {
|
||||
coreaudio_logerr (status, "Could not pause playback\n");
|
||||
}
|
||||
|
||||
469
block.c
469
block.c
@@ -29,6 +29,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
@@ -623,6 +624,20 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines a QDict of new block driver @options with any missing options taken
|
||||
* from @old_options, so that leaving out an option defaults to its old value.
|
||||
*/
|
||||
static void bdrv_join_options(BlockDriverState *bs, QDict *options,
|
||||
QDict *old_options)
|
||||
{
|
||||
if (bs->drv && bs->drv->bdrv_join_options) {
|
||||
bs->drv->bdrv_join_options(options, old_options);
|
||||
} else {
|
||||
qdict_join(options, old_options, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set open flags for a given discard mode
|
||||
*
|
||||
@@ -681,60 +696,81 @@ static int bdrv_temp_snapshot_flags(int flags)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->file should get if a protocol driver is expected,
|
||||
* based on the given flags for the parent BDS
|
||||
* Returns the options and flags that bs->file should get if a protocol driver
|
||||
* is expected, based on the given options and flags for the parent BDS
|
||||
*/
|
||||
static int bdrv_inherited_flags(int flags)
|
||||
static void bdrv_inherited_options(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
{
|
||||
int flags = parent_flags;
|
||||
|
||||
/* Enable protocol handling, disable format probing for bs->file */
|
||||
flags |= BDRV_O_PROTOCOL;
|
||||
|
||||
/* If the cache mode isn't explicitly set, inherit direct and no-flush from
|
||||
* the parent. */
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
|
||||
|
||||
/* Our block drivers take care to send flushes and respect unmap policy,
|
||||
* so we can enable both unconditionally on lower layers. */
|
||||
flags |= BDRV_O_CACHE_WB | BDRV_O_UNMAP;
|
||||
* so we can default to enable both on lower layers regardless of the
|
||||
* corresponding parent options. */
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_WB, "on");
|
||||
flags |= BDRV_O_UNMAP;
|
||||
|
||||
/* Clear flags that only apply to the top layer */
|
||||
flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ);
|
||||
|
||||
return flags;
|
||||
*child_flags = flags;
|
||||
}
|
||||
|
||||
const BdrvChildRole child_file = {
|
||||
.inherit_flags = bdrv_inherited_flags,
|
||||
.inherit_options = bdrv_inherited_options,
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->file should get if the use of formats (and not
|
||||
* only protocols) is permitted for it, based on the given flags for the parent
|
||||
* BDS
|
||||
* Returns the options and flags that bs->file should get if the use of formats
|
||||
* (and not only protocols) is permitted for it, based on the given options and
|
||||
* flags for the parent BDS
|
||||
*/
|
||||
static int bdrv_inherited_fmt_flags(int parent_flags)
|
||||
static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
{
|
||||
int flags = child_file.inherit_flags(parent_flags);
|
||||
return flags & ~BDRV_O_PROTOCOL;
|
||||
child_file.inherit_options(child_flags, child_options,
|
||||
parent_flags, parent_options);
|
||||
|
||||
*child_flags &= ~BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
const BdrvChildRole child_format = {
|
||||
.inherit_flags = bdrv_inherited_fmt_flags,
|
||||
.inherit_options = bdrv_inherited_fmt_options,
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the flags that bs->backing should get, based on the given flags
|
||||
* for the parent BDS
|
||||
* Returns the options and flags that bs->backing should get, based on the
|
||||
* given options and flags for the parent BDS
|
||||
*/
|
||||
static int bdrv_backing_flags(int flags)
|
||||
static void bdrv_backing_options(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
{
|
||||
int flags = parent_flags;
|
||||
|
||||
/* The cache mode is inherited unmodified for backing files */
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_WB);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
|
||||
|
||||
/* backing files always opened read-only */
|
||||
flags &= ~(BDRV_O_RDWR | BDRV_O_COPY_ON_READ);
|
||||
|
||||
/* snapshot=on is handled on the top layer */
|
||||
flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_TEMPORARY);
|
||||
|
||||
return flags;
|
||||
*child_flags = flags;
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_backing = {
|
||||
.inherit_flags = bdrv_backing_flags,
|
||||
.inherit_options = bdrv_backing_options,
|
||||
};
|
||||
|
||||
static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
@@ -757,6 +793,42 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
return open_flags;
|
||||
}
|
||||
|
||||
static void update_flags_from_options(int *flags, QemuOpts *opts)
|
||||
{
|
||||
*flags &= ~BDRV_O_CACHE_MASK;
|
||||
|
||||
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_WB));
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, false)) {
|
||||
*flags |= BDRV_O_CACHE_WB;
|
||||
}
|
||||
|
||||
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH));
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
|
||||
*flags |= BDRV_O_NO_FLUSH;
|
||||
}
|
||||
|
||||
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_DIRECT));
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
|
||||
*flags |= BDRV_O_NOCACHE;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_options_from_flags(QDict *options, int flags)
|
||||
{
|
||||
if (!qdict_haskey(options, BDRV_OPT_CACHE_WB)) {
|
||||
qdict_put(options, BDRV_OPT_CACHE_WB,
|
||||
qbool_from_bool(flags & BDRV_O_CACHE_WB));
|
||||
}
|
||||
if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) {
|
||||
qdict_put(options, BDRV_OPT_CACHE_DIRECT,
|
||||
qbool_from_bool(flags & BDRV_O_NOCACHE));
|
||||
}
|
||||
if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) {
|
||||
qdict_put(options, BDRV_OPT_CACHE_NO_FLUSH,
|
||||
qbool_from_bool(flags & BDRV_O_NO_FLUSH));
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_assign_node_name(BlockDriverState *bs,
|
||||
const char *node_name,
|
||||
Error **errp)
|
||||
@@ -803,6 +875,26 @@ static QemuOptsList bdrv_runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Node name of the block device node",
|
||||
},
|
||||
{
|
||||
.name = "driver",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "Block driver to use for the node",
|
||||
},
|
||||
{
|
||||
.name = BDRV_OPT_CACHE_WB,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Enable writeback mode",
|
||||
},
|
||||
{
|
||||
.name = BDRV_OPT_CACHE_DIRECT,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Bypass software writeback cache on the host",
|
||||
},
|
||||
{
|
||||
.name = BDRV_OPT_CACHE_NO_FLUSH,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Ignore flush requests",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
@@ -813,18 +905,31 @@ static QemuOptsList bdrv_runtime_opts = {
|
||||
* Removes all processed options from *options.
|
||||
*/
|
||||
static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
|
||||
QDict *options, int flags, BlockDriver *drv, Error **errp)
|
||||
QDict *options, int flags, Error **errp)
|
||||
{
|
||||
int ret, open_flags;
|
||||
const char *filename;
|
||||
const char *driver_name = NULL;
|
||||
const char *node_name = NULL;
|
||||
QemuOpts *opts;
|
||||
BlockDriver *drv;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(drv != NULL);
|
||||
assert(bs->file == NULL);
|
||||
assert(options != NULL && bs->options != options);
|
||||
|
||||
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
driver_name = qemu_opt_get(opts, "driver");
|
||||
drv = bdrv_find_format(driver_name);
|
||||
assert(drv != NULL);
|
||||
|
||||
if (file != NULL) {
|
||||
filename = file->bs->filename;
|
||||
} else {
|
||||
@@ -834,19 +939,12 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
|
||||
if (drv->bdrv_needs_filename && !filename) {
|
||||
error_setg(errp, "The '%s' block driver requires a file name",
|
||||
drv->format_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
|
||||
|
||||
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
|
||||
|
||||
node_name = qemu_opt_get(opts, "node-name");
|
||||
bdrv_assign_node_name(bs, node_name, &local_err);
|
||||
if (local_err) {
|
||||
@@ -891,7 +989,9 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
|
||||
bs->drv = drv;
|
||||
bs->opaque = g_malloc0(drv->instance_size);
|
||||
|
||||
bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
|
||||
/* Apply cache mode options */
|
||||
update_flags_from_options(&bs->open_flags, opts);
|
||||
bdrv_set_enable_write_cache(bs, bs->open_flags & BDRV_O_CACHE_WB);
|
||||
|
||||
/* Open the image, either directly or using a protocol */
|
||||
if (drv->bdrv_file_open) {
|
||||
@@ -984,37 +1084,45 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||
return options;
|
||||
}
|
||||
|
||||
static void parse_json_protocol(QDict *options, const char **pfilename,
|
||||
Error **errp)
|
||||
{
|
||||
QDict *json_options;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Parse json: pseudo-protocol */
|
||||
if (!*pfilename || !g_str_has_prefix(*pfilename, "json:")) {
|
||||
return;
|
||||
}
|
||||
|
||||
json_options = parse_json_filename(*pfilename, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Options given in the filename have lower priority than options
|
||||
* specified directly */
|
||||
qdict_join(options, json_options, false);
|
||||
QDECREF(json_options);
|
||||
*pfilename = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills in default options for opening images and converts the legacy
|
||||
* filename/flags pair to option QDict entries.
|
||||
* The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a
|
||||
* block driver has been specified explicitly.
|
||||
*/
|
||||
static int bdrv_fill_options(QDict **options, const char **pfilename,
|
||||
static int bdrv_fill_options(QDict **options, const char *filename,
|
||||
int *flags, Error **errp)
|
||||
{
|
||||
const char *filename = *pfilename;
|
||||
const char *drvname;
|
||||
bool protocol = *flags & BDRV_O_PROTOCOL;
|
||||
bool parse_filename = false;
|
||||
BlockDriver *drv = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* Parse json: pseudo-protocol */
|
||||
if (filename && g_str_has_prefix(filename, "json:")) {
|
||||
QDict *json_options = parse_json_filename(filename, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Options given in the filename have lower priority than options
|
||||
* specified directly */
|
||||
qdict_join(*options, json_options, false);
|
||||
QDECREF(json_options);
|
||||
*pfilename = filename = NULL;
|
||||
}
|
||||
|
||||
drvname = qdict_get_try_str(*options, "driver");
|
||||
if (drvname) {
|
||||
drv = bdrv_find_format(drvname);
|
||||
@@ -1033,6 +1141,9 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
|
||||
*flags &= ~BDRV_O_PROTOCOL;
|
||||
}
|
||||
|
||||
/* Translate cache options from flags into options */
|
||||
update_options_from_flags(*options, *flags);
|
||||
|
||||
/* Fetch the file name from the options QDict if necessary */
|
||||
if (protocol && filename) {
|
||||
if (!qdict_haskey(*options, "filename")) {
|
||||
@@ -1087,11 +1198,13 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
|
||||
|
||||
static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
|
||||
BlockDriverState *child_bs,
|
||||
const char *child_name,
|
||||
const BdrvChildRole *child_role)
|
||||
{
|
||||
BdrvChild *child = g_new(BdrvChild, 1);
|
||||
*child = (BdrvChild) {
|
||||
.bs = child_bs,
|
||||
.name = g_strdup(child_name),
|
||||
.role = child_role,
|
||||
};
|
||||
|
||||
@@ -1105,6 +1218,7 @@ static void bdrv_detach_child(BdrvChild *child)
|
||||
{
|
||||
QLIST_REMOVE(child, next);
|
||||
QLIST_REMOVE(child, next_parent);
|
||||
g_free(child->name);
|
||||
g_free(child);
|
||||
}
|
||||
|
||||
@@ -1151,7 +1265,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
|
||||
bs->backing = NULL;
|
||||
goto out;
|
||||
}
|
||||
bs->backing = bdrv_attach_child(bs, backing_hd, &child_backing);
|
||||
bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
|
||||
bs->open_flags &= ~BDRV_O_NO_BACKING;
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
|
||||
pstrcpy(bs->backing_format, sizeof(bs->backing_format),
|
||||
@@ -1168,30 +1282,43 @@ out:
|
||||
/*
|
||||
* Opens the backing file for a BlockDriverState if not yet open
|
||||
*
|
||||
* options is a QDict of options to pass to the block drivers, or NULL for an
|
||||
* empty set of options. The reference to the QDict is transferred to this
|
||||
* function (even on failure), so if the caller intends to reuse the dictionary,
|
||||
* it needs to use QINCREF() before calling bdrv_file_open.
|
||||
* bdref_key specifies the key for the image's BlockdevRef in the options QDict.
|
||||
* That QDict has to be flattened; therefore, if the BlockdevRef is a QDict
|
||||
* itself, all options starting with "${bdref_key}." are considered part of the
|
||||
* BlockdevRef.
|
||||
*
|
||||
* TODO Can this be unified with bdrv_open_image()?
|
||||
*/
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
const char *bdref_key, Error **errp)
|
||||
{
|
||||
char *backing_filename = g_malloc0(PATH_MAX);
|
||||
char *bdref_key_dot;
|
||||
const char *reference = NULL;
|
||||
int ret = 0;
|
||||
BlockDriverState *backing_hd;
|
||||
QDict *options;
|
||||
QDict *tmp_parent_options = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (bs->backing != NULL) {
|
||||
QDECREF(options);
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
/* NULL means an empty set of options */
|
||||
if (options == NULL) {
|
||||
options = qdict_new();
|
||||
if (parent_options == NULL) {
|
||||
tmp_parent_options = qdict_new();
|
||||
parent_options = tmp_parent_options;
|
||||
}
|
||||
|
||||
bs->open_flags &= ~BDRV_O_NO_BACKING;
|
||||
if (qdict_haskey(options, "file.filename")) {
|
||||
|
||||
bdref_key_dot = g_strdup_printf("%s.", bdref_key);
|
||||
qdict_extract_subqdict(parent_options, &options, bdref_key_dot);
|
||||
g_free(bdref_key_dot);
|
||||
|
||||
reference = qdict_get_try_str(parent_options, bdref_key);
|
||||
if (reference || qdict_haskey(options, "file.filename")) {
|
||||
backing_filename[0] = '\0';
|
||||
} else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
|
||||
QDECREF(options);
|
||||
@@ -1214,19 +1341,16 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
backing_hd = bdrv_new();
|
||||
|
||||
if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) {
|
||||
qdict_put(options, "driver", qstring_from_str(bs->backing_format));
|
||||
}
|
||||
|
||||
assert(bs->backing == NULL);
|
||||
backing_hd = NULL;
|
||||
ret = bdrv_open_inherit(&backing_hd,
|
||||
*backing_filename ? backing_filename : NULL,
|
||||
NULL, options, 0, bs, &child_backing, &local_err);
|
||||
reference, options, 0, bs, &child_backing,
|
||||
&local_err);
|
||||
if (ret < 0) {
|
||||
bdrv_unref(backing_hd);
|
||||
backing_hd = NULL;
|
||||
bs->open_flags |= BDRV_O_NO_BACKING;
|
||||
error_setg(errp, "Could not open backing file: %s",
|
||||
error_get_pretty(local_err));
|
||||
@@ -1239,8 +1363,11 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
||||
bdrv_set_backing_hd(bs, backing_hd);
|
||||
bdrv_unref(backing_hd);
|
||||
|
||||
qdict_del(parent_options, bdref_key);
|
||||
|
||||
free_exit:
|
||||
g_free(backing_filename);
|
||||
QDECREF(tmp_parent_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1294,7 +1421,7 @@ BdrvChild *bdrv_open_child(const char *filename,
|
||||
goto done;
|
||||
}
|
||||
|
||||
c = bdrv_attach_child(parent, bs, child_role);
|
||||
c = bdrv_attach_child(parent, bs, bdref_key, child_role);
|
||||
|
||||
done:
|
||||
qdict_del(options, bdref_key);
|
||||
@@ -1437,21 +1564,34 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
options = qdict_new();
|
||||
}
|
||||
|
||||
if (child_role) {
|
||||
bs->inherits_from = parent;
|
||||
flags = child_role->inherit_flags(parent->open_flags);
|
||||
/* json: syntax counts as explicit options, as if in the QDict */
|
||||
parse_json_protocol(options, &filename, &local_err);
|
||||
if (local_err) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_fill_options(&options, &filename, &flags, &local_err);
|
||||
bs->explicit_options = qdict_clone_shallow(options);
|
||||
|
||||
if (child_role) {
|
||||
bs->inherits_from = parent;
|
||||
child_role->inherit_options(&flags, options,
|
||||
parent->open_flags, parent->options);
|
||||
}
|
||||
|
||||
ret = bdrv_fill_options(&options, filename, &flags, &local_err);
|
||||
if (local_err) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs->open_flags = flags;
|
||||
bs->options = options;
|
||||
options = qdict_clone_shallow(options);
|
||||
|
||||
/* Find the right image format driver */
|
||||
drvname = qdict_get_try_str(options, "driver");
|
||||
if (drvname) {
|
||||
drv = bdrv_find_format(drvname);
|
||||
qdict_del(options, "driver");
|
||||
if (!drv) {
|
||||
error_setg(errp, "Unknown driver: '%s'", drvname);
|
||||
ret = -EINVAL;
|
||||
@@ -1467,10 +1607,6 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
qdict_del(options, "backing");
|
||||
}
|
||||
|
||||
bs->open_flags = flags;
|
||||
bs->options = options;
|
||||
options = qdict_clone_shallow(options);
|
||||
|
||||
/* Open image file without format layer */
|
||||
if ((flags & BDRV_O_PROTOCOL) == 0) {
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
@@ -1478,7 +1614,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
}
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
snapshot_flags = bdrv_temp_snapshot_flags(flags);
|
||||
flags = bdrv_backing_flags(flags);
|
||||
bdrv_backing_options(&flags, options, flags, options);
|
||||
}
|
||||
|
||||
bs->open_flags = flags;
|
||||
@@ -1498,6 +1634,19 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
/*
|
||||
* This option update would logically belong in bdrv_fill_options(),
|
||||
* but we first need to open bs->file for the probing to work, while
|
||||
* opening bs->file already requires the (mostly) final set of options
|
||||
* so that cache mode etc. can be inherited.
|
||||
*
|
||||
* Adding the driver later is somewhat ugly, but it's not an option
|
||||
* that would ever be inherited, so it's correct. We just need to make
|
||||
* sure to update both bs->options (which has the full effective
|
||||
* options for bs) and options (which has file.* already removed).
|
||||
*/
|
||||
qdict_put(bs->options, "driver", qstring_from_str(drv->format_name));
|
||||
qdict_put(options, "driver", qstring_from_str(drv->format_name));
|
||||
} else if (!drv) {
|
||||
error_setg(errp, "Must specify either driver or file");
|
||||
ret = -EINVAL;
|
||||
@@ -1511,7 +1660,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
assert(!(flags & BDRV_O_PROTOCOL) || !file);
|
||||
|
||||
/* Open the image */
|
||||
ret = bdrv_open_common(bs, file, options, flags, drv, &local_err);
|
||||
ret = bdrv_open_common(bs, file, options, flags, &local_err);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1523,10 +1672,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||
|
||||
/* If there is a backing file, use it */
|
||||
if ((flags & BDRV_O_NO_BACKING) == 0) {
|
||||
QDict *backing_options;
|
||||
|
||||
qdict_extract_subqdict(options, &backing_options, "backing.");
|
||||
ret = bdrv_open_backing_file(bs, backing_options, &local_err);
|
||||
ret = bdrv_open_backing_file(bs, options, "backing", &local_err);
|
||||
if (ret < 0) {
|
||||
goto close_and_fail;
|
||||
}
|
||||
@@ -1581,6 +1727,7 @@ fail:
|
||||
if (file != NULL) {
|
||||
bdrv_unref_child(bs, file);
|
||||
}
|
||||
QDECREF(bs->explicit_options);
|
||||
QDECREF(bs->options);
|
||||
QDECREF(options);
|
||||
bs->options = NULL;
|
||||
@@ -1643,15 +1790,19 @@ typedef struct BlockReopenQueueEntry {
|
||||
* bs_queue, or the existing bs_queue being used.
|
||||
*
|
||||
*/
|
||||
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
BlockDriverState *bs,
|
||||
QDict *options, int flags)
|
||||
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
BlockDriverState *bs,
|
||||
QDict *options,
|
||||
int flags,
|
||||
const BdrvChildRole *role,
|
||||
QDict *parent_options,
|
||||
int parent_flags)
|
||||
{
|
||||
assert(bs != NULL);
|
||||
|
||||
BlockReopenQueueEntry *bs_entry;
|
||||
BdrvChild *child;
|
||||
QDict *old_options;
|
||||
QDict *old_options, *explicit_options;
|
||||
|
||||
if (bs_queue == NULL) {
|
||||
bs_queue = g_new0(BlockReopenQueue, 1);
|
||||
@@ -1662,23 +1813,63 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
options = qdict_new();
|
||||
}
|
||||
|
||||
/*
|
||||
* Precedence of options:
|
||||
* 1. Explicitly passed in options (highest)
|
||||
* 2. Set in flags (only for top level)
|
||||
* 3. Retained from explicitly set options of bs
|
||||
* 4. Inherited from parent node
|
||||
* 5. Retained from effective options of bs
|
||||
*/
|
||||
|
||||
if (!parent_options) {
|
||||
/*
|
||||
* Any setting represented by flags is always updated. If the
|
||||
* corresponding QDict option is set, it takes precedence. Otherwise
|
||||
* the flag is translated into a QDict option. The old setting of bs is
|
||||
* not considered.
|
||||
*/
|
||||
update_options_from_flags(options, flags);
|
||||
}
|
||||
|
||||
/* Old explicitly set values (don't overwrite by inherited value) */
|
||||
old_options = qdict_clone_shallow(bs->explicit_options);
|
||||
bdrv_join_options(bs, options, old_options);
|
||||
QDECREF(old_options);
|
||||
|
||||
explicit_options = qdict_clone_shallow(options);
|
||||
|
||||
/* Inherit from parent node */
|
||||
if (parent_options) {
|
||||
assert(!flags);
|
||||
role->inherit_options(&flags, options, parent_flags, parent_options);
|
||||
}
|
||||
|
||||
/* Old values are used for options that aren't set yet */
|
||||
old_options = qdict_clone_shallow(bs->options);
|
||||
qdict_join(options, old_options, false);
|
||||
bdrv_join_options(bs, options, old_options);
|
||||
QDECREF(old_options);
|
||||
|
||||
/* bdrv_open() masks this flag out */
|
||||
flags &= ~BDRV_O_PROTOCOL;
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
int child_flags;
|
||||
QDict *new_child_options;
|
||||
char *child_key_dot;
|
||||
|
||||
/* reopen can only change the options of block devices that were
|
||||
* implicitly created and inherited options. For other (referenced)
|
||||
* block devices, a syntax like "backing.foo" results in an error. */
|
||||
if (child->bs->inherits_from != bs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
child_flags = child->role->inherit_flags(flags);
|
||||
/* TODO Pass down child flags (backing.*, extents.*, ...) */
|
||||
bdrv_reopen_queue(bs_queue, child->bs, NULL, child_flags);
|
||||
child_key_dot = g_strdup_printf("%s.", child->name);
|
||||
qdict_extract_subqdict(options, &new_child_options, child_key_dot);
|
||||
g_free(child_key_dot);
|
||||
|
||||
bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, 0,
|
||||
child->role, options, flags);
|
||||
}
|
||||
|
||||
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
||||
@@ -1686,11 +1877,20 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
|
||||
bs_entry->state.bs = bs;
|
||||
bs_entry->state.options = options;
|
||||
bs_entry->state.explicit_options = explicit_options;
|
||||
bs_entry->state.flags = flags;
|
||||
|
||||
return bs_queue;
|
||||
}
|
||||
|
||||
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
BlockDriverState *bs,
|
||||
QDict *options, int flags)
|
||||
{
|
||||
return bdrv_reopen_queue_child(bs_queue, bs, options, flags,
|
||||
NULL, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reopen multiple BlockDriverStates atomically & transactionally.
|
||||
*
|
||||
@@ -1737,6 +1937,8 @@ cleanup:
|
||||
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
||||
if (ret && bs_entry->prepared) {
|
||||
bdrv_reopen_abort(&bs_entry->state);
|
||||
} else if (ret) {
|
||||
QDECREF(bs_entry->state.explicit_options);
|
||||
}
|
||||
QDECREF(bs_entry->state.options);
|
||||
g_free(bs_entry);
|
||||
@@ -1784,11 +1986,47 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
||||
int ret = -1;
|
||||
Error *local_err = NULL;
|
||||
BlockDriver *drv;
|
||||
QemuOpts *opts;
|
||||
const char *value;
|
||||
|
||||
assert(reopen_state != NULL);
|
||||
assert(reopen_state->bs->drv != NULL);
|
||||
drv = reopen_state->bs->drv;
|
||||
|
||||
/* Process generic block layer options */
|
||||
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, reopen_state->options, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
update_flags_from_options(&reopen_state->flags, opts);
|
||||
|
||||
/* If a guest device is attached, it owns WCE */
|
||||
if (reopen_state->bs->blk && blk_get_attached_dev(reopen_state->bs->blk)) {
|
||||
bool old_wce = bdrv_enable_write_cache(reopen_state->bs);
|
||||
bool new_wce = (reopen_state->flags & BDRV_O_CACHE_WB);
|
||||
if (old_wce != new_wce) {
|
||||
error_setg(errp, "Cannot change cache.writeback: Device attached");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* node-name and driver must be unchanged. Put them back into the QDict, so
|
||||
* that they are checked at the end of this function. */
|
||||
value = qemu_opt_get(opts, "node-name");
|
||||
if (value) {
|
||||
qdict_put(reopen_state->options, "node-name", qstring_from_str(value));
|
||||
}
|
||||
|
||||
value = qemu_opt_get(opts, "driver");
|
||||
if (value) {
|
||||
qdict_put(reopen_state->options, "driver", qstring_from_str(value));
|
||||
}
|
||||
|
||||
/* if we are to stay read-only, do not allow permission change
|
||||
* to r/w */
|
||||
if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) &&
|
||||
@@ -1849,6 +2087,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
qemu_opts_del(opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1871,6 +2110,9 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||
}
|
||||
|
||||
/* set BDS specific flags now */
|
||||
QDECREF(reopen_state->bs->explicit_options);
|
||||
|
||||
reopen_state->bs->explicit_options = reopen_state->explicit_options;
|
||||
reopen_state->bs->open_flags = reopen_state->flags;
|
||||
reopen_state->bs->enable_write_cache = !!(reopen_state->flags &
|
||||
BDRV_O_CACHE_WB);
|
||||
@@ -1894,6 +2136,8 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
|
||||
if (drv->bdrv_reopen_abort) {
|
||||
drv->bdrv_reopen_abort(reopen_state);
|
||||
}
|
||||
|
||||
QDECREF(reopen_state->explicit_options);
|
||||
}
|
||||
|
||||
|
||||
@@ -1952,6 +2196,7 @@ void bdrv_close(BlockDriverState *bs)
|
||||
bs->sg = 0;
|
||||
bs->zero_beyond_eof = false;
|
||||
QDECREF(bs->options);
|
||||
QDECREF(bs->explicit_options);
|
||||
bs->options = NULL;
|
||||
QDECREF(bs->full_open_options);
|
||||
bs->full_open_options = NULL;
|
||||
@@ -2851,7 +3096,7 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
|
||||
{
|
||||
if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
|
||||
return;
|
||||
@@ -3823,12 +4068,12 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
|
||||
{
|
||||
if (!bs->drv->bdrv_amend_options) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return bs->drv->bdrv_amend_options(bs, opts, status_cb);
|
||||
return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque);
|
||||
}
|
||||
|
||||
/* This function will be called by the bdrv_recurse_is_first_non_filter method
|
||||
@@ -3926,20 +4171,39 @@ out:
|
||||
static bool append_open_options(QDict *d, BlockDriverState *bs)
|
||||
{
|
||||
const QDictEntry *entry;
|
||||
QemuOptDesc *desc;
|
||||
BdrvChild *child;
|
||||
bool found_any = false;
|
||||
const char *p;
|
||||
|
||||
for (entry = qdict_first(bs->options); entry;
|
||||
entry = qdict_next(bs->options, entry))
|
||||
{
|
||||
/* Only take options for this level and exclude all non-driver-specific
|
||||
* options */
|
||||
if (!strchr(qdict_entry_key(entry), '.') &&
|
||||
strcmp(qdict_entry_key(entry), "node-name"))
|
||||
{
|
||||
qobject_incref(qdict_entry_value(entry));
|
||||
qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry));
|
||||
found_any = true;
|
||||
/* Exclude options for children */
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
if (strstart(qdict_entry_key(entry), child->name, &p)
|
||||
&& (!*p || *p == '.'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (child) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* And exclude all non-driver-specific options */
|
||||
for (desc = bdrv_runtime_opts.desc; desc->name; desc++) {
|
||||
if (!strcmp(qdict_entry_key(entry), desc->name)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (desc->name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qobject_incref(qdict_entry_value(entry));
|
||||
qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry));
|
||||
found_any = true;
|
||||
}
|
||||
|
||||
return found_any;
|
||||
@@ -3981,7 +4245,10 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
||||
bs->full_open_options = NULL;
|
||||
}
|
||||
|
||||
drv->bdrv_refresh_filename(bs);
|
||||
opts = qdict_new();
|
||||
append_open_options(opts, bs);
|
||||
drv->bdrv_refresh_filename(bs, opts);
|
||||
QDECREF(opts);
|
||||
} else if (bs->file) {
|
||||
/* Try to reconstruct valid information from the underlying file */
|
||||
bool has_open_options;
|
||||
|
||||
103
block/blkdebug.c
103
block/blkdebug.c
@@ -36,7 +36,7 @@ typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
int new_state;
|
||||
|
||||
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
|
||||
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
|
||||
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
|
||||
QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
|
||||
} BDRVBlkdebugState;
|
||||
@@ -64,7 +64,7 @@ enum {
|
||||
};
|
||||
|
||||
typedef struct BlkdebugRule {
|
||||
BlkDebugEvent event;
|
||||
BlkdebugEvent event;
|
||||
int action;
|
||||
int state;
|
||||
union {
|
||||
@@ -143,69 +143,12 @@ static QemuOptsList *config_groups[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *event_names[BLKDBG_EVENT_MAX] = {
|
||||
[BLKDBG_L1_UPDATE] = "l1_update",
|
||||
[BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow.alloc_table",
|
||||
[BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow.write_table",
|
||||
[BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow.activate_table",
|
||||
|
||||
[BLKDBG_L2_LOAD] = "l2_load",
|
||||
[BLKDBG_L2_UPDATE] = "l2_update",
|
||||
[BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed",
|
||||
[BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read",
|
||||
[BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write",
|
||||
|
||||
[BLKDBG_READ_AIO] = "read_aio",
|
||||
[BLKDBG_READ_BACKING_AIO] = "read_backing_aio",
|
||||
[BLKDBG_READ_COMPRESSED] = "read_compressed",
|
||||
|
||||
[BLKDBG_WRITE_AIO] = "write_aio",
|
||||
[BLKDBG_WRITE_COMPRESSED] = "write_compressed",
|
||||
|
||||
[BLKDBG_VMSTATE_LOAD] = "vmstate_load",
|
||||
[BLKDBG_VMSTATE_SAVE] = "vmstate_save",
|
||||
|
||||
[BLKDBG_COW_READ] = "cow_read",
|
||||
[BLKDBG_COW_WRITE] = "cow_write",
|
||||
|
||||
[BLKDBG_REFTABLE_LOAD] = "reftable_load",
|
||||
[BLKDBG_REFTABLE_GROW] = "reftable_grow",
|
||||
[BLKDBG_REFTABLE_UPDATE] = "reftable_update",
|
||||
|
||||
[BLKDBG_REFBLOCK_LOAD] = "refblock_load",
|
||||
[BLKDBG_REFBLOCK_UPDATE] = "refblock_update",
|
||||
[BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part",
|
||||
[BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc",
|
||||
[BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc.hookup",
|
||||
[BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc.write",
|
||||
[BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc.write_blocks",
|
||||
[BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc.write_table",
|
||||
[BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc.switch_table",
|
||||
|
||||
[BLKDBG_CLUSTER_ALLOC] = "cluster_alloc",
|
||||
[BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes",
|
||||
[BLKDBG_CLUSTER_FREE] = "cluster_free",
|
||||
|
||||
[BLKDBG_FLUSH_TO_OS] = "flush_to_os",
|
||||
[BLKDBG_FLUSH_TO_DISK] = "flush_to_disk",
|
||||
|
||||
[BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw.head",
|
||||
[BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw.after_head",
|
||||
[BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw.tail",
|
||||
[BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw.after_tail",
|
||||
[BLKDBG_PWRITEV] = "pwritev",
|
||||
[BLKDBG_PWRITEV_ZERO] = "pwritev_zero",
|
||||
[BLKDBG_PWRITEV_DONE] = "pwritev_done",
|
||||
|
||||
[BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare",
|
||||
};
|
||||
|
||||
static int get_event_by_name(const char *name, BlkDebugEvent *event)
|
||||
static int get_event_by_name(const char *name, BlkdebugEvent *event)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
if (!strcmp(event_names[i], name)) {
|
||||
for (i = 0; i < BLKDBG__MAX; i++) {
|
||||
if (!strcmp(BlkdebugEvent_lookup[i], name)) {
|
||||
*event = i;
|
||||
return 0;
|
||||
}
|
||||
@@ -224,7 +167,7 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
|
||||
struct add_rule_data *d = opaque;
|
||||
BDRVBlkdebugState *s = d->s;
|
||||
const char* event_name;
|
||||
BlkDebugEvent event;
|
||||
BlkdebugEvent event;
|
||||
struct BlkdebugRule *rule;
|
||||
|
||||
/* Find the right event for the rule */
|
||||
@@ -564,7 +507,7 @@ static void blkdebug_close(BlockDriverState *bs)
|
||||
BlkdebugRule *rule, *next;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
for (i = 0; i < BLKDBG__MAX; i++) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
|
||||
remove_rule(rule);
|
||||
}
|
||||
@@ -627,13 +570,13 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
return injected;
|
||||
}
|
||||
|
||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule, *next;
|
||||
bool injected;
|
||||
|
||||
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
|
||||
assert((int)event >= 0 && event < BLKDBG__MAX);
|
||||
|
||||
injected = false;
|
||||
s->new_state = s->state;
|
||||
@@ -648,7 +591,7 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule;
|
||||
BlkDebugEvent blkdebug_event;
|
||||
BlkdebugEvent blkdebug_event;
|
||||
|
||||
if (get_event_by_name(event, &blkdebug_event) < 0) {
|
||||
return -ENOENT;
|
||||
@@ -690,7 +633,7 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
|
||||
BlkdebugRule *rule, *next;
|
||||
int i, ret = -ENOENT;
|
||||
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
for (i = 0; i < BLKDBG__MAX; i++) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
|
||||
if (rule->action == ACTION_SUSPEND &&
|
||||
!strcmp(rule->options.suspend.tag, tag)) {
|
||||
@@ -731,17 +674,15 @@ static int blkdebug_truncate(BlockDriverState *bs, int64_t offset)
|
||||
return bdrv_truncate(bs->file->bs, offset);
|
||||
}
|
||||
|
||||
static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
{
|
||||
QDict *opts;
|
||||
const QDictEntry *e;
|
||||
bool force_json = false;
|
||||
|
||||
for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
|
||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "config") &&
|
||||
strcmp(qdict_entry_key(e), "x-image") &&
|
||||
strcmp(qdict_entry_key(e), "image") &&
|
||||
strncmp(qdict_entry_key(e), "image.", strlen("image.")))
|
||||
strcmp(qdict_entry_key(e), "x-image"))
|
||||
{
|
||||
force_json = true;
|
||||
break;
|
||||
@@ -757,7 +698,7 @@ static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
if (!force_json && bs->file->bs->exact_filename[0]) {
|
||||
snprintf(bs->exact_filename, sizeof(bs->exact_filename),
|
||||
"blkdebug:%s:%s",
|
||||
qdict_get_try_str(bs->options, "config") ?: "",
|
||||
qdict_get_try_str(options, "config") ?: "",
|
||||
bs->file->bs->exact_filename);
|
||||
}
|
||||
|
||||
@@ -767,11 +708,8 @@ static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options));
|
||||
|
||||
for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "x-image") &&
|
||||
strcmp(qdict_entry_key(e), "image") &&
|
||||
strncmp(qdict_entry_key(e), "image.", strlen("image.")))
|
||||
{
|
||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "x-image")) {
|
||||
qobject_incref(qdict_entry_value(e));
|
||||
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
|
||||
}
|
||||
@@ -780,6 +718,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs)
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
||||
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_blkdebug = {
|
||||
.format_name = "blkdebug",
|
||||
.protocol_name = "blkdebug",
|
||||
@@ -788,6 +732,7 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_parse_filename = blkdebug_parse_filename,
|
||||
.bdrv_file_open = blkdebug_open,
|
||||
.bdrv_close = blkdebug_close,
|
||||
.bdrv_reopen_prepare = blkdebug_reopen_prepare,
|
||||
.bdrv_getlength = blkdebug_getlength,
|
||||
.bdrv_truncate = blkdebug_truncate,
|
||||
.bdrv_refresh_filename = blkdebug_refresh_filename,
|
||||
|
||||
@@ -307,7 +307,7 @@ static void blkverify_attach_aio_context(BlockDriverState *bs,
|
||||
bdrv_attach_aio_context(s->test_file->bs, new_context);
|
||||
}
|
||||
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs)
|
||||
static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
|
||||
|
||||
@@ -1023,6 +1023,11 @@ int blk_get_max_transfer_length(BlockBackend *blk)
|
||||
}
|
||||
}
|
||||
|
||||
int blk_get_max_iov(BlockBackend *blk)
|
||||
{
|
||||
return blk->bs->bl.max_iov;
|
||||
}
|
||||
|
||||
void blk_set_guest_block_size(BlockBackend *blk, int align)
|
||||
{
|
||||
blk->guest_block_size = align;
|
||||
|
||||
17
block/io.c
17
block/io.c
@@ -166,9 +166,13 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
bs->bl.max_transfer_length = bs->file->bs->bl.max_transfer_length;
|
||||
bs->bl.min_mem_alignment = bs->file->bs->bl.min_mem_alignment;
|
||||
bs->bl.opt_mem_alignment = bs->file->bs->bl.opt_mem_alignment;
|
||||
bs->bl.max_iov = bs->file->bs->bl.max_iov;
|
||||
} else {
|
||||
bs->bl.min_mem_alignment = 512;
|
||||
bs->bl.opt_mem_alignment = getpagesize();
|
||||
|
||||
/* Safe default since most protocols use readv()/writev()/etc */
|
||||
bs->bl.max_iov = IOV_MAX;
|
||||
}
|
||||
|
||||
if (bs->backing) {
|
||||
@@ -189,6 +193,9 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
bs->bl.min_mem_alignment =
|
||||
MAX(bs->bl.min_mem_alignment,
|
||||
bs->backing->bs->bl.min_mem_alignment);
|
||||
bs->bl.max_iov =
|
||||
MIN(bs->bl.max_iov,
|
||||
bs->backing->bs->bl.max_iov);
|
||||
}
|
||||
|
||||
/* Then let the driver override it */
|
||||
@@ -1882,7 +1889,8 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
|
||||
merge = 1;
|
||||
}
|
||||
|
||||
if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > IOV_MAX) {
|
||||
if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 >
|
||||
bs->bl.max_iov) {
|
||||
merge = 0;
|
||||
}
|
||||
|
||||
@@ -2614,10 +2622,11 @@ int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
bdrv_co_ioctl_entry(&data);
|
||||
} else {
|
||||
Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry);
|
||||
|
||||
qemu_coroutine_enter(co, &data);
|
||||
}
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
}
|
||||
return data.ret;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/ratelimit.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
#define MAX_IN_FLIGHT 16
|
||||
@@ -160,13 +161,15 @@ static void mirror_read_complete(void *opaque, int ret)
|
||||
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
{
|
||||
BlockDriverState *source = s->common.bs;
|
||||
int nb_sectors, sectors_per_chunk, nb_chunks;
|
||||
int nb_sectors, sectors_per_chunk, nb_chunks, max_iov;
|
||||
int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector;
|
||||
uint64_t delay_ns = 0;
|
||||
MirrorOp *op;
|
||||
int pnum;
|
||||
int64_t ret;
|
||||
|
||||
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);
|
||||
@@ -247,7 +250,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight);
|
||||
break;
|
||||
}
|
||||
if (IOV_MAX < nb_chunks + added_chunks) {
|
||||
if (max_iov < nb_chunks + added_chunks) {
|
||||
trace_mirror_break_iov_max(s, nb_chunks, added_chunks);
|
||||
break;
|
||||
}
|
||||
@@ -370,11 +373,22 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
||||
if (s->to_replace) {
|
||||
to_replace = s->to_replace;
|
||||
}
|
||||
|
||||
/* This was checked in mirror_start_job(), but meanwhile one of the
|
||||
* nodes could have been newly attached to a BlockBackend. */
|
||||
if (to_replace->blk && s->target->blk) {
|
||||
error_report("block job: Can't create node with two BlockBackends");
|
||||
data->ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
|
||||
bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
|
||||
}
|
||||
bdrv_replace_in_backing_chain(to_replace, s->target);
|
||||
}
|
||||
|
||||
out:
|
||||
if (s->to_replace) {
|
||||
bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
|
||||
error_free(s->replace_blocker);
|
||||
@@ -640,7 +654,7 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_open_backing_file(s->target, NULL, &local_err);
|
||||
ret = bdrv_open_backing_file(s->target, NULL, "backing", &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
@@ -705,6 +719,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
bool is_none_mode, BlockDriverState *base)
|
||||
{
|
||||
MirrorBlockJob *s;
|
||||
BlockDriverState *replaced_bs;
|
||||
|
||||
if (granularity == 0) {
|
||||
granularity = bdrv_get_default_bitmap_granularity(target);
|
||||
@@ -728,6 +743,21 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
buf_size = DEFAULT_MIRROR_BUF_SIZE;
|
||||
}
|
||||
|
||||
/* We can't support this case as long as the block layer can't handle
|
||||
* multiple BlockBackends per BlockDriverState. */
|
||||
if (replaces) {
|
||||
replaced_bs = bdrv_lookup_bs(replaces, replaces, errp);
|
||||
if (replaced_bs == NULL) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
replaced_bs = bs;
|
||||
}
|
||||
if (replaced_bs->blk && target->blk) {
|
||||
error_setg(errp, "Can't create node with two BlockBackends");
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(driver, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
|
||||
10
block/nbd.c
10
block/nbd.c
@@ -342,13 +342,13 @@ static void nbd_attach_aio_context(BlockDriverState *bs,
|
||||
nbd_client_attach_aio_context(bs, new_context);
|
||||
}
|
||||
|
||||
static void nbd_refresh_filename(BlockDriverState *bs)
|
||||
static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
{
|
||||
QDict *opts = qdict_new();
|
||||
const char *path = qdict_get_try_str(bs->options, "path");
|
||||
const char *host = qdict_get_try_str(bs->options, "host");
|
||||
const char *port = qdict_get_try_str(bs->options, "port");
|
||||
const char *export = qdict_get_try_str(bs->options, "export");
|
||||
const char *path = qdict_get_try_str(options, "path");
|
||||
const char *host = qdict_get_try_str(options, "host");
|
||||
const char *port = qdict_get_try_str(options, "port");
|
||||
const char *export = qdict_get_try_str(options, "export");
|
||||
|
||||
qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ typedef struct ParallelsHeader {
|
||||
typedef enum ParallelsPreallocMode {
|
||||
PRL_PREALLOC_MODE_FALLOCATE = 0,
|
||||
PRL_PREALLOC_MODE_TRUNCATE = 1,
|
||||
PRL_PREALLOC_MODE_MAX = 2,
|
||||
PRL_PREALLOC_MODE__MAX = 2,
|
||||
} ParallelsPreallocMode;
|
||||
|
||||
static const char *prealloc_mode_lookup[] = {
|
||||
@@ -660,7 +660,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS);
|
||||
buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE);
|
||||
s->prealloc_mode = qapi_enum_parse(prealloc_mode_lookup, buf,
|
||||
PRL_PREALLOC_MODE_MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err);
|
||||
PRL_PREALLOC_MODE__MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err);
|
||||
g_free(buf);
|
||||
if (local_err != NULL) {
|
||||
goto fail_options;
|
||||
|
||||
23
block/qapi.c
23
block/qapi.c
@@ -245,15 +245,17 @@ void bdrv_query_image_info(BlockDriverState *bs,
|
||||
info->has_backing_filename = true;
|
||||
bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
qapi_free_ImageInfo(info);
|
||||
/* Can't reconstruct the full backing filename, so we must omit
|
||||
* this field and apply a Best Effort to this query. */
|
||||
g_free(backing_filename2);
|
||||
return;
|
||||
backing_filename2 = NULL;
|
||||
error_free(err);
|
||||
}
|
||||
|
||||
if (strcmp(backing_filename, backing_filename2) != 0) {
|
||||
info->full_backing_filename =
|
||||
g_strdup(backing_filename2);
|
||||
/* Always report the full_backing_filename if present, even if it's the
|
||||
* same as backing_filename. That they are same is useful info. */
|
||||
if (backing_filename2) {
|
||||
info->full_backing_filename = g_strdup(backing_filename2);
|
||||
info->has_full_backing_filename = true;
|
||||
}
|
||||
|
||||
@@ -588,7 +590,7 @@ static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
|
||||
int i = 0;
|
||||
|
||||
for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
|
||||
qtype_code type = qobject_type(entry->value);
|
||||
QType type = qobject_type(entry->value);
|
||||
bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
|
||||
const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: ";
|
||||
|
||||
@@ -606,7 +608,7 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
|
||||
const QDictEntry *entry;
|
||||
|
||||
for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
|
||||
qtype_code type = qobject_type(entry->value);
|
||||
QType type = qobject_type(entry->value);
|
||||
bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
|
||||
const char *format = composite ? "%*s%s:\n" : "%*s%s: ";
|
||||
char key[strlen(entry->key) + 1];
|
||||
@@ -676,7 +678,10 @@ void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
|
||||
|
||||
if (info->has_backing_filename) {
|
||||
func_fprintf(f, "backing file: %s", info->backing_filename);
|
||||
if (info->has_full_backing_filename) {
|
||||
if (!info->has_full_backing_filename) {
|
||||
func_fprintf(f, " (cannot determine actual path)");
|
||||
} else if (strcmp(info->backing_filename,
|
||||
info->full_backing_filename) != 0) {
|
||||
func_fprintf(f, " (actual path: %s)", info->full_backing_filename);
|
||||
}
|
||||
func_fprintf(f, "\n");
|
||||
|
||||
@@ -1641,7 +1641,8 @@ fail:
|
||||
static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
int l1_size, int64_t *visited_l1_entries,
|
||||
int64_t l1_entries,
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
bool is_active_l1 = (l1_table == s->l1_table);
|
||||
@@ -1667,7 +1668,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
/* unallocated */
|
||||
(*visited_l1_entries)++;
|
||||
if (status_cb) {
|
||||
status_cb(bs, *visited_l1_entries, l1_entries);
|
||||
status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -1804,7 +1805,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
|
||||
(*visited_l1_entries)++;
|
||||
if (status_cb) {
|
||||
status_cb(bs, *visited_l1_entries, l1_entries);
|
||||
status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1828,7 +1829,8 @@ fail:
|
||||
* qcow2 version which doesn't yet support metadata zero clusters.
|
||||
*/
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t *l1_table = NULL;
|
||||
@@ -1845,7 +1847,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
|
||||
ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size,
|
||||
&visited_l1_entries, l1_entries,
|
||||
status_cb);
|
||||
status_cb, cb_opaque);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1881,7 +1883,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
|
||||
ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size,
|
||||
&visited_l1_entries, l1_entries,
|
||||
status_cb);
|
||||
status_cb, cb_opaque);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -1345,6 +1345,9 @@ static int inc_refcounts(BlockDriverState *bs,
|
||||
if (refcount == s->refcount_max) {
|
||||
fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64
|
||||
"\n", cluster_offset);
|
||||
fprintf(stderr, "Use qemu-img amend to increase the refcount entry "
|
||||
"width or qemu-img convert to create a clean copy if the "
|
||||
"image cannot be opened for writing\n");
|
||||
res->corruptions++;
|
||||
continue;
|
||||
}
|
||||
@@ -2467,3 +2470,450 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A pointer to a function of this type is given to walk_over_reftable(). That
|
||||
* function will create refblocks and pass them to a RefblockFinishOp once they
|
||||
* are completed (@refblock). @refblock_empty is set if the refblock is
|
||||
* completely empty.
|
||||
*
|
||||
* Along with the refblock, a corresponding reftable entry is passed, in the
|
||||
* reftable @reftable (which may be reallocated) at @reftable_index.
|
||||
*
|
||||
* @allocated should be set to true if a new cluster has been allocated.
|
||||
*/
|
||||
typedef int (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable,
|
||||
uint64_t reftable_index, uint64_t *reftable_size,
|
||||
void *refblock, bool refblock_empty,
|
||||
bool *allocated, Error **errp);
|
||||
|
||||
/**
|
||||
* This "operation" for walk_over_reftable() allocates the refblock on disk (if
|
||||
* it is not empty) and inserts its offset into the new reftable. The size of
|
||||
* this new reftable is increased as required.
|
||||
*/
|
||||
static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable,
|
||||
uint64_t reftable_index, uint64_t *reftable_size,
|
||||
void *refblock, bool refblock_empty, bool *allocated,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int64_t offset;
|
||||
|
||||
if (!refblock_empty && reftable_index >= *reftable_size) {
|
||||
uint64_t *new_reftable;
|
||||
uint64_t new_reftable_size;
|
||||
|
||||
new_reftable_size = ROUND_UP(reftable_index + 1,
|
||||
s->cluster_size / sizeof(uint64_t));
|
||||
if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
|
||||
error_setg(errp,
|
||||
"This operation would make the refcount table grow "
|
||||
"beyond the maximum size supported by QEMU, aborting");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
new_reftable = g_try_realloc(*reftable, new_reftable_size *
|
||||
sizeof(uint64_t));
|
||||
if (!new_reftable) {
|
||||
error_setg(errp, "Failed to increase reftable buffer size");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(new_reftable + *reftable_size, 0,
|
||||
(new_reftable_size - *reftable_size) * sizeof(uint64_t));
|
||||
|
||||
*reftable = new_reftable;
|
||||
*reftable_size = new_reftable_size;
|
||||
}
|
||||
|
||||
if (!refblock_empty && !(*reftable)[reftable_index]) {
|
||||
offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||
if (offset < 0) {
|
||||
error_setg_errno(errp, -offset, "Failed to allocate refblock");
|
||||
return offset;
|
||||
}
|
||||
(*reftable)[reftable_index] = offset;
|
||||
*allocated = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This "operation" for walk_over_reftable() writes the refblock to disk at the
|
||||
* offset specified by the new reftable's entry. It does not modify the new
|
||||
* reftable or change any refcounts.
|
||||
*/
|
||||
static int flush_refblock(BlockDriverState *bs, uint64_t **reftable,
|
||||
uint64_t reftable_index, uint64_t *reftable_size,
|
||||
void *refblock, bool refblock_empty, bool *allocated,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int64_t offset;
|
||||
int ret;
|
||||
|
||||
if (reftable_index < *reftable_size && (*reftable)[reftable_index]) {
|
||||
offset = (*reftable)[reftable_index];
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Overlap check failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, offset, refblock, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to write refblock");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
assert(refblock_empty);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function walks over the existing reftable and every referenced refblock;
|
||||
* if @new_set_refcount is non-NULL, it is called for every refcount entry to
|
||||
* create an equal new entry in the passed @new_refblock. Once that
|
||||
* @new_refblock is completely filled, @operation will be called.
|
||||
*
|
||||
* @status_cb and @cb_opaque are used for the amend operation's status callback.
|
||||
* @index is the index of the walk_over_reftable() calls and @total is the total
|
||||
* number of walk_over_reftable() calls per amend operation. Both are used for
|
||||
* calculating the parameters for the status callback.
|
||||
*
|
||||
* @allocated is set to true if a new cluster has been allocated.
|
||||
*/
|
||||
static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable,
|
||||
uint64_t *new_reftable_index,
|
||||
uint64_t *new_reftable_size,
|
||||
void *new_refblock, int new_refblock_size,
|
||||
int new_refcount_bits,
|
||||
RefblockFinishOp *operation, bool *allocated,
|
||||
Qcow2SetRefcountFunc *new_set_refcount,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque, int index, int total,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t reftable_index;
|
||||
bool new_refblock_empty = true;
|
||||
int refblock_index;
|
||||
int new_refblock_index = 0;
|
||||
int ret;
|
||||
|
||||
for (reftable_index = 0; reftable_index < s->refcount_table_size;
|
||||
reftable_index++)
|
||||
{
|
||||
uint64_t refblock_offset = s->refcount_table[reftable_index]
|
||||
& REFT_OFFSET_MASK;
|
||||
|
||||
status_cb(bs, (uint64_t)index * s->refcount_table_size + reftable_index,
|
||||
(uint64_t)total * s->refcount_table_size, cb_opaque);
|
||||
|
||||
if (refblock_offset) {
|
||||
void *refblock;
|
||||
|
||||
if (offset_into_cluster(s, refblock_offset)) {
|
||||
qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#"
|
||||
PRIx64 " unaligned (reftable index: %#"
|
||||
PRIx64 ")", refblock_offset,
|
||||
reftable_index);
|
||||
error_setg(errp,
|
||||
"Image is corrupt (unaligned refblock offset)");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offset,
|
||||
&refblock);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to retrieve refblock");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (refblock_index = 0; refblock_index < s->refcount_block_size;
|
||||
refblock_index++)
|
||||
{
|
||||
uint64_t refcount;
|
||||
|
||||
if (new_refblock_index >= new_refblock_size) {
|
||||
/* new_refblock is now complete */
|
||||
ret = operation(bs, new_reftable, *new_reftable_index,
|
||||
new_reftable_size, new_refblock,
|
||||
new_refblock_empty, allocated, errp);
|
||||
if (ret < 0) {
|
||||
qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*new_reftable_index)++;
|
||||
new_refblock_index = 0;
|
||||
new_refblock_empty = true;
|
||||
}
|
||||
|
||||
refcount = s->get_refcount(refblock, refblock_index);
|
||||
if (new_refcount_bits < 64 && refcount >> new_refcount_bits) {
|
||||
uint64_t offset;
|
||||
|
||||
qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
|
||||
|
||||
offset = ((reftable_index << s->refcount_block_bits)
|
||||
+ refblock_index) << s->cluster_bits;
|
||||
|
||||
error_setg(errp, "Cannot decrease refcount entry width to "
|
||||
"%i bits: Cluster at offset %#" PRIx64 " has a "
|
||||
"refcount of %" PRIu64, new_refcount_bits,
|
||||
offset, refcount);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_set_refcount) {
|
||||
new_set_refcount(new_refblock, new_refblock_index++,
|
||||
refcount);
|
||||
} else {
|
||||
new_refblock_index++;
|
||||
}
|
||||
new_refblock_empty = new_refblock_empty && refcount == 0;
|
||||
}
|
||||
|
||||
qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
|
||||
} else {
|
||||
/* No refblock means every refcount is 0 */
|
||||
for (refblock_index = 0; refblock_index < s->refcount_block_size;
|
||||
refblock_index++)
|
||||
{
|
||||
if (new_refblock_index >= new_refblock_size) {
|
||||
/* new_refblock is now complete */
|
||||
ret = operation(bs, new_reftable, *new_reftable_index,
|
||||
new_reftable_size, new_refblock,
|
||||
new_refblock_empty, allocated, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*new_reftable_index)++;
|
||||
new_refblock_index = 0;
|
||||
new_refblock_empty = true;
|
||||
}
|
||||
|
||||
if (new_set_refcount) {
|
||||
new_set_refcount(new_refblock, new_refblock_index++, 0);
|
||||
} else {
|
||||
new_refblock_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_refblock_index > 0) {
|
||||
/* Complete the potentially existing partially filled final refblock */
|
||||
if (new_set_refcount) {
|
||||
for (; new_refblock_index < new_refblock_size;
|
||||
new_refblock_index++)
|
||||
{
|
||||
new_set_refcount(new_refblock, new_refblock_index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ret = operation(bs, new_reftable, *new_reftable_index,
|
||||
new_reftable_size, new_refblock, new_refblock_empty,
|
||||
allocated, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
(*new_reftable_index)++;
|
||||
}
|
||||
|
||||
status_cb(bs, (uint64_t)(index + 1) * s->refcount_table_size,
|
||||
(uint64_t)total * s->refcount_table_size, cb_opaque);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque, Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
Qcow2GetRefcountFunc *new_get_refcount;
|
||||
Qcow2SetRefcountFunc *new_set_refcount;
|
||||
void *new_refblock = qemu_blockalign(bs->file->bs, s->cluster_size);
|
||||
uint64_t *new_reftable = NULL, new_reftable_size = 0;
|
||||
uint64_t *old_reftable, old_reftable_size, old_reftable_offset;
|
||||
uint64_t new_reftable_index = 0;
|
||||
uint64_t i;
|
||||
int64_t new_reftable_offset = 0, allocated_reftable_size = 0;
|
||||
int new_refblock_size, new_refcount_bits = 1 << refcount_order;
|
||||
int old_refcount_order;
|
||||
int walk_index = 0;
|
||||
int ret;
|
||||
bool new_allocation;
|
||||
|
||||
assert(s->qcow_version >= 3);
|
||||
assert(refcount_order >= 0 && refcount_order <= 6);
|
||||
|
||||
/* see qcow2_open() */
|
||||
new_refblock_size = 1 << (s->cluster_bits - (refcount_order - 3));
|
||||
|
||||
new_get_refcount = get_refcount_funcs[refcount_order];
|
||||
new_set_refcount = set_refcount_funcs[refcount_order];
|
||||
|
||||
|
||||
do {
|
||||
int total_walks;
|
||||
|
||||
new_allocation = false;
|
||||
|
||||
/* At least we have to do this walk and the one which writes the
|
||||
* refblocks; also, at least we have to do this loop here at least
|
||||
* twice (normally), first to do the allocations, and second to
|
||||
* determine that everything is correctly allocated, this then makes
|
||||
* three walks in total */
|
||||
total_walks = MAX(walk_index + 2, 3);
|
||||
|
||||
/* First, allocate the structures so they are present in the refcount
|
||||
* structures */
|
||||
ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index,
|
||||
&new_reftable_size, NULL, new_refblock_size,
|
||||
new_refcount_bits, &alloc_refblock,
|
||||
&new_allocation, NULL, status_cb, cb_opaque,
|
||||
walk_index++, total_walks, errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
new_reftable_index = 0;
|
||||
|
||||
if (new_allocation) {
|
||||
if (new_reftable_offset) {
|
||||
qcow2_free_clusters(bs, new_reftable_offset,
|
||||
allocated_reftable_size * sizeof(uint64_t),
|
||||
QCOW2_DISCARD_NEVER);
|
||||
}
|
||||
|
||||
new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size *
|
||||
sizeof(uint64_t));
|
||||
if (new_reftable_offset < 0) {
|
||||
error_setg_errno(errp, -new_reftable_offset,
|
||||
"Failed to allocate the new reftable");
|
||||
ret = new_reftable_offset;
|
||||
goto done;
|
||||
}
|
||||
allocated_reftable_size = new_reftable_size;
|
||||
}
|
||||
} while (new_allocation);
|
||||
|
||||
/* Second, write the new refblocks */
|
||||
ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index,
|
||||
&new_reftable_size, new_refblock,
|
||||
new_refblock_size, new_refcount_bits,
|
||||
&flush_refblock, &new_allocation, new_set_refcount,
|
||||
status_cb, cb_opaque, walk_index, walk_index + 1,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
goto done;
|
||||
}
|
||||
assert(!new_allocation);
|
||||
|
||||
|
||||
/* Write the new reftable */
|
||||
ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset,
|
||||
new_reftable_size * sizeof(uint64_t));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Overlap check failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < new_reftable_size; i++) {
|
||||
cpu_to_be64s(&new_reftable[i]);
|
||||
}
|
||||
|
||||
ret = bdrv_pwrite(bs->file->bs, new_reftable_offset, new_reftable,
|
||||
new_reftable_size * sizeof(uint64_t));
|
||||
|
||||
for (i = 0; i < new_reftable_size; i++) {
|
||||
be64_to_cpus(&new_reftable[i]);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to write the new reftable");
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/* Empty the refcount cache */
|
||||
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to flush the refblock cache");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Update the image header to point to the new reftable; this only updates
|
||||
* the fields which are relevant to qcow2_update_header(); other fields
|
||||
* such as s->refcount_table or s->refcount_bits stay stale for now
|
||||
* (because we have to restore everything if qcow2_update_header() fails) */
|
||||
old_refcount_order = s->refcount_order;
|
||||
old_reftable_size = s->refcount_table_size;
|
||||
old_reftable_offset = s->refcount_table_offset;
|
||||
|
||||
s->refcount_order = refcount_order;
|
||||
s->refcount_table_size = new_reftable_size;
|
||||
s->refcount_table_offset = new_reftable_offset;
|
||||
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->refcount_order = old_refcount_order;
|
||||
s->refcount_table_size = old_reftable_size;
|
||||
s->refcount_table_offset = old_reftable_offset;
|
||||
error_setg_errno(errp, -ret, "Failed to update the qcow2 header");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Now update the rest of the in-memory information */
|
||||
old_reftable = s->refcount_table;
|
||||
s->refcount_table = new_reftable;
|
||||
|
||||
s->refcount_bits = 1 << refcount_order;
|
||||
s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1);
|
||||
s->refcount_max += s->refcount_max - 1;
|
||||
|
||||
s->refcount_block_bits = s->cluster_bits - (refcount_order - 3);
|
||||
s->refcount_block_size = 1 << s->refcount_block_bits;
|
||||
|
||||
s->get_refcount = new_get_refcount;
|
||||
s->set_refcount = new_set_refcount;
|
||||
|
||||
/* For cleaning up all old refblocks and the old reftable below the "done"
|
||||
* label */
|
||||
new_reftable = old_reftable;
|
||||
new_reftable_size = old_reftable_size;
|
||||
new_reftable_offset = old_reftable_offset;
|
||||
|
||||
done:
|
||||
if (new_reftable) {
|
||||
/* On success, new_reftable actually points to the old reftable (and
|
||||
* new_reftable_size is the old reftable's size); but that is just
|
||||
* fine */
|
||||
for (i = 0; i < new_reftable_size; i++) {
|
||||
uint64_t offset = new_reftable[i] & REFT_OFFSET_MASK;
|
||||
if (offset) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_OTHER);
|
||||
}
|
||||
}
|
||||
g_free(new_reftable);
|
||||
|
||||
if (new_reftable_offset > 0) {
|
||||
qcow2_free_clusters(bs, new_reftable_offset,
|
||||
new_reftable_size * sizeof(uint64_t),
|
||||
QCOW2_DISCARD_OTHER);
|
||||
}
|
||||
}
|
||||
|
||||
qemu_vfree(new_refblock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
231
block/qcow2.c
231
block/qcow2.c
@@ -1282,6 +1282,52 @@ static void qcow2_reopen_abort(BDRVReopenState *state)
|
||||
g_free(state->opaque);
|
||||
}
|
||||
|
||||
static void qcow2_join_options(QDict *options, QDict *old_options)
|
||||
{
|
||||
bool has_new_overlap_template =
|
||||
qdict_haskey(options, QCOW2_OPT_OVERLAP) ||
|
||||
qdict_haskey(options, QCOW2_OPT_OVERLAP_TEMPLATE);
|
||||
bool has_new_total_cache_size =
|
||||
qdict_haskey(options, QCOW2_OPT_CACHE_SIZE);
|
||||
bool has_all_cache_options;
|
||||
|
||||
/* New overlap template overrides all old overlap options */
|
||||
if (has_new_overlap_template) {
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_TEMPLATE);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_MAIN_HEADER);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L1);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L2);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_TABLE);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L1);
|
||||
qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L2);
|
||||
}
|
||||
|
||||
/* New total cache size overrides all old options */
|
||||
if (qdict_haskey(options, QCOW2_OPT_CACHE_SIZE)) {
|
||||
qdict_del(old_options, QCOW2_OPT_L2_CACHE_SIZE);
|
||||
qdict_del(old_options, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
|
||||
}
|
||||
|
||||
qdict_join(options, old_options, false);
|
||||
|
||||
/*
|
||||
* If after merging all cache size options are set, an old total size is
|
||||
* overwritten. Do keep all options, however, if all three are new. The
|
||||
* resulting error message is what we want to happen.
|
||||
*/
|
||||
has_all_cache_options =
|
||||
qdict_haskey(options, QCOW2_OPT_CACHE_SIZE) ||
|
||||
qdict_haskey(options, QCOW2_OPT_L2_CACHE_SIZE) ||
|
||||
qdict_haskey(options, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
|
||||
|
||||
if (has_all_cache_options && !has_new_total_cache_size) {
|
||||
qdict_del(options, QCOW2_OPT_CACHE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors, int *pnum)
|
||||
{
|
||||
@@ -2269,7 +2315,7 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
DEFAULT_CLUSTER_SIZE);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(PreallocMode_lookup, buf,
|
||||
PREALLOC_MODE_MAX, PREALLOC_MODE_OFF,
|
||||
PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
@@ -2757,6 +2803,10 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
|
||||
.has_corrupt = true,
|
||||
.refcount_bits = s->refcount_bits,
|
||||
};
|
||||
} else {
|
||||
/* if this assertion fails, this probably means a new version was
|
||||
* added without having it covered here */
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return spec_info;
|
||||
@@ -2824,7 +2874,7 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||
* have to be removed.
|
||||
*/
|
||||
static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int current_version = s->qcow_version;
|
||||
@@ -2839,13 +2889,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
}
|
||||
|
||||
if (s->refcount_order != 4) {
|
||||
/* we would have to convert the image to a refcount_order == 4 image
|
||||
* here; however, since qemu (at the time of writing this) does not
|
||||
* support anything different than 4 anyway, there is no point in doing
|
||||
* so right now; however, we should error out (if qemu supports this in
|
||||
* the future and this code has not been adapted) */
|
||||
error_report("qcow2_downgrade: Image refcount orders other than 4 are "
|
||||
"currently not supported.");
|
||||
error_report("compat=0.10 requires refcount_bits=16");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@@ -2873,7 +2917,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
/* clearing autoclear features is trivial */
|
||||
s->autoclear_features = 0;
|
||||
|
||||
ret = qcow2_expand_zero_clusters(bs, status_cb);
|
||||
ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -2887,8 +2931,79 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef enum Qcow2AmendOperation {
|
||||
/* This is the value Qcow2AmendHelperCBInfo::last_operation will be
|
||||
* statically initialized to so that the helper CB can discern the first
|
||||
* invocation from an operation change */
|
||||
QCOW2_NO_OPERATION = 0,
|
||||
|
||||
QCOW2_CHANGING_REFCOUNT_ORDER,
|
||||
QCOW2_DOWNGRADING,
|
||||
} Qcow2AmendOperation;
|
||||
|
||||
typedef struct Qcow2AmendHelperCBInfo {
|
||||
/* The code coordinating the amend operations should only modify
|
||||
* these four fields; the rest will be managed by the CB */
|
||||
BlockDriverAmendStatusCB *original_status_cb;
|
||||
void *original_cb_opaque;
|
||||
|
||||
Qcow2AmendOperation current_operation;
|
||||
|
||||
/* Total number of operations to perform (only set once) */
|
||||
int total_operations;
|
||||
|
||||
/* The following fields are managed by the CB */
|
||||
|
||||
/* Number of operations completed */
|
||||
int operations_completed;
|
||||
|
||||
/* Cumulative offset of all completed operations */
|
||||
int64_t offset_completed;
|
||||
|
||||
Qcow2AmendOperation last_operation;
|
||||
int64_t last_work_size;
|
||||
} Qcow2AmendHelperCBInfo;
|
||||
|
||||
static void qcow2_amend_helper_cb(BlockDriverState *bs,
|
||||
int64_t operation_offset,
|
||||
int64_t operation_work_size, void *opaque)
|
||||
{
|
||||
Qcow2AmendHelperCBInfo *info = opaque;
|
||||
int64_t current_work_size;
|
||||
int64_t projected_work_size;
|
||||
|
||||
if (info->current_operation != info->last_operation) {
|
||||
if (info->last_operation != QCOW2_NO_OPERATION) {
|
||||
info->offset_completed += info->last_work_size;
|
||||
info->operations_completed++;
|
||||
}
|
||||
|
||||
info->last_operation = info->current_operation;
|
||||
}
|
||||
|
||||
assert(info->total_operations > 0);
|
||||
assert(info->operations_completed < info->total_operations);
|
||||
|
||||
info->last_work_size = operation_work_size;
|
||||
|
||||
current_work_size = info->offset_completed + operation_work_size;
|
||||
|
||||
/* current_work_size is the total work size for (operations_completed + 1)
|
||||
* operations (which includes this one), so multiply it by the number of
|
||||
* operations not covered and divide it by the number of operations
|
||||
* covered to get a projection for the operations not covered */
|
||||
projected_work_size = current_work_size * (info->total_operations -
|
||||
info->operations_completed - 1)
|
||||
/ (info->operations_completed + 1);
|
||||
|
||||
info->original_status_cb(bs, info->offset_completed + operation_offset,
|
||||
current_work_size + projected_work_size,
|
||||
info->original_cb_opaque);
|
||||
}
|
||||
|
||||
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
BlockDriverAmendStatusCB *status_cb)
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int old_version = s->qcow_version, new_version = old_version;
|
||||
@@ -2898,8 +3013,10 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
const char *compat = NULL;
|
||||
uint64_t cluster_size = s->cluster_size;
|
||||
bool encrypt;
|
||||
int refcount_bits = s->refcount_bits;
|
||||
int ret;
|
||||
QemuOptDesc *desc = opts->list->desc;
|
||||
Qcow2AmendHelperCBInfo helper_cb_info;
|
||||
|
||||
while (desc && desc->name) {
|
||||
if (!qemu_opt_find(opts, desc->name)) {
|
||||
@@ -2917,11 +3034,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
} else if (!strcmp(compat, "1.1")) {
|
||||
new_version = 3;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown compatibility level %s.\n", compat);
|
||||
error_report("Unknown compatibility level %s", compat);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
|
||||
fprintf(stderr, "Cannot change preallocation mode.\n");
|
||||
error_report("Cannot change preallocation mode");
|
||||
return -ENOTSUP;
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
|
||||
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
|
||||
@@ -2934,47 +3051,74 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
!!s->cipher);
|
||||
|
||||
if (encrypt != !!s->cipher) {
|
||||
fprintf(stderr, "Changing the encryption flag is not "
|
||||
"supported.\n");
|
||||
error_report("Changing the encryption flag is not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
|
||||
cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
|
||||
cluster_size);
|
||||
if (cluster_size != s->cluster_size) {
|
||||
fprintf(stderr, "Changing the cluster size is not "
|
||||
"supported.\n");
|
||||
error_report("Changing the cluster size is not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
|
||||
lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS,
|
||||
lazy_refcounts);
|
||||
} else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) {
|
||||
error_report("Cannot change refcount entry width");
|
||||
return -ENOTSUP;
|
||||
refcount_bits = qemu_opt_get_number(opts, BLOCK_OPT_REFCOUNT_BITS,
|
||||
refcount_bits);
|
||||
|
||||
if (refcount_bits <= 0 || refcount_bits > 64 ||
|
||||
!is_power_of_2(refcount_bits))
|
||||
{
|
||||
error_report("Refcount width must be a power of two and may "
|
||||
"not exceed 64 bits");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
/* if this assertion fails, this probably means a new option was
|
||||
/* if this point is reached, this probably means a new option was
|
||||
* added without having it covered here */
|
||||
assert(false);
|
||||
abort();
|
||||
}
|
||||
|
||||
desc++;
|
||||
}
|
||||
|
||||
if (new_version != old_version) {
|
||||
if (new_version > old_version) {
|
||||
/* Upgrade */
|
||||
s->qcow_version = new_version;
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->qcow_version = old_version;
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = qcow2_downgrade(bs, new_version, status_cb);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
helper_cb_info = (Qcow2AmendHelperCBInfo){
|
||||
.original_status_cb = status_cb,
|
||||
.original_cb_opaque = cb_opaque,
|
||||
.total_operations = (new_version < old_version)
|
||||
+ (s->refcount_bits != refcount_bits)
|
||||
};
|
||||
|
||||
/* Upgrade first (some features may require compat=1.1) */
|
||||
if (new_version > old_version) {
|
||||
s->qcow_version = new_version;
|
||||
ret = qcow2_update_header(bs);
|
||||
if (ret < 0) {
|
||||
s->qcow_version = old_version;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->refcount_bits != refcount_bits) {
|
||||
int refcount_order = ctz32(refcount_bits);
|
||||
Error *local_error = NULL;
|
||||
|
||||
if (new_version < 3 && refcount_bits != 16) {
|
||||
error_report("Different refcount widths than 16 bits require "
|
||||
"compatibility level 1.1 or above (use compat=1.1 or "
|
||||
"greater)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
|
||||
ret = qcow2_change_refcount_order(bs, refcount_order,
|
||||
&qcow2_amend_helper_cb,
|
||||
&helper_cb_info, &local_error);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_error);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2989,9 +3133,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
|
||||
if (s->use_lazy_refcounts != lazy_refcounts) {
|
||||
if (lazy_refcounts) {
|
||||
if (s->qcow_version < 3) {
|
||||
fprintf(stderr, "Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use compat=1.1 or greater)\n");
|
||||
if (new_version < 3) {
|
||||
error_report("Lazy refcounts only supported with compatibility "
|
||||
"level 1.1 and above (use compat=1.1 or greater)");
|
||||
return -EINVAL;
|
||||
}
|
||||
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
|
||||
@@ -3025,6 +3169,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||
}
|
||||
}
|
||||
|
||||
/* Downgrade last (so unsupported features can be removed before) */
|
||||
if (new_version < old_version) {
|
||||
helper_cb_info.current_operation = QCOW2_DOWNGRADING;
|
||||
ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb,
|
||||
&helper_cb_info);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3145,6 +3299,7 @@ BlockDriver bdrv_qcow2 = {
|
||||
.bdrv_reopen_prepare = qcow2_reopen_prepare,
|
||||
.bdrv_reopen_commit = qcow2_reopen_commit,
|
||||
.bdrv_reopen_abort = qcow2_reopen_abort,
|
||||
.bdrv_join_options = qcow2_join_options,
|
||||
.bdrv_create = qcow2_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_co_get_block_status = qcow2_co_get_block_status,
|
||||
|
||||
@@ -529,6 +529,10 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
|
||||
int64_t size);
|
||||
|
||||
int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque, Error **errp);
|
||||
|
||||
/* qcow2-cluster.c functions */
|
||||
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
bool exact_size);
|
||||
@@ -553,7 +557,8 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
|
||||
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||
BlockDriverAmendStatusCB *status_cb);
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque);
|
||||
|
||||
/* qcow2-snapshot.c functions */
|
||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
|
||||
|
||||
@@ -847,7 +847,7 @@ static int parse_read_pattern(const char *opt)
|
||||
return QUORUM_READ_PATTERN_QUORUM;
|
||||
}
|
||||
|
||||
for (i = 0; i < QUORUM_READ_PATTERN_MAX; i++) {
|
||||
for (i = 0; i < QUORUM_READ_PATTERN__MAX; i++) {
|
||||
if (!strcmp(opt, QuorumReadPattern_lookup[i])) {
|
||||
return i;
|
||||
}
|
||||
@@ -997,7 +997,7 @@ static void quorum_attach_aio_context(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
static void quorum_refresh_filename(BlockDriverState *bs)
|
||||
static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QDict *opts;
|
||||
|
||||
@@ -500,21 +500,17 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
goto fail;
|
||||
}
|
||||
if (!s->use_aio && (bdrv_flags & BDRV_O_NATIVE_AIO)) {
|
||||
error_printf("WARNING: aio=native was specified for '%s', but "
|
||||
"it requires cache.direct=on, which was not "
|
||||
"specified. Falling back to aio=threads.\n"
|
||||
" This will become an error condition in "
|
||||
"future QEMU versions.\n",
|
||||
bs->filename);
|
||||
error_setg(errp, "aio=native was specified, but it requires "
|
||||
"cache.direct=on, which was not specified.");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
#else
|
||||
if (bdrv_flags & BDRV_O_NATIVE_AIO) {
|
||||
error_printf("WARNING: aio=native was specified for '%s', but "
|
||||
"is not supported in this build. Falling back to "
|
||||
"aio=threads.\n"
|
||||
" This will become an error condition in "
|
||||
"future QEMU versions.\n",
|
||||
bs->filename);
|
||||
error_setg(errp, "aio=native was specified, but is not supported "
|
||||
"in this build.");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
#endif /* !defined(CONFIG_LINUX_AIO) */
|
||||
|
||||
@@ -1636,7 +1632,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||
nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
|
||||
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
|
||||
prealloc = qapi_enum_parse(PreallocMode_lookup, buf,
|
||||
PREALLOC_MODE_MAX, PREALLOC_MODE_OFF,
|
||||
PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
|
||||
&local_err);
|
||||
g_free(buf);
|
||||
if (local_err) {
|
||||
|
||||
@@ -229,6 +229,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
int ret;
|
||||
|
||||
if (!drv) {
|
||||
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
|
||||
return -ENOMEDIUM;
|
||||
@@ -239,18 +241,21 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* drain all pending i/o before deleting snapshot */
|
||||
bdrv_drain(bs);
|
||||
bdrv_drained_begin(bs);
|
||||
|
||||
if (drv->bdrv_snapshot_delete) {
|
||||
return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
|
||||
ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
|
||||
} else if (bs->file) {
|
||||
ret = bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp);
|
||||
} else {
|
||||
error_setg(errp, "Block format '%s' used by device '%s' "
|
||||
"does not support internal snapshot deletion",
|
||||
drv->format_name, bdrv_get_device_name(bs));
|
||||
ret = -ENOTSUP;
|
||||
}
|
||||
if (bs->file) {
|
||||
return bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp);
|
||||
}
|
||||
error_setg(errp, "Block format '%s' used by device '%s' "
|
||||
"does not support internal snapshot deletion",
|
||||
drv->format_name, bdrv_get_device_name(bs));
|
||||
return -ENOTSUP;
|
||||
|
||||
bdrv_drained_end(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
|
||||
|
||||
59
blockdev.c
59
blockdev.c
@@ -387,16 +387,6 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) {
|
||||
*bdrv_flags |= BDRV_O_CACHE_WB;
|
||||
}
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
|
||||
*bdrv_flags |= BDRV_O_NOCACHE;
|
||||
}
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
|
||||
*bdrv_flags |= BDRV_O_NO_FLUSH;
|
||||
}
|
||||
|
||||
if ((aio = qemu_opt_get(opts, "aio")) != NULL) {
|
||||
if (!strcmp(aio, "native")) {
|
||||
*bdrv_flags |= BDRV_O_NATIVE_AIO;
|
||||
@@ -454,7 +444,7 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
|
||||
*detect_zeroes =
|
||||
qapi_enum_parse(BlockdevDetectZeroesOptions_lookup,
|
||||
qemu_opt_get(opts, "detect-zeroes"),
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX,
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
|
||||
&local_error);
|
||||
if (local_error) {
|
||||
@@ -490,7 +480,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
QDict *interval_dict = NULL;
|
||||
QList *interval_list = NULL;
|
||||
const char *id;
|
||||
bool has_driver_specific_opts;
|
||||
BlockdevDetectZeroesOptions detect_zeroes =
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
|
||||
const char *throttling_group = NULL;
|
||||
@@ -514,8 +503,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
qdict_del(bs_opts, "id");
|
||||
}
|
||||
|
||||
has_driver_specific_opts = !!qdict_size(bs_opts);
|
||||
|
||||
/* extract parameters */
|
||||
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
|
||||
|
||||
@@ -572,13 +559,11 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
}
|
||||
|
||||
if (snapshot) {
|
||||
/* always use cache=unsafe with snapshot */
|
||||
bdrv_flags &= ~BDRV_O_CACHE_MASK;
|
||||
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
|
||||
bdrv_flags |= BDRV_O_SNAPSHOT;
|
||||
}
|
||||
|
||||
/* init */
|
||||
if ((!file || !*file) && !has_driver_specific_opts) {
|
||||
if ((!file || !*file) && !qdict_size(bs_opts)) {
|
||||
BlockBackendRootState *blk_rs;
|
||||
|
||||
blk = blk_new(qemu_opts_id(opts), errp);
|
||||
@@ -606,6 +591,20 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
file = NULL;
|
||||
}
|
||||
|
||||
/* bdrv_open() defaults to the values in bdrv_flags (for compatibility
|
||||
* with other callers) rather than what we want as the real defaults.
|
||||
* Apply the defaults here instead. */
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_WB, "on");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||
|
||||
if (snapshot) {
|
||||
/* always use cache=unsafe with snapshot */
|
||||
qdict_put(bs_opts, BDRV_OPT_CACHE_WB, qstring_from_str("on"));
|
||||
qdict_put(bs_opts, BDRV_OPT_CACHE_DIRECT, qstring_from_str("off"));
|
||||
qdict_put(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, qstring_from_str("on"));
|
||||
}
|
||||
|
||||
blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags,
|
||||
errp);
|
||||
if (!blk) {
|
||||
@@ -3872,18 +3871,6 @@ QemuOptsList qemu_common_drive_opts = {
|
||||
.name = "discard",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},{
|
||||
.name = BDRV_OPT_CACHE_WB,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables writeback mode for any caches",
|
||||
},{
|
||||
.name = BDRV_OPT_CACHE_DIRECT,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables use of O_DIRECT (bypass the host page cache)",
|
||||
},{
|
||||
.name = BDRV_OPT_CACHE_NO_FLUSH,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "ignore any flush requests for the device",
|
||||
},{
|
||||
.name = "aio",
|
||||
.type = QEMU_OPT_STRING,
|
||||
@@ -3991,18 +3978,6 @@ static QemuOptsList qemu_root_bds_opts = {
|
||||
.name = "discard",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},{
|
||||
.name = "cache.writeback",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables writeback mode for any caches",
|
||||
},{
|
||||
.name = "cache.direct",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "enables use of O_DIRECT (bypass the host page cache)",
|
||||
},{
|
||||
.name = "cache.no-flush",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "ignore any flush requests for the device",
|
||||
},{
|
||||
.name = "aio",
|
||||
.type = QEMU_OPT_STRING,
|
||||
|
||||
@@ -938,7 +938,7 @@ int main(int argc, char **argv)
|
||||
unsigned long tmp;
|
||||
if (fscanf(fp, "%lu", &tmp) == 1) {
|
||||
mmap_min_addr = tmp;
|
||||
qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr);
|
||||
qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n", mmap_min_addr);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
@@ -955,7 +955,7 @@ int main(int argc, char **argv)
|
||||
|
||||
free(target_environ);
|
||||
|
||||
if (qemu_log_enabled()) {
|
||||
if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
|
||||
qemu_log("guest_base 0x%lx\n", guest_base);
|
||||
log_page_dump();
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
#include "qemu.h"
|
||||
#include "target_signal.h"
|
||||
|
||||
//#define DEBUG_SIGNAL
|
||||
|
||||
void signal_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
17
configure
vendored
17
configure
vendored
@@ -2426,6 +2426,14 @@ else
|
||||
fi
|
||||
|
||||
|
||||
##########################################
|
||||
# getifaddrs (for tests/test-io-channel-socket )
|
||||
|
||||
have_ifaddrs_h=yes
|
||||
if ! check_include "ifaddrs.h" ; then
|
||||
have_ifaddrs_h=no
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# VTE probe
|
||||
|
||||
@@ -4758,7 +4766,11 @@ echo "GTK GL support $gtk_gl"
|
||||
echo "GNUTLS support $gnutls"
|
||||
echo "GNUTLS hash $gnutls_hash"
|
||||
echo "libgcrypt $gcrypt"
|
||||
echo "nettle $nettle ${nettle+($nettle_version)}"
|
||||
if test "$nettle" = "yes"; then
|
||||
echo "nettle $nettle ($nettle_version)"
|
||||
else
|
||||
echo "nettle $nettle"
|
||||
fi
|
||||
echo "libtasn1 $tasn1"
|
||||
echo "VTE support $vte"
|
||||
echo "curses support $curses"
|
||||
@@ -5137,6 +5149,9 @@ fi
|
||||
if test "$tasn1" = "yes" ; then
|
||||
echo "CONFIG_TASN1=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$have_ifaddrs_h" = "yes" ; then
|
||||
echo "HAVE_IFADDRS_H=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vte" = "yes" ; then
|
||||
echo "CONFIG_VTE=y" >> $config_host_mak
|
||||
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
|
||||
|
||||
31
cpus.c
31
cpus.c
@@ -1558,22 +1558,29 @@ CpuInfoList *qmp_query_cpus(Error **errp)
|
||||
info->value->qom_path = object_get_canonical_path(OBJECT(cpu));
|
||||
info->value->thread_id = cpu->thread_id;
|
||||
#if defined(TARGET_I386)
|
||||
info->value->has_pc = true;
|
||||
info->value->pc = env->eip + env->segs[R_CS].base;
|
||||
info->value->arch = CPU_INFO_ARCH_X86;
|
||||
info->value->u.x86 = g_new0(CpuInfoX86, 1);
|
||||
info->value->u.x86->pc = env->eip + env->segs[R_CS].base;
|
||||
#elif defined(TARGET_PPC)
|
||||
info->value->has_nip = true;
|
||||
info->value->nip = env->nip;
|
||||
info->value->arch = CPU_INFO_ARCH_PPC;
|
||||
info->value->u.ppc = g_new0(CpuInfoPPC, 1);
|
||||
info->value->u.ppc->nip = env->nip;
|
||||
#elif defined(TARGET_SPARC)
|
||||
info->value->has_pc = true;
|
||||
info->value->pc = env->pc;
|
||||
info->value->has_npc = true;
|
||||
info->value->npc = env->npc;
|
||||
info->value->arch = CPU_INFO_ARCH_SPARC;
|
||||
info->value->u.sparc = g_new0(CpuInfoSPARC, 1);
|
||||
info->value->u.sparc->pc = env->pc;
|
||||
info->value->u.sparc->npc = env->npc;
|
||||
#elif defined(TARGET_MIPS)
|
||||
info->value->has_PC = true;
|
||||
info->value->PC = env->active_tc.PC;
|
||||
info->value->arch = CPU_INFO_ARCH_MIPS;
|
||||
info->value->u.mips = g_new0(CpuInfoMIPS, 1);
|
||||
info->value->u.mips->PC = env->active_tc.PC;
|
||||
#elif defined(TARGET_TRICORE)
|
||||
info->value->has_PC = true;
|
||||
info->value->PC = env->PC;
|
||||
info->value->arch = CPU_INFO_ARCH_TRICORE;
|
||||
info->value->u.tricore = g_new0(CpuInfoTricore, 1);
|
||||
info->value->u.tricore->PC = env->PC;
|
||||
#else
|
||||
info->value->arch = CPU_INFO_ARCH_OTHER;
|
||||
info->value->u.other = g_new0(CpuInfoOther, 1);
|
||||
#endif
|
||||
|
||||
/* XXX: waiting for the qapi to support GSList */
|
||||
|
||||
@@ -7,6 +7,7 @@ crypto-obj-y += tlscreds.o
|
||||
crypto-obj-y += tlscredsanon.o
|
||||
crypto-obj-y += tlscredsx509.o
|
||||
crypto-obj-y += tlssession.o
|
||||
crypto-obj-y += secret.o
|
||||
|
||||
# Let the userspace emulators avoid linking gnutls/etc
|
||||
crypto-aes-obj-y = aes.o
|
||||
|
||||
@@ -21,19 +21,67 @@
|
||||
#include "crypto/cipher.h"
|
||||
|
||||
|
||||
static size_t alg_key_len[QCRYPTO_CIPHER_ALG_LAST] = {
|
||||
static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = {
|
||||
[QCRYPTO_CIPHER_ALG_AES_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_192] = 24,
|
||||
[QCRYPTO_CIPHER_ALG_AES_256] = 32,
|
||||
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
|
||||
};
|
||||
|
||||
static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {
|
||||
[QCRYPTO_CIPHER_ALG_AES_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_192] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_256] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
|
||||
};
|
||||
|
||||
static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = {
|
||||
[QCRYPTO_CIPHER_MODE_ECB] = false,
|
||||
[QCRYPTO_CIPHER_MODE_CBC] = true,
|
||||
};
|
||||
|
||||
|
||||
size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg)
|
||||
{
|
||||
if (alg >= G_N_ELEMENTS(alg_key_len)) {
|
||||
return 0;
|
||||
}
|
||||
return alg_block_len[alg];
|
||||
}
|
||||
|
||||
|
||||
size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg)
|
||||
{
|
||||
if (alg >= G_N_ELEMENTS(alg_key_len)) {
|
||||
return 0;
|
||||
}
|
||||
return alg_key_len[alg];
|
||||
}
|
||||
|
||||
|
||||
size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
if (alg >= G_N_ELEMENTS(alg_block_len)) {
|
||||
return 0;
|
||||
}
|
||||
if (mode >= G_N_ELEMENTS(mode_need_iv)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mode_need_iv[mode]) {
|
||||
return alg_block_len[alg];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
|
||||
size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
if ((unsigned)alg >= QCRYPTO_CIPHER_ALG_LAST) {
|
||||
if ((unsigned)alg >= QCRYPTO_CIPHER_ALG__MAX) {
|
||||
error_setg(errp, "Cipher algorithm %d out of range",
|
||||
alg);
|
||||
return false;
|
||||
@@ -41,7 +89,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
|
||||
|
||||
if (alg_key_len[alg] != nkey) {
|
||||
error_setg(errp, "Cipher key length %zu should be %zu",
|
||||
alg_key_len[alg], nkey);
|
||||
nkey, alg_key_len[alg]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -24,12 +24,18 @@
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = {
|
||||
static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
|
||||
};
|
||||
|
||||
static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = 16,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = 20,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = 32,
|
||||
};
|
||||
|
||||
gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) {
|
||||
@@ -38,6 +44,15 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_size)) {
|
||||
return 0;
|
||||
}
|
||||
return qcrypto_hash_alg_size[alg];
|
||||
}
|
||||
|
||||
|
||||
int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
|
||||
513
crypto/secret.c
Normal file
513
crypto/secret.c
Normal file
@@ -0,0 +1,513 @@
|
||||
/*
|
||||
* QEMU crypto secret support
|
||||
*
|
||||
* Copyright (c) 2015 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "crypto/secret.h"
|
||||
#include "crypto/cipher.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/base64.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_load_data(QCryptoSecret *secret,
|
||||
uint8_t **output,
|
||||
size_t *outputlen,
|
||||
Error **errp)
|
||||
{
|
||||
char *data = NULL;
|
||||
size_t length = 0;
|
||||
GError *gerr = NULL;
|
||||
|
||||
*output = NULL;
|
||||
*outputlen = 0;
|
||||
|
||||
if (secret->file) {
|
||||
if (secret->data) {
|
||||
error_setg(errp,
|
||||
"'file' and 'data' are mutually exclusive");
|
||||
return;
|
||||
}
|
||||
if (!g_file_get_contents(secret->file, &data, &length, &gerr)) {
|
||||
error_setg(errp,
|
||||
"Unable to read %s: %s",
|
||||
secret->file, gerr->message);
|
||||
g_error_free(gerr);
|
||||
return;
|
||||
}
|
||||
*output = (uint8_t *)data;
|
||||
*outputlen = length;
|
||||
} else if (secret->data) {
|
||||
*outputlen = strlen(secret->data);
|
||||
*output = (uint8_t *)g_strdup(secret->data);
|
||||
} else {
|
||||
error_setg(errp, "Either 'file' or 'data' must be provided");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void qcrypto_secret_decrypt(QCryptoSecret *secret,
|
||||
const uint8_t *input,
|
||||
size_t inputlen,
|
||||
uint8_t **output,
|
||||
size_t *outputlen,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL;
|
||||
size_t keylen, ciphertextlen, ivlen;
|
||||
QCryptoCipher *aes = NULL;
|
||||
uint8_t *plaintext = NULL;
|
||||
|
||||
*output = NULL;
|
||||
*outputlen = 0;
|
||||
|
||||
if (qcrypto_secret_lookup(secret->keyid,
|
||||
&key, &keylen,
|
||||
errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (keylen != 32) {
|
||||
error_setg(errp, "Key should be 32 bytes in length");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!secret->iv) {
|
||||
error_setg(errp, "IV is required to decrypt secret");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
iv = qbase64_decode(secret->iv, -1, &ivlen, errp);
|
||||
if (!iv) {
|
||||
goto cleanup;
|
||||
}
|
||||
if (ivlen != 16) {
|
||||
error_setg(errp, "IV should be 16 bytes in length not %zu",
|
||||
ivlen);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
|
||||
QCRYPTO_CIPHER_MODE_CBC,
|
||||
key, keylen,
|
||||
errp);
|
||||
if (!aes) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
|
||||
ciphertext = qbase64_decode((const gchar*)input,
|
||||
inputlen,
|
||||
&ciphertextlen,
|
||||
errp);
|
||||
if (!ciphertext) {
|
||||
goto cleanup;
|
||||
}
|
||||
plaintext = g_new0(uint8_t, ciphertextlen + 1);
|
||||
} else {
|
||||
ciphertextlen = inputlen;
|
||||
plaintext = g_new0(uint8_t, inputlen + 1);
|
||||
}
|
||||
if (qcrypto_cipher_decrypt(aes,
|
||||
ciphertext ? ciphertext : input,
|
||||
plaintext,
|
||||
ciphertextlen,
|
||||
errp) < 0) {
|
||||
plaintext = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (plaintext[ciphertextlen - 1] > 16 ||
|
||||
plaintext[ciphertextlen - 1] > ciphertextlen) {
|
||||
error_setg(errp, "Incorrect number of padding bytes (%d) "
|
||||
"found on decrypted data",
|
||||
(int)plaintext[ciphertextlen - 1]);
|
||||
g_free(plaintext);
|
||||
plaintext = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Even though plaintext may contain arbitrary NUL
|
||||
* ensure it is explicitly NUL terminated.
|
||||
*/
|
||||
ciphertextlen -= plaintext[ciphertextlen - 1];
|
||||
plaintext[ciphertextlen] = '\0';
|
||||
|
||||
*output = plaintext;
|
||||
*outputlen = ciphertextlen;
|
||||
|
||||
cleanup:
|
||||
g_free(ciphertext);
|
||||
g_free(iv);
|
||||
g_free(key);
|
||||
qcrypto_cipher_free(aes);
|
||||
}
|
||||
|
||||
|
||||
static void qcrypto_secret_decode(const uint8_t *input,
|
||||
size_t inputlen,
|
||||
uint8_t **output,
|
||||
size_t *outputlen,
|
||||
Error **errp)
|
||||
{
|
||||
*output = qbase64_decode((const gchar*)input,
|
||||
inputlen,
|
||||
outputlen,
|
||||
errp);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_prop_set_loaded(Object *obj,
|
||||
bool value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
if (value) {
|
||||
Error *local_err = NULL;
|
||||
uint8_t *input = NULL;
|
||||
size_t inputlen = 0;
|
||||
uint8_t *output = NULL;
|
||||
size_t outputlen = 0;
|
||||
|
||||
qcrypto_secret_load_data(secret, &input, &inputlen, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (secret->keyid) {
|
||||
qcrypto_secret_decrypt(secret, input, inputlen,
|
||||
&output, &outputlen, &local_err);
|
||||
g_free(input);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
input = output;
|
||||
inputlen = outputlen;
|
||||
} else {
|
||||
if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) {
|
||||
qcrypto_secret_decode(input, inputlen,
|
||||
&output, &outputlen, &local_err);
|
||||
g_free(input);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
input = output;
|
||||
inputlen = outputlen;
|
||||
}
|
||||
}
|
||||
|
||||
secret->rawdata = input;
|
||||
secret->rawlen = inputlen;
|
||||
} else {
|
||||
g_free(secret->rawdata);
|
||||
secret->rawlen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_secret_prop_get_loaded(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
return secret->data != NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_prop_set_format(Object *obj,
|
||||
int value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoSecret *creds = QCRYPTO_SECRET(obj);
|
||||
|
||||
creds->format = value;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_secret_prop_get_format(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoSecret *creds = QCRYPTO_SECRET(obj);
|
||||
|
||||
return creds->format;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_prop_set_data(Object *obj,
|
||||
const char *value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
g_free(secret->data);
|
||||
secret->data = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_secret_prop_get_data(Object *obj,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
return g_strdup(secret->data);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_prop_set_file(Object *obj,
|
||||
const char *value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
g_free(secret->file);
|
||||
secret->file = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_secret_prop_get_file(Object *obj,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
return g_strdup(secret->file);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_prop_set_iv(Object *obj,
|
||||
const char *value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
g_free(secret->iv);
|
||||
secret->iv = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_secret_prop_get_iv(Object *obj,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
return g_strdup(secret->iv);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_prop_set_keyid(Object *obj,
|
||||
const char *value,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
g_free(secret->keyid);
|
||||
secret->keyid = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_secret_prop_get_keyid(Object *obj,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
return g_strdup(secret->keyid);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_complete(UserCreatable *uc, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(uc), true, "loaded", errp);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_init(Object *obj)
|
||||
{
|
||||
object_property_add_bool(obj, "loaded",
|
||||
qcrypto_secret_prop_get_loaded,
|
||||
qcrypto_secret_prop_set_loaded,
|
||||
NULL);
|
||||
object_property_add_enum(obj, "format",
|
||||
"QCryptoSecretFormat",
|
||||
QCryptoSecretFormat_lookup,
|
||||
qcrypto_secret_prop_get_format,
|
||||
qcrypto_secret_prop_set_format,
|
||||
NULL);
|
||||
object_property_add_str(obj, "data",
|
||||
qcrypto_secret_prop_get_data,
|
||||
qcrypto_secret_prop_set_data,
|
||||
NULL);
|
||||
object_property_add_str(obj, "file",
|
||||
qcrypto_secret_prop_get_file,
|
||||
qcrypto_secret_prop_set_file,
|
||||
NULL);
|
||||
object_property_add_str(obj, "keyid",
|
||||
qcrypto_secret_prop_get_keyid,
|
||||
qcrypto_secret_prop_set_keyid,
|
||||
NULL);
|
||||
object_property_add_str(obj, "iv",
|
||||
qcrypto_secret_prop_get_iv,
|
||||
qcrypto_secret_prop_set_iv,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_finalize(Object *obj)
|
||||
{
|
||||
QCryptoSecret *secret = QCRYPTO_SECRET(obj);
|
||||
|
||||
g_free(secret->iv);
|
||||
g_free(secret->file);
|
||||
g_free(secret->keyid);
|
||||
g_free(secret->rawdata);
|
||||
g_free(secret->data);
|
||||
}
|
||||
|
||||
static void
|
||||
qcrypto_secret_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
|
||||
|
||||
ucc->complete = qcrypto_secret_complete;
|
||||
}
|
||||
|
||||
|
||||
int qcrypto_secret_lookup(const char *secretid,
|
||||
uint8_t **data,
|
||||
size_t *datalen,
|
||||
Error **errp)
|
||||
{
|
||||
Object *obj;
|
||||
QCryptoSecret *secret;
|
||||
|
||||
obj = object_resolve_path_component(
|
||||
object_get_objects_root(), secretid);
|
||||
if (!obj) {
|
||||
error_setg(errp, "No secret with id '%s'", secretid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
secret = (QCryptoSecret *)
|
||||
object_dynamic_cast(obj,
|
||||
TYPE_QCRYPTO_SECRET);
|
||||
if (!secret) {
|
||||
error_setg(errp, "Object with id '%s' is not a secret",
|
||||
secretid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!secret->rawdata) {
|
||||
error_setg(errp, "Secret with id '%s' has no data",
|
||||
secretid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*data = g_new0(uint8, secret->rawlen + 1);
|
||||
memcpy(*data, secret->rawdata, secret->rawlen);
|
||||
(*data)[secret->rawlen] = '\0';
|
||||
*datalen = secret->rawlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char *qcrypto_secret_lookup_as_utf8(const char *secretid,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t *data;
|
||||
size_t datalen;
|
||||
|
||||
if (qcrypto_secret_lookup(secretid,
|
||||
&data,
|
||||
&datalen,
|
||||
errp) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!g_utf8_validate((const gchar*)data, datalen, NULL)) {
|
||||
error_setg(errp,
|
||||
"Data from secret %s is not valid UTF-8",
|
||||
secretid);
|
||||
g_free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (char *)data;
|
||||
}
|
||||
|
||||
|
||||
char *qcrypto_secret_lookup_as_base64(const char *secretid,
|
||||
Error **errp)
|
||||
{
|
||||
uint8_t *data;
|
||||
size_t datalen;
|
||||
char *ret;
|
||||
|
||||
if (qcrypto_secret_lookup(secretid,
|
||||
&data,
|
||||
&datalen,
|
||||
errp) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = g_base64_encode(data, datalen);
|
||||
g_free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const TypeInfo qcrypto_secret_info = {
|
||||
.parent = TYPE_OBJECT,
|
||||
.name = TYPE_QCRYPTO_SECRET,
|
||||
.instance_size = sizeof(QCryptoSecret),
|
||||
.instance_init = qcrypto_secret_init,
|
||||
.instance_finalize = qcrypto_secret_finalize,
|
||||
.class_size = sizeof(QCryptoSecretClass),
|
||||
.class_init = qcrypto_secret_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_secret_register_types(void)
|
||||
{
|
||||
type_register_static(&qcrypto_secret_info);
|
||||
}
|
||||
|
||||
|
||||
type_init(qcrypto_secret_register_types);
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "crypto/tlscredsx509.h"
|
||||
#include "crypto/tlscredspriv.h"
|
||||
#include "crypto/secret.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "trace.h"
|
||||
|
||||
@@ -607,9 +608,30 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
|
||||
}
|
||||
|
||||
if (cert != NULL && key != NULL) {
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030111
|
||||
char *password = NULL;
|
||||
if (creds->passwordid) {
|
||||
password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
|
||||
errp);
|
||||
if (!password) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
ret = gnutls_certificate_set_x509_key_file2(creds->data,
|
||||
cert, key,
|
||||
GNUTLS_X509_FMT_PEM,
|
||||
password,
|
||||
0);
|
||||
g_free(password);
|
||||
#else /* GNUTLS_VERSION_NUMBER < 0x030111 */
|
||||
if (creds->passwordid) {
|
||||
error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11");
|
||||
goto cleanup;
|
||||
}
|
||||
ret = gnutls_certificate_set_x509_key_file(creds->data,
|
||||
cert, key,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
#endif /* GNUTLS_VERSION_NUMBER < 0x030111 */
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
|
||||
cert, key, gnutls_strerror(ret));
|
||||
@@ -737,6 +759,27 @@ qcrypto_tls_creds_x509_prop_set_sanity(Object *obj,
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_tls_creds_x509_prop_set_passwordid(Object *obj,
|
||||
const char *value,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
creds->passwordid = g_strdup(value);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
qcrypto_tls_creds_x509_prop_get_passwordid(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
return g_strdup(creds->passwordid);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
qcrypto_tls_creds_x509_prop_get_sanity(Object *obj,
|
||||
Error **errp G_GNUC_UNUSED)
|
||||
@@ -769,6 +812,10 @@ qcrypto_tls_creds_x509_init(Object *obj)
|
||||
qcrypto_tls_creds_x509_prop_get_sanity,
|
||||
qcrypto_tls_creds_x509_prop_set_sanity,
|
||||
NULL);
|
||||
object_property_add_str(obj, "passwordid",
|
||||
qcrypto_tls_creds_x509_prop_get_passwordid,
|
||||
qcrypto_tls_creds_x509_prop_set_passwordid,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -777,6 +824,7 @@ qcrypto_tls_creds_x509_finalize(Object *obj)
|
||||
{
|
||||
QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
|
||||
|
||||
g_free(creds->passwordid);
|
||||
qcrypto_tls_creds_x509_unload(creds);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@ CONFIG_VGA_CIRRUS=y
|
||||
CONFIG_VMWARE_VGA=y
|
||||
CONFIG_VIRTIO_VGA=y
|
||||
CONFIG_VMMOUSE=y
|
||||
CONFIG_IPMI=y
|
||||
CONFIG_IPMI_LOCAL=y
|
||||
CONFIG_IPMI_EXTERN=y
|
||||
CONFIG_ISA_IPMI_KCS=y
|
||||
CONFIG_ISA_IPMI_BT=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PARALLEL=y
|
||||
CONFIG_I8254=y
|
||||
@@ -46,7 +51,10 @@ CONFIG_APIC=y
|
||||
CONFIG_IOAPIC=y
|
||||
CONFIG_PVPANIC=y
|
||||
CONFIG_MEM_HOTPLUG=y
|
||||
CONFIG_NVDIMM=y
|
||||
CONFIG_ACPI_NVDIMM=y
|
||||
CONFIG_XIO3130=y
|
||||
CONFIG_IOH3420=y
|
||||
CONFIG_I82801B11=y
|
||||
CONFIG_SMBIOS=y
|
||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
|
||||
|
||||
@@ -9,6 +9,11 @@ CONFIG_VGA_CIRRUS=y
|
||||
CONFIG_VMWARE_VGA=y
|
||||
CONFIG_VIRTIO_VGA=y
|
||||
CONFIG_VMMOUSE=y
|
||||
CONFIG_IPMI=y
|
||||
CONFIG_IPMI_LOCAL=y
|
||||
CONFIG_IPMI_EXTERN=y
|
||||
CONFIG_ISA_IPMI_KCS=y
|
||||
CONFIG_ISA_IPMI_BT=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_PARALLEL=y
|
||||
CONFIG_I8254=y
|
||||
@@ -46,7 +51,10 @@ CONFIG_APIC=y
|
||||
CONFIG_IOAPIC=y
|
||||
CONFIG_PVPANIC=y
|
||||
CONFIG_MEM_HOTPLUG=y
|
||||
CONFIG_NVDIMM=y
|
||||
CONFIG_ACPI_NVDIMM=y
|
||||
CONFIG_XIO3130=y
|
||||
CONFIG_IOH3420=y
|
||||
CONFIG_I82801B11=y
|
||||
CONFIG_SMBIOS=y
|
||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Block I/O error injection using blkdebug
|
||||
----------------------------------------
|
||||
Copyright (C) 2014 Red Hat Inc
|
||||
Copyright (C) 2014-2015 Red Hat Inc
|
||||
|
||||
This work is licensed under the terms of the GNU GPL, version 2 or later. See
|
||||
the COPYING file in the top-level directory.
|
||||
@@ -92,8 +92,9 @@ The core events are:
|
||||
|
||||
flush_to_disk - flush the host block device's disk cache
|
||||
|
||||
See block/blkdebug.c:event_names[] for the full list of events. You may need
|
||||
to grep block driver source code to understand the meaning of specific events.
|
||||
See qapi/block-core.json:BlkdebugEvent for the full list of events.
|
||||
You may need to grep block driver source code to understand the
|
||||
meaning of specific events.
|
||||
|
||||
State transitions
|
||||
-----------------
|
||||
|
||||
@@ -23,9 +23,9 @@ A detailed command line would be:
|
||||
-m 2G
|
||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0
|
||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1
|
||||
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd-device e1000,bus=bridge1,addr=0x4,netdev=nd
|
||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8,bus=pci.0 -device e1000,bus=bridge2,addr=0x3
|
||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40,bus=pci.0 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
|
||||
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd
|
||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8, -device e1000,bus=bridge2,addr=0x3
|
||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40, -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
|
||||
|
||||
Here you have:
|
||||
- 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes)
|
||||
|
||||
@@ -118,17 +118,17 @@ tracking optional fields.
|
||||
|
||||
Any name (command, event, type, field, or enum value) beginning with
|
||||
"x-" is marked experimental, and may be withdrawn or changed
|
||||
incompatibly in a future release. Downstream vendors may add
|
||||
extensions; such extensions should begin with a prefix matching
|
||||
"__RFQDN_" (for the reverse-fully-qualified-domain-name of the
|
||||
vendor), even if the rest of the name uses dash (example:
|
||||
__com.redhat_drive-mirror). Other than downstream extensions (with
|
||||
leading underscore and the use of dots), all names should begin with a
|
||||
letter, and contain only ASCII letters, digits, dash, and underscore.
|
||||
Names beginning with 'q_' are reserved for the generator: QMP names
|
||||
that resemble C keywords or other problematic strings will be munged
|
||||
in C to use this prefix. For example, a field named "default" in
|
||||
qapi becomes "q_default" in the generated C code.
|
||||
incompatibly in a future release. All names must begin with a letter,
|
||||
and contain only ASCII letters, digits, dash, and underscore. There
|
||||
are two exceptions: enum values may start with a digit, and any
|
||||
extensions added by downstream vendors should start with a prefix
|
||||
matching "__RFQDN_" (for the reverse-fully-qualified-domain-name of
|
||||
the vendor), even if the rest of the name uses dash (example:
|
||||
__com.redhat_drive-mirror). Names beginning with 'q_' are reserved
|
||||
for the generator: QMP names that resemble C keywords or other
|
||||
problematic strings will be munged in C to use this prefix. For
|
||||
example, a field named "default" in qapi becomes "q_default" in the
|
||||
generated C code.
|
||||
|
||||
In the rest of this document, usage lines are given for each
|
||||
expression type, with literal strings written in lower case and
|
||||
@@ -160,6 +160,7 @@ The following types are predefined, and map to C as follows:
|
||||
accepts size suffixes
|
||||
bool bool JSON true or false
|
||||
any QObject * any JSON value
|
||||
QType QType JSON string matching enum QType values
|
||||
|
||||
|
||||
=== Includes ===
|
||||
@@ -383,9 +384,6 @@ where each branch of the union names a QAPI type. For example:
|
||||
'data': { 'definition': 'BlockdevOptions',
|
||||
'reference': 'str' } }
|
||||
|
||||
Just like for a simple union, an implicit C enum 'NameKind' is created
|
||||
to enumerate the branches for the alternate 'Name'.
|
||||
|
||||
Unlike a union, the discriminator string is never passed on the wire
|
||||
for the Client JSON Protocol. Instead, the value's JSON type serves
|
||||
as an implicit discriminator, which in turn means that an alternate
|
||||
@@ -1053,7 +1051,7 @@ Example:
|
||||
|
||||
const char *const example_QAPIEvent_lookup[] = {
|
||||
[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...]
|
||||
@@ -1070,7 +1068,7 @@ Example:
|
||||
|
||||
typedef enum example_QAPIEvent {
|
||||
EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
|
||||
EXAMPLE_QAPI_EVENT_MAX = 1,
|
||||
EXAMPLE_QAPI_EVENT__MAX = 1,
|
||||
} example_QAPIEvent;
|
||||
|
||||
extern const char *const example_QAPIEvent_lookup[];
|
||||
|
||||
@@ -192,90 +192,7 @@ To check the result, read the "control" field:
|
||||
today due to implementation not being async,
|
||||
but may in the future).
|
||||
|
||||
= Host-side API =
|
||||
|
||||
The following functions are available to the QEMU programmer for adding
|
||||
data to a fw_cfg device during guest initialization (see fw_cfg.h for
|
||||
each function's complete prototype):
|
||||
|
||||
== fw_cfg_add_bytes() ==
|
||||
|
||||
Given a selector key value, starting pointer, and size, create an item
|
||||
as a raw "blob" of the given size, available by selecting the given key.
|
||||
The data referenced by the starting pointer is only linked, NOT copied,
|
||||
into the data structure of the fw_cfg device.
|
||||
|
||||
== fw_cfg_add_string() ==
|
||||
|
||||
Instead of a starting pointer and size, this function accepts a pointer
|
||||
to a NUL-terminated ascii string, and inserts a newly allocated copy of
|
||||
the string (including the NUL terminator) into the fw_cfg device data
|
||||
structure.
|
||||
|
||||
== fw_cfg_add_iXX() ==
|
||||
|
||||
Insert an XX-bit item, where XX may be 16, 32, or 64. These functions
|
||||
will convert a 16-, 32-, or 64-bit integer to little-endian, then add
|
||||
a dynamically allocated copy of the appropriately sized item to fw_cfg
|
||||
under the given selector key value.
|
||||
|
||||
== fw_cfg_modify_iXX() ==
|
||||
|
||||
Modify the value of an XX-bit item (where XX may be 16, 32, or 64).
|
||||
Similarly to the corresponding fw_cfg_add_iXX() function set, convert
|
||||
a 16-, 32-, or 64-bit integer to little endian, create a dynamically
|
||||
allocated copy of the required size, and replace the existing item at
|
||||
the given selector key value with the newly allocated one. The previous
|
||||
item, assumed to have been allocated during an earlier call to
|
||||
fw_cfg_add_iXX() or fw_cfg_modify_iXX() (of the same width XX), is freed
|
||||
before the function returns.
|
||||
|
||||
== fw_cfg_add_file() ==
|
||||
|
||||
Given a filename (i.e., fw_cfg item name), starting pointer, and size,
|
||||
create an item as a raw "blob" of the given size. Unlike fw_cfg_add_bytes()
|
||||
above, the next available selector key (above 0x0020, FW_CFG_FILE_FIRST)
|
||||
will be used, and a new entry will be added to the file directory structure
|
||||
(at key 0x0019), containing the item name, blob size, and automatically
|
||||
assigned selector key value. The data referenced by the starting pointer
|
||||
is only linked, NOT copied, into the fw_cfg data structure.
|
||||
|
||||
== fw_cfg_add_file_callback() ==
|
||||
|
||||
Like fw_cfg_add_file(), but additionally sets pointers to a callback
|
||||
function (and opaque argument), which will be executed host-side by
|
||||
QEMU each time a byte is read by the guest from this particular item.
|
||||
|
||||
NOTE: The callback function is given the opaque argument set by
|
||||
fw_cfg_add_file_callback(), but also the current data offset,
|
||||
allowing it the option of only acting upon specific offset values
|
||||
(e.g., 0, before the first data byte of the selected item is
|
||||
returned to the guest).
|
||||
|
||||
== fw_cfg_modify_file() ==
|
||||
|
||||
Given a filename (i.e., fw_cfg item name), starting pointer, and size,
|
||||
completely replace the configuration item referenced by the given item
|
||||
name with the new given blob. If an existing blob is found, its
|
||||
callback information is removed, and a pointer to the old data is
|
||||
returned to allow the caller to free it, helping avoid memory leaks.
|
||||
If a configuration item does not already exist under the given item
|
||||
name, a new item will be created as with fw_cfg_add_file(), and NULL
|
||||
is returned to the caller. In any case, the data referenced by the
|
||||
starting pointer is only linked, NOT copied, into the fw_cfg data
|
||||
structure.
|
||||
|
||||
== fw_cfg_add_callback() ==
|
||||
|
||||
Like fw_cfg_add_bytes(), but additionally sets pointers to a callback
|
||||
function (and opaque argument), which will be executed host-side by
|
||||
QEMU each time a guest-side write operation to this particular item
|
||||
completes fully overwriting the item's data.
|
||||
|
||||
NOTE: This function is deprecated, and will be completely removed
|
||||
starting with QEMU v2.4.
|
||||
|
||||
== Externally Provided Items ==
|
||||
= Externally Provided Items =
|
||||
|
||||
As of v2.4, "file" fw_cfg items (i.e., items with selector keys above
|
||||
FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file
|
||||
|
||||
228
docs/specs/parallels.txt
Normal file
228
docs/specs/parallels.txt
Normal file
@@ -0,0 +1,228 @@
|
||||
= License =
|
||||
|
||||
Copyright (c) 2015 Denis Lunev
|
||||
Copyright (c) 2015 Vladimir Sementsov-Ogievskiy
|
||||
|
||||
This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
See the COPYING file in the top-level directory.
|
||||
|
||||
= Parallels Expandable Image File Format =
|
||||
|
||||
A Parallels expandable image file consists of three consecutive parts:
|
||||
* header
|
||||
* BAT
|
||||
* data area
|
||||
|
||||
All numbers in a Parallels expandable image are stored in little-endian byte
|
||||
order.
|
||||
|
||||
|
||||
== Definitions ==
|
||||
|
||||
Sector A 512-byte data chunk.
|
||||
|
||||
Cluster A data chunk of the size specified in the image header.
|
||||
Currently, the default size is 1MiB (2048 sectors). In previous
|
||||
versions, cluster sizes of 63 sectors, 256 and 252 kilobytes were
|
||||
used.
|
||||
|
||||
BAT Block Allocation Table, an entity that contains information for
|
||||
guest-to-host I/O data address translation.
|
||||
|
||||
|
||||
== Header ==
|
||||
|
||||
The header is placed at the start of an image and contains the following
|
||||
fields:
|
||||
|
||||
Bytes:
|
||||
0 - 15: magic
|
||||
Must contain "WithoutFreeSpace" or "WithouFreSpacExt".
|
||||
|
||||
16 - 19: version
|
||||
Must be 2.
|
||||
|
||||
20 - 23: heads
|
||||
Disk geometry parameter for guest.
|
||||
|
||||
24 - 27: cylinders
|
||||
Disk geometry parameter for guest.
|
||||
|
||||
28 - 31: tracks
|
||||
Cluster size, in sectors.
|
||||
|
||||
32 - 35: nb_bat_entries
|
||||
Disk size, in clusters (BAT size).
|
||||
|
||||
36 - 43: nb_sectors
|
||||
Disk size, in sectors.
|
||||
|
||||
For "WithoutFreeSpace" images:
|
||||
Only the lowest 4 bytes are used. The highest 4 bytes must be
|
||||
cleared in this case.
|
||||
|
||||
For "WithouFreSpacExt" images, there are no such
|
||||
restrictions.
|
||||
|
||||
44 - 47: in_use
|
||||
Set to 0x746F6E59 when the image is opened by software in R/W
|
||||
mode; set to 0x312e3276 when the image is closed.
|
||||
|
||||
A zero in this field means that the image was opened by an old
|
||||
version of the software that doesn't support Format Extension
|
||||
(see below).
|
||||
|
||||
Other values are not allowed.
|
||||
|
||||
48 - 51: data_off
|
||||
An offset, in sectors, from the start of the file to the start of
|
||||
the data area.
|
||||
|
||||
For "WithoutFreeSpace" images:
|
||||
- If data_off is zero, the offset is calculated as the end of BAT
|
||||
table plus some padding to ensure sector size alignment.
|
||||
- If data_off is non-zero, the offset should be aligned to sector
|
||||
size. However it is recommended to align it to cluster size for
|
||||
newly created images.
|
||||
|
||||
For "WithouFreSpacExt" images:
|
||||
data_off must be non-zero and aligned to cluster size.
|
||||
|
||||
52 - 55: flags
|
||||
Miscellaneous flags.
|
||||
|
||||
Bit 0: Empty Image bit. If set, the image should be
|
||||
considered clear.
|
||||
|
||||
Bits 2-31: Unused.
|
||||
|
||||
56 - 63: ext_off
|
||||
Format Extension offset, an offset, in sectors, from the start of
|
||||
the file to the start of the Format Extension Cluster.
|
||||
|
||||
ext_off must meet the same requirements as cluster offsets
|
||||
defined by BAT entries (see below).
|
||||
|
||||
|
||||
== BAT ==
|
||||
|
||||
BAT is placed immediately after the image header. In the file, BAT is a
|
||||
contiguous array of 32-bit unsigned little-endian integers with
|
||||
(bat_entries * 4) bytes size.
|
||||
|
||||
Each BAT entry contains an offset from the start of the file to the
|
||||
corresponding cluster. The offset set in clusters for "WithouFreSpacExt" images
|
||||
and in sectors for "WithoutFreeSpace" images.
|
||||
|
||||
If a BAT entry is zero, the corresponding cluster is not allocated and should
|
||||
be considered as filled with zeroes.
|
||||
|
||||
Cluster offsets specified by BAT entries must meet the following requirements:
|
||||
- the value must not be lower than data offset (provided by header.data_off
|
||||
or calculated as specified above),
|
||||
- the value must be lower than the desired file size,
|
||||
- the value must be unique among all BAT entries,
|
||||
- the result of (cluster offset - data offset) must be aligned to cluster
|
||||
size.
|
||||
|
||||
|
||||
== Data Area ==
|
||||
|
||||
The data area is an area from the data offset (provided by header.data_off or
|
||||
calculated as specified above) to the end of the file. It represents a
|
||||
contiguous array of clusters. Most of them are allocated by the BAT, some may
|
||||
be allocated by the ext_off field in the header while other may be allocated by
|
||||
extensions. All clusters allocated by ext_off and extensions should meet the
|
||||
same requirements as clusters specified by BAT entries.
|
||||
|
||||
|
||||
== Format Extension ==
|
||||
|
||||
The Format Extension is an area 1 cluster in size that provides additional
|
||||
format features. This cluster is addressed by the ext_off field in the header.
|
||||
The format of the Format Extension area is the following:
|
||||
|
||||
0 - 7: magic
|
||||
Must be 0xAB234CEF23DCEA87
|
||||
|
||||
8 - 23: m_CheckSum
|
||||
The MD5 checksum of the entire Header Extension cluster except
|
||||
the first 24 bytes.
|
||||
|
||||
The above are followed by feature sections or "extensions". The last
|
||||
extension must be "End of features" (see below).
|
||||
|
||||
Each feature section has the following format:
|
||||
|
||||
0 - 7: magic
|
||||
The identifier of the feature:
|
||||
0x0000000000000000 - End of features
|
||||
0x20385FAE252CB34A - Dirty bitmap
|
||||
|
||||
8 - 15: flags
|
||||
External flags for extension:
|
||||
|
||||
Bit 0: NECESSARY
|
||||
If the software cannot load the extension (due to an
|
||||
unknown magic number or error), the file should not be
|
||||
changed. If this flag is unset and there is an error on
|
||||
loading the extension, said extension should be dropped.
|
||||
|
||||
Bit 1: TRANSIT
|
||||
If there is an unknown extension with this flag set,
|
||||
said extension should be left as is.
|
||||
|
||||
If neither NECESSARY nor TRANSIT are set, the extension should be
|
||||
dropped.
|
||||
|
||||
16 - 19: data_size
|
||||
The size of the following feature data, in bytes.
|
||||
|
||||
20 - 23: unused32
|
||||
Align header to 8 bytes boundary.
|
||||
|
||||
variable: data (data_size bytes)
|
||||
|
||||
The above is followed by padding to the next 8 bytes boundary, then the
|
||||
next extension starts.
|
||||
|
||||
The last extension must be "End of features" with all the fields set to 0.
|
||||
|
||||
|
||||
=== Dirty bitmaps feature ===
|
||||
|
||||
This feature provides a way of storing dirty bitmaps in the image. The fields
|
||||
of its data area are:
|
||||
|
||||
0 - 7: size
|
||||
The bitmap size, should be equal to disk size in sectors.
|
||||
|
||||
8 - 23: id
|
||||
An identifier for backup consistency checking.
|
||||
|
||||
24 - 27: granularity
|
||||
Bitmap granularity, in sectors. I.e., the number of sectors
|
||||
corresponding to one bit of the bitmap. Granularity must be
|
||||
a power of 2.
|
||||
|
||||
28 - 31: l1_size
|
||||
The number of entries in the L1 table of the bitmap.
|
||||
|
||||
variable: l1 (64 * l1_size bytes)
|
||||
L1 offset table (in bytes)
|
||||
|
||||
A dirty bitmap is stored using a one-level structure for the mapping to host
|
||||
clusters - an L1 table.
|
||||
|
||||
Given an offset in bytes into the bitmap data, the offset in bytes into the
|
||||
image file can be obtained as follows:
|
||||
|
||||
offset = l1_table[offset / cluster_size] + (offset % cluster_size)
|
||||
|
||||
If an L1 table entry is 0, the corresponding cluster of the bitmap is assumed
|
||||
to be zero.
|
||||
|
||||
If an L1 table entry is 1, the corresponding cluster of the bitmap is assumed
|
||||
to have all bits set.
|
||||
|
||||
If an L1 table entry is not 0 or 1, it allocates a cluster from the data area.
|
||||
365
exec.c
365
exec.c
@@ -88,9 +88,6 @@ static MemoryRegion io_mem_unassigned;
|
||||
*/
|
||||
#define RAM_RESIZEABLE (1 << 2)
|
||||
|
||||
/* RAM is backed by an mmapped file.
|
||||
*/
|
||||
#define RAM_FILE (1 << 3)
|
||||
#endif
|
||||
|
||||
struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
|
||||
@@ -393,18 +390,6 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
|
||||
return section;
|
||||
}
|
||||
|
||||
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
|
||||
{
|
||||
if (memory_region_is_ram(mr)) {
|
||||
return !(is_write && mr->readonly);
|
||||
}
|
||||
if (memory_region_is_romd(mr)) {
|
||||
return !is_write;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Called from RCU critical section */
|
||||
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
|
||||
hwaddr *xlat, hwaddr *plen,
|
||||
@@ -873,7 +858,7 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...)
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_FPU | CPU_DUMP_CCOP);
|
||||
if (qemu_log_enabled()) {
|
||||
if (qemu_log_separate()) {
|
||||
qemu_log("qemu: fatal: ");
|
||||
qemu_log_vprintf(fmt, ap2);
|
||||
qemu_log("\n");
|
||||
@@ -1601,7 +1586,6 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
|
||||
new_block->used_length = size;
|
||||
new_block->max_length = size;
|
||||
new_block->flags = share ? RAM_SHARED : 0;
|
||||
new_block->flags |= RAM_FILE;
|
||||
new_block->host = file_ram_alloc(new_block, size,
|
||||
mem_path, errp);
|
||||
if (!new_block->host) {
|
||||
@@ -1676,25 +1660,6 @@ ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz,
|
||||
return qemu_ram_alloc_internal(size, maxsz, resized, NULL, true, mr, errp);
|
||||
}
|
||||
|
||||
void qemu_ram_free_from_ptr(ram_addr_t addr)
|
||||
{
|
||||
RAMBlock *block;
|
||||
|
||||
qemu_mutex_lock_ramlist();
|
||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
||||
if (addr == block->offset) {
|
||||
QLIST_REMOVE_RCU(block, next);
|
||||
ram_list.mru_block = NULL;
|
||||
/* Write list before version */
|
||||
smp_wmb();
|
||||
ram_list.version++;
|
||||
g_free_rcu(block, rcu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
qemu_mutex_unlock_ramlist();
|
||||
}
|
||||
|
||||
static void reclaim_ramblock(RAMBlock *block)
|
||||
{
|
||||
if (block->flags & RAM_PREALLOC) {
|
||||
@@ -1703,11 +1668,7 @@ static void reclaim_ramblock(RAMBlock *block)
|
||||
xen_invalidate_map_cache_entry(block->host);
|
||||
#ifndef _WIN32
|
||||
} else if (block->fd >= 0) {
|
||||
if (block->flags & RAM_FILE) {
|
||||
qemu_ram_munmap(block->host, block->max_length);
|
||||
} else {
|
||||
munmap(block->host, block->max_length);
|
||||
}
|
||||
qemu_ram_munmap(block->host, block->max_length);
|
||||
close(block->fd);
|
||||
#endif
|
||||
} else {
|
||||
@@ -1813,19 +1774,11 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
|
||||
* or address_space_rw instead. For local memory (e.g. video ram) that the
|
||||
* device owns, use memory_region_get_ram_ptr.
|
||||
*
|
||||
* By the time this function returns, the returned pointer is not protected
|
||||
* by RCU anymore. If the caller is not within an RCU critical section and
|
||||
* does not hold the iothread lock, it must have other means of protecting the
|
||||
* pointer, such as a reference to the region that includes the incoming
|
||||
* ram_addr_t.
|
||||
* Called within RCU critical section.
|
||||
*/
|
||||
void *qemu_get_ram_ptr(ram_addr_t addr)
|
||||
{
|
||||
RAMBlock *block;
|
||||
void *ptr;
|
||||
|
||||
rcu_read_lock();
|
||||
block = qemu_get_ram_block(addr);
|
||||
RAMBlock *block = qemu_get_ram_block(addr);
|
||||
|
||||
if (xen_enabled() && block->host == NULL) {
|
||||
/* We need to check if the requested address is in the RAM
|
||||
@@ -1833,52 +1786,44 @@ void *qemu_get_ram_ptr(ram_addr_t addr)
|
||||
* In that case just map until the end of the page.
|
||||
*/
|
||||
if (block->offset == 0) {
|
||||
ptr = xen_map_cache(addr, 0, 0);
|
||||
goto unlock;
|
||||
return xen_map_cache(addr, 0, 0);
|
||||
}
|
||||
|
||||
block->host = xen_map_cache(block->offset, block->max_length, 1);
|
||||
}
|
||||
ptr = ramblock_ptr(block, addr - block->offset);
|
||||
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
return ptr;
|
||||
return ramblock_ptr(block, addr - block->offset);
|
||||
}
|
||||
|
||||
/* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr
|
||||
* but takes a size argument.
|
||||
*
|
||||
* By the time this function returns, the returned pointer is not protected
|
||||
* by RCU anymore. If the caller is not within an RCU critical section and
|
||||
* does not hold the iothread lock, it must have other means of protecting the
|
||||
* pointer, such as a reference to the region that includes the incoming
|
||||
* ram_addr_t.
|
||||
* Called within RCU critical section.
|
||||
*/
|
||||
static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size)
|
||||
{
|
||||
void *ptr;
|
||||
RAMBlock *block;
|
||||
ram_addr_t offset_inside_block;
|
||||
if (*size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (xen_enabled()) {
|
||||
return xen_map_cache(addr, *size, 1);
|
||||
} else {
|
||||
RAMBlock *block;
|
||||
rcu_read_lock();
|
||||
QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
|
||||
if (addr - block->offset < block->max_length) {
|
||||
if (addr - block->offset + *size > block->max_length)
|
||||
*size = block->max_length - addr + block->offset;
|
||||
ptr = ramblock_ptr(block, addr - block->offset);
|
||||
rcu_read_unlock();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
block = qemu_get_ram_block(addr);
|
||||
offset_inside_block = addr - block->offset;
|
||||
*size = MIN(*size, block->max_length - offset_inside_block);
|
||||
|
||||
if (xen_enabled() && block->host == NULL) {
|
||||
/* We need to check if the requested address is in the RAM
|
||||
* because we don't want to map the entire memory in QEMU.
|
||||
* In that case just map the requested area.
|
||||
*/
|
||||
if (block->offset == 0) {
|
||||
return xen_map_cache(addr, *size, 1);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr);
|
||||
abort();
|
||||
block->host = xen_map_cache(block->offset, block->max_length, 1);
|
||||
}
|
||||
|
||||
return ramblock_ptr(block, offset_inside_block);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1981,6 +1926,7 @@ MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr)
|
||||
return block->mr;
|
||||
}
|
||||
|
||||
/* Called within RCU critical section. */
|
||||
static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
@@ -2511,101 +2457,58 @@ static bool prepare_mmio_access(MemoryRegion *mr)
|
||||
return release_lock;
|
||||
}
|
||||
|
||||
MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
|
||||
uint8_t *buf, int len, bool is_write)
|
||||
/* Called within RCU critical section. */
|
||||
static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
|
||||
MemTxAttrs attrs,
|
||||
const uint8_t *buf,
|
||||
int len, hwaddr addr1,
|
||||
hwaddr l, MemoryRegion *mr)
|
||||
{
|
||||
hwaddr l;
|
||||
uint8_t *ptr;
|
||||
uint64_t val;
|
||||
hwaddr addr1;
|
||||
MemoryRegion *mr;
|
||||
MemTxResult result = MEMTX_OK;
|
||||
bool release_lock = false;
|
||||
|
||||
rcu_read_lock();
|
||||
while (len > 0) {
|
||||
l = len;
|
||||
mr = address_space_translate(as, addr, &addr1, &l, is_write);
|
||||
|
||||
if (is_write) {
|
||||
if (!memory_access_is_direct(mr, is_write)) {
|
||||
release_lock |= prepare_mmio_access(mr);
|
||||
l = memory_access_size(mr, l, addr1);
|
||||
/* XXX: could force current_cpu to NULL to avoid
|
||||
potential bugs */
|
||||
switch (l) {
|
||||
case 8:
|
||||
/* 64 bit write access */
|
||||
val = ldq_p(buf);
|
||||
result |= memory_region_dispatch_write(mr, addr1, val, 8,
|
||||
attrs);
|
||||
break;
|
||||
case 4:
|
||||
/* 32 bit write access */
|
||||
val = ldl_p(buf);
|
||||
result |= memory_region_dispatch_write(mr, addr1, val, 4,
|
||||
attrs);
|
||||
break;
|
||||
case 2:
|
||||
/* 16 bit write access */
|
||||
val = lduw_p(buf);
|
||||
result |= memory_region_dispatch_write(mr, addr1, val, 2,
|
||||
attrs);
|
||||
break;
|
||||
case 1:
|
||||
/* 8 bit write access */
|
||||
val = ldub_p(buf);
|
||||
result |= memory_region_dispatch_write(mr, addr1, val, 1,
|
||||
attrs);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
addr1 += memory_region_get_ram_addr(mr);
|
||||
/* RAM case */
|
||||
ptr = qemu_get_ram_ptr(addr1);
|
||||
memcpy(ptr, buf, l);
|
||||
invalidate_and_set_dirty(mr, addr1, l);
|
||||
for (;;) {
|
||||
if (!memory_access_is_direct(mr, true)) {
|
||||
release_lock |= prepare_mmio_access(mr);
|
||||
l = memory_access_size(mr, l, addr1);
|
||||
/* XXX: could force current_cpu to NULL to avoid
|
||||
potential bugs */
|
||||
switch (l) {
|
||||
case 8:
|
||||
/* 64 bit write access */
|
||||
val = ldq_p(buf);
|
||||
result |= memory_region_dispatch_write(mr, addr1, val, 8,
|
||||
attrs);
|
||||
break;
|
||||
case 4:
|
||||
/* 32 bit write access */
|
||||
val = ldl_p(buf);
|
||||
result |= memory_region_dispatch_write(mr, addr1, val, 4,
|
||||
attrs);
|
||||
break;
|
||||
case 2:
|
||||
/* 16 bit write access */
|
||||
val = lduw_p(buf);
|
||||
result |= memory_region_dispatch_write(mr, addr1, val, 2,
|
||||
attrs);
|
||||
break;
|
||||
case 1:
|
||||
/* 8 bit write access */
|
||||
val = ldub_p(buf);
|
||||
result |= memory_region_dispatch_write(mr, addr1, val, 1,
|
||||
attrs);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
if (!memory_access_is_direct(mr, is_write)) {
|
||||
/* I/O case */
|
||||
release_lock |= prepare_mmio_access(mr);
|
||||
l = memory_access_size(mr, l, addr1);
|
||||
switch (l) {
|
||||
case 8:
|
||||
/* 64 bit read access */
|
||||
result |= memory_region_dispatch_read(mr, addr1, &val, 8,
|
||||
attrs);
|
||||
stq_p(buf, val);
|
||||
break;
|
||||
case 4:
|
||||
/* 32 bit read access */
|
||||
result |= memory_region_dispatch_read(mr, addr1, &val, 4,
|
||||
attrs);
|
||||
stl_p(buf, val);
|
||||
break;
|
||||
case 2:
|
||||
/* 16 bit read access */
|
||||
result |= memory_region_dispatch_read(mr, addr1, &val, 2,
|
||||
attrs);
|
||||
stw_p(buf, val);
|
||||
break;
|
||||
case 1:
|
||||
/* 8 bit read access */
|
||||
result |= memory_region_dispatch_read(mr, addr1, &val, 1,
|
||||
attrs);
|
||||
stb_p(buf, val);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
/* RAM case */
|
||||
ptr = qemu_get_ram_ptr(mr->ram_addr + addr1);
|
||||
memcpy(buf, ptr, l);
|
||||
}
|
||||
addr1 += memory_region_get_ram_addr(mr);
|
||||
/* RAM case */
|
||||
ptr = qemu_get_ram_ptr(addr1);
|
||||
memcpy(ptr, buf, l);
|
||||
invalidate_and_set_dirty(mr, addr1, l);
|
||||
}
|
||||
|
||||
if (release_lock) {
|
||||
@@ -2616,8 +2519,14 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
|
||||
len -= l;
|
||||
buf += l;
|
||||
addr += l;
|
||||
|
||||
if (!len) {
|
||||
break;
|
||||
}
|
||||
|
||||
l = len;
|
||||
mr = address_space_translate(as, addr, &addr1, &l, true);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -2625,15 +2534,122 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
|
||||
MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
|
||||
const uint8_t *buf, int len)
|
||||
{
|
||||
return address_space_rw(as, addr, attrs, (uint8_t *)buf, len, true);
|
||||
hwaddr l;
|
||||
hwaddr addr1;
|
||||
MemoryRegion *mr;
|
||||
MemTxResult result = MEMTX_OK;
|
||||
|
||||
if (len > 0) {
|
||||
rcu_read_lock();
|
||||
l = len;
|
||||
mr = address_space_translate(as, addr, &addr1, &l, true);
|
||||
result = address_space_write_continue(as, addr, attrs, buf, len,
|
||||
addr1, l, mr);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
|
||||
uint8_t *buf, int len)
|
||||
/* Called within RCU critical section. */
|
||||
MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
|
||||
MemTxAttrs attrs, uint8_t *buf,
|
||||
int len, hwaddr addr1, hwaddr l,
|
||||
MemoryRegion *mr)
|
||||
{
|
||||
return address_space_rw(as, addr, attrs, buf, len, false);
|
||||
uint8_t *ptr;
|
||||
uint64_t val;
|
||||
MemTxResult result = MEMTX_OK;
|
||||
bool release_lock = false;
|
||||
|
||||
for (;;) {
|
||||
if (!memory_access_is_direct(mr, false)) {
|
||||
/* I/O case */
|
||||
release_lock |= prepare_mmio_access(mr);
|
||||
l = memory_access_size(mr, l, addr1);
|
||||
switch (l) {
|
||||
case 8:
|
||||
/* 64 bit read access */
|
||||
result |= memory_region_dispatch_read(mr, addr1, &val, 8,
|
||||
attrs);
|
||||
stq_p(buf, val);
|
||||
break;
|
||||
case 4:
|
||||
/* 32 bit read access */
|
||||
result |= memory_region_dispatch_read(mr, addr1, &val, 4,
|
||||
attrs);
|
||||
stl_p(buf, val);
|
||||
break;
|
||||
case 2:
|
||||
/* 16 bit read access */
|
||||
result |= memory_region_dispatch_read(mr, addr1, &val, 2,
|
||||
attrs);
|
||||
stw_p(buf, val);
|
||||
break;
|
||||
case 1:
|
||||
/* 8 bit read access */
|
||||
result |= memory_region_dispatch_read(mr, addr1, &val, 1,
|
||||
attrs);
|
||||
stb_p(buf, val);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
/* RAM case */
|
||||
ptr = qemu_get_ram_ptr(mr->ram_addr + addr1);
|
||||
memcpy(buf, ptr, l);
|
||||
}
|
||||
|
||||
if (release_lock) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
release_lock = false;
|
||||
}
|
||||
|
||||
len -= l;
|
||||
buf += l;
|
||||
addr += l;
|
||||
|
||||
if (!len) {
|
||||
break;
|
||||
}
|
||||
|
||||
l = len;
|
||||
mr = address_space_translate(as, addr, &addr1, &l, false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
|
||||
MemTxAttrs attrs, uint8_t *buf, int len)
|
||||
{
|
||||
hwaddr l;
|
||||
hwaddr addr1;
|
||||
MemoryRegion *mr;
|
||||
MemTxResult result = MEMTX_OK;
|
||||
|
||||
if (len > 0) {
|
||||
rcu_read_lock();
|
||||
l = len;
|
||||
mr = address_space_translate(as, addr, &addr1, &l, false);
|
||||
result = address_space_read_continue(as, addr, attrs, buf, len,
|
||||
addr1, l, mr);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
|
||||
uint8_t *buf, int len, bool is_write)
|
||||
{
|
||||
if (is_write) {
|
||||
return address_space_write(as, addr, attrs, (uint8_t *)buf, len);
|
||||
} else {
|
||||
return address_space_read(as, addr, attrs, (uint8_t *)buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf,
|
||||
int len, int is_write)
|
||||
@@ -2825,6 +2841,7 @@ void *address_space_map(AddressSpace *as,
|
||||
hwaddr l, xlat, base;
|
||||
MemoryRegion *mr, *this_mr;
|
||||
ram_addr_t raddr;
|
||||
void *ptr;
|
||||
|
||||
if (len == 0) {
|
||||
return NULL;
|
||||
@@ -2876,9 +2893,11 @@ void *address_space_map(AddressSpace *as,
|
||||
}
|
||||
|
||||
memory_region_ref(mr);
|
||||
rcu_read_unlock();
|
||||
*plen = done;
|
||||
return qemu_ram_ptr_length(raddr + base, plen);
|
||||
ptr = qemu_ram_ptr_length(raddr + base, plen);
|
||||
rcu_read_unlock();
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* Unmaps a memory region previously mapped by address_space_map().
|
||||
|
||||
44
hmp.c
44
hmp.c
@@ -311,17 +311,25 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
|
||||
|
||||
monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU);
|
||||
|
||||
if (cpu->value->has_pc) {
|
||||
monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->pc);
|
||||
}
|
||||
if (cpu->value->has_nip) {
|
||||
monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->nip);
|
||||
}
|
||||
if (cpu->value->has_npc) {
|
||||
monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->npc);
|
||||
}
|
||||
if (cpu->value->has_PC) {
|
||||
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->PC);
|
||||
switch (cpu->value->arch) {
|
||||
case CPU_INFO_ARCH_X86:
|
||||
monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86->pc);
|
||||
break;
|
||||
case CPU_INFO_ARCH_PPC:
|
||||
monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc->nip);
|
||||
break;
|
||||
case CPU_INFO_ARCH_SPARC:
|
||||
monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.sparc->pc);
|
||||
monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->u.sparc->npc);
|
||||
break;
|
||||
case CPU_INFO_ARCH_MIPS:
|
||||
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.mips->PC);
|
||||
break;
|
||||
case CPU_INFO_ARCH_TRICORE:
|
||||
monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore->PC);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (cpu->value->halted) {
|
||||
@@ -855,7 +863,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
|
||||
tpo->has_cancel_path ? ",cancel-path=" : "",
|
||||
tpo->has_cancel_path ? tpo->cancel_path : "");
|
||||
break;
|
||||
case TPM_TYPE_OPTIONS_KIND_MAX:
|
||||
case TPM_TYPE_OPTIONS_KIND__MAX:
|
||||
break;
|
||||
}
|
||||
monitor_printf(mon, "\n");
|
||||
@@ -1203,7 +1211,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
|
||||
MigrationCapabilityStatusList *caps = g_malloc0(sizeof(*caps));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
|
||||
for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
|
||||
if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
|
||||
caps->value = g_malloc0(sizeof(*caps->value));
|
||||
caps->value->capability = i;
|
||||
@@ -1214,7 +1222,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MIGRATION_CAPABILITY_MAX) {
|
||||
if (i == MIGRATION_CAPABILITY__MAX) {
|
||||
error_setg(&err, QERR_INVALID_PARAMETER, cap);
|
||||
}
|
||||
|
||||
@@ -1239,7 +1247,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
|
||||
bool has_x_cpu_throttle_increment = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MIGRATION_PARAMETER_MAX; i++) {
|
||||
for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) {
|
||||
if (strcmp(param, MigrationParameter_lookup[i]) == 0) {
|
||||
switch (i) {
|
||||
case MIGRATION_PARAMETER_COMPRESS_LEVEL:
|
||||
@@ -1268,7 +1276,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
}
|
||||
|
||||
if (i == MIGRATION_PARAMETER_MAX) {
|
||||
if (i == MIGRATION_PARAMETER__MAX) {
|
||||
error_setg(&err, QERR_INVALID_PARAMETER, param);
|
||||
}
|
||||
|
||||
@@ -1368,7 +1376,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
|
||||
if (read_only) {
|
||||
read_only_mode =
|
||||
qapi_enum_parse(BlockdevChangeReadOnlyMode_lookup,
|
||||
read_only, BLOCKDEV_CHANGE_READ_ONLY_MODE_MAX,
|
||||
read_only, BLOCKDEV_CHANGE_READ_ONLY_MODE__MAX,
|
||||
BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, &err);
|
||||
if (err) {
|
||||
hmp_handle_error(mon, &err);
|
||||
@@ -1771,7 +1779,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict)
|
||||
keylist->value->u.number = value;
|
||||
} else {
|
||||
int idx = index_from_key(keyname_buf);
|
||||
if (idx == Q_KEY_CODE_MAX) {
|
||||
if (idx == Q_KEY_CODE__MAX) {
|
||||
goto err_out;
|
||||
}
|
||||
keylist->value->type = KEY_VALUE_KIND_QCODE;
|
||||
|
||||
@@ -36,6 +36,6 @@ static int coroutine_enter_func(void *arg)
|
||||
void co_run_in_worker_bh(void *opaque)
|
||||
{
|
||||
Coroutine *co = opaque;
|
||||
thread_pool_submit_aio(qemu_get_aio_context()->thread_pool,
|
||||
thread_pool_submit_aio(aio_get_thread_pool(qemu_get_aio_context()),
|
||||
coroutine_enter_func, co, coroutine_enter_cb, co);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ide/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += input/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += intc/
|
||||
devices-dirs-$(CONFIG_IPACK) += ipack/
|
||||
devices-dirs-$(CONFIG_IPMI) += ipmi/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += isa/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += misc/
|
||||
devices-dirs-$(CONFIG_SOFTMMU) += net/
|
||||
|
||||
@@ -2,6 +2,7 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
|
||||
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
|
||||
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
|
||||
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
|
||||
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
|
||||
common-obj-$(CONFIG_ACPI) += acpi_interface.o
|
||||
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
|
||||
common-obj-$(CONFIG_ACPI) += aml-build.o
|
||||
|
||||
@@ -427,6 +427,41 @@ Aml *aml_arg(int pos)
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToInteger */
|
||||
Aml *aml_to_integer(Aml *arg)
|
||||
{
|
||||
Aml *var = aml_opcode(0x99 /* ToIntegerOp */);
|
||||
aml_append(var, arg);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToHexString */
|
||||
Aml *aml_to_hexstring(Aml *src, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x98 /* ToHexStringOp */);
|
||||
aml_append(var, src);
|
||||
if (dst) {
|
||||
aml_append(var, dst);
|
||||
} else {
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToBuffer */
|
||||
Aml *aml_to_buffer(Aml *src, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x96 /* ToBufferOp */);
|
||||
aml_append(var, src);
|
||||
if (dst) {
|
||||
aml_append(var, dst);
|
||||
} else {
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */
|
||||
Aml *aml_store(Aml *val, Aml *target)
|
||||
{
|
||||
@@ -436,44 +471,64 @@ Aml *aml_store(Aml *val, Aml *target)
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */
|
||||
Aml *aml_and(Aml *arg1, Aml *arg2)
|
||||
/**
|
||||
* build_opcode_2arg_dst:
|
||||
* @op: 1-byte opcode
|
||||
* @arg1: 1st operand
|
||||
* @arg2: 2nd operand
|
||||
* @dst: optional target to store to, set to NULL if it's not required
|
||||
*
|
||||
* An internal helper to compose AML terms that have
|
||||
* "Op Operand Operand Target"
|
||||
* pattern.
|
||||
*
|
||||
* Returns: The newly allocated and composed according to patter Aml object.
|
||||
*/
|
||||
static Aml *
|
||||
build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x7B /* AndOp */);
|
||||
Aml *var = aml_opcode(op);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
if (dst) {
|
||||
aml_append(var, dst);
|
||||
} else {
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */
|
||||
Aml *aml_or(Aml *arg1, Aml *arg2)
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */
|
||||
Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x7D /* OrOp */);
|
||||
return build_opcode_2arg_dst(0x7B /* AndOp */, arg1, arg2, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */
|
||||
Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLOr */
|
||||
Aml *aml_lor(Aml *arg1, Aml *arg2)
|
||||
{
|
||||
Aml *var = aml_opcode(0x91 /* LOrOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */
|
||||
Aml *aml_shiftleft(Aml *arg1, Aml *count)
|
||||
{
|
||||
Aml *var = aml_opcode(0x79 /* ShiftLeftOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, count);
|
||||
build_append_byte(var->buf, 0x00); /* NullNameOp */
|
||||
return var;
|
||||
return build_opcode_2arg_dst(0x79 /* ShiftLeftOp */, arg1, count, NULL);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftRight */
|
||||
Aml *aml_shiftright(Aml *arg1, Aml *count)
|
||||
Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x7A /* ShiftRightOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, count);
|
||||
build_append_byte(var->buf, 0x00); /* NullNameOp */
|
||||
return var;
|
||||
return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */
|
||||
@@ -486,13 +541,15 @@ Aml *aml_lless(Aml *arg1, Aml *arg2)
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */
|
||||
Aml *aml_add(Aml *arg1, Aml *arg2)
|
||||
Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
Aml *var = aml_opcode(0x72 /* AddOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
return var;
|
||||
return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSubtract */
|
||||
Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst)
|
||||
{
|
||||
return build_opcode_2arg_dst(0x74 /* SubtractOp */, arg1, arg2, dst);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */
|
||||
@@ -503,14 +560,18 @@ Aml *aml_increment(Aml *arg)
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDecrement */
|
||||
Aml *aml_decrement(Aml *arg)
|
||||
{
|
||||
Aml *var = aml_opcode(0x76 /* DecrementOp */);
|
||||
aml_append(var, arg);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIndex */
|
||||
Aml *aml_index(Aml *arg1, Aml *idx)
|
||||
{
|
||||
Aml *var = aml_opcode(0x88 /* IndexOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, idx);
|
||||
build_append_byte(var->buf, 0x00 /* NullNameOp */);
|
||||
return var;
|
||||
return build_opcode_2arg_dst(0x88 /* IndexOp */, arg1, idx, NULL);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefNotify */
|
||||
@@ -522,6 +583,14 @@ Aml *aml_notify(Aml *arg1, Aml *arg2)
|
||||
return var;
|
||||
}
|
||||
|
||||
/* helper to call method with 1 argument */
|
||||
Aml *aml_call0(const char *method)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_namestring(var->buf, "%s", method);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* helper to call method with 1 argument */
|
||||
Aml *aml_call1(const char *method, Aml *arg1)
|
||||
{
|
||||
@@ -564,6 +633,94 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4)
|
||||
return var;
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI 5.0: 6.4.3.8.1 GPIO Connection Descriptor
|
||||
* Type 1, Large Item Name 0xC
|
||||
*/
|
||||
|
||||
static Aml *aml_gpio_connection(AmlGpioConnectionType type,
|
||||
AmlConsumerAndProducer con_and_pro,
|
||||
uint8_t flags, AmlPinConfig pin_config,
|
||||
uint16_t output_drive,
|
||||
uint16_t debounce_timeout,
|
||||
const uint32_t pin_list[], uint32_t pin_count,
|
||||
const char *resource_source_name,
|
||||
const uint8_t *vendor_data,
|
||||
uint16_t vendor_data_len)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
const uint16_t min_desc_len = 0x16;
|
||||
uint16_t resource_source_name_len, length;
|
||||
uint16_t pin_table_offset, resource_source_name_offset, vendor_data_offset;
|
||||
uint32_t i;
|
||||
|
||||
assert(resource_source_name);
|
||||
resource_source_name_len = strlen(resource_source_name) + 1;
|
||||
length = min_desc_len + resource_source_name_len + vendor_data_len;
|
||||
pin_table_offset = min_desc_len + 1;
|
||||
resource_source_name_offset = pin_table_offset + pin_count * 2;
|
||||
vendor_data_offset = resource_source_name_offset + resource_source_name_len;
|
||||
|
||||
build_append_byte(var->buf, 0x8C); /* GPIO Connection Descriptor */
|
||||
build_append_int_noprefix(var->buf, length, 2); /* Length */
|
||||
build_append_byte(var->buf, 1); /* Revision ID */
|
||||
build_append_byte(var->buf, type); /* GPIO Connection Type */
|
||||
/* General Flags (2 bytes) */
|
||||
build_append_int_noprefix(var->buf, con_and_pro, 2);
|
||||
/* Interrupt and IO Flags (2 bytes) */
|
||||
build_append_int_noprefix(var->buf, flags, 2);
|
||||
/* Pin Configuration 0 = Default 1 = Pull-up 2 = Pull-down 3 = No Pull */
|
||||
build_append_byte(var->buf, pin_config);
|
||||
/* Output Drive Strength (2 bytes) */
|
||||
build_append_int_noprefix(var->buf, output_drive, 2);
|
||||
/* Debounce Timeout (2 bytes) */
|
||||
build_append_int_noprefix(var->buf, debounce_timeout, 2);
|
||||
/* Pin Table Offset (2 bytes) */
|
||||
build_append_int_noprefix(var->buf, pin_table_offset, 2);
|
||||
build_append_byte(var->buf, 0); /* Resource Source Index */
|
||||
/* Resource Source Name Offset (2 bytes) */
|
||||
build_append_int_noprefix(var->buf, resource_source_name_offset, 2);
|
||||
/* Vendor Data Offset (2 bytes) */
|
||||
build_append_int_noprefix(var->buf, vendor_data_offset, 2);
|
||||
/* Vendor Data Length (2 bytes) */
|
||||
build_append_int_noprefix(var->buf, vendor_data_len, 2);
|
||||
/* Pin Number (2n bytes)*/
|
||||
for (i = 0; i < pin_count; i++) {
|
||||
build_append_int_noprefix(var->buf, pin_list[i], 2);
|
||||
}
|
||||
|
||||
/* Resource Source Name */
|
||||
build_append_namestring(var->buf, "%s", resource_source_name);
|
||||
build_append_byte(var->buf, '\0');
|
||||
|
||||
/* Vendor-defined Data */
|
||||
if (vendor_data != NULL) {
|
||||
g_array_append_vals(var->buf, vendor_data, vendor_data_len);
|
||||
}
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI 5.0: 19.5.53
|
||||
* GpioInt(GPIO Interrupt Connection Resource Descriptor Macro)
|
||||
*/
|
||||
Aml *aml_gpio_int(AmlConsumerAndProducer con_and_pro,
|
||||
AmlLevelAndEdge edge_level,
|
||||
AmlActiveHighAndLow active_level, AmlShared shared,
|
||||
AmlPinConfig pin_config, uint16_t debounce_timeout,
|
||||
const uint32_t pin_list[], uint32_t pin_count,
|
||||
const char *resource_source_name,
|
||||
const uint8_t *vendor_data, uint16_t vendor_data_len)
|
||||
{
|
||||
uint8_t flags = edge_level | (active_level << 1) | (shared << 3);
|
||||
|
||||
return aml_gpio_connection(AML_INTERRUPT_CONNECTION, con_and_pro, flags,
|
||||
pin_config, 0, debounce_timeout, pin_list,
|
||||
pin_count, resource_source_name, vendor_data,
|
||||
vendor_data_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI 1.0b: 6.4.3.4 32-Bit Fixed Location Memory Range Descriptor
|
||||
* (Type 1, Large Item Name 0x6)
|
||||
@@ -598,23 +755,27 @@ Aml *aml_memory32_fixed(uint32_t addr, uint32_t size,
|
||||
Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro,
|
||||
AmlLevelAndEdge level_and_edge,
|
||||
AmlActiveHighAndLow high_and_low, AmlShared shared,
|
||||
uint32_t irq)
|
||||
uint32_t *irq_list, uint8_t irq_count)
|
||||
{
|
||||
int i;
|
||||
Aml *var = aml_alloc();
|
||||
uint8_t irq_flags = con_and_pro | (level_and_edge << 1)
|
||||
| (high_and_low << 2) | (shared << 3);
|
||||
const int header_bytes_in_len = 2;
|
||||
uint16_t len = header_bytes_in_len + irq_count * sizeof(uint32_t);
|
||||
|
||||
assert(irq_count > 0);
|
||||
|
||||
build_append_byte(var->buf, 0x89); /* Extended irq descriptor */
|
||||
build_append_byte(var->buf, 6); /* Length, bits[7:0] minimum value = 6 */
|
||||
build_append_byte(var->buf, 0); /* Length, bits[15:8] minimum value = 0 */
|
||||
build_append_byte(var->buf, len & 0xFF); /* Length, bits[7:0] */
|
||||
build_append_byte(var->buf, len >> 8); /* Length, bits[15:8] */
|
||||
build_append_byte(var->buf, irq_flags); /* Interrupt Vector Information. */
|
||||
build_append_byte(var->buf, 0x01); /* Interrupt table length = 1 */
|
||||
build_append_byte(var->buf, irq_count); /* Interrupt table length */
|
||||
|
||||
/* Interrupt Number */
|
||||
build_append_byte(var->buf, extract32(irq, 0, 8)); /* bits[7:0] */
|
||||
build_append_byte(var->buf, extract32(irq, 8, 8)); /* bits[15:8] */
|
||||
build_append_byte(var->buf, extract32(irq, 16, 8)); /* bits[23:16] */
|
||||
build_append_byte(var->buf, extract32(irq, 24, 8)); /* bits[31:24] */
|
||||
/* Interrupt Number List */
|
||||
for (i = 0; i < irq_count; i++) {
|
||||
build_append_int_noprefix(var->buf, irq_list[i], 4);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
|
||||
@@ -672,6 +833,26 @@ Aml *aml_equal(Aml *arg1, Aml *arg2)
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreater */
|
||||
Aml *aml_lgreater(Aml *arg1, Aml *arg2)
|
||||
{
|
||||
Aml *var = aml_opcode(0x94 /* LGreaterOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreaterEqual */
|
||||
Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2)
|
||||
{
|
||||
/* LGreaterEqualOp := LNotOp LLessOp */
|
||||
Aml *var = aml_opcode(0x92 /* LNotOp */);
|
||||
build_append_byte(var->buf, 0x95 /* LLessOp */);
|
||||
aml_append(var, arg1);
|
||||
aml_append(var, arg2);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefIfElse */
|
||||
Aml *aml_if(Aml *predicate)
|
||||
{
|
||||
@@ -696,11 +877,24 @@ Aml *aml_while(Aml *predicate)
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */
|
||||
Aml *aml_method(const char *name, int arg_count)
|
||||
Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag)
|
||||
{
|
||||
Aml *var = aml_bundle(0x14 /* MethodOp */, AML_PACKAGE);
|
||||
int methodflags;
|
||||
|
||||
/*
|
||||
* MethodFlags:
|
||||
* bit 0-2: ArgCount (0-7)
|
||||
* bit 3: SerializeFlag
|
||||
* 0: NotSerialized
|
||||
* 1: Serialized
|
||||
* bit 4-7: reserved (must be 0)
|
||||
*/
|
||||
assert(arg_count < 8);
|
||||
methodflags = arg_count | (sflag << 3);
|
||||
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
build_append_byte(var->buf, arg_count); /* MethodFlags: ArgCount */
|
||||
build_append_byte(var->buf, methodflags); /* MethodFlags: ArgCount */
|
||||
return var;
|
||||
}
|
||||
|
||||
@@ -784,27 +978,43 @@ Aml *aml_reserved_field(unsigned length)
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */
|
||||
Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule)
|
||||
Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock,
|
||||
AmlUpdateRule rule)
|
||||
{
|
||||
Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE);
|
||||
uint8_t flags = rule << 5 | type;
|
||||
|
||||
flags |= lock << 4; /* LockRule at 4 bit offset */
|
||||
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
build_append_byte(var->buf, flags);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
|
||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
|
||||
static
|
||||
Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x8A); /* CreateDWordFieldOp */
|
||||
Aml *var = aml_opcode(opcode);
|
||||
aml_append(var, srcbuf);
|
||||
aml_append(var, index);
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
|
||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
|
||||
{
|
||||
return create_field_common(0x8A /* CreateDWordFieldOp */,
|
||||
srcbuf, index, name);
|
||||
}
|
||||
|
||||
/* ACPI 2.0a: 17.2.4.2 Named Objects Encoding: DefCreateQWordField */
|
||||
Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name)
|
||||
{
|
||||
return create_field_common(0x8F /* CreateQWordFieldOp */,
|
||||
srcbuf, index, name);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.3 Data Objects Encoding: String */
|
||||
Aml *aml_string(const char *name_format, ...)
|
||||
{
|
||||
@@ -1065,6 +1275,30 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
|
||||
addr_trans, len, flags);
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 6.4.2.2 DMA Format/6.4.2.2.1 ASL Macro for DMA Descriptor */
|
||||
Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
|
||||
uint8_t channel)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
uint8_t flags = sz | bm << 2 | typ << 5;
|
||||
|
||||
assert(channel < 8);
|
||||
build_append_byte(var->buf, 0x2A); /* Byte 0: DMA Descriptor */
|
||||
build_append_byte(var->buf, 1U << channel); /* Byte 1: _DMA - DmaChannel */
|
||||
build_append_byte(var->buf, flags); /* Byte 2 */
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefSleep */
|
||||
Aml *aml_sleep(uint64_t msec)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x22); /* SleepOp */
|
||||
aml_append(var, aml_int(msec));
|
||||
return var;
|
||||
}
|
||||
|
||||
static uint8_t Hex2Byte(const char *src)
|
||||
{
|
||||
int hi, lo;
|
||||
@@ -1135,16 +1369,81 @@ Aml *aml_unicode(const char *str)
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDerefOf */
|
||||
Aml *aml_derefof(Aml *arg)
|
||||
{
|
||||
Aml *var = aml_opcode(0x83 /* DerefOfOp */);
|
||||
aml_append(var, arg);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSizeOf */
|
||||
Aml *aml_sizeof(Aml *arg)
|
||||
{
|
||||
Aml *var = aml_opcode(0x87 /* SizeOfOp */);
|
||||
aml_append(var, arg);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMutex */
|
||||
Aml *aml_mutex(const char *name, uint8_t sync_level)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x01); /* MutexOp */
|
||||
build_append_namestring(var->buf, "%s", name);
|
||||
assert(!(sync_level & 0xF0));
|
||||
build_append_byte(var->buf, sync_level);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAcquire */
|
||||
Aml *aml_acquire(Aml *mutex, uint16_t timeout)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x23); /* AcquireOp */
|
||||
aml_append(var, mutex);
|
||||
build_append_int_noprefix(var->buf, timeout, sizeof(timeout));
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefRelease */
|
||||
Aml *aml_release(Aml *mutex)
|
||||
{
|
||||
Aml *var = aml_alloc();
|
||||
build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
|
||||
build_append_byte(var->buf, 0x27); /* ReleaseOp */
|
||||
aml_append(var, mutex);
|
||||
return var;
|
||||
}
|
||||
|
||||
/* ACPI 1.0b: 16.2.5.1 Name Space Modifier Objects Encoding: DefAlias */
|
||||
Aml *aml_alias(const char *source_object, const char *alias_object)
|
||||
{
|
||||
Aml *var = aml_opcode(0x06 /* AliasOp */);
|
||||
aml_append(var, aml_name("%s", source_object));
|
||||
aml_append(var, aml_name("%s", alias_object));
|
||||
return var;
|
||||
}
|
||||
|
||||
void
|
||||
build_header(GArray *linker, GArray *table_data,
|
||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev)
|
||||
AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
|
||||
const char *oem_table_id)
|
||||
{
|
||||
memcpy(&h->signature, sig, 4);
|
||||
h->length = cpu_to_le32(len);
|
||||
h->revision = rev;
|
||||
memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6);
|
||||
memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
|
||||
memcpy(h->oem_table_id + 4, sig, 4);
|
||||
|
||||
if (oem_table_id) {
|
||||
strncpy((char *)h->oem_table_id, oem_table_id, sizeof(h->oem_table_id));
|
||||
} else {
|
||||
memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
|
||||
memcpy(h->oem_table_id + 4, sig, 4);
|
||||
}
|
||||
|
||||
h->oem_revision = cpu_to_le32(1);
|
||||
memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4);
|
||||
h->asl_compiler_revision = cpu_to_le32(1);
|
||||
@@ -1211,5 +1510,5 @@ build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets)
|
||||
sizeof(uint32_t));
|
||||
}
|
||||
build_header(linker, table_data,
|
||||
(void *)rsdt, "RSDT", rsdt_len, 1);
|
||||
(void *)rsdt, "RSDT", rsdt_len, 1, NULL);
|
||||
}
|
||||
|
||||
@@ -231,6 +231,11 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
|
||||
DeviceState *dev, Error **errp)
|
||||
{
|
||||
MemStatus *mdev;
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
||||
|
||||
if (!dc->hotpluggable) {
|
||||
return;
|
||||
}
|
||||
|
||||
mdev = acpi_memory_slot_status(mem_st, dev, errp);
|
||||
if (!mdev) {
|
||||
|
||||
488
hw/acpi/nvdimm.c
Normal file
488
hw/acpi/nvdimm.c
Normal file
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* NVDIMM ACPI Implementation
|
||||
*
|
||||
* Copyright(C) 2015 Intel Corporation.
|
||||
*
|
||||
* Author:
|
||||
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||
*
|
||||
* NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
|
||||
* and the DSM specification can be found at:
|
||||
* http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
|
||||
*
|
||||
* Currently, it only supports PMEM Virtualization.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/acpi/aml-build.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
|
||||
static int nvdimm_plugged_device_list(Object *obj, void *opaque)
|
||||
{
|
||||
GSList **list = opaque;
|
||||
|
||||
if (object_dynamic_cast(obj, TYPE_NVDIMM)) {
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
|
||||
if (dev->realized) { /* only realized NVDIMMs matter */
|
||||
*list = g_slist_append(*list, DEVICE(obj));
|
||||
}
|
||||
}
|
||||
|
||||
object_child_foreach(obj, nvdimm_plugged_device_list, opaque);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* inquire plugged NVDIMM devices and link them into the list which is
|
||||
* returned to the caller.
|
||||
*
|
||||
* Note: it is the caller's responsibility to free the list to avoid
|
||||
* memory leak.
|
||||
*/
|
||||
static GSList *nvdimm_get_plugged_device_list(void)
|
||||
{
|
||||
GSList *list = NULL;
|
||||
|
||||
object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list,
|
||||
&list);
|
||||
return list;
|
||||
}
|
||||
|
||||
#define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
|
||||
{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
|
||||
(b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \
|
||||
(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }
|
||||
|
||||
/*
|
||||
* define Byte Addressable Persistent Memory (PM) Region according to
|
||||
* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure.
|
||||
*/
|
||||
static const uint8_t nvdimm_nfit_spa_uuid[] =
|
||||
NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33,
|
||||
0x18, 0xb7, 0x8c, 0xdb);
|
||||
|
||||
/*
|
||||
* NVDIMM Firmware Interface Table
|
||||
* @signature: "NFIT"
|
||||
*
|
||||
* It provides information that allows OSPM to enumerate NVDIMM present in
|
||||
* the platform and associate system physical address ranges created by the
|
||||
* NVDIMMs.
|
||||
*
|
||||
* It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
|
||||
*/
|
||||
struct NvdimmNfitHeader {
|
||||
ACPI_TABLE_HEADER_DEF
|
||||
uint32_t reserved;
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmNfitHeader NvdimmNfitHeader;
|
||||
|
||||
/*
|
||||
* define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware
|
||||
* Interface Table (NFIT).
|
||||
*/
|
||||
|
||||
/*
|
||||
* System Physical Address Range Structure
|
||||
*
|
||||
* It describes the system physical address ranges occupied by NVDIMMs and
|
||||
* the types of the regions.
|
||||
*/
|
||||
struct NvdimmNfitSpa {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
uint16_t spa_index;
|
||||
uint16_t flags;
|
||||
uint32_t reserved;
|
||||
uint32_t proximity_domain;
|
||||
uint8_t type_guid[16];
|
||||
uint64_t spa_base;
|
||||
uint64_t spa_length;
|
||||
uint64_t mem_attr;
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmNfitSpa NvdimmNfitSpa;
|
||||
|
||||
/*
|
||||
* Memory Device to System Physical Address Range Mapping Structure
|
||||
*
|
||||
* It enables identifying each NVDIMM region and the corresponding SPA
|
||||
* describing the memory interleave
|
||||
*/
|
||||
struct NvdimmNfitMemDev {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
uint32_t nfit_handle;
|
||||
uint16_t phys_id;
|
||||
uint16_t region_id;
|
||||
uint16_t spa_index;
|
||||
uint16_t dcr_index;
|
||||
uint64_t region_len;
|
||||
uint64_t region_offset;
|
||||
uint64_t region_dpa;
|
||||
uint16_t interleave_index;
|
||||
uint16_t interleave_ways;
|
||||
uint16_t flags;
|
||||
uint16_t reserved;
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmNfitMemDev NvdimmNfitMemDev;
|
||||
|
||||
/*
|
||||
* NVDIMM Control Region Structure
|
||||
*
|
||||
* It describes the NVDIMM and if applicable, Block Control Window.
|
||||
*/
|
||||
struct NvdimmNfitControlRegion {
|
||||
uint16_t type;
|
||||
uint16_t length;
|
||||
uint16_t dcr_index;
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t revision_id;
|
||||
uint16_t sub_vendor_id;
|
||||
uint16_t sub_device_id;
|
||||
uint16_t sub_revision_id;
|
||||
uint8_t reserved[6];
|
||||
uint32_t serial_number;
|
||||
uint16_t fic;
|
||||
uint16_t num_bcw;
|
||||
uint64_t bcw_size;
|
||||
uint64_t cmd_offset;
|
||||
uint64_t cmd_size;
|
||||
uint64_t status_offset;
|
||||
uint64_t status_size;
|
||||
uint16_t flags;
|
||||
uint8_t reserved2[6];
|
||||
} QEMU_PACKED;
|
||||
typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion;
|
||||
|
||||
/*
|
||||
* Module serial number is a unique number for each device. We use the
|
||||
* slot id of NVDIMM device to generate this number so that each device
|
||||
* associates with a different number.
|
||||
*
|
||||
* 0x123456 is a magic number we arbitrarily chose.
|
||||
*/
|
||||
static uint32_t nvdimm_slot_to_sn(int slot)
|
||||
{
|
||||
return 0x123456 + slot;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle is used to uniquely associate nfit_memdev structure with NVDIMM
|
||||
* ACPI device - nfit_memdev.nfit_handle matches with the value returned
|
||||
* by ACPI device _ADR method.
|
||||
*
|
||||
* We generate the handle with the slot id of NVDIMM device and reserve
|
||||
* 0 for NVDIMM root device.
|
||||
*/
|
||||
static uint32_t nvdimm_slot_to_handle(int slot)
|
||||
{
|
||||
return slot + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* index uniquely identifies the structure, 0 is reserved which indicates
|
||||
* that the structure is not valid or the associated structure is not
|
||||
* present.
|
||||
*
|
||||
* Each NVDIMM device needs two indexes, one for nfit_spa and another for
|
||||
* nfit_dc which are generated by the slot id of NVDIMM device.
|
||||
*/
|
||||
static uint16_t nvdimm_slot_to_spa_index(int slot)
|
||||
{
|
||||
return (slot + 1) << 1;
|
||||
}
|
||||
|
||||
/* See the comments of nvdimm_slot_to_spa_index(). */
|
||||
static uint32_t nvdimm_slot_to_dcr_index(int slot)
|
||||
{
|
||||
return nvdimm_slot_to_spa_index(slot) + 1;
|
||||
}
|
||||
|
||||
/* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */
|
||||
static void
|
||||
nvdimm_build_structure_spa(GArray *structures, DeviceState *dev)
|
||||
{
|
||||
NvdimmNfitSpa *nfit_spa;
|
||||
uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
|
||||
NULL);
|
||||
uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
|
||||
NULL);
|
||||
uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP,
|
||||
NULL);
|
||||
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
|
||||
NULL);
|
||||
|
||||
nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa));
|
||||
|
||||
nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range
|
||||
Structure */);
|
||||
nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa));
|
||||
nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
|
||||
|
||||
/*
|
||||
* Control region is strict as all the device info, such as SN, index,
|
||||
* is associated with slot id.
|
||||
*/
|
||||
nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for
|
||||
management during hot add/online
|
||||
operation */ |
|
||||
2 /* Data in Proximity Domain field is
|
||||
valid*/);
|
||||
|
||||
/* NUMA node. */
|
||||
nfit_spa->proximity_domain = cpu_to_le32(node);
|
||||
/* the region reported as PMEM. */
|
||||
memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid,
|
||||
sizeof(nvdimm_nfit_spa_uuid));
|
||||
|
||||
nfit_spa->spa_base = cpu_to_le64(addr);
|
||||
nfit_spa->spa_length = cpu_to_le64(size);
|
||||
|
||||
/* It is the PMEM and can be cached as writeback. */
|
||||
nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ |
|
||||
0x8000ULL /* EFI_MEMORY_NV */);
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping
|
||||
* Structure
|
||||
*/
|
||||
static void
|
||||
nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev)
|
||||
{
|
||||
NvdimmNfitMemDev *nfit_memdev;
|
||||
uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
|
||||
NULL);
|
||||
uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
|
||||
NULL);
|
||||
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
|
||||
NULL);
|
||||
uint32_t handle = nvdimm_slot_to_handle(slot);
|
||||
|
||||
nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev));
|
||||
|
||||
nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address
|
||||
Range Map Structure*/);
|
||||
nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev));
|
||||
nfit_memdev->nfit_handle = cpu_to_le32(handle);
|
||||
|
||||
/*
|
||||
* associate memory device with System Physical Address Range
|
||||
* Structure.
|
||||
*/
|
||||
nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
|
||||
/* associate memory device with Control Region Structure. */
|
||||
nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));
|
||||
|
||||
/* The memory region on the device. */
|
||||
nfit_memdev->region_len = cpu_to_le64(size);
|
||||
nfit_memdev->region_dpa = cpu_to_le64(addr);
|
||||
|
||||
/* Only one interleave for PMEM. */
|
||||
nfit_memdev->interleave_ways = cpu_to_le16(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure.
|
||||
*/
|
||||
static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev)
|
||||
{
|
||||
NvdimmNfitControlRegion *nfit_dcr;
|
||||
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
|
||||
NULL);
|
||||
uint32_t sn = nvdimm_slot_to_sn(slot);
|
||||
|
||||
nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr));
|
||||
|
||||
nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */);
|
||||
nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr));
|
||||
nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));
|
||||
|
||||
/* vendor: Intel. */
|
||||
nfit_dcr->vendor_id = cpu_to_le16(0x8086);
|
||||
nfit_dcr->device_id = cpu_to_le16(1);
|
||||
|
||||
/* The _DSM method is following Intel's DSM specification. */
|
||||
nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported
|
||||
in ACPI 6.0 is 1. */);
|
||||
nfit_dcr->serial_number = cpu_to_le32(sn);
|
||||
nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter
|
||||
2: NVDIMM Device Specific Method
|
||||
(DSM) in DSM Spec Rev1.*/);
|
||||
}
|
||||
|
||||
static GArray *nvdimm_build_device_structure(GSList *device_list)
|
||||
{
|
||||
GArray *structures = g_array_new(false, true /* clear */, 1);
|
||||
|
||||
for (; device_list; device_list = device_list->next) {
|
||||
DeviceState *dev = device_list->data;
|
||||
|
||||
/* build System Physical Address Range Structure. */
|
||||
nvdimm_build_structure_spa(structures, dev);
|
||||
|
||||
/*
|
||||
* build Memory Device to System Physical Address Range Mapping
|
||||
* Structure.
|
||||
*/
|
||||
nvdimm_build_structure_memdev(structures, dev);
|
||||
|
||||
/* build NVDIMM Control Region Structure. */
|
||||
nvdimm_build_structure_dcr(structures, dev);
|
||||
}
|
||||
|
||||
return structures;
|
||||
}
|
||||
|
||||
static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets,
|
||||
GArray *table_data, GArray *linker)
|
||||
{
|
||||
GArray *structures = nvdimm_build_device_structure(device_list);
|
||||
void *header;
|
||||
|
||||
acpi_add_table(table_offsets, table_data);
|
||||
|
||||
/* NFIT header. */
|
||||
header = acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
|
||||
/* NVDIMM device structures. */
|
||||
g_array_append_vals(table_data, structures->data, structures->len);
|
||||
|
||||
build_header(linker, table_data, header, "NFIT",
|
||||
sizeof(NvdimmNfitHeader) + structures->len, 1, NULL);
|
||||
g_array_free(structures, true);
|
||||
}
|
||||
|
||||
#define NVDIMM_COMMON_DSM "NCAL"
|
||||
|
||||
static void nvdimm_build_common_dsm(Aml *dev)
|
||||
{
|
||||
Aml *method, *ifctx, *function;
|
||||
uint8_t byte_list[1];
|
||||
|
||||
method = aml_method(NVDIMM_COMMON_DSM, 4, AML_NOTSERIALIZED);
|
||||
function = aml_arg(2);
|
||||
|
||||
/*
|
||||
* function 0 is called to inquire what functions are supported by
|
||||
* OSPM
|
||||
*/
|
||||
ifctx = aml_if(aml_equal(function, aml_int(0)));
|
||||
byte_list[0] = 0 /* No function Supported */;
|
||||
aml_append(ifctx, aml_return(aml_buffer(1, byte_list)));
|
||||
aml_append(method, ifctx);
|
||||
|
||||
/* No function is supported yet. */
|
||||
byte_list[0] = 1 /* Not Supported */;
|
||||
aml_append(method, aml_return(aml_buffer(1, byte_list)));
|
||||
|
||||
aml_append(dev, method);
|
||||
}
|
||||
|
||||
static void nvdimm_build_device_dsm(Aml *dev)
|
||||
{
|
||||
Aml *method;
|
||||
|
||||
method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_call4(NVDIMM_COMMON_DSM, aml_arg(0),
|
||||
aml_arg(1), aml_arg(2), aml_arg(3))));
|
||||
aml_append(dev, method);
|
||||
}
|
||||
|
||||
static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev)
|
||||
{
|
||||
for (; device_list; device_list = device_list->next) {
|
||||
DeviceState *dev = device_list->data;
|
||||
int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
|
||||
NULL);
|
||||
uint32_t handle = nvdimm_slot_to_handle(slot);
|
||||
Aml *nvdimm_dev;
|
||||
|
||||
nvdimm_dev = aml_device("NV%02X", slot);
|
||||
|
||||
/*
|
||||
* ACPI 6.0: 9.20 NVDIMM Devices:
|
||||
*
|
||||
* _ADR object that is used to supply OSPM with unique address
|
||||
* of the NVDIMM device. This is done by returning the NFIT Device
|
||||
* handle that is used to identify the associated entries in ACPI
|
||||
* table NFIT or _FIT.
|
||||
*/
|
||||
aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle)));
|
||||
|
||||
nvdimm_build_device_dsm(nvdimm_dev);
|
||||
aml_append(root_dev, nvdimm_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
|
||||
GArray *table_data, GArray *linker)
|
||||
{
|
||||
Aml *ssdt, *sb_scope, *dev;
|
||||
|
||||
acpi_add_table(table_offsets, table_data);
|
||||
|
||||
ssdt = init_aml_allocator();
|
||||
acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
|
||||
|
||||
sb_scope = aml_scope("\\_SB");
|
||||
|
||||
dev = aml_device("NVDR");
|
||||
|
||||
/*
|
||||
* ACPI 6.0: 9.20 NVDIMM Devices:
|
||||
*
|
||||
* The ACPI Name Space device uses _HID of ACPI0012 to identify the root
|
||||
* NVDIMM interface device. Platform firmware is required to contain one
|
||||
* such device in _SB scope if NVDIMMs support is exposed by platform to
|
||||
* OSPM.
|
||||
* For each NVDIMM present or intended to be supported by platform,
|
||||
* platform firmware also exposes an ACPI Namespace Device under the
|
||||
* root device.
|
||||
*/
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012")));
|
||||
|
||||
nvdimm_build_common_dsm(dev);
|
||||
nvdimm_build_device_dsm(dev);
|
||||
|
||||
nvdimm_build_nvdimm_devices(device_list, dev);
|
||||
|
||||
aml_append(sb_scope, dev);
|
||||
|
||||
aml_append(ssdt, sb_scope);
|
||||
/* copy AML table into ACPI tables blob and patch header there */
|
||||
g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + table_data->len - ssdt->buf->len),
|
||||
"SSDT", ssdt->buf->len, 1, "NVDIMM");
|
||||
free_aml_allocator();
|
||||
}
|
||||
|
||||
void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data,
|
||||
GArray *linker)
|
||||
{
|
||||
GSList *device_list;
|
||||
|
||||
/* no NVDIMM device is plugged. */
|
||||
device_list = nvdimm_get_plugged_device_list();
|
||||
if (!device_list) {
|
||||
return;
|
||||
}
|
||||
nvdimm_build_nfit(device_list, table_offsets, table_data, linker);
|
||||
nvdimm_build_ssdt(device_list, table_offsets, table_data, linker);
|
||||
g_slist_free(device_list);
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "alpha_sys.h"
|
||||
#include "qemu/log.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
/* Fallback for unassigned PCI I/O operations. Avoids MCHK. */
|
||||
@@ -73,7 +74,7 @@ static uint64_t iack_read(void *opaque, hwaddr addr, unsigned size)
|
||||
static void special_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
qemu_log("pci: special write cycle");
|
||||
trace_alpha_pci_iack_write();
|
||||
}
|
||||
|
||||
const MemoryRegionOps alpha_pci_iack_ops = {
|
||||
|
||||
@@ -38,7 +38,7 @@ static void fsl_imx25_init(Object *obj)
|
||||
object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
|
||||
qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
|
||||
|
||||
object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
|
||||
object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM);
|
||||
qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
|
||||
|
||||
for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
|
||||
@@ -150,7 +150,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
|
||||
{ FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ }
|
||||
};
|
||||
|
||||
s->gpt[i].ccm = DEVICE(&s->ccm);
|
||||
s->gpt[i].ccm = IMX_CCM(&s->ccm);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err);
|
||||
if (err) {
|
||||
@@ -173,7 +173,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
|
||||
{ FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ }
|
||||
};
|
||||
|
||||
s->epit[i].ccm = DEVICE(&s->ccm);
|
||||
s->epit[i].ccm = IMX_CCM(&s->ccm);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
|
||||
if (err) {
|
||||
|
||||
@@ -35,7 +35,7 @@ static void fsl_imx31_init(Object *obj)
|
||||
object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
|
||||
qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
|
||||
|
||||
object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
|
||||
object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM);
|
||||
qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
|
||||
|
||||
for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) {
|
||||
@@ -128,7 +128,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
|
||||
serial_table[i].irq));
|
||||
}
|
||||
|
||||
s->gpt.ccm = DEVICE(&s->ccm);
|
||||
s->gpt.ccm = IMX_CCM(&s->ccm);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err);
|
||||
if (err) {
|
||||
@@ -150,7 +150,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
|
||||
{ FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ },
|
||||
};
|
||||
|
||||
s->epit[i].ccm = DEVICE(&s->ccm);
|
||||
s->epit[i].ccm = IMX_CCM(&s->ccm);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
|
||||
if (err) {
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "hw/pci/pci.h"
|
||||
|
||||
#define ARM_SPI_BASE 32
|
||||
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
|
||||
|
||||
typedef struct VirtAcpiCpuInfo {
|
||||
DECLARE_BITMAP(found_cpus, VIRT_ACPI_CPU_ID_LIMIT);
|
||||
@@ -71,7 +72,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
||||
int uart_irq)
|
||||
uint32_t uart_irq)
|
||||
{
|
||||
Aml *dev = aml_device("COM0");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011")));
|
||||
@@ -82,7 +83,7 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
||||
uart_memmap->size, AML_READ_WRITE));
|
||||
aml_append(crs,
|
||||
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, uart_irq));
|
||||
AML_EXCLUSIVE, &uart_irq, 1));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
/* The _ADR entry is used to link this device to the UART described
|
||||
@@ -94,7 +95,7 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
|
||||
int rtc_irq)
|
||||
uint32_t rtc_irq)
|
||||
{
|
||||
Aml *dev = aml_device("RTC0");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013")));
|
||||
@@ -105,7 +106,7 @@ static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
|
||||
rtc_memmap->size, AML_READ_WRITE));
|
||||
aml_append(crs,
|
||||
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, rtc_irq));
|
||||
AML_EXCLUSIVE, &rtc_irq, 1));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
@@ -136,14 +137,14 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
|
||||
|
||||
static void acpi_dsdt_add_virtio(Aml *scope,
|
||||
const MemMapEntry *virtio_mmio_memmap,
|
||||
int mmio_irq, int num)
|
||||
uint32_t mmio_irq, int num)
|
||||
{
|
||||
hwaddr base = virtio_mmio_memmap->base;
|
||||
hwaddr size = virtio_mmio_memmap->size;
|
||||
int irq = mmio_irq;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
uint32_t irq = mmio_irq + i;
|
||||
Aml *dev = aml_device("VR%02u", i);
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(i)));
|
||||
@@ -152,15 +153,15 @@ static void acpi_dsdt_add_virtio(Aml *scope,
|
||||
aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
|
||||
aml_append(crs,
|
||||
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, irq + i));
|
||||
AML_EXCLUSIVE, &irq, 1));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
aml_append(scope, dev);
|
||||
base += size;
|
||||
}
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
|
||||
bool use_highmem)
|
||||
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
|
||||
uint32_t irq, bool use_highmem)
|
||||
{
|
||||
Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf;
|
||||
int i, bus_no;
|
||||
@@ -199,29 +200,30 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
|
||||
|
||||
/* Create GSI link device */
|
||||
for (i = 0; i < PCI_NUM_PINS; i++) {
|
||||
uint32_t irqs = irq + i;
|
||||
Aml *dev_gsi = aml_device("GSI%d", i);
|
||||
aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F")));
|
||||
aml_append(dev_gsi, aml_name_decl("_UID", aml_int(0)));
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, irq + i));
|
||||
AML_EXCLUSIVE, &irqs, 1));
|
||||
aml_append(dev_gsi, aml_name_decl("_PRS", crs));
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, irq + i));
|
||||
AML_EXCLUSIVE, &irqs, 1));
|
||||
aml_append(dev_gsi, aml_name_decl("_CRS", crs));
|
||||
method = aml_method("_SRS", 1);
|
||||
method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
|
||||
aml_append(dev_gsi, method);
|
||||
aml_append(dev, dev_gsi);
|
||||
}
|
||||
|
||||
method = aml_method("_CBA", 0);
|
||||
method = aml_method("_CBA", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_int(base_ecam)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_CRS", 0);
|
||||
method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
|
||||
Aml *rbuf = aml_resource_template();
|
||||
aml_append(rbuf,
|
||||
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
|
||||
@@ -254,7 +256,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
|
||||
/* Declare an _OSC (OS Control Handoff) method */
|
||||
aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
|
||||
aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
|
||||
method = aml_method("_OSC", 4);
|
||||
method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
|
||||
|
||||
@@ -272,16 +274,16 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
|
||||
aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
|
||||
aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP")));
|
||||
aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL")));
|
||||
aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D)),
|
||||
aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D), NULL),
|
||||
aml_name("CTRL")));
|
||||
|
||||
ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
|
||||
aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08)),
|
||||
aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08), NULL),
|
||||
aml_name("CDW1")));
|
||||
aml_append(ifctx, ifctx1);
|
||||
|
||||
ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL"))));
|
||||
aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10)),
|
||||
aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10), NULL),
|
||||
aml_name("CDW1")));
|
||||
aml_append(ifctx, ifctx1);
|
||||
|
||||
@@ -290,13 +292,13 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
|
||||
aml_append(method, ifctx);
|
||||
|
||||
elsectx = aml_else();
|
||||
aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4)),
|
||||
aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4), NULL),
|
||||
aml_name("CDW1")));
|
||||
aml_append(elsectx, aml_return(aml_arg(3)));
|
||||
aml_append(method, elsectx);
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_DSM", 4);
|
||||
method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
|
||||
|
||||
/* PCI Firmware Specification 3.0
|
||||
* 4.6.1. _DSM for PCI Express Slot Information
|
||||
@@ -323,6 +325,46 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
|
||||
uint32_t gpio_irq)
|
||||
{
|
||||
Aml *dev = aml_device("GPO0");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0061")));
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
|
||||
|
||||
Aml *crs = aml_resource_template();
|
||||
aml_append(crs, aml_memory32_fixed(gpio_memmap->base, gpio_memmap->size,
|
||||
AML_READ_WRITE));
|
||||
aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, &gpio_irq, 1));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
Aml *aei = aml_resource_template();
|
||||
/* Pin 3 for power button */
|
||||
const uint32_t pin_list[1] = {3};
|
||||
aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
|
||||
AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1,
|
||||
"GPO0", NULL, 0));
|
||||
aml_append(dev, aml_name_decl("_AEI", aei));
|
||||
|
||||
/* _E03 is handle for power button */
|
||||
Aml *method = aml_method("_E03", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
|
||||
aml_int(0x80)));
|
||||
aml_append(dev, method);
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
static void acpi_dsdt_add_power_button(Aml *scope)
|
||||
{
|
||||
Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE);
|
||||
aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C")));
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
|
||||
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
|
||||
aml_append(scope, dev);
|
||||
}
|
||||
|
||||
/* RSDP */
|
||||
static GArray *
|
||||
build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
|
||||
@@ -381,7 +423,8 @@ build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
||||
spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */
|
||||
spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */
|
||||
|
||||
build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2);
|
||||
build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -400,7 +443,7 @@ build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
||||
mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
|
||||
/ PCIE_MMCFG_SIZE_MIN) - 1;
|
||||
|
||||
build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1);
|
||||
build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL);
|
||||
}
|
||||
|
||||
/* GTDT */
|
||||
@@ -426,7 +469,7 @@ build_gtdt(GArray *table_data, GArray *linker)
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + gtdt_start), "GTDT",
|
||||
table_data->len - gtdt_start, 2);
|
||||
table_data->len - gtdt_start, 2, NULL);
|
||||
}
|
||||
|
||||
/* MADT */
|
||||
@@ -488,7 +531,7 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + madt_start), "APIC",
|
||||
table_data->len - madt_start, 3);
|
||||
table_data->len - madt_start, 3, NULL);
|
||||
}
|
||||
|
||||
/* FADT */
|
||||
@@ -513,7 +556,7 @@ build_fadt(GArray *table_data, GArray *linker, unsigned dsdt)
|
||||
sizeof fadt->dsdt);
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 5);
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 5, NULL);
|
||||
}
|
||||
|
||||
/* DSDT */
|
||||
@@ -539,6 +582,9 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
||||
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
|
||||
acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
|
||||
guest_info->use_highmem);
|
||||
acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
|
||||
(irqmap[VIRT_GPIO] + ARM_SPI_BASE));
|
||||
acpi_dsdt_add_power_button(scope);
|
||||
|
||||
aml_append(dsdt, scope);
|
||||
|
||||
@@ -546,7 +592,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
|
||||
g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + table_data->len - dsdt->buf->len),
|
||||
"DSDT", dsdt->buf->len, 2);
|
||||
"DSDT", dsdt->buf->len, 2, NULL);
|
||||
free_aml_allocator();
|
||||
}
|
||||
|
||||
@@ -631,7 +677,7 @@ static void acpi_ram_update(MemoryRegion *mr, GArray *data)
|
||||
memory_region_set_dirty(mr, 0, size);
|
||||
}
|
||||
|
||||
static void virt_acpi_build_update(void *build_opaque, uint32_t offset)
|
||||
static void virt_acpi_build_update(void *build_opaque)
|
||||
{
|
||||
AcpiBuildState *build_state = build_opaque;
|
||||
AcpiBuildTables tables;
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include "kvm_arm.h"
|
||||
#include "hw/smbios/smbios.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "standard-headers/linux/input.h"
|
||||
|
||||
/* Number of external interrupt lines to configure the GIC with */
|
||||
#define NUM_IRQS 256
|
||||
@@ -120,6 +121,7 @@ static const MemMapEntry a15memmap[] = {
|
||||
[VIRT_UART] = { 0x09000000, 0x00001000 },
|
||||
[VIRT_RTC] = { 0x09010000, 0x00001000 },
|
||||
[VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
|
||||
[VIRT_GPIO] = { 0x09030000, 0x00001000 },
|
||||
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
|
||||
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
|
||||
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
|
||||
@@ -135,6 +137,7 @@ static const int a15irqmap[] = {
|
||||
[VIRT_UART] = 1,
|
||||
[VIRT_RTC] = 2,
|
||||
[VIRT_PCIE] = 3, /* ... to 6 */
|
||||
[VIRT_GPIO] = 7,
|
||||
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
|
||||
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
|
||||
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
|
||||
@@ -538,6 +541,61 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
static DeviceState *pl061_dev;
|
||||
static void virt_powerdown_req(Notifier *n, void *opaque)
|
||||
{
|
||||
/* use gpio Pin 3 for power button event */
|
||||
qemu_set_irq(qdev_get_gpio_in(pl061_dev, 3), 1);
|
||||
}
|
||||
|
||||
static Notifier virt_system_powerdown_notifier = {
|
||||
.notify = virt_powerdown_req
|
||||
};
|
||||
|
||||
static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
{
|
||||
char *nodename;
|
||||
hwaddr base = vbi->memmap[VIRT_GPIO].base;
|
||||
hwaddr size = vbi->memmap[VIRT_GPIO].size;
|
||||
int irq = vbi->irqmap[VIRT_GPIO];
|
||||
const char compat[] = "arm,pl061\0arm,primecell";
|
||||
|
||||
pl061_dev = sysbus_create_simple("pl061", base, pic[irq]);
|
||||
|
||||
uint32_t phandle = qemu_fdt_alloc_phandle(vbi->fdt);
|
||||
nodename = g_strdup_printf("/pl061@%" PRIx64, base);
|
||||
qemu_fdt_add_subnode(vbi->fdt, nodename);
|
||||
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
|
||||
2, base, 2, size);
|
||||
qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat));
|
||||
qemu_fdt_setprop_cell(vbi->fdt, nodename, "#gpio-cells", 2);
|
||||
qemu_fdt_setprop(vbi->fdt, nodename, "gpio-controller", NULL, 0);
|
||||
qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
|
||||
GIC_FDT_IRQ_TYPE_SPI, irq,
|
||||
GIC_FDT_IRQ_FLAGS_LEVEL_HI);
|
||||
qemu_fdt_setprop_cell(vbi->fdt, nodename, "clocks", vbi->clock_phandle);
|
||||
qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk");
|
||||
qemu_fdt_setprop_cell(vbi->fdt, nodename, "phandle", phandle);
|
||||
|
||||
qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys");
|
||||
qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys", "compatible", "gpio-keys");
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#size-cells", 0);
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#address-cells", 1);
|
||||
|
||||
qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys/poweroff");
|
||||
qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys/poweroff",
|
||||
"label", "GPIO Key Poweroff");
|
||||
qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys/poweroff", "linux,code",
|
||||
KEY_POWER);
|
||||
qemu_fdt_setprop_cells(vbi->fdt, "/gpio-keys/poweroff",
|
||||
"gpios", phandle, 3, 0);
|
||||
|
||||
/* connect powerdown request */
|
||||
qemu_register_powerdown_notifier(&virt_system_powerdown_notifier);
|
||||
|
||||
g_free(nodename);
|
||||
}
|
||||
|
||||
static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic)
|
||||
{
|
||||
int i;
|
||||
@@ -1041,6 +1099,8 @@ static void machvirt_init(MachineState *machine)
|
||||
|
||||
create_pcie(vbi, pic, vms->highmem);
|
||||
|
||||
create_gpio(vbi, pic);
|
||||
|
||||
/* Create mmio transports, so the user can create virtio backends
|
||||
* (which will be automatically plugged in to the transports). If
|
||||
* no backend is created the transport will just sit harmlessly idle.
|
||||
|
||||
@@ -407,24 +407,16 @@ void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb)
|
||||
for (i = 0; i < mrb->num_reqs; i++) {
|
||||
VirtIOBlockReq *req = mrb->reqs[i];
|
||||
if (num_reqs > 0) {
|
||||
bool merge = true;
|
||||
|
||||
/* merge would exceed maximum number of IOVs */
|
||||
if (niov + req->qiov.niov > IOV_MAX) {
|
||||
merge = false;
|
||||
}
|
||||
|
||||
/* merge would exceed maximum transfer length of backend device */
|
||||
if (req->qiov.size / BDRV_SECTOR_SIZE + nb_sectors > max_xfer_len) {
|
||||
merge = false;
|
||||
}
|
||||
|
||||
/* requests are not sequential */
|
||||
if (sector_num + nb_sectors != req->sector_num) {
|
||||
merge = false;
|
||||
}
|
||||
|
||||
if (!merge) {
|
||||
/*
|
||||
* NOTE: We cannot merge the requests in below situations:
|
||||
* 1. requests are not sequential
|
||||
* 2. merge would exceed maximum number of IOVs
|
||||
* 3. merge would exceed maximum transfer length of backend device
|
||||
*/
|
||||
if (sector_num + nb_sectors != req->sector_num ||
|
||||
niov > blk_get_max_iov(blk) - req->qiov.niov ||
|
||||
req->qiov.size / BDRV_SECTOR_SIZE > max_xfer_len ||
|
||||
nb_sectors > max_xfer_len - req->qiov.size / BDRV_SECTOR_SIZE) {
|
||||
submit_requests(blk, mrb, start, num_reqs, niov);
|
||||
num_reqs = 0;
|
||||
}
|
||||
|
||||
@@ -85,8 +85,10 @@ static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_reque
|
||||
d->nr_sectors = s->nr_sectors;
|
||||
return;
|
||||
}
|
||||
if (n > src->nr_segments)
|
||||
n = src->nr_segments;
|
||||
/* prevent the compiler from optimizing the code and using src->nr_segments instead */
|
||||
barrier();
|
||||
if (n > dst->nr_segments)
|
||||
n = dst->nr_segments;
|
||||
for (i = 0; i < n; i++)
|
||||
dst->seg[i] = src->seg[i];
|
||||
}
|
||||
@@ -106,8 +108,10 @@ static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_reque
|
||||
d->nr_sectors = s->nr_sectors;
|
||||
return;
|
||||
}
|
||||
if (n > src->nr_segments)
|
||||
n = src->nr_segments;
|
||||
/* prevent the compiler from optimizing the code and using src->nr_segments instead */
|
||||
barrier();
|
||||
if (n > dst->nr_segments)
|
||||
n = dst->nr_segments;
|
||||
for (i = 0; i < n; i++)
|
||||
dst->seg[i] = src->seg[i];
|
||||
}
|
||||
|
||||
@@ -825,6 +825,9 @@ static int blk_init(struct XenDevice *xendev)
|
||||
if (!strcmp("aio", blkdev->fileproto)) {
|
||||
blkdev->fileproto = "raw";
|
||||
}
|
||||
if (!strcmp("vhd", blkdev->fileproto)) {
|
||||
blkdev->fileproto = "vpc";
|
||||
}
|
||||
if (blkdev->mode == NULL) {
|
||||
blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode");
|
||||
}
|
||||
|
||||
@@ -714,7 +714,7 @@ MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
|
||||
return &d->mmio;
|
||||
}
|
||||
|
||||
static const uint8_t qcode_to_keycode[Q_KEY_CODE_MAX] = {
|
||||
static const uint8_t qcode_to_keycode[Q_KEY_CODE__MAX] = {
|
||||
[Q_KEY_CODE_SHIFT] = 99,
|
||||
[Q_KEY_CODE_SHIFT_R] = 110,
|
||||
[Q_KEY_CODE_ALT] = 19,
|
||||
|
||||
@@ -165,7 +165,7 @@ static void serial_receive(void *opaque, const uint8_t *buf, int size)
|
||||
|
||||
/* Got a byte. */
|
||||
if (s->rx_fifo_len >= 16) {
|
||||
qemu_log("WARNING: UART dropped char.\n");
|
||||
D(qemu_log("WARNING: UART dropped char.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,12 +51,10 @@
|
||||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "exec/memory.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
bool option_rom_has_mr = false;
|
||||
bool rom_file_has_mr = true;
|
||||
|
||||
static int roms_loaded;
|
||||
|
||||
/* return the size or -1 if error */
|
||||
@@ -754,6 +752,7 @@ int rom_add_file(const char *file, const char *fw_dir,
|
||||
hwaddr addr, int32_t bootindex,
|
||||
bool option_rom)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
||||
Rom *rom;
|
||||
int rc, fd = -1;
|
||||
char devpath[100];
|
||||
@@ -810,7 +809,7 @@ int rom_add_file(const char *file, const char *fw_dir,
|
||||
basename);
|
||||
snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
|
||||
|
||||
if ((!option_rom || option_rom_has_mr) && rom_file_has_mr) {
|
||||
if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) {
|
||||
data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
|
||||
} else {
|
||||
data = rom->data;
|
||||
@@ -838,6 +837,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
|
||||
size_t max_len, hwaddr addr, const char *fw_file_name,
|
||||
FWCfgReadCallback fw_callback, void *callback_opaque)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
||||
Rom *rom;
|
||||
MemoryRegion *mr = NULL;
|
||||
|
||||
@@ -855,7 +855,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
|
||||
|
||||
snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
|
||||
|
||||
if (rom_file_has_mr) {
|
||||
if (mc->rom_file_has_mr) {
|
||||
data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
|
||||
mr = rom->mr;
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include "hw/boards.h"
|
||||
#include "qapi-visit.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
@@ -31,12 +32,39 @@ static void machine_set_accel(Object *obj, const char *value, Error **errp)
|
||||
ms->accel = g_strdup(value);
|
||||
}
|
||||
|
||||
static void machine_set_kernel_irqchip(Object *obj, bool value, Error **errp)
|
||||
static void machine_set_kernel_irqchip(Object *obj, Visitor *v,
|
||||
void *opaque, const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
MachineState *ms = MACHINE(obj);
|
||||
OnOffSplit mode;
|
||||
|
||||
ms->kernel_irqchip_allowed = value;
|
||||
ms->kernel_irqchip_required = value;
|
||||
visit_type_OnOffSplit(v, &mode, name, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
} else {
|
||||
switch (mode) {
|
||||
case ON_OFF_SPLIT_ON:
|
||||
ms->kernel_irqchip_allowed = true;
|
||||
ms->kernel_irqchip_required = true;
|
||||
ms->kernel_irqchip_split = false;
|
||||
break;
|
||||
case ON_OFF_SPLIT_OFF:
|
||||
ms->kernel_irqchip_allowed = false;
|
||||
ms->kernel_irqchip_required = false;
|
||||
ms->kernel_irqchip_split = false;
|
||||
break;
|
||||
case ON_OFF_SPLIT_SPLIT:
|
||||
ms->kernel_irqchip_allowed = true;
|
||||
ms->kernel_irqchip_required = true;
|
||||
ms->kernel_irqchip_split = true;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v,
|
||||
@@ -314,6 +342,7 @@ static void machine_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
/* Default 128 MB as guest ram size */
|
||||
mc->default_ram_size = 128 * M_BYTE;
|
||||
mc->rom_file_has_mr = true;
|
||||
}
|
||||
|
||||
static void machine_class_base_init(ObjectClass *oc, void *data)
|
||||
@@ -341,12 +370,12 @@ static void machine_initfn(Object *obj)
|
||||
object_property_set_description(obj, "accel",
|
||||
"Accelerator list",
|
||||
NULL);
|
||||
object_property_add_bool(obj, "kernel-irqchip",
|
||||
NULL,
|
||||
machine_set_kernel_irqchip,
|
||||
NULL);
|
||||
object_property_add(obj, "kernel-irqchip", "OnOffSplit",
|
||||
NULL,
|
||||
machine_set_kernel_irqchip,
|
||||
NULL, NULL, NULL);
|
||||
object_property_set_description(obj, "kernel-irqchip",
|
||||
"Use KVM in-kernel irqchip",
|
||||
"Configure KVM in-kernel irqchip",
|
||||
NULL);
|
||||
object_property_add(obj, "kvm-shadow-mem", "int",
|
||||
machine_get_kvm_shadow_mem,
|
||||
@@ -472,6 +501,11 @@ bool machine_kernel_irqchip_required(MachineState *machine)
|
||||
return machine->kernel_irqchip_required;
|
||||
}
|
||||
|
||||
bool machine_kernel_irqchip_split(MachineState *machine)
|
||||
{
|
||||
return machine->kernel_irqchip_split;
|
||||
}
|
||||
|
||||
int machine_kvm_shadow_mem(MachineState *machine)
|
||||
{
|
||||
return machine->kvm_shadow_mem;
|
||||
|
||||
@@ -233,8 +233,10 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
|
||||
|
||||
/* TODO: draw the segments */
|
||||
snprintf(buf, 2, "%02hhx\n", s->segments);
|
||||
console_write_ch(chardata++, 0x00200100 | buf[0]);
|
||||
console_write_ch(chardata++, 0x00200100 | buf[1]);
|
||||
console_write_ch(chardata++, ATTR2CHTYPE(buf[0], QEMU_COLOR_BLUE,
|
||||
QEMU_COLOR_BLACK, 1));
|
||||
console_write_ch(chardata++, ATTR2CHTYPE(buf[1], QEMU_COLOR_BLUE,
|
||||
QEMU_COLOR_BLACK, 1));
|
||||
|
||||
dpy_text_update(s->con, 0, 0, 2, 1);
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ static void glue(draw_line12_, DEPTH)(void *opaque,
|
||||
uint8_t r, g, b;
|
||||
|
||||
do {
|
||||
v = lduw_p((void *) s);
|
||||
v = lduw_le_p((void *) s);
|
||||
r = (v >> 4) & 0xf0;
|
||||
g = v & 0xf0;
|
||||
b = (v << 4) & 0xf0;
|
||||
@@ -159,7 +159,7 @@ static void glue(draw_line16_, DEPTH)(void *opaque,
|
||||
uint8_t r, g, b;
|
||||
|
||||
do {
|
||||
v = lduw_p((void *) s);
|
||||
v = lduw_le_p((void *) s);
|
||||
r = (v >> 8) & 0xf8;
|
||||
g = (v >> 3) & 0xfc;
|
||||
b = (v << 3) & 0xf8;
|
||||
|
||||
@@ -309,10 +309,10 @@ static void pxa2xx_descriptor_load(PXA2xxLCDState *s)
|
||||
}
|
||||
|
||||
cpu_physical_memory_read(descptr, &desc, sizeof(desc));
|
||||
s->dma_ch[i].descriptor = tswap32(desc.fdaddr);
|
||||
s->dma_ch[i].source = tswap32(desc.fsaddr);
|
||||
s->dma_ch[i].id = tswap32(desc.fidr);
|
||||
s->dma_ch[i].command = tswap32(desc.ldcmd);
|
||||
s->dma_ch[i].descriptor = le32_to_cpu(desc.fdaddr);
|
||||
s->dma_ch[i].source = le32_to_cpu(desc.fsaddr);
|
||||
s->dma_ch[i].id = le32_to_cpu(desc.fidr);
|
||||
s->dma_ch[i].command = le32_to_cpu(desc.ldcmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1979,7 +1979,8 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
|
||||
width = (s->last_width - size) / 2;
|
||||
dst = chardata + s->last_width + width;
|
||||
for (i = 0; i < size; i ++)
|
||||
console_write_ch(dst ++, 0x00200100 | msg_buffer[i]);
|
||||
console_write_ch(dst ++, ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
|
||||
QEMU_COLOR_BLACK, 1));
|
||||
|
||||
dpy_text_update(s->con, 0, 0, s->last_width, height);
|
||||
}
|
||||
|
||||
@@ -146,14 +146,14 @@ static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features,
|
||||
VirtIOGPU *g = VIRTIO_GPU(vdev);
|
||||
|
||||
if (virtio_gpu_virgl_enabled(g->conf)) {
|
||||
features |= (1 << VIRTIO_GPU_FEATURE_VIRGL);
|
||||
features |= (1 << VIRTIO_GPU_F_VIRGL);
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
||||
static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features)
|
||||
{
|
||||
static const uint32_t virgl = (1 << VIRTIO_GPU_FEATURE_VIRGL);
|
||||
static const uint32_t virgl = (1 << VIRTIO_GPU_F_VIRGL);
|
||||
VirtIOGPU *g = VIRTIO_GPU(vdev);
|
||||
|
||||
g->use_virgl_renderer = ((features & virgl) == virgl);
|
||||
|
||||
@@ -784,18 +784,20 @@ static void xenfb_invalidate(void *opaque)
|
||||
|
||||
static void xenfb_handle_events(struct XenFB *xenfb)
|
||||
{
|
||||
uint32_t prod, cons;
|
||||
uint32_t prod, cons, out_cons;
|
||||
struct xenfb_page *page = xenfb->c.page;
|
||||
|
||||
prod = page->out_prod;
|
||||
if (prod == page->out_cons)
|
||||
out_cons = page->out_cons;
|
||||
if (prod == out_cons)
|
||||
return;
|
||||
xen_rmb(); /* ensure we see ring contents up to prod */
|
||||
for (cons = page->out_cons; cons != prod; cons++) {
|
||||
for (cons = out_cons; cons != prod; cons++) {
|
||||
union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
|
||||
uint8_t type = event->type;
|
||||
int x, y, w, h;
|
||||
|
||||
switch (event->type) {
|
||||
switch (type) {
|
||||
case XENFB_TYPE_UPDATE:
|
||||
if (xenfb->up_count == UP_QUEUE)
|
||||
xenfb->up_fullscreen = 1;
|
||||
|
||||
@@ -269,11 +269,10 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
|
||||
if (entry->type == soc_dma_port_mem) {
|
||||
if (entry->addr <= virt_base &&
|
||||
entry->addr + entry->u.mem.size > virt_base) {
|
||||
fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
|
||||
" collides with RAM region at " TARGET_FMT_lx
|
||||
"-" TARGET_FMT_lx "\n", __FUNCTION__,
|
||||
(target_ulong) virt_base,
|
||||
(target_ulong) entry->addr, (target_ulong)
|
||||
fprintf(stderr, "%s: FIFO at %"PRIx64
|
||||
" collides with RAM region at %"PRIx64
|
||||
"-%"PRIx64 "\n", __func__,
|
||||
virt_base, entry->addr,
|
||||
(entry->addr + entry->u.mem.size));
|
||||
exit(-1);
|
||||
}
|
||||
@@ -284,10 +283,9 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
|
||||
while (entry < dma->memmap + dma->memmap_size &&
|
||||
entry->addr <= virt_base) {
|
||||
if (entry->addr == virt_base && entry->u.fifo.out == out) {
|
||||
fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
|
||||
" collides FIFO at " TARGET_FMT_lx "\n",
|
||||
__FUNCTION__, (target_ulong) virt_base,
|
||||
(target_ulong) entry->addr);
|
||||
fprintf(stderr, "%s: FIFO at %"PRIx64
|
||||
" collides FIFO at %"PRIx64 "\n",
|
||||
__func__, virt_base, entry->addr);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -322,13 +320,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
|
||||
if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
|
||||
(entry->addr <= virt_base &&
|
||||
entry->addr + entry->u.mem.size > virt_base)) {
|
||||
fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
|
||||
" collides with RAM region at " TARGET_FMT_lx
|
||||
"-" TARGET_FMT_lx "\n", __FUNCTION__,
|
||||
(target_ulong) virt_base,
|
||||
(target_ulong) (virt_base + size),
|
||||
(target_ulong) entry->addr, (target_ulong)
|
||||
(entry->addr + entry->u.mem.size));
|
||||
fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
|
||||
" collides with RAM region at %"PRIx64
|
||||
"-%"PRIx64 "\n", __func__,
|
||||
virt_base, virt_base + size,
|
||||
entry->addr, entry->addr + entry->u.mem.size);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@@ -337,12 +333,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
|
||||
} else {
|
||||
if (entry->addr >= virt_base &&
|
||||
entry->addr < virt_base + size) {
|
||||
fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
|
||||
" collides with FIFO at " TARGET_FMT_lx
|
||||
"\n", __FUNCTION__,
|
||||
(target_ulong) virt_base,
|
||||
(target_ulong) (virt_base + size),
|
||||
(target_ulong) entry->addr);
|
||||
fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
|
||||
" collides with FIFO at %"PRIx64
|
||||
"\n", __func__,
|
||||
virt_base, virt_base + size,
|
||||
entry->addr);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,12 @@ static const char *imx_gpio_reg_name(uint32_t reg)
|
||||
|
||||
static void imx_gpio_update_int(IMXGPIOState *s)
|
||||
{
|
||||
qemu_set_irq(s->irq, (s->isr & s->imr) ? 1 : 0);
|
||||
if (s->has_upper_pin_irq) {
|
||||
qemu_set_irq(s->irq[0], (s->isr & s->imr & 0x0000FFFF) ? 1 : 0);
|
||||
qemu_set_irq(s->irq[1], (s->isr & s->imr & 0xFFFF0000) ? 1 : 0);
|
||||
} else {
|
||||
qemu_set_irq(s->irq[0], (s->isr & s->imr) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level)
|
||||
@@ -282,6 +287,8 @@ static const VMStateDescription vmstate_imx_gpio = {
|
||||
|
||||
static Property imx_gpio_properties[] = {
|
||||
DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true),
|
||||
DEFINE_PROP_BOOL("has-upper-pin-irq", IMXGPIOState, has_upper_pin_irq,
|
||||
false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
@@ -311,7 +318,8 @@ static void imx_gpio_realize(DeviceState *dev, Error **errp)
|
||||
qdev_init_gpio_in(DEVICE(s), imx_gpio_set, IMX_GPIO_PIN_COUNT);
|
||||
qdev_init_gpio_out(DEVICE(s), s->output, IMX_GPIO_PIN_COUNT);
|
||||
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[0]);
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[1]);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "hw/loader.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/acpi/memory_hotplug.h"
|
||||
#include "hw/mem/nvdimm.h"
|
||||
#include "sysemu/tpm.h"
|
||||
#include "hw/acpi/tpm.h"
|
||||
#include "sysemu/tpm_backend.h"
|
||||
@@ -361,7 +362,7 @@ build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm,
|
||||
fadt_setup(fadt, pm);
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 1);
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -431,7 +432,7 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + madt_start), "APIC",
|
||||
table_data->len - madt_start, 1);
|
||||
table_data->len - madt_start, 1, NULL);
|
||||
}
|
||||
|
||||
/* Assign BSEL property to all buses. In the future, this can be changed
|
||||
@@ -469,7 +470,7 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot)
|
||||
Aml *if_ctx;
|
||||
int32_t devfn = PCI_DEVFN(slot, 0);
|
||||
|
||||
if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot)));
|
||||
if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL));
|
||||
aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1)));
|
||||
aml_append(method, if_ctx);
|
||||
}
|
||||
@@ -487,7 +488,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
|
||||
int64_t bsel_val = qint_get_int(qobject_to_qint(bsel));
|
||||
|
||||
aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
|
||||
notify_method = aml_method("DVNT", 2);
|
||||
notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) {
|
||||
@@ -503,7 +504,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
|
||||
dev = aml_device("S%.02X", PCI_DEVFN(slot, 0));
|
||||
aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
|
||||
aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16)));
|
||||
method = aml_method("_EJ0", 1);
|
||||
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
|
||||
);
|
||||
@@ -546,22 +547,22 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
|
||||
s3d = 0;
|
||||
}
|
||||
|
||||
method = aml_method("_S1D", 0);
|
||||
method = aml_method("_S1D", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_int(0)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_S2D", 0);
|
||||
method = aml_method("_S2D", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_int(0)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_S3D", 0);
|
||||
method = aml_method("_S3D", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_int(s3d)));
|
||||
aml_append(dev, method);
|
||||
} else if (hotplug_enabled_dev) {
|
||||
/* add _SUN/_EJ0 to make slot hotpluggable */
|
||||
aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
|
||||
|
||||
method = aml_method("_EJ0", 1);
|
||||
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
|
||||
);
|
||||
@@ -590,7 +591,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
|
||||
/* Append PCNT method to notify about events on local and child buses.
|
||||
* Add unconditionally for root since DSDT expects it.
|
||||
*/
|
||||
method = aml_method("PCNT", 0);
|
||||
method = aml_method("PCNT", 0, AML_NOTSERIALIZED);
|
||||
|
||||
/* If bus supports hotplug select it and notify about local events */
|
||||
if (bsel) {
|
||||
@@ -651,7 +652,7 @@ static Aml *build_prt(void)
|
||||
{
|
||||
Aml *method, *while_ctx, *pin, *res;
|
||||
|
||||
method = aml_method("_PRT", 0);
|
||||
method = aml_method("_PRT", 0, AML_NOTSERIALIZED);
|
||||
res = aml_local(0);
|
||||
pin = aml_local(1);
|
||||
aml_append(method, aml_store(aml_package(128), res));
|
||||
@@ -666,10 +667,11 @@ static Aml *build_prt(void)
|
||||
|
||||
/* slot = pin >> 2 */
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_shiftright(pin, aml_int(2)), slot));
|
||||
aml_store(aml_shiftright(pin, aml_int(2), NULL), slot));
|
||||
/* lnk_idx = (slot + pin) & 3 */
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_and(aml_add(pin, slot), aml_int(3)), lnk_idx));
|
||||
aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL),
|
||||
lnk_idx));
|
||||
|
||||
/* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */
|
||||
aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0));
|
||||
@@ -679,11 +681,13 @@ static Aml *build_prt(void)
|
||||
|
||||
/* route[0] = 0x[slot]FFFF */
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF)),
|
||||
aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF),
|
||||
NULL),
|
||||
aml_index(route, aml_int(0))));
|
||||
/* route[1] = pin & 3 */
|
||||
aml_append(while_ctx,
|
||||
aml_store(aml_and(pin, aml_int(3)), aml_index(route, aml_int(1))));
|
||||
aml_store(aml_and(pin, aml_int(3), NULL),
|
||||
aml_index(route, aml_int(1))));
|
||||
/* res[pin] = route */
|
||||
aml_append(while_ctx, aml_store(route, aml_index(res, pin)));
|
||||
/* pin++ */
|
||||
@@ -762,16 +766,59 @@ static void crs_replace_with_free_ranges(GPtrArray *ranges,
|
||||
g_ptr_array_free(free_ranges, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* crs_range_merge - merges adjacent ranges in the given array.
|
||||
* Array elements are deleted and replaced with the merged ranges.
|
||||
*/
|
||||
static void crs_range_merge(GPtrArray *range)
|
||||
{
|
||||
GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free);
|
||||
CrsRangeEntry *entry;
|
||||
uint64_t range_base, range_limit;
|
||||
int i;
|
||||
|
||||
if (!range->len) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_ptr_array_sort(range, crs_range_compare);
|
||||
|
||||
entry = g_ptr_array_index(range, 0);
|
||||
range_base = entry->base;
|
||||
range_limit = entry->limit;
|
||||
for (i = 1; i < range->len; i++) {
|
||||
entry = g_ptr_array_index(range, i);
|
||||
if (entry->base - 1 == range_limit) {
|
||||
range_limit = entry->limit;
|
||||
} else {
|
||||
crs_range_insert(tmp, range_base, range_limit);
|
||||
range_base = entry->base;
|
||||
range_limit = entry->limit;
|
||||
}
|
||||
}
|
||||
crs_range_insert(tmp, range_base, range_limit);
|
||||
|
||||
g_ptr_array_set_size(range, 0);
|
||||
for (i = 0; i < tmp->len; i++) {
|
||||
entry = g_ptr_array_index(tmp, i);
|
||||
crs_range_insert(range, entry->base, entry->limit);
|
||||
}
|
||||
g_ptr_array_free(tmp, true);
|
||||
}
|
||||
|
||||
static Aml *build_crs(PCIHostState *host,
|
||||
GPtrArray *io_ranges, GPtrArray *mem_ranges)
|
||||
{
|
||||
Aml *crs = aml_resource_template();
|
||||
GPtrArray *host_io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
||||
GPtrArray *host_mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
|
||||
CrsRangeEntry *entry;
|
||||
uint8_t max_bus = pci_bus_num(host->bus);
|
||||
uint8_t type;
|
||||
int devfn;
|
||||
int i;
|
||||
|
||||
for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) {
|
||||
int i;
|
||||
uint64_t range_base, range_limit;
|
||||
PCIDevice *dev = host->bus->devices[devfn];
|
||||
|
||||
@@ -794,26 +841,9 @@ static Aml *build_crs(PCIHostState *host,
|
||||
}
|
||||
|
||||
if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
aml_append(crs,
|
||||
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
|
||||
AML_POS_DECODE, AML_ENTIRE_RANGE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(io_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_io_ranges, range_base, range_limit);
|
||||
} else { /* "memory" */
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(mem_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_mem_ranges, range_base, range_limit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,15 +862,7 @@ static Aml *build_crs(PCIHostState *host,
|
||||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
|
||||
AML_POS_DECODE, AML_ENTIRE_RANGE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(io_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_io_ranges, range_base, range_limit);
|
||||
}
|
||||
|
||||
range_base =
|
||||
@@ -853,16 +875,7 @@ static Aml *build_crs(PCIHostState *host,
|
||||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(mem_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_mem_ranges, range_base, range_limit);
|
||||
}
|
||||
|
||||
range_base =
|
||||
@@ -875,20 +888,36 @@ static Aml *build_crs(PCIHostState *host,
|
||||
* that do not support multiple root buses
|
||||
*/
|
||||
if (range_base && range_base <= range_limit) {
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0,
|
||||
range_base,
|
||||
range_limit,
|
||||
0,
|
||||
range_limit - range_base + 1));
|
||||
crs_range_insert(mem_ranges, range_base, range_limit);
|
||||
crs_range_insert(host_mem_ranges, range_base, range_limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crs_range_merge(host_io_ranges);
|
||||
for (i = 0; i < host_io_ranges->len; i++) {
|
||||
entry = g_ptr_array_index(host_io_ranges, i);
|
||||
aml_append(crs,
|
||||
aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
|
||||
AML_POS_DECODE, AML_ENTIRE_RANGE,
|
||||
0, entry->base, entry->limit, 0,
|
||||
entry->limit - entry->base + 1));
|
||||
crs_range_insert(io_ranges, entry->base, entry->limit);
|
||||
}
|
||||
g_ptr_array_free(host_io_ranges, true);
|
||||
|
||||
crs_range_merge(host_mem_ranges);
|
||||
for (i = 0; i < host_mem_ranges->len; i++) {
|
||||
entry = g_ptr_array_index(host_mem_ranges, i);
|
||||
aml_append(crs,
|
||||
aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE,
|
||||
0, entry->base, entry->limit, 0,
|
||||
entry->limit - entry->base + 1));
|
||||
crs_range_insert(mem_ranges, entry->base, entry->limit);
|
||||
}
|
||||
g_ptr_array_free(host_mem_ranges, true);
|
||||
|
||||
aml_append(crs,
|
||||
aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
|
||||
0,
|
||||
@@ -925,8 +954,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
/* Reserve space for header */
|
||||
acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
|
||||
|
||||
/* Extra PCI root buses are implemented only for i440fx */
|
||||
bus = find_i440fx();
|
||||
bus = PC_MACHINE(machine)->bus;
|
||||
if (bus) {
|
||||
QLIST_FOREACH(bus, &bus->child, sibling) {
|
||||
uint8_t bus_num = pci_bus_num(bus);
|
||||
@@ -1105,19 +1133,19 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
|
||||
aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
|
||||
misc->pvpanic_port, 1));
|
||||
field = aml_field("PEOR", AML_BYTE_ACC, AML_PRESERVE);
|
||||
field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PEPT", 8));
|
||||
aml_append(dev, field);
|
||||
|
||||
/* device present, functioning, decoding, shown in UI */
|
||||
aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
|
||||
|
||||
method = aml_method("RDPT", 0);
|
||||
method = aml_method("RDPT", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_store(aml_name("PEPT"), aml_local(0)));
|
||||
aml_append(method, aml_return(aml_local(0)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("WRPT", 1);
|
||||
method = aml_method("WRPT", 1, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_store(aml_arg(0), aml_name("PEPT")));
|
||||
aml_append(dev, method);
|
||||
|
||||
@@ -1145,7 +1173,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
/* declare CPU hotplug MMIO region and PRS field to access it */
|
||||
aml_append(sb_scope, aml_operation_region(
|
||||
"PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
|
||||
field = aml_field("PRST", AML_BYTE_ACC, AML_PRESERVE);
|
||||
field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, aml_named_field("PRS", 256));
|
||||
aml_append(sb_scope, field);
|
||||
|
||||
@@ -1153,15 +1181,15 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
for (i = 0; i < acpi_cpus; i++) {
|
||||
dev = aml_processor(i, 0, 0, "CP%.02X", i);
|
||||
|
||||
method = aml_method("_MAT", 0);
|
||||
method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_call1("CPMA", aml_int(i))));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_STA", 0);
|
||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_call1("CPST", aml_int(i))));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_EJ0", 1);
|
||||
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
|
||||
aml_append(method,
|
||||
aml_return(aml_call2("CPEJ", aml_int(i), aml_arg(0)))
|
||||
);
|
||||
@@ -1174,7 +1202,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
* Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
|
||||
*/
|
||||
/* Arg0 = Processor ID = APIC ID */
|
||||
method = aml_method("NTFY", 2);
|
||||
method = aml_method("NTFY", 2, AML_NOTSERIALIZED);
|
||||
for (i = 0; i < acpi_cpus; i++) {
|
||||
ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
|
||||
aml_append(ifctx,
|
||||
@@ -1220,7 +1248,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
);
|
||||
|
||||
field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
|
||||
AML_PRESERVE);
|
||||
AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, /* read only */
|
||||
aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32));
|
||||
aml_append(field, /* read only */
|
||||
@@ -1234,7 +1262,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
aml_append(scope, field);
|
||||
|
||||
field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_BYTE_ACC,
|
||||
AML_WRITE_AS_ZEROS);
|
||||
AML_NOLOCK, AML_WRITE_AS_ZEROS);
|
||||
aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */));
|
||||
aml_append(field, /* 1 if enabled, read only */
|
||||
aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1));
|
||||
@@ -1250,7 +1278,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
aml_append(scope, field);
|
||||
|
||||
field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
|
||||
AML_PRESERVE);
|
||||
AML_NOLOCK, AML_PRESERVE);
|
||||
aml_append(field, /* DIMM selector, write only */
|
||||
aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32));
|
||||
aml_append(field, /* _OST event code, write only */
|
||||
@@ -1269,29 +1297,29 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i)));
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80")));
|
||||
|
||||
method = aml_method("_CRS", 0);
|
||||
method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
|
||||
s = BASEPATH stringify(MEMORY_SLOT_CRS_METHOD);
|
||||
aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_STA", 0);
|
||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||
s = BASEPATH stringify(MEMORY_SLOT_STATUS_METHOD);
|
||||
aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_PXM", 0);
|
||||
method = aml_method("_PXM", 0, AML_NOTSERIALIZED);
|
||||
s = BASEPATH stringify(MEMORY_SLOT_PROXIMITY_METHOD);
|
||||
aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_OST", 3);
|
||||
method = aml_method("_OST", 3, AML_NOTSERIALIZED);
|
||||
s = BASEPATH stringify(MEMORY_SLOT_OST_METHOD);
|
||||
aml_append(method, aml_return(aml_call4(
|
||||
s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2)
|
||||
)));
|
||||
aml_append(dev, method);
|
||||
|
||||
method = aml_method("_EJ0", 1);
|
||||
method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
|
||||
s = BASEPATH stringify(MEMORY_SLOT_EJECT_METHOD);
|
||||
aml_append(method, aml_return(aml_call2(
|
||||
s, aml_name("_UID"), aml_arg(0))));
|
||||
@@ -1303,7 +1331,8 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
/* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) {
|
||||
* If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... }
|
||||
*/
|
||||
method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2);
|
||||
method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2,
|
||||
AML_NOTSERIALIZED);
|
||||
for (i = 0; i < nr_mem; i++) {
|
||||
ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
|
||||
aml_append(ifctx,
|
||||
@@ -1349,7 +1378,7 @@ build_ssdt(GArray *table_data, GArray *linker,
|
||||
g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + table_data->len - ssdt->buf->len),
|
||||
"SSDT", ssdt->buf->len, 1);
|
||||
"SSDT", ssdt->buf->len, 1, NULL);
|
||||
free_aml_allocator();
|
||||
}
|
||||
|
||||
@@ -1365,7 +1394,7 @@ build_hpet(GArray *table_data, GArray *linker)
|
||||
hpet->timer_block_id = cpu_to_le32(0x8086a201);
|
||||
hpet->addr.address = cpu_to_le64(HPET_BASE);
|
||||
build_header(linker, table_data,
|
||||
(void *)hpet, "HPET", sizeof(*hpet), 1);
|
||||
(void *)hpet, "HPET", sizeof(*hpet), 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1388,7 +1417,7 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog)
|
||||
sizeof(tcpa->log_area_start_address));
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)tcpa, "TCPA", sizeof(*tcpa), 2);
|
||||
(void *)tcpa, "TCPA", sizeof(*tcpa), 2, NULL);
|
||||
|
||||
acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE);
|
||||
}
|
||||
@@ -1405,7 +1434,7 @@ build_tpm2(GArray *table_data, GArray *linker)
|
||||
tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4);
|
||||
(void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
@@ -1519,7 +1548,7 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
|
||||
build_header(linker, table_data,
|
||||
(void *)(table_data->data + srat_start),
|
||||
"SRAT",
|
||||
table_data->len - srat_start, 1);
|
||||
table_data->len - srat_start, 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1548,7 +1577,7 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info)
|
||||
} else {
|
||||
sig = "MCFG";
|
||||
}
|
||||
build_header(linker, table_data, (void *)mcfg, sig, len, 1);
|
||||
build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1572,7 +1601,7 @@ build_dmar_q35(GArray *table_data, GArray *linker)
|
||||
drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR);
|
||||
|
||||
build_header(linker, table_data, (void *)(table_data->data + dmar_start),
|
||||
"DMAR", table_data->len - dmar_start, 1);
|
||||
"DMAR", table_data->len - dmar_start, 1, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1587,7 +1616,7 @@ build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc)
|
||||
|
||||
memset(dsdt, 0, sizeof *dsdt);
|
||||
build_header(linker, table_data, dsdt, "DSDT",
|
||||
misc->dsdt_size, 1);
|
||||
misc->dsdt_size, 1, NULL);
|
||||
}
|
||||
|
||||
static GArray *
|
||||
@@ -1658,6 +1687,13 @@ static bool acpi_has_iommu(void)
|
||||
return intel_iommu && !ambiguous;
|
||||
}
|
||||
|
||||
static bool acpi_has_nvdimm(void)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
||||
|
||||
return pcms->nvdimm;
|
||||
}
|
||||
|
||||
static
|
||||
void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
|
||||
{
|
||||
@@ -1742,6 +1778,10 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
|
||||
build_dmar_q35(tables_blob, tables->linker);
|
||||
}
|
||||
|
||||
if (acpi_has_nvdimm()) {
|
||||
nvdimm_build_acpi(table_offsets, tables_blob, tables->linker);
|
||||
}
|
||||
|
||||
/* Add tables supplied by user (if any) */
|
||||
for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
|
||||
unsigned len = acpi_table_len(u);
|
||||
@@ -1818,7 +1858,7 @@ static void acpi_ram_update(MemoryRegion *mr, GArray *data)
|
||||
memory_region_set_dirty(mr, 0, size);
|
||||
}
|
||||
|
||||
static void acpi_build_update(void *build_opaque, uint32_t offset)
|
||||
static void acpi_build_update(void *build_opaque)
|
||||
{
|
||||
AcpiBuildState *build_state = build_opaque;
|
||||
AcpiBuildTables tables;
|
||||
|
||||
58
hw/i386/pc.c
58
hw/i386/pc.c
@@ -65,6 +65,7 @@
|
||||
#include "hw/mem/pc-dimm.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi-visit.h"
|
||||
#include "qom/cpu.h"
|
||||
|
||||
/* debug PC/ISA interrupts */
|
||||
//#define DEBUG_IRQ
|
||||
@@ -76,15 +77,6 @@
|
||||
#define DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables
|
||||
* (128K) and other BIOS datastructures (less than 4K reported to be used at
|
||||
* the moment, 32K should be enough for a while). */
|
||||
static unsigned acpi_data_size = 0x20000 + 0x8000;
|
||||
void pc_set_legacy_acpi_data_size(void)
|
||||
{
|
||||
acpi_data_size = 0x10000;
|
||||
}
|
||||
|
||||
#define BIOS_CFG_IOPORT 0x510
|
||||
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
|
||||
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
|
||||
@@ -840,6 +832,7 @@ static void load_linux(PCMachineState *pcms,
|
||||
FILE *f;
|
||||
char *vmode;
|
||||
MachineState *machine = MACHINE(pcms);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *initrd_filename = machine->initrd_filename;
|
||||
const char *kernel_cmdline = machine->kernel_cmdline;
|
||||
@@ -907,8 +900,8 @@ static void load_linux(PCMachineState *pcms,
|
||||
initrd_max = 0x37ffffff;
|
||||
}
|
||||
|
||||
if (initrd_max >= pcms->below_4g_mem_size - acpi_data_size) {
|
||||
initrd_max = pcms->below_4g_mem_size - acpi_data_size - 1;
|
||||
if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) {
|
||||
initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1;
|
||||
}
|
||||
|
||||
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
|
||||
@@ -1174,7 +1167,7 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data)
|
||||
PcGuestInfoState *guest_info_state = container_of(notifier,
|
||||
PcGuestInfoState,
|
||||
machine_done);
|
||||
PCIBus *bus = find_i440fx();
|
||||
PCIBus *bus = PC_MACHINE(qdev_get_machine())->bus;
|
||||
|
||||
if (bus) {
|
||||
int extra_hosts = 0;
|
||||
@@ -1302,6 +1295,7 @@ FWCfgState *pc_memory_init(PCMachineState *pcms,
|
||||
MemoryRegion *ram_below_4g, *ram_above_4g;
|
||||
FWCfgState *fw_cfg;
|
||||
MachineState *machine = MACHINE(pcms);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
|
||||
assert(machine->ram_size == pcms->below_4g_mem_size +
|
||||
pcms->above_4g_mem_size);
|
||||
@@ -1363,7 +1357,7 @@ FWCfgState *pc_memory_init(PCMachineState *pcms,
|
||||
pcms->hotplug_memory.base =
|
||||
ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30);
|
||||
|
||||
if (pcms->enforce_aligned_dimm) {
|
||||
if (pcmc->enforce_aligned_dimm) {
|
||||
/* size hotplug region assuming 1G page max alignment per slot */
|
||||
hotplug_mem_size += (1ULL << 30) * machine->ram_slots;
|
||||
}
|
||||
@@ -1517,7 +1511,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
|
||||
qemu_register_boot_set(pc_boot_set, *rtc_state);
|
||||
|
||||
if (!xen_enabled()) {
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
if (kvm_pit_in_kernel()) {
|
||||
pit = kvm_pit_init(isa_bus, 0x40);
|
||||
} else {
|
||||
pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
|
||||
@@ -1592,7 +1586,7 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
|
||||
SysBusDevice *d;
|
||||
unsigned int i;
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
if (kvm_ioapic_in_kernel()) {
|
||||
dev = qdev_create(NULL, "kvm-ioapic");
|
||||
} else {
|
||||
dev = qdev_create(NULL, "ioapic");
|
||||
@@ -1616,12 +1610,13 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
|
||||
HotplugHandlerClass *hhc;
|
||||
Error *local_err = NULL;
|
||||
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
PCDIMMDevice *dimm = PC_DIMM(dev);
|
||||
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
||||
MemoryRegion *mr = ddc->get_memory_region(dimm);
|
||||
uint64_t align = TARGET_PAGE_SIZE;
|
||||
|
||||
if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) {
|
||||
if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
|
||||
align = memory_region_get_alignment(mr);
|
||||
}
|
||||
|
||||
@@ -1870,11 +1865,18 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
|
||||
visit_type_OnOffAuto(v, &pcms->smm, name, errp);
|
||||
}
|
||||
|
||||
static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp)
|
||||
static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(obj);
|
||||
|
||||
return pcms->enforce_aligned_dimm;
|
||||
return pcms->nvdimm;
|
||||
}
|
||||
|
||||
static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(obj);
|
||||
|
||||
pcms->nvdimm = value;
|
||||
}
|
||||
|
||||
static void pc_machine_initfn(Object *obj)
|
||||
@@ -1912,10 +1914,10 @@ static void pc_machine_initfn(Object *obj)
|
||||
"Enable vmport (pc & q35)",
|
||||
&error_abort);
|
||||
|
||||
pcms->enforce_aligned_dimm = true;
|
||||
object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM,
|
||||
pc_machine_get_aligned_dimm,
|
||||
NULL, &error_abort);
|
||||
/* nvdimm is disabled on default. */
|
||||
pcms->nvdimm = false;
|
||||
object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
|
||||
pc_machine_set_nvdimm, &error_abort);
|
||||
}
|
||||
|
||||
static void pc_machine_reset(void)
|
||||
@@ -1952,6 +1954,18 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
|
||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
|
||||
|
||||
pcmc->get_hotplug_handler = mc->get_hotplug_handler;
|
||||
pcmc->pci_enabled = true;
|
||||
pcmc->has_acpi_build = true;
|
||||
pcmc->rsdp_in_ram = true;
|
||||
pcmc->smbios_defaults = true;
|
||||
pcmc->smbios_uuid_encoded = true;
|
||||
pcmc->gigabyte_align = true;
|
||||
pcmc->has_reserved_memory = true;
|
||||
pcmc->kvmclock_enabled = true;
|
||||
pcmc->enforce_aligned_dimm = true;
|
||||
/* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported
|
||||
* to be used at the moment, 32K should be enough for a while. */
|
||||
pcmc->acpi_data_size = 0x20000 + 0x8000;
|
||||
mc->get_hotplug_handler = pc_get_hotpug_handler;
|
||||
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
|
||||
mc->default_boot_order = "cad";
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "hw/xen/xen_pt.h"
|
||||
#endif
|
||||
#include "migration/migration.h"
|
||||
#include "kvm_i386.h"
|
||||
|
||||
#define MAX_IDE_BUS 2
|
||||
|
||||
@@ -60,26 +61,12 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
|
||||
static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
|
||||
static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
|
||||
|
||||
static bool pci_enabled = true;
|
||||
static bool has_acpi_build = true;
|
||||
static bool rsdp_in_ram = true;
|
||||
static int legacy_acpi_table_size;
|
||||
static bool smbios_defaults = true;
|
||||
static bool smbios_legacy_mode;
|
||||
static bool smbios_uuid_encoded = true;
|
||||
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
|
||||
* host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
|
||||
* pages in the host.
|
||||
*/
|
||||
static bool gigabyte_align = true;
|
||||
static bool has_reserved_memory = true;
|
||||
static bool kvmclock_enabled = true;
|
||||
|
||||
/* PC hardware initialisation */
|
||||
static void pc_init1(MachineState *machine,
|
||||
const char *host_type, const char *pci_type)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
MemoryRegion *system_memory = get_system_memory();
|
||||
MemoryRegion *system_io = get_system_io();
|
||||
int i;
|
||||
@@ -108,7 +95,7 @@ static void pc_init1(MachineState *machine,
|
||||
* breaking migration.
|
||||
*/
|
||||
if (machine->ram_size >= 0xe0000000) {
|
||||
lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000;
|
||||
lowmem = pcmc->gigabyte_align ? 0xc0000000 : 0xe0000000;
|
||||
} else {
|
||||
lowmem = 0xe0000000;
|
||||
}
|
||||
@@ -141,11 +128,11 @@ static void pc_init1(MachineState *machine,
|
||||
|
||||
pc_cpus_init(pcms);
|
||||
|
||||
if (kvm_enabled() && kvmclock_enabled) {
|
||||
if (kvm_enabled() && pcmc->kvmclock_enabled) {
|
||||
kvmclock_create();
|
||||
}
|
||||
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pci_memory = g_new(MemoryRegion, 1);
|
||||
memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
|
||||
rom_memory = pci_memory;
|
||||
@@ -156,18 +143,19 @@ static void pc_init1(MachineState *machine,
|
||||
|
||||
guest_info = pc_guest_info_init(pcms);
|
||||
|
||||
guest_info->has_acpi_build = has_acpi_build;
|
||||
guest_info->legacy_acpi_table_size = legacy_acpi_table_size;
|
||||
guest_info->has_acpi_build = pcmc->has_acpi_build;
|
||||
guest_info->legacy_acpi_table_size = pcmc->legacy_acpi_table_size;
|
||||
|
||||
guest_info->isapc_ram_fw = !pci_enabled;
|
||||
guest_info->has_reserved_memory = has_reserved_memory;
|
||||
guest_info->rsdp_in_ram = rsdp_in_ram;
|
||||
guest_info->isapc_ram_fw = !pcmc->pci_enabled;
|
||||
guest_info->has_reserved_memory = pcmc->has_reserved_memory;
|
||||
guest_info->rsdp_in_ram = pcmc->rsdp_in_ram;
|
||||
|
||||
if (smbios_defaults) {
|
||||
if (pcmc->smbios_defaults) {
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
/* These values are guest ABI, do not change */
|
||||
smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)",
|
||||
mc->name, smbios_legacy_mode, smbios_uuid_encoded,
|
||||
mc->name, pcmc->smbios_legacy_mode,
|
||||
pcmc->smbios_uuid_encoded,
|
||||
SMBIOS_ENTRY_POINT_21);
|
||||
}
|
||||
|
||||
@@ -181,15 +169,15 @@ static void pc_init1(MachineState *machine,
|
||||
}
|
||||
|
||||
gsi_state = g_malloc0(sizeof(*gsi_state));
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
kvm_pc_setup_irq_routing(pci_enabled);
|
||||
if (kvm_ioapic_in_kernel()) {
|
||||
kvm_pc_setup_irq_routing(pcmc->pci_enabled);
|
||||
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
|
||||
GSI_NUM_PINS);
|
||||
} else {
|
||||
gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
|
||||
}
|
||||
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pci_bus = i440fx_init(host_type,
|
||||
pci_type,
|
||||
&i440fx_state, &piix3_devfn, &isa_bus, gsi,
|
||||
@@ -197,6 +185,7 @@ static void pc_init1(MachineState *machine,
|
||||
pcms->below_4g_mem_size,
|
||||
pcms->above_4g_mem_size,
|
||||
pci_memory, ram_memory);
|
||||
pcms->bus = pci_bus;
|
||||
} else {
|
||||
pci_bus = NULL;
|
||||
i440fx_state = NULL;
|
||||
@@ -205,7 +194,7 @@ static void pc_init1(MachineState *machine,
|
||||
}
|
||||
isa_bus_irqs(isa_bus, gsi);
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
if (kvm_pic_in_kernel()) {
|
||||
i8259 = kvm_i8259_init(isa_bus);
|
||||
} else if (xen_enabled()) {
|
||||
i8259 = xen_interrupt_controller_init();
|
||||
@@ -217,15 +206,15 @@ static void pc_init1(MachineState *machine,
|
||||
gsi_state->i8259_irq[i] = i8259[i];
|
||||
}
|
||||
g_free(i8259);
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
ioapic_init_gsi(gsi_state, "i440fx");
|
||||
}
|
||||
|
||||
pc_register_ferr_irq(gsi[13]);
|
||||
|
||||
pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL);
|
||||
pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL);
|
||||
|
||||
assert(pcms->vmport != ON_OFF_AUTO_MAX);
|
||||
assert(pcms->vmport != ON_OFF_AUTO__MAX);
|
||||
if (pcms->vmport == ON_OFF_AUTO_AUTO) {
|
||||
pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
|
||||
}
|
||||
@@ -237,7 +226,7 @@ static void pc_init1(MachineState *machine,
|
||||
pc_nic_init(isa_bus, pci_bus);
|
||||
|
||||
ide_drive_get(hd, ARRAY_SIZE(hd));
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
PCIDevice *dev;
|
||||
if (xen_enabled()) {
|
||||
dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1);
|
||||
@@ -264,11 +253,11 @@ static void pc_init1(MachineState *machine,
|
||||
|
||||
pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
|
||||
|
||||
if (pci_enabled && usb_enabled()) {
|
||||
if (pcmc->pci_enabled && usb_enabled()) {
|
||||
pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
|
||||
}
|
||||
|
||||
if (pci_enabled && acpi_enabled) {
|
||||
if (pcmc->pci_enabled && acpi_enabled) {
|
||||
DeviceState *piix4_pm;
|
||||
I2CBus *smbus;
|
||||
|
||||
@@ -289,7 +278,7 @@ static void pc_init1(MachineState *machine,
|
||||
PC_MACHINE_ACPI_DEVICE_PROP, &error_abort);
|
||||
}
|
||||
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pc_pci_device_init(pci_bus);
|
||||
}
|
||||
}
|
||||
@@ -315,60 +304,29 @@ static void pc_compat_2_3(MachineState *machine)
|
||||
static void pc_compat_2_2(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_3(machine);
|
||||
rsdp_in_ram = false;
|
||||
machine->suppress_vmdesc = true;
|
||||
}
|
||||
|
||||
static void pc_compat_2_1(MachineState *machine)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
|
||||
pc_compat_2_2(machine);
|
||||
smbios_uuid_encoded = false;
|
||||
x86_cpu_change_kvm_default("svm", NULL);
|
||||
pcms->enforce_aligned_dimm = false;
|
||||
}
|
||||
|
||||
static void pc_compat_2_0(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_1(machine);
|
||||
/* This value depends on the actual DSDT and SSDT compiled into
|
||||
* the source QEMU; unfortunately it depends on the binary and
|
||||
* not on the machine type, so we cannot make pc-i440fx-1.7 work on
|
||||
* both QEMU 1.7 and QEMU 2.0.
|
||||
*
|
||||
* Large variations cause migration to fail for more than one
|
||||
* consecutive value of the "-smp" maxcpus option.
|
||||
*
|
||||
* For small variations of the kind caused by different iasl versions,
|
||||
* the 4k rounding usually leaves slack. However, there could be still
|
||||
* one or two values that break. For QEMU 1.7 and QEMU 2.0 the
|
||||
* slack is only ~10 bytes before one "-smp maxcpus" value breaks!
|
||||
*
|
||||
* 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on
|
||||
* QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418.
|
||||
*/
|
||||
legacy_acpi_table_size = 6652;
|
||||
smbios_legacy_mode = true;
|
||||
has_reserved_memory = false;
|
||||
pc_set_legacy_acpi_data_size();
|
||||
}
|
||||
|
||||
static void pc_compat_1_7(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_0(machine);
|
||||
smbios_defaults = false;
|
||||
gigabyte_align = false;
|
||||
option_rom_has_mr = true;
|
||||
legacy_acpi_table_size = 6414;
|
||||
x86_cpu_change_kvm_default("x2apic", NULL);
|
||||
}
|
||||
|
||||
static void pc_compat_1_6(MachineState *machine)
|
||||
{
|
||||
pc_compat_1_7(machine);
|
||||
rom_file_has_mr = false;
|
||||
has_acpi_build = false;
|
||||
}
|
||||
|
||||
static void pc_compat_1_5(MachineState *machine)
|
||||
@@ -398,19 +356,10 @@ static void pc_compat_1_2(MachineState *machine)
|
||||
static void pc_compat_0_13(MachineState *machine)
|
||||
{
|
||||
pc_compat_1_2(machine);
|
||||
kvmclock_enabled = false;
|
||||
}
|
||||
|
||||
static void pc_init_isa(MachineState *machine)
|
||||
{
|
||||
pci_enabled = false;
|
||||
has_acpi_build = false;
|
||||
smbios_defaults = false;
|
||||
gigabyte_align = false;
|
||||
smbios_legacy_mode = true;
|
||||
has_reserved_memory = false;
|
||||
option_rom_has_mr = true;
|
||||
rom_file_has_mr = false;
|
||||
if (!machine->cpu_model) {
|
||||
machine->cpu_model = "486";
|
||||
}
|
||||
@@ -469,13 +418,25 @@ static void pc_i440fx_machine_options(MachineClass *m)
|
||||
m->default_display = "std";
|
||||
}
|
||||
|
||||
static void pc_i440fx_2_5_machine_options(MachineClass *m)
|
||||
static void pc_i440fx_2_6_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_i440fx_machine_options(m);
|
||||
m->alias = "pc";
|
||||
m->is_default = 1;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL,
|
||||
pc_i440fx_2_6_machine_options);
|
||||
|
||||
|
||||
static void pc_i440fx_2_5_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_i440fx_2_6_machine_options(m);
|
||||
m->alias = NULL;
|
||||
m->is_default = 0;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL,
|
||||
pc_i440fx_2_5_machine_options);
|
||||
|
||||
@@ -485,8 +446,6 @@ static void pc_i440fx_2_4_machine_options(MachineClass *m)
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_2_5_machine_options(m);
|
||||
m->hw_version = "2.4.0";
|
||||
m->alias = NULL;
|
||||
m->is_default = 0;
|
||||
pcmc->broken_reserved_end = true;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
|
||||
}
|
||||
@@ -499,8 +458,6 @@ static void pc_i440fx_2_3_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_i440fx_2_4_machine_options(m);
|
||||
m->hw_version = "2.3.0";
|
||||
m->alias = NULL;
|
||||
m->is_default = 0;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
|
||||
}
|
||||
|
||||
@@ -510,9 +467,11 @@ DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3,
|
||||
|
||||
static void pc_i440fx_2_2_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_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_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
|
||||
@@ -521,10 +480,13 @@ DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
|
||||
|
||||
static void pc_i440fx_2_1_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_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_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
|
||||
@@ -534,9 +496,30 @@ DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
|
||||
|
||||
static void pc_i440fx_2_0_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_2_1_machine_options(m);
|
||||
m->hw_version = "2.0.0";
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
|
||||
pcmc->smbios_legacy_mode = true;
|
||||
pcmc->has_reserved_memory = false;
|
||||
/* This value depends on the actual DSDT and SSDT compiled into
|
||||
* the source QEMU; unfortunately it depends on the binary and
|
||||
* not on the machine type, so we cannot make pc-i440fx-1.7 work on
|
||||
* both QEMU 1.7 and QEMU 2.0.
|
||||
*
|
||||
* Large variations cause migration to fail for more than one
|
||||
* consecutive value of the "-smp" maxcpus option.
|
||||
*
|
||||
* For small variations of the kind caused by different iasl versions,
|
||||
* the 4k rounding usually leaves slack. However, there could be still
|
||||
* one or two values that break. For QEMU 1.7 and QEMU 2.0 the
|
||||
* slack is only ~10 bytes before one "-smp maxcpus" value breaks!
|
||||
*
|
||||
* 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on
|
||||
* QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418.
|
||||
*/
|
||||
pcmc->legacy_acpi_table_size = 6652;
|
||||
pcmc->acpi_data_size = 0x10000;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
|
||||
@@ -545,10 +528,15 @@ DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
|
||||
|
||||
static void pc_i440fx_1_7_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_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;
|
||||
pcmc->legacy_acpi_table_size = 6414;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
|
||||
@@ -557,9 +545,12 @@ DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
|
||||
|
||||
static void pc_i440fx_1_6_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_1_7_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_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6,
|
||||
@@ -813,9 +804,11 @@ DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2,
|
||||
|
||||
static void pc_i440fx_0_13_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_i440fx_0_14_machine_options(m);
|
||||
m->hw_version = "0.13";
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_0_13);
|
||||
pcmc->kvmclock_enabled = false;
|
||||
}
|
||||
|
||||
DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13,
|
||||
@@ -1037,8 +1030,17 @@ void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id)
|
||||
|
||||
static void isapc_machine_options(MachineClass *m)
|
||||
{
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
m->desc = "ISA-only PC";
|
||||
m->max_cpus = 1;
|
||||
m->option_rom_has_mr = true;
|
||||
m->rom_file_has_mr = false;
|
||||
pcmc->pci_enabled = false;
|
||||
pcmc->has_acpi_build = false;
|
||||
pcmc->smbios_defaults = false;
|
||||
pcmc->gigabyte_align = false;
|
||||
pcmc->smbios_legacy_mode = true;
|
||||
pcmc->has_reserved_memory = false;
|
||||
}
|
||||
|
||||
DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa,
|
||||
|
||||
@@ -49,22 +49,11 @@
|
||||
/* ICH9 AHCI has 6 ports */
|
||||
#define MAX_SATA_PORTS 6
|
||||
|
||||
static bool has_acpi_build = true;
|
||||
static bool rsdp_in_ram = true;
|
||||
static bool smbios_defaults = true;
|
||||
static bool smbios_legacy_mode;
|
||||
static bool smbios_uuid_encoded = true;
|
||||
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
|
||||
* host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte
|
||||
* pages in the host.
|
||||
*/
|
||||
static bool gigabyte_align = true;
|
||||
static bool has_reserved_memory = true;
|
||||
|
||||
/* PC hardware initialisation */
|
||||
static void pc_q35_init(MachineState *machine)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
||||
Q35PCIHost *q35_host;
|
||||
PCIHostState *phb;
|
||||
PCIBus *host_bus;
|
||||
@@ -76,7 +65,6 @@ static void pc_q35_init(MachineState *machine)
|
||||
MemoryRegion *ram_memory;
|
||||
GSIState *gsi_state;
|
||||
ISABus *isa_bus;
|
||||
int pci_enabled = 1;
|
||||
qemu_irq *gsi;
|
||||
qemu_irq *i8259;
|
||||
int i;
|
||||
@@ -97,7 +85,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
* breaking migration.
|
||||
*/
|
||||
if (machine->ram_size >= 0xb0000000) {
|
||||
lowmem = gigabyte_align ? 0x80000000 : 0xb0000000;
|
||||
lowmem = pcmc->gigabyte_align ? 0x80000000 : 0xb0000000;
|
||||
} else {
|
||||
lowmem = 0xb0000000;
|
||||
}
|
||||
@@ -129,12 +117,15 @@ static void pc_q35_init(MachineState *machine)
|
||||
}
|
||||
|
||||
pc_cpus_init(pcms);
|
||||
pc_acpi_init("q35-acpi-dsdt.aml");
|
||||
if (!pcmc->has_acpi_build) {
|
||||
/* only machine types 1.7 & older need this */
|
||||
pc_acpi_init("q35-acpi-dsdt.aml");
|
||||
}
|
||||
|
||||
kvmclock_create();
|
||||
|
||||
/* pci enabled */
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pci_memory = g_new(MemoryRegion, 1);
|
||||
memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
|
||||
rom_memory = pci_memory;
|
||||
@@ -145,19 +136,20 @@ static void pc_q35_init(MachineState *machine)
|
||||
|
||||
guest_info = pc_guest_info_init(pcms);
|
||||
guest_info->isapc_ram_fw = false;
|
||||
guest_info->has_acpi_build = has_acpi_build;
|
||||
guest_info->has_reserved_memory = has_reserved_memory;
|
||||
guest_info->rsdp_in_ram = rsdp_in_ram;
|
||||
guest_info->has_acpi_build = pcmc->has_acpi_build;
|
||||
guest_info->has_reserved_memory = pcmc->has_reserved_memory;
|
||||
guest_info->rsdp_in_ram = pcmc->rsdp_in_ram;
|
||||
|
||||
/* Migration was not supported in 2.0 for Q35, so do not bother
|
||||
* with this hack (see hw/i386/acpi-build.c).
|
||||
*/
|
||||
guest_info->legacy_acpi_table_size = 0;
|
||||
|
||||
if (smbios_defaults) {
|
||||
if (pcmc->smbios_defaults) {
|
||||
/* These values are guest ABI, do not change */
|
||||
smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)",
|
||||
mc->name, smbios_legacy_mode, smbios_uuid_encoded,
|
||||
mc->name, pcmc->smbios_legacy_mode,
|
||||
pcmc->smbios_uuid_encoded,
|
||||
SMBIOS_ENTRY_POINT_21);
|
||||
}
|
||||
|
||||
@@ -170,7 +162,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
/* irq lines */
|
||||
gsi_state = g_malloc0(sizeof(*gsi_state));
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
kvm_pc_setup_irq_routing(pci_enabled);
|
||||
kvm_pc_setup_irq_routing(pcmc->pci_enabled);
|
||||
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
|
||||
GSI_NUM_PINS);
|
||||
} else {
|
||||
@@ -187,11 +179,11 @@ static void pc_q35_init(MachineState *machine)
|
||||
q35_host->mch.address_space_io = get_system_io();
|
||||
q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
|
||||
q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
|
||||
q35_host->mch.guest_info = guest_info;
|
||||
/* pci */
|
||||
qdev_init_nofail(DEVICE(q35_host));
|
||||
phb = PCI_HOST_BRIDGE(q35_host);
|
||||
host_bus = phb->bus;
|
||||
pcms->bus = phb->bus;
|
||||
/* create ISA bus */
|
||||
lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV,
|
||||
ICH9_LPC_FUNC), true,
|
||||
@@ -227,13 +219,13 @@ static void pc_q35_init(MachineState *machine)
|
||||
for (i = 0; i < ISA_NUM_IRQS; i++) {
|
||||
gsi_state->i8259_irq[i] = i8259[i];
|
||||
}
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
ioapic_init_gsi(gsi_state, "q35");
|
||||
}
|
||||
|
||||
pc_register_ferr_irq(gsi[13]);
|
||||
|
||||
assert(pcms->vmport != ON_OFF_AUTO_MAX);
|
||||
assert(pcms->vmport != ON_OFF_AUTO__MAX);
|
||||
if (pcms->vmport == ON_OFF_AUTO_AUTO) {
|
||||
pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
|
||||
}
|
||||
@@ -272,7 +264,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
/* the rest devices to which pci devfn is automatically assigned */
|
||||
pc_vga_init(isa_bus, host_bus);
|
||||
pc_nic_init(isa_bus, host_bus);
|
||||
if (pci_enabled) {
|
||||
if (pcmc->pci_enabled) {
|
||||
pc_pci_device_init(host_bus);
|
||||
}
|
||||
}
|
||||
@@ -298,42 +290,29 @@ static void pc_compat_2_3(MachineState *machine)
|
||||
static void pc_compat_2_2(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_3(machine);
|
||||
rsdp_in_ram = false;
|
||||
machine->suppress_vmdesc = true;
|
||||
}
|
||||
|
||||
static void pc_compat_2_1(MachineState *machine)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(machine);
|
||||
|
||||
pc_compat_2_2(machine);
|
||||
pcms->enforce_aligned_dimm = false;
|
||||
smbios_uuid_encoded = false;
|
||||
x86_cpu_change_kvm_default("svm", NULL);
|
||||
}
|
||||
|
||||
static void pc_compat_2_0(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_1(machine);
|
||||
smbios_legacy_mode = true;
|
||||
has_reserved_memory = false;
|
||||
pc_set_legacy_acpi_data_size();
|
||||
}
|
||||
|
||||
static void pc_compat_1_7(MachineState *machine)
|
||||
{
|
||||
pc_compat_2_0(machine);
|
||||
smbios_defaults = false;
|
||||
gigabyte_align = false;
|
||||
option_rom_has_mr = true;
|
||||
x86_cpu_change_kvm_default("x2apic", NULL);
|
||||
}
|
||||
|
||||
static void pc_compat_1_6(MachineState *machine)
|
||||
{
|
||||
pc_compat_1_7(machine);
|
||||
rom_file_has_mr = false;
|
||||
has_acpi_build = false;
|
||||
}
|
||||
|
||||
static void pc_compat_1_5(MachineState *machine)
|
||||
@@ -370,12 +349,22 @@ static void pc_q35_machine_options(MachineClass *m)
|
||||
m->no_tco = 0;
|
||||
}
|
||||
|
||||
static void pc_q35_2_5_machine_options(MachineClass *m)
|
||||
static void pc_q35_2_6_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_q35_machine_options(m);
|
||||
m->alias = "q35";
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL,
|
||||
pc_q35_2_6_machine_options);
|
||||
|
||||
static void pc_q35_2_5_machine_options(MachineClass *m)
|
||||
{
|
||||
pc_q35_2_6_machine_options(m);
|
||||
m->alias = NULL;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
|
||||
}
|
||||
|
||||
DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL,
|
||||
pc_q35_2_5_machine_options);
|
||||
|
||||
@@ -384,7 +373,6 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
|
||||
PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
|
||||
pc_q35_2_5_machine_options(m);
|
||||
m->hw_version = "2.4.0";
|
||||
m->alias = NULL;
|
||||
pcmc->broken_reserved_end = true;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
|
||||
}
|
||||
@@ -399,7 +387,6 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
|
||||
m->hw_version = "2.3.0";
|
||||
m->no_floppy = 0;
|
||||
m->no_tco = 1;
|
||||
m->alias = NULL;
|
||||
SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
|
||||
}
|
||||
|
||||
@@ -409,9 +396,11 @@ DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3,
|
||||
|
||||
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,
|
||||
@@ -420,10 +409,13 @@ DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
|
||||
|
||||
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,
|
||||
@@ -432,9 +424,13 @@ DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
|
||||
|
||||
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,
|
||||
@@ -443,10 +439,14 @@ DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
|
||||
|
||||
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,
|
||||
@@ -455,9 +455,12 @@ DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
|
||||
|
||||
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,
|
||||
|
||||
@@ -108,7 +108,7 @@ void hid_set_next_idle(HIDState *hs)
|
||||
static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
|
||||
InputEvent *evt)
|
||||
{
|
||||
static const int bmap[INPUT_BUTTON_MAX] = {
|
||||
static const int bmap[INPUT_BUTTON__MAX] = {
|
||||
[INPUT_BUTTON_LEFT] = 0x01,
|
||||
[INPUT_BUTTON_RIGHT] = 0x02,
|
||||
[INPUT_BUTTON_MIDDLE] = 0x04,
|
||||
@@ -139,9 +139,9 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
|
||||
case INPUT_EVENT_KIND_BTN:
|
||||
if (evt->u.btn->down) {
|
||||
e->buttons_state |= bmap[evt->u.btn->button];
|
||||
if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
|
||||
if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
|
||||
e->dz--;
|
||||
} else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
|
||||
} else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
|
||||
e->dz++;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -382,7 +382,7 @@ static void ps2_mouse_send_packet(PS2MouseState *s)
|
||||
static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
|
||||
InputEvent *evt)
|
||||
{
|
||||
static const int bmap[INPUT_BUTTON_MAX] = {
|
||||
static const int bmap[INPUT_BUTTON__MAX] = {
|
||||
[INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
|
||||
[INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
|
||||
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
|
||||
@@ -405,9 +405,9 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
|
||||
case INPUT_EVENT_KIND_BTN:
|
||||
if (evt->u.btn->down) {
|
||||
s->mouse_buttons |= bmap[evt->u.btn->button];
|
||||
if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
|
||||
if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
|
||||
s->mouse_dz--;
|
||||
} else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
|
||||
} else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
|
||||
s->mouse_dz++;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
|
||||
static const unsigned int keymap_qcode[Q_KEY_CODE__MAX] = {
|
||||
[Q_KEY_CODE_ESC] = KEY_ESC,
|
||||
[Q_KEY_CODE_1] = KEY_1,
|
||||
[Q_KEY_CODE_2] = KEY_2,
|
||||
@@ -138,20 +138,20 @@ static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
|
||||
[Q_KEY_CODE_MENU] = KEY_MENU,
|
||||
};
|
||||
|
||||
static const unsigned int keymap_button[INPUT_BUTTON_MAX] = {
|
||||
static const unsigned int keymap_button[INPUT_BUTTON__MAX] = {
|
||||
[INPUT_BUTTON_LEFT] = BTN_LEFT,
|
||||
[INPUT_BUTTON_RIGHT] = BTN_RIGHT,
|
||||
[INPUT_BUTTON_MIDDLE] = BTN_MIDDLE,
|
||||
[INPUT_BUTTON_WHEEL_UP] = BTN_GEAR_UP,
|
||||
[INPUT_BUTTON_WHEEL_DOWN] = BTN_GEAR_DOWN,
|
||||
[INPUT_BUTTON_WHEELUP] = BTN_GEAR_UP,
|
||||
[INPUT_BUTTON_WHEELDOWN] = BTN_GEAR_DOWN,
|
||||
};
|
||||
|
||||
static const unsigned int axismap_rel[INPUT_AXIS_MAX] = {
|
||||
static const unsigned int axismap_rel[INPUT_AXIS__MAX] = {
|
||||
[INPUT_AXIS_X] = REL_X,
|
||||
[INPUT_AXIS_Y] = REL_Y,
|
||||
};
|
||||
|
||||
static const unsigned int axismap_abs[INPUT_AXIS_MAX] = {
|
||||
static const unsigned int axismap_abs[INPUT_AXIS__MAX] = {
|
||||
[INPUT_AXIS_X] = ABS_X,
|
||||
[INPUT_AXIS_Y] = ABS_Y,
|
||||
};
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/i386/ioapic.h"
|
||||
#include "hw/i386/ioapic_internal.h"
|
||||
#include "include/hw/pci/msi.h"
|
||||
#include "sysemu/kvm.h"
|
||||
|
||||
//#define DEBUG_IOAPIC
|
||||
|
||||
@@ -35,6 +37,10 @@
|
||||
#define DPRINTF(fmt, ...)
|
||||
#endif
|
||||
|
||||
#define APIC_DELIVERY_MODE_SHIFT 8
|
||||
#define APIC_POLARITY_SHIFT 14
|
||||
#define APIC_TRIG_MODE_SHIFT 15
|
||||
|
||||
static IOAPICCommonState *ioapics[MAX_IOAPICS];
|
||||
|
||||
/* global variable from ioapic_common.c */
|
||||
@@ -54,6 +60,8 @@ static void ioapic_service(IOAPICCommonState *s)
|
||||
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
|
||||
mask = 1 << i;
|
||||
if (s->irr & mask) {
|
||||
int coalesce = 0;
|
||||
|
||||
entry = s->ioredtbl[i];
|
||||
if (!(entry & IOAPIC_LVT_MASKED)) {
|
||||
trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
|
||||
@@ -64,6 +72,7 @@ static void ioapic_service(IOAPICCommonState *s)
|
||||
if (trig_mode == IOAPIC_TRIGGER_EDGE) {
|
||||
s->irr &= ~mask;
|
||||
} else {
|
||||
coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR;
|
||||
s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
|
||||
}
|
||||
if (delivery_mode == IOAPIC_DM_EXTINT) {
|
||||
@@ -71,8 +80,23 @@ static void ioapic_service(IOAPICCommonState *s)
|
||||
} else {
|
||||
vector = entry & IOAPIC_VECTOR_MASK;
|
||||
}
|
||||
apic_deliver_irq(dest, dest_mode, delivery_mode,
|
||||
vector, trig_mode);
|
||||
#ifdef CONFIG_KVM
|
||||
if (kvm_irqchip_is_split()) {
|
||||
if (trig_mode == IOAPIC_TRIGGER_EDGE) {
|
||||
kvm_set_irq(kvm_state, i, 1);
|
||||
kvm_set_irq(kvm_state, i, 0);
|
||||
} else {
|
||||
if (!coalesce) {
|
||||
kvm_set_irq(kvm_state, i, 1);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
(void)coalesce;
|
||||
#endif
|
||||
apic_deliver_irq(dest, dest_mode, delivery_mode, vector,
|
||||
trig_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,6 +140,44 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
|
||||
}
|
||||
}
|
||||
|
||||
static void ioapic_update_kvm_routes(IOAPICCommonState *s)
|
||||
{
|
||||
#ifdef CONFIG_KVM
|
||||
int i;
|
||||
|
||||
if (kvm_irqchip_is_split()) {
|
||||
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
|
||||
uint64_t entry = s->ioredtbl[i];
|
||||
uint8_t trig_mode;
|
||||
uint8_t delivery_mode;
|
||||
uint8_t dest;
|
||||
uint8_t dest_mode;
|
||||
uint64_t pin_polarity;
|
||||
MSIMessage msg;
|
||||
|
||||
trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
|
||||
dest = entry >> IOAPIC_LVT_DEST_SHIFT;
|
||||
dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
|
||||
pin_polarity = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1;
|
||||
delivery_mode =
|
||||
(entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;
|
||||
|
||||
msg.address = APIC_DEFAULT_ADDRESS;
|
||||
msg.address |= dest_mode << 2;
|
||||
msg.address |= dest << 12;
|
||||
|
||||
msg.data = entry & IOAPIC_VECTOR_MASK;
|
||||
msg.data |= delivery_mode << APIC_DELIVERY_MODE_SHIFT;
|
||||
msg.data |= pin_polarity << APIC_POLARITY_SHIFT;
|
||||
msg.data |= trig_mode << APIC_TRIG_MODE_SHIFT;
|
||||
|
||||
kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL);
|
||||
}
|
||||
kvm_irqchip_commit_routes(kvm_state);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ioapic_eoi_broadcast(int vector)
|
||||
{
|
||||
IOAPICCommonState *s;
|
||||
@@ -229,6 +291,8 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ioapic_update_kvm_routes(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ioapic_io_ops = {
|
||||
|
||||
5
hw/ipmi/Makefile.objs
Normal file
5
hw/ipmi/Makefile.objs
Normal file
@@ -0,0 +1,5 @@
|
||||
common-obj-$(CONFIG_IPMI) += ipmi.o
|
||||
common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
|
||||
common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o
|
||||
common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o
|
||||
common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o
|
||||
152
hw/ipmi/ipmi.c
Normal file
152
hw/ipmi/ipmi.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* QEMU IPMI emulation
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi/visitor.h"
|
||||
|
||||
static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly)
|
||||
{
|
||||
switch (op) {
|
||||
case IPMI_RESET_CHASSIS:
|
||||
if (checkonly) {
|
||||
return 0;
|
||||
}
|
||||
qemu_system_reset_request();
|
||||
return 0;
|
||||
|
||||
case IPMI_POWEROFF_CHASSIS:
|
||||
if (checkonly) {
|
||||
return 0;
|
||||
}
|
||||
qemu_system_powerdown_request();
|
||||
return 0;
|
||||
|
||||
case IPMI_SEND_NMI:
|
||||
if (checkonly) {
|
||||
return 0;
|
||||
}
|
||||
qemu_mutex_lock_iothread();
|
||||
qmp_inject_nmi(NULL);
|
||||
qemu_mutex_unlock_iothread();
|
||||
return 0;
|
||||
|
||||
case IPMI_POWERCYCLE_CHASSIS:
|
||||
case IPMI_PULSE_DIAG_IRQ:
|
||||
case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP:
|
||||
case IPMI_POWERON_CHASSIS:
|
||||
default:
|
||||
return IPMI_CC_COMMAND_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_interface_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class);
|
||||
|
||||
ik->do_hw_op = ipmi_do_hw_op;
|
||||
}
|
||||
|
||||
static TypeInfo ipmi_interface_type_info = {
|
||||
.name = TYPE_IPMI_INTERFACE,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(IPMIInterfaceClass),
|
||||
.class_init = ipmi_interface_class_init,
|
||||
};
|
||||
|
||||
static void isa_ipmi_bmc_check(Object *obj, const char *name,
|
||||
Object *val, Error **errp)
|
||||
{
|
||||
IPMIBmc *bmc = IPMI_BMC(val);
|
||||
|
||||
if (bmc->intf)
|
||||
error_setg(errp, "BMC object is already in use");
|
||||
}
|
||||
|
||||
void ipmi_bmc_find_and_link(Object *obj, Object **bmc)
|
||||
{
|
||||
object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc,
|
||||
isa_ipmi_bmc_check,
|
||||
OBJ_PROP_LINK_UNREF_ON_RELEASE,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static Property ipmi_bmc_properties[] = {
|
||||
DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void bmc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->props = ipmi_bmc_properties;
|
||||
}
|
||||
|
||||
static TypeInfo ipmi_bmc_type_info = {
|
||||
.name = TYPE_IPMI_BMC,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(IPMIBmc),
|
||||
.abstract = true,
|
||||
.class_size = sizeof(IPMIBmcClass),
|
||||
.class_init = bmc_class_init,
|
||||
};
|
||||
|
||||
static void ipmi_register_types(void)
|
||||
{
|
||||
type_register_static(&ipmi_interface_type_info);
|
||||
type_register_static(&ipmi_bmc_type_info);
|
||||
}
|
||||
|
||||
type_init(ipmi_register_types)
|
||||
|
||||
static IPMIFwInfo *ipmi_fw_info;
|
||||
static unsigned int ipmi_fw_info_len;
|
||||
|
||||
static uint32_t current_uuid = 1;
|
||||
|
||||
void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp)
|
||||
{
|
||||
info->uuid = current_uuid++;
|
||||
ipmi_fw_info = g_realloc(ipmi_fw_info,
|
||||
sizeof(*ipmi_fw_info) * (ipmi_fw_info_len + 1));
|
||||
ipmi_fw_info[ipmi_fw_info_len] = *info;
|
||||
}
|
||||
|
||||
IPMIFwInfo *ipmi_first_fwinfo(void)
|
||||
{
|
||||
return ipmi_fw_info;
|
||||
}
|
||||
|
||||
IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current)
|
||||
{
|
||||
current++;
|
||||
if (current >= &ipmi_fw_info[ipmi_fw_info_len]) {
|
||||
return NULL;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
518
hw/ipmi/ipmi_bmc_extern.c
Normal file
518
hw/ipmi/ipmi_bmc_extern.c
Normal file
@@ -0,0 +1,518 @@
|
||||
/*
|
||||
* IPMI BMC external connection
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is designed to connect with OpenIPMI's lanserv serial interface
|
||||
* using the "VM" connection type. See that for details.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
|
||||
#define VM_MSG_CHAR 0xA0 /* Marks end of message */
|
||||
#define VM_CMD_CHAR 0xA1 /* Marks end of a command */
|
||||
#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */
|
||||
|
||||
#define VM_PROTOCOL_VERSION 1
|
||||
#define VM_CMD_VERSION 0xff /* A version number byte follows */
|
||||
#define VM_CMD_NOATTN 0x00
|
||||
#define VM_CMD_ATTN 0x01
|
||||
#define VM_CMD_ATTN_IRQ 0x02
|
||||
#define VM_CMD_POWEROFF 0x03
|
||||
#define VM_CMD_RESET 0x04
|
||||
#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */
|
||||
#define VM_CMD_DISABLE_IRQ 0x06
|
||||
#define VM_CMD_SEND_NMI 0x07
|
||||
#define VM_CMD_CAPABILITIES 0x08
|
||||
#define VM_CAPABILITIES_POWER 0x01
|
||||
#define VM_CAPABILITIES_RESET 0x02
|
||||
#define VM_CAPABILITIES_IRQ 0x04
|
||||
#define VM_CAPABILITIES_NMI 0x08
|
||||
#define VM_CAPABILITIES_ATTN 0x10
|
||||
#define VM_CMD_FORCEOFF 0x09
|
||||
|
||||
#define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern"
|
||||
#define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \
|
||||
TYPE_IPMI_BMC_EXTERN)
|
||||
typedef struct IPMIBmcExtern {
|
||||
IPMIBmc parent;
|
||||
|
||||
CharDriverState *chr;
|
||||
|
||||
bool connected;
|
||||
|
||||
unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2];
|
||||
unsigned int inpos;
|
||||
bool in_escape;
|
||||
bool in_too_many;
|
||||
bool waiting_rsp;
|
||||
bool sending_cmd;
|
||||
|
||||
unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1];
|
||||
unsigned int outpos;
|
||||
unsigned int outlen;
|
||||
|
||||
struct QEMUTimer *extern_timer;
|
||||
|
||||
/* A reset event is pending to be sent upstream. */
|
||||
bool send_reset;
|
||||
} IPMIBmcExtern;
|
||||
|
||||
static int can_receive(void *opaque);
|
||||
static void receive(void *opaque, const uint8_t *buf, int size);
|
||||
static void chr_event(void *opaque, int event);
|
||||
|
||||
static unsigned char
|
||||
ipmb_checksum(const unsigned char *data, int size, unsigned char start)
|
||||
{
|
||||
unsigned char csum = start;
|
||||
|
||||
for (; size > 0; size--, data++) {
|
||||
csum += *data;
|
||||
}
|
||||
return csum;
|
||||
}
|
||||
|
||||
static void continue_send(IPMIBmcExtern *ibe)
|
||||
{
|
||||
if (ibe->outlen == 0) {
|
||||
goto check_reset;
|
||||
}
|
||||
send:
|
||||
ibe->outpos += qemu_chr_fe_write(ibe->chr, ibe->outbuf + ibe->outpos,
|
||||
ibe->outlen - ibe->outpos);
|
||||
if (ibe->outpos < ibe->outlen) {
|
||||
/* Not fully transmitted, try again in a 10ms */
|
||||
timer_mod_ns(ibe->extern_timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000);
|
||||
} else {
|
||||
/* Sent */
|
||||
ibe->outlen = 0;
|
||||
ibe->outpos = 0;
|
||||
if (!ibe->sending_cmd) {
|
||||
ibe->waiting_rsp = true;
|
||||
} else {
|
||||
ibe->sending_cmd = false;
|
||||
}
|
||||
check_reset:
|
||||
if (ibe->connected && ibe->send_reset) {
|
||||
/* Send the reset */
|
||||
ibe->outbuf[0] = VM_CMD_RESET;
|
||||
ibe->outbuf[1] = VM_CMD_CHAR;
|
||||
ibe->outlen = 2;
|
||||
ibe->outpos = 0;
|
||||
ibe->send_reset = false;
|
||||
ibe->sending_cmd = true;
|
||||
goto send;
|
||||
}
|
||||
|
||||
if (ibe->waiting_rsp) {
|
||||
/* Make sure we get a response within 4 seconds. */
|
||||
timer_mod_ns(ibe->extern_timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void extern_timeout(void *opaque)
|
||||
{
|
||||
IPMIBmcExtern *ibe = opaque;
|
||||
IPMIInterface *s = ibe->parent.intf;
|
||||
|
||||
if (ibe->connected) {
|
||||
if (ibe->waiting_rsp && (ibe->outlen == 0)) {
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||
/* The message response timed out, return an error. */
|
||||
ibe->waiting_rsp = false;
|
||||
ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
|
||||
ibe->inbuf[2] = ibe->outbuf[2];
|
||||
ibe->inbuf[3] = IPMI_CC_TIMEOUT;
|
||||
k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
|
||||
} else {
|
||||
continue_send(ibe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addchar(IPMIBmcExtern *ibe, unsigned char ch)
|
||||
{
|
||||
switch (ch) {
|
||||
case VM_MSG_CHAR:
|
||||
case VM_CMD_CHAR:
|
||||
case VM_ESCAPE_CHAR:
|
||||
ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR;
|
||||
ibe->outlen++;
|
||||
ch |= 0x10;
|
||||
/* No break */
|
||||
|
||||
default:
|
||||
ibe->outbuf[ibe->outlen] = ch;
|
||||
ibe->outlen++;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bmc_extern_handle_command(IPMIBmc *b,
|
||||
uint8_t *cmd, unsigned int cmd_len,
|
||||
unsigned int max_cmd_len,
|
||||
uint8_t msg_id)
|
||||
{
|
||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
|
||||
IPMIInterface *s = ibe->parent.intf;
|
||||
uint8_t err = 0, csum;
|
||||
unsigned int i;
|
||||
|
||||
if (ibe->outlen) {
|
||||
/* We already have a command queued. Shouldn't ever happen. */
|
||||
fprintf(stderr, "IPMI KCS: Got command when not finished with the"
|
||||
" previous commmand\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* If it's too short or it was truncated, return an error. */
|
||||
if (cmd_len < 2) {
|
||||
err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
|
||||
} else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) {
|
||||
err = IPMI_CC_REQUEST_DATA_TRUNCATED;
|
||||
} else if (!ibe->connected) {
|
||||
err = IPMI_CC_BMC_INIT_IN_PROGRESS;
|
||||
}
|
||||
if (err) {
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||
unsigned char rsp[3];
|
||||
rsp[0] = cmd[0] | 0x04;
|
||||
rsp[1] = cmd[1];
|
||||
rsp[2] = err;
|
||||
ibe->waiting_rsp = false;
|
||||
k->handle_rsp(s, msg_id, rsp, 3);
|
||||
goto out;
|
||||
}
|
||||
|
||||
addchar(ibe, msg_id);
|
||||
for (i = 0; i < cmd_len; i++) {
|
||||
addchar(ibe, cmd[i]);
|
||||
}
|
||||
csum = ipmb_checksum(&msg_id, 1, 0);
|
||||
addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum));
|
||||
|
||||
ibe->outbuf[ibe->outlen] = VM_MSG_CHAR;
|
||||
ibe->outlen++;
|
||||
|
||||
/* Start the transmit */
|
||||
continue_send(ibe);
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op)
|
||||
{
|
||||
IPMIInterface *s = ibe->parent.intf;
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||
|
||||
switch (hw_op) {
|
||||
case VM_CMD_VERSION:
|
||||
/* We only support one version at this time. */
|
||||
break;
|
||||
|
||||
case VM_CMD_NOATTN:
|
||||
k->set_atn(s, 0, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_ATTN:
|
||||
k->set_atn(s, 1, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_ATTN_IRQ:
|
||||
k->set_atn(s, 1, 1);
|
||||
break;
|
||||
|
||||
case VM_CMD_POWEROFF:
|
||||
k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_RESET:
|
||||
k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_ENABLE_IRQ:
|
||||
k->set_irq_enable(s, 1);
|
||||
break;
|
||||
|
||||
case VM_CMD_DISABLE_IRQ:
|
||||
k->set_irq_enable(s, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_SEND_NMI:
|
||||
k->do_hw_op(s, IPMI_SEND_NMI, 0);
|
||||
break;
|
||||
|
||||
case VM_CMD_FORCEOFF:
|
||||
qemu_system_shutdown_request();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_msg(IPMIBmcExtern *ibe)
|
||||
{
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->parent.intf);
|
||||
|
||||
if (ibe->in_escape) {
|
||||
ipmi_debug("msg escape not ended\n");
|
||||
return;
|
||||
}
|
||||
if (ibe->inpos < 5) {
|
||||
ipmi_debug("msg too short\n");
|
||||
return;
|
||||
}
|
||||
if (ibe->in_too_many) {
|
||||
ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED;
|
||||
ibe->inpos = 4;
|
||||
} else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) {
|
||||
ipmi_debug("msg checksum failure\n");
|
||||
return;
|
||||
} else {
|
||||
ibe->inpos--; /* Remove checkum */
|
||||
}
|
||||
|
||||
timer_del(ibe->extern_timer);
|
||||
ibe->waiting_rsp = false;
|
||||
k->handle_rsp(ibe->parent.intf, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1);
|
||||
}
|
||||
|
||||
static int can_receive(void *opaque)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
IPMIBmcExtern *ibe = opaque;
|
||||
int i;
|
||||
unsigned char hw_op;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
unsigned char ch = buf[i];
|
||||
|
||||
switch (ch) {
|
||||
case VM_MSG_CHAR:
|
||||
handle_msg(ibe);
|
||||
ibe->in_too_many = false;
|
||||
ibe->inpos = 0;
|
||||
break;
|
||||
|
||||
case VM_CMD_CHAR:
|
||||
if (ibe->in_too_many) {
|
||||
ipmi_debug("cmd in too many\n");
|
||||
ibe->in_too_many = false;
|
||||
ibe->inpos = 0;
|
||||
break;
|
||||
}
|
||||
if (ibe->in_escape) {
|
||||
ipmi_debug("cmd in escape\n");
|
||||
ibe->in_too_many = false;
|
||||
ibe->inpos = 0;
|
||||
ibe->in_escape = false;
|
||||
break;
|
||||
}
|
||||
ibe->in_too_many = false;
|
||||
if (ibe->inpos < 1) {
|
||||
break;
|
||||
}
|
||||
hw_op = ibe->inbuf[0];
|
||||
ibe->inpos = 0;
|
||||
goto out_hw_op;
|
||||
break;
|
||||
|
||||
case VM_ESCAPE_CHAR:
|
||||
ibe->in_escape = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ibe->in_escape) {
|
||||
ch &= ~0x10;
|
||||
ibe->in_escape = false;
|
||||
}
|
||||
if (ibe->in_too_many) {
|
||||
break;
|
||||
}
|
||||
if (ibe->inpos >= sizeof(ibe->inbuf)) {
|
||||
ibe->in_too_many = true;
|
||||
break;
|
||||
}
|
||||
ibe->inbuf[ibe->inpos] = ch;
|
||||
ibe->inpos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
out_hw_op:
|
||||
handle_hw_op(ibe, hw_op);
|
||||
}
|
||||
|
||||
static void chr_event(void *opaque, int event)
|
||||
{
|
||||
IPMIBmcExtern *ibe = opaque;
|
||||
IPMIInterface *s = ibe->parent.intf;
|
||||
IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
|
||||
unsigned char v;
|
||||
|
||||
switch (event) {
|
||||
case CHR_EVENT_OPENED:
|
||||
ibe->connected = true;
|
||||
ibe->outpos = 0;
|
||||
ibe->outlen = 0;
|
||||
addchar(ibe, VM_CMD_VERSION);
|
||||
addchar(ibe, VM_PROTOCOL_VERSION);
|
||||
ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
|
||||
ibe->outlen++;
|
||||
addchar(ibe, VM_CMD_CAPABILITIES);
|
||||
v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN;
|
||||
if (k->do_hw_op(ibe->parent.intf, IPMI_POWEROFF_CHASSIS, 1) == 0) {
|
||||
v |= VM_CAPABILITIES_POWER;
|
||||
}
|
||||
if (k->do_hw_op(ibe->parent.intf, IPMI_RESET_CHASSIS, 1) == 0) {
|
||||
v |= VM_CAPABILITIES_RESET;
|
||||
}
|
||||
if (k->do_hw_op(ibe->parent.intf, IPMI_SEND_NMI, 1) == 0) {
|
||||
v |= VM_CAPABILITIES_NMI;
|
||||
}
|
||||
addchar(ibe, v);
|
||||
ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
|
||||
ibe->outlen++;
|
||||
ibe->sending_cmd = false;
|
||||
continue_send(ibe);
|
||||
break;
|
||||
|
||||
case CHR_EVENT_CLOSED:
|
||||
if (!ibe->connected) {
|
||||
return;
|
||||
}
|
||||
ibe->connected = false;
|
||||
if (ibe->waiting_rsp) {
|
||||
ibe->waiting_rsp = false;
|
||||
ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
|
||||
ibe->inbuf[2] = ibe->outbuf[2];
|
||||
ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
|
||||
k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bmc_extern_handle_reset(IPMIBmc *b)
|
||||
{
|
||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
|
||||
|
||||
ibe->send_reset = true;
|
||||
continue_send(ibe);
|
||||
}
|
||||
|
||||
static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
|
||||
|
||||
if (!ibe->chr) {
|
||||
error_setg(errp, "IPMI external bmc requires chardev attribute");
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe);
|
||||
}
|
||||
|
||||
static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
|
||||
{
|
||||
IPMIBmcExtern *ibe = opaque;
|
||||
|
||||
/*
|
||||
* We don't directly restore waiting_rsp, Instead, we return an
|
||||
* error on the interface if a response was being waited for.
|
||||
*/
|
||||
if (ibe->waiting_rsp) {
|
||||
IPMIInterface *ii = ibe->parent.intf;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
ibe->waiting_rsp = false;
|
||||
ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
|
||||
ibe->inbuf[2] = ibe->outbuf[2];
|
||||
ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
|
||||
iic->handle_rsp(ii, ibe->outbuf[0], ibe->inbuf + 1, 3);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ipmi_bmc_extern = {
|
||||
.name = TYPE_IPMI_BMC_EXTERN,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.post_load = ipmi_bmc_extern_post_migrate,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(send_reset, IPMIBmcExtern),
|
||||
VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void ipmi_bmc_extern_init(Object *obj)
|
||||
{
|
||||
IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
|
||||
|
||||
ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
|
||||
vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
|
||||
}
|
||||
|
||||
static Property ipmi_bmc_extern_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIBmcClass *bk = IPMI_BMC_CLASS(oc);
|
||||
|
||||
bk->handle_command = ipmi_bmc_extern_handle_command;
|
||||
bk->handle_reset = ipmi_bmc_extern_handle_reset;
|
||||
dc->realize = ipmi_bmc_extern_realize;
|
||||
dc->props = ipmi_bmc_extern_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo ipmi_bmc_extern_type = {
|
||||
.name = TYPE_IPMI_BMC_EXTERN,
|
||||
.parent = TYPE_IPMI_BMC,
|
||||
.instance_size = sizeof(IPMIBmcExtern),
|
||||
.instance_init = ipmi_bmc_extern_init,
|
||||
.class_init = ipmi_bmc_extern_class_init,
|
||||
};
|
||||
|
||||
static void ipmi_bmc_extern_register_types(void)
|
||||
{
|
||||
type_register_static(&ipmi_bmc_extern_type);
|
||||
}
|
||||
|
||||
type_init(ipmi_bmc_extern_register_types)
|
||||
1756
hw/ipmi/ipmi_bmc_sim.c
Normal file
1756
hw/ipmi/ipmi_bmc_sim.c
Normal file
File diff suppressed because it is too large
Load Diff
528
hw/ipmi/isa_ipmi_bt.c
Normal file
528
hw/ipmi/isa_ipmi_bt.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* QEMU ISA IPMI BT emulation
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/i386/pc.h"
|
||||
|
||||
/* Control register */
|
||||
#define IPMI_BT_CLR_WR_BIT 0
|
||||
#define IPMI_BT_CLR_RD_BIT 1
|
||||
#define IPMI_BT_H2B_ATN_BIT 2
|
||||
#define IPMI_BT_B2H_ATN_BIT 3
|
||||
#define IPMI_BT_SMS_ATN_BIT 4
|
||||
#define IPMI_BT_HBUSY_BIT 6
|
||||
#define IPMI_BT_BBUSY_BIT 7
|
||||
|
||||
#define IPMI_BT_CLR_WR_MASK (1 << IPMI_BT_CLR_WR_BIT)
|
||||
#define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_CLR_WR(d, v) (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \
|
||||
(((v & 1) << IPMI_BT_CLR_WR_BIT)))
|
||||
|
||||
#define IPMI_BT_CLR_RD_MASK (1 << IPMI_BT_CLR_RD_BIT)
|
||||
#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_CLR_RD(d, v) (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \
|
||||
(((v & 1) << IPMI_BT_CLR_RD_BIT)))
|
||||
|
||||
#define IPMI_BT_H2B_ATN_MASK (1 << IPMI_BT_H2B_ATN_BIT)
|
||||
#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_H2B_ATN(d, v) (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \
|
||||
(((v & 1) << IPMI_BT_H2B_ATN_BIT)))
|
||||
|
||||
#define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT)
|
||||
#define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_B2H_ATN(d, v) (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \
|
||||
(((v & 1) << IPMI_BT_B2H_ATN_BIT)))
|
||||
|
||||
#define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT)
|
||||
#define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \
|
||||
(((v & 1) << IPMI_BT_SMS_ATN_BIT)))
|
||||
|
||||
#define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT)
|
||||
#define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_HBUSY(d, v) (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \
|
||||
(((v & 1) << IPMI_BT_HBUSY_BIT)))
|
||||
|
||||
#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT)
|
||||
#define IPMI_BT_GET_BBUSY(d) (((d) >> IPMI_BT_BBUSY_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_BBUSY(d, v) (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \
|
||||
(((v & 1) << IPMI_BT_BBUSY_BIT)))
|
||||
|
||||
|
||||
/* Mask register */
|
||||
#define IPMI_BT_B2H_IRQ_EN_BIT 0
|
||||
#define IPMI_BT_B2H_IRQ_BIT 1
|
||||
|
||||
#define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT)
|
||||
#define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_B2H_IRQ_EN(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \
|
||||
(((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT)))
|
||||
|
||||
#define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT)
|
||||
#define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1)
|
||||
#define IPMI_BT_SET_B2H_IRQ(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \
|
||||
(((v & 1) << IPMI_BT_B2H_IRQ_BIT)))
|
||||
|
||||
typedef struct IPMIBT {
|
||||
IPMIBmc *bmc;
|
||||
|
||||
bool do_wake;
|
||||
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t io_base;
|
||||
unsigned long io_length;
|
||||
MemoryRegion io;
|
||||
|
||||
bool obf_irq_set;
|
||||
bool atn_irq_set;
|
||||
bool use_irq;
|
||||
bool irqs_enabled;
|
||||
|
||||
uint8_t outmsg[MAX_IPMI_MSG_SIZE];
|
||||
uint32_t outpos;
|
||||
uint32_t outlen;
|
||||
|
||||
uint8_t inmsg[MAX_IPMI_MSG_SIZE];
|
||||
uint32_t inlen;
|
||||
|
||||
uint8_t control_reg;
|
||||
uint8_t mask_reg;
|
||||
|
||||
/*
|
||||
* This is a response number that we send with the command to make
|
||||
* sure that the response matches the command.
|
||||
*/
|
||||
uint8_t waiting_rsp;
|
||||
uint8_t waiting_seq;
|
||||
} IPMIBT;
|
||||
|
||||
#define IPMI_CMD_GET_BT_INTF_CAP 0x36
|
||||
|
||||
static void ipmi_bt_handle_event(IPMIInterface *ii)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
if (ib->inlen < 4) {
|
||||
goto out;
|
||||
}
|
||||
/* Note that overruns are handled by handle_command */
|
||||
if (ib->inmsg[0] != (ib->inlen - 1)) {
|
||||
/* Length mismatch, just ignore. */
|
||||
IPMI_BT_SET_BBUSY(ib->control_reg, 1);
|
||||
ib->inlen = 0;
|
||||
goto out;
|
||||
}
|
||||
if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) &&
|
||||
(ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) {
|
||||
/* We handle this one ourselves. */
|
||||
ib->outmsg[0] = 9;
|
||||
ib->outmsg[1] = ib->inmsg[1] | 0x04;
|
||||
ib->outmsg[2] = ib->inmsg[2];
|
||||
ib->outmsg[3] = ib->inmsg[3];
|
||||
ib->outmsg[4] = 0;
|
||||
ib->outmsg[5] = 1; /* Only support 1 outstanding request. */
|
||||
if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */
|
||||
ib->outmsg[6] = 0xff;
|
||||
} else {
|
||||
ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg);
|
||||
}
|
||||
if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */
|
||||
ib->outmsg[7] = 0xff;
|
||||
} else {
|
||||
ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg);
|
||||
}
|
||||
ib->outmsg[8] = 10; /* Max request to response time */
|
||||
ib->outmsg[9] = 0; /* Don't recommend retries */
|
||||
ib->outlen = 10;
|
||||
IPMI_BT_SET_BBUSY(ib->control_reg, 0);
|
||||
IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
|
||||
if (ib->use_irq && ib->irqs_enabled &&
|
||||
!IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
|
||||
IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
|
||||
qemu_irq_raise(ib->irq);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
ib->waiting_seq = ib->inmsg[2];
|
||||
ib->inmsg[2] = ib->inmsg[1];
|
||||
{
|
||||
IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc);
|
||||
bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2,
|
||||
sizeof(ib->inmsg), ib->waiting_rsp);
|
||||
}
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
|
||||
unsigned char *rsp, unsigned int rsp_len)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
if (ib->waiting_rsp == msg_id) {
|
||||
ib->waiting_rsp++;
|
||||
if (rsp_len > (sizeof(ib->outmsg) - 2)) {
|
||||
ib->outmsg[0] = 4;
|
||||
ib->outmsg[1] = rsp[0];
|
||||
ib->outmsg[2] = ib->waiting_seq;
|
||||
ib->outmsg[3] = rsp[1];
|
||||
ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
|
||||
ib->outlen = 5;
|
||||
} else {
|
||||
ib->outmsg[0] = rsp_len + 1;
|
||||
ib->outmsg[1] = rsp[0];
|
||||
ib->outmsg[2] = ib->waiting_seq;
|
||||
memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1);
|
||||
ib->outlen = rsp_len + 2;
|
||||
}
|
||||
IPMI_BT_SET_BBUSY(ib->control_reg, 0);
|
||||
IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
|
||||
if (ib->use_irq && ib->irqs_enabled &&
|
||||
!IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
|
||||
IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
|
||||
qemu_irq_raise(ib->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPMIInterface *ii = opaque;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
uint32_t ret = 0xff;
|
||||
|
||||
switch (addr & 3) {
|
||||
case 0:
|
||||
ret = ib->control_reg;
|
||||
break;
|
||||
case 1:
|
||||
if (ib->outpos < ib->outlen) {
|
||||
ret = ib->outmsg[ib->outpos];
|
||||
ib->outpos++;
|
||||
if (ib->outpos == ib->outlen) {
|
||||
ib->outpos = 0;
|
||||
ib->outlen = 0;
|
||||
}
|
||||
} else {
|
||||
ret = 0xff;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
ret = ib->mask_reg;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
ib->do_wake = 1;
|
||||
while (ib->do_wake) {
|
||||
ib->do_wake = 0;
|
||||
iic->handle_if_event(ii);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
IPMIInterface *ii = opaque;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
switch (addr & 3) {
|
||||
case 0:
|
||||
if (IPMI_BT_GET_CLR_WR(val)) {
|
||||
ib->inlen = 0;
|
||||
}
|
||||
if (IPMI_BT_GET_CLR_RD(val)) {
|
||||
ib->outpos = 0;
|
||||
}
|
||||
if (IPMI_BT_GET_B2H_ATN(val)) {
|
||||
IPMI_BT_SET_B2H_ATN(ib->control_reg, 0);
|
||||
}
|
||||
if (IPMI_BT_GET_SMS_ATN(val)) {
|
||||
IPMI_BT_SET_SMS_ATN(ib->control_reg, 0);
|
||||
}
|
||||
if (IPMI_BT_GET_HBUSY(val)) {
|
||||
/* Toggle */
|
||||
IPMI_BT_SET_HBUSY(ib->control_reg,
|
||||
!IPMI_BT_GET_HBUSY(ib->control_reg));
|
||||
}
|
||||
if (IPMI_BT_GET_H2B_ATN(val)) {
|
||||
IPMI_BT_SET_BBUSY(ib->control_reg, 1);
|
||||
ipmi_bt_signal(ib, ii);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (ib->inlen < sizeof(ib->inmsg)) {
|
||||
ib->inmsg[ib->inlen] = val;
|
||||
}
|
||||
ib->inlen++;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (IPMI_BT_GET_B2H_IRQ_EN(val) !=
|
||||
IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
|
||||
if (IPMI_BT_GET_B2H_IRQ_EN(val)) {
|
||||
if (IPMI_BT_GET_B2H_ATN(ib->control_reg) ||
|
||||
IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
|
||||
qemu_irq_raise(ib->irq);
|
||||
}
|
||||
IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1);
|
||||
} else {
|
||||
if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
|
||||
qemu_irq_lower(ib->irq);
|
||||
}
|
||||
IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
|
||||
}
|
||||
}
|
||||
if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
|
||||
qemu_irq_lower(ib->irq);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipmi_bt_io_ops = {
|
||||
.read = ipmi_bt_ioport_read,
|
||||
.write = ipmi_bt_ioport_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IPMI_BT_SET_SMS_ATN(ib->control_reg, val);
|
||||
if (val) {
|
||||
if (irq && ib->use_irq && ib->irqs_enabled &&
|
||||
!IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
|
||||
IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
|
||||
qemu_irq_raise(ib->irq);
|
||||
}
|
||||
} else {
|
||||
if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
|
||||
IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
|
||||
qemu_irq_lower(ib->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
if (is_cold) {
|
||||
/* Disable the BT interrupt on reset */
|
||||
if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
|
||||
IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
|
||||
qemu_irq_lower(ib->irq);
|
||||
}
|
||||
IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
ib->irqs_enabled = val;
|
||||
}
|
||||
|
||||
static void ipmi_bt_init(IPMIInterface *ii, Error **errp)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIBT *ib = iic->get_backend_data(ii);
|
||||
|
||||
ib->io_length = 3;
|
||||
|
||||
memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3);
|
||||
}
|
||||
|
||||
static void ipmi_bt_class_init(IPMIInterfaceClass *iic)
|
||||
{
|
||||
iic->init = ipmi_bt_init;
|
||||
iic->set_atn = ipmi_bt_set_atn;
|
||||
iic->handle_rsp = ipmi_bt_handle_rsp;
|
||||
iic->handle_if_event = ipmi_bt_handle_event;
|
||||
iic->set_irq_enable = ipmi_bt_set_irq_enable;
|
||||
iic->reset = ipmi_bt_handle_reset;
|
||||
}
|
||||
|
||||
|
||||
#define TYPE_ISA_IPMI_BT "isa-ipmi-bt"
|
||||
#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \
|
||||
TYPE_ISA_IPMI_BT)
|
||||
|
||||
typedef struct ISAIPMIBTDevice {
|
||||
ISADevice dev;
|
||||
int32 isairq;
|
||||
IPMIBT bt;
|
||||
IPMIFwInfo fwinfo;
|
||||
} ISAIPMIBTDevice;
|
||||
|
||||
static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ISADevice *isadev = ISA_DEVICE(dev);
|
||||
ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev);
|
||||
IPMIInterface *ii = IPMI_INTERFACE(dev);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
if (!iib->bt.bmc) {
|
||||
error_setg(errp, "IPMI device requires a bmc attribute to be set");
|
||||
return;
|
||||
}
|
||||
|
||||
iib->bt.bmc->intf = ii;
|
||||
|
||||
iic->init(ii, errp);
|
||||
if (*errp)
|
||||
return;
|
||||
|
||||
if (iib->isairq > 0) {
|
||||
isa_init_irq(isadev, &iib->bt.irq, iib->isairq);
|
||||
iib->bt.use_irq = 1;
|
||||
}
|
||||
|
||||
qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length);
|
||||
|
||||
isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
|
||||
|
||||
iib->fwinfo.interface_name = "bt";
|
||||
iib->fwinfo.interface_type = IPMI_SMBIOS_BT;
|
||||
iib->fwinfo.ipmi_spec_major_revision = 2;
|
||||
iib->fwinfo.ipmi_spec_minor_revision = 0;
|
||||
iib->fwinfo.base_address = iib->bt.io_base;
|
||||
iib->fwinfo.register_length = iib->bt.io_length;
|
||||
iib->fwinfo.register_spacing = 1;
|
||||
iib->fwinfo.memspace = IPMI_MEMSPACE_IO;
|
||||
iib->fwinfo.irq_type = IPMI_LEVEL_IRQ;
|
||||
iib->fwinfo.interrupt_number = iib->isairq;
|
||||
iib->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
|
||||
iib->fwinfo.i2c_slave_address = iib->bt.bmc->slave_addr;
|
||||
ipmi_add_fwinfo(&iib->fwinfo, errp);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_ISAIPMIBTDevice = {
|
||||
.name = TYPE_IPMI_INTERFACE,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice),
|
||||
VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice),
|
||||
VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice),
|
||||
VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice),
|
||||
VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice),
|
||||
VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, 0,
|
||||
bt.outlen),
|
||||
VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 0,
|
||||
bt.inlen),
|
||||
VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice),
|
||||
VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice),
|
||||
VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice),
|
||||
VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void isa_ipmi_bt_init(Object *obj)
|
||||
{
|
||||
ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
|
||||
|
||||
ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc);
|
||||
|
||||
vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib);
|
||||
}
|
||||
|
||||
static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii)
|
||||
{
|
||||
ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii);
|
||||
|
||||
return &iib->bt;
|
||||
}
|
||||
|
||||
static Property ipmi_isa_properties[] = {
|
||||
DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4),
|
||||
DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
|
||||
|
||||
dc->realize = isa_ipmi_bt_realize;
|
||||
dc->props = ipmi_isa_properties;
|
||||
|
||||
iic->get_backend_data = isa_ipmi_bt_get_backend_data;
|
||||
ipmi_bt_class_init(iic);
|
||||
}
|
||||
|
||||
static const TypeInfo isa_ipmi_bt_info = {
|
||||
.name = TYPE_ISA_IPMI_BT,
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(ISAIPMIBTDevice),
|
||||
.instance_init = isa_ipmi_bt_init,
|
||||
.class_init = isa_ipmi_bt_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_IPMI_INTERFACE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void ipmi_register_types(void)
|
||||
{
|
||||
type_register_static(&isa_ipmi_bt_info);
|
||||
}
|
||||
|
||||
type_init(ipmi_register_types)
|
||||
493
hw/ipmi/isa_ipmi_kcs.c
Normal file
493
hw/ipmi/isa_ipmi_kcs.c
Normal file
@@ -0,0 +1,493 @@
|
||||
/*
|
||||
* QEMU ISA IPMI KCS emulation
|
||||
*
|
||||
* Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ipmi/ipmi.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/i386/pc.h"
|
||||
|
||||
#define IPMI_KCS_OBF_BIT 0
|
||||
#define IPMI_KCS_IBF_BIT 1
|
||||
#define IPMI_KCS_SMS_ATN_BIT 2
|
||||
#define IPMI_KCS_CD_BIT 3
|
||||
|
||||
#define IPMI_KCS_OBF_MASK (1 << IPMI_KCS_OBF_BIT)
|
||||
#define IPMI_KCS_GET_OBF(d) (((d) >> IPMI_KCS_OBF_BIT) & 0x1)
|
||||
#define IPMI_KCS_SET_OBF(d, v) (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \
|
||||
(((v) & 1) << IPMI_KCS_OBF_BIT))
|
||||
#define IPMI_KCS_IBF_MASK (1 << IPMI_KCS_IBF_BIT)
|
||||
#define IPMI_KCS_GET_IBF(d) (((d) >> IPMI_KCS_IBF_BIT) & 0x1)
|
||||
#define IPMI_KCS_SET_IBF(d, v) (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \
|
||||
(((v) & 1) << IPMI_KCS_IBF_BIT))
|
||||
#define IPMI_KCS_SMS_ATN_MASK (1 << IPMI_KCS_SMS_ATN_BIT)
|
||||
#define IPMI_KCS_GET_SMS_ATN(d) (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1)
|
||||
#define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \
|
||||
(((v) & 1) << IPMI_KCS_SMS_ATN_BIT))
|
||||
#define IPMI_KCS_CD_MASK (1 << IPMI_KCS_CD_BIT)
|
||||
#define IPMI_KCS_GET_CD(d) (((d) >> IPMI_KCS_CD_BIT) & 0x1)
|
||||
#define IPMI_KCS_SET_CD(d, v) (d) = (((d) & ~IPMI_KCS_CD_MASK) | \
|
||||
(((v) & 1) << IPMI_KCS_CD_BIT))
|
||||
|
||||
#define IPMI_KCS_IDLE_STATE 0
|
||||
#define IPMI_KCS_READ_STATE 1
|
||||
#define IPMI_KCS_WRITE_STATE 2
|
||||
#define IPMI_KCS_ERROR_STATE 3
|
||||
|
||||
#define IPMI_KCS_GET_STATE(d) (((d) >> 6) & 0x3)
|
||||
#define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6))
|
||||
|
||||
#define IPMI_KCS_ABORT_STATUS_CMD 0x60
|
||||
#define IPMI_KCS_WRITE_START_CMD 0x61
|
||||
#define IPMI_KCS_WRITE_END_CMD 0x62
|
||||
#define IPMI_KCS_READ_CMD 0x68
|
||||
|
||||
#define IPMI_KCS_STATUS_NO_ERR 0x00
|
||||
#define IPMI_KCS_STATUS_ABORTED_ERR 0x01
|
||||
#define IPMI_KCS_STATUS_BAD_CC_ERR 0x02
|
||||
#define IPMI_KCS_STATUS_LENGTH_ERR 0x06
|
||||
|
||||
typedef struct IPMIKCS {
|
||||
IPMIBmc *bmc;
|
||||
|
||||
bool do_wake;
|
||||
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t io_base;
|
||||
unsigned long io_length;
|
||||
MemoryRegion io;
|
||||
|
||||
bool obf_irq_set;
|
||||
bool atn_irq_set;
|
||||
bool use_irq;
|
||||
bool irqs_enabled;
|
||||
|
||||
uint8_t outmsg[MAX_IPMI_MSG_SIZE];
|
||||
uint32_t outpos;
|
||||
uint32_t outlen;
|
||||
|
||||
uint8_t inmsg[MAX_IPMI_MSG_SIZE];
|
||||
uint32_t inlen;
|
||||
bool write_end;
|
||||
|
||||
uint8_t status_reg;
|
||||
uint8_t data_out_reg;
|
||||
|
||||
int16_t data_in_reg; /* -1 means not written */
|
||||
int16_t cmd_reg;
|
||||
|
||||
/*
|
||||
* This is a response number that we send with the command to make
|
||||
* sure that the response matches the command.
|
||||
*/
|
||||
uint8_t waiting_rsp;
|
||||
} IPMIKCS;
|
||||
|
||||
#define SET_OBF() \
|
||||
do { \
|
||||
IPMI_KCS_SET_OBF(ik->status_reg, 1); \
|
||||
if (ik->use_irq && ik->irqs_enabled && !ik->obf_irq_set) { \
|
||||
ik->obf_irq_set = 1; \
|
||||
if (!ik->atn_irq_set) { \
|
||||
qemu_irq_raise(ik->irq); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
ik->do_wake = 1;
|
||||
while (ik->do_wake) {
|
||||
ik->do_wake = 0;
|
||||
iic->handle_if_event(ii);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_kcs_handle_event(IPMIInterface *ii)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) {
|
||||
if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) {
|
||||
ik->waiting_rsp++; /* Invalidate the message */
|
||||
ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR;
|
||||
ik->outlen = 1;
|
||||
ik->outpos = 0;
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
|
||||
SET_OBF();
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (IPMI_KCS_GET_STATE(ik->status_reg)) {
|
||||
case IPMI_KCS_IDLE_STATE:
|
||||
if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) {
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE);
|
||||
ik->cmd_reg = -1;
|
||||
ik->write_end = 0;
|
||||
ik->inlen = 0;
|
||||
SET_OBF();
|
||||
}
|
||||
break;
|
||||
|
||||
case IPMI_KCS_READ_STATE:
|
||||
handle_read:
|
||||
if (ik->outpos >= ik->outlen) {
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE);
|
||||
SET_OBF();
|
||||
} else if (ik->data_in_reg == IPMI_KCS_READ_CMD) {
|
||||
ik->data_out_reg = ik->outmsg[ik->outpos];
|
||||
ik->outpos++;
|
||||
SET_OBF();
|
||||
} else {
|
||||
ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
|
||||
ik->outlen = 1;
|
||||
ik->outpos = 0;
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
|
||||
SET_OBF();
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case IPMI_KCS_WRITE_STATE:
|
||||
if (ik->data_in_reg != -1) {
|
||||
/*
|
||||
* Don't worry about input overrun here, that will be
|
||||
* handled in the BMC.
|
||||
*/
|
||||
if (ik->inlen < sizeof(ik->inmsg)) {
|
||||
ik->inmsg[ik->inlen] = ik->data_in_reg;
|
||||
}
|
||||
ik->inlen++;
|
||||
}
|
||||
if (ik->write_end) {
|
||||
IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc);
|
||||
ik->outlen = 0;
|
||||
ik->write_end = 0;
|
||||
ik->outpos = 0;
|
||||
bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg),
|
||||
ik->waiting_rsp);
|
||||
goto out_noibf;
|
||||
} else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) {
|
||||
ik->cmd_reg = -1;
|
||||
ik->write_end = 1;
|
||||
}
|
||||
SET_OBF();
|
||||
break;
|
||||
|
||||
case IPMI_KCS_ERROR_STATE:
|
||||
if (ik->data_in_reg != -1) {
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
|
||||
ik->data_in_reg = IPMI_KCS_READ_CMD;
|
||||
goto handle_read;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (ik->cmd_reg != -1) {
|
||||
/* Got an invalid command */
|
||||
ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
|
||||
ik->outlen = 1;
|
||||
ik->outpos = 0;
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
|
||||
}
|
||||
|
||||
out:
|
||||
ik->cmd_reg = -1;
|
||||
ik->data_in_reg = -1;
|
||||
IPMI_KCS_SET_IBF(ik->status_reg, 0);
|
||||
out_noibf:
|
||||
return;
|
||||
}
|
||||
|
||||
static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
|
||||
unsigned char *rsp, unsigned int rsp_len)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
if (ik->waiting_rsp == msg_id) {
|
||||
ik->waiting_rsp++;
|
||||
if (rsp_len > sizeof(ik->outmsg)) {
|
||||
ik->outmsg[0] = rsp[0];
|
||||
ik->outmsg[1] = rsp[1];
|
||||
ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
|
||||
ik->outlen = 3;
|
||||
} else {
|
||||
memcpy(ik->outmsg, rsp, rsp_len);
|
||||
ik->outlen = rsp_len;
|
||||
}
|
||||
IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
|
||||
ik->data_in_reg = IPMI_KCS_READ_CMD;
|
||||
ipmi_kcs_signal(ik, ii);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPMIInterface *ii = opaque;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
uint32_t ret;
|
||||
|
||||
switch (addr & 1) {
|
||||
case 0:
|
||||
ret = ik->data_out_reg;
|
||||
IPMI_KCS_SET_OBF(ik->status_reg, 0);
|
||||
if (ik->obf_irq_set) {
|
||||
ik->obf_irq_set = 0;
|
||||
if (!ik->atn_irq_set) {
|
||||
qemu_irq_lower(ik->irq);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
ret = ik->status_reg;
|
||||
if (ik->atn_irq_set) {
|
||||
ik->atn_irq_set = 0;
|
||||
if (!ik->obf_irq_set) {
|
||||
qemu_irq_lower(ik->irq);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
IPMIInterface *ii = opaque;
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
if (IPMI_KCS_GET_IBF(ik->status_reg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr & 1) {
|
||||
case 0:
|
||||
ik->data_in_reg = val;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ik->cmd_reg = val;
|
||||
break;
|
||||
}
|
||||
IPMI_KCS_SET_IBF(ik->status_reg, 1);
|
||||
ipmi_kcs_signal(ik, ii);
|
||||
}
|
||||
|
||||
const MemoryRegionOps ipmi_kcs_io_ops = {
|
||||
.read = ipmi_kcs_ioport_read,
|
||||
.write = ipmi_kcs_ioport_write,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
IPMI_KCS_SET_SMS_ATN(ik->status_reg, val);
|
||||
if (val) {
|
||||
if (irq && !ik->atn_irq_set && ik->use_irq && ik->irqs_enabled) {
|
||||
ik->atn_irq_set = 1;
|
||||
if (!ik->obf_irq_set) {
|
||||
qemu_irq_raise(ik->irq);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ik->atn_irq_set) {
|
||||
ik->atn_irq_set = 0;
|
||||
if (!ik->obf_irq_set) {
|
||||
qemu_irq_lower(ik->irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
ik->irqs_enabled = val;
|
||||
}
|
||||
|
||||
static void ipmi_kcs_init(IPMIInterface *ii, Error **errp)
|
||||
{
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
IPMIKCS *ik = iic->get_backend_data(ii);
|
||||
|
||||
ik->io_length = 2;
|
||||
memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 2);
|
||||
}
|
||||
|
||||
static void ipmi_kcs_class_init(IPMIInterfaceClass *iic)
|
||||
{
|
||||
iic->init = ipmi_kcs_init;
|
||||
iic->set_atn = ipmi_kcs_set_atn;
|
||||
iic->handle_rsp = ipmi_kcs_handle_rsp;
|
||||
iic->handle_if_event = ipmi_kcs_handle_event;
|
||||
iic->set_irq_enable = ipmi_kcs_set_irq_enable;
|
||||
}
|
||||
|
||||
|
||||
#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs"
|
||||
#define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \
|
||||
TYPE_ISA_IPMI_KCS)
|
||||
|
||||
typedef struct ISAIPMIKCSDevice {
|
||||
ISADevice dev;
|
||||
int32 isairq;
|
||||
IPMIKCS kcs;
|
||||
IPMIFwInfo fwinfo;
|
||||
} ISAIPMIKCSDevice;
|
||||
|
||||
static void ipmi_isa_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ISADevice *isadev = ISA_DEVICE(dev);
|
||||
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(dev);
|
||||
IPMIInterface *ii = IPMI_INTERFACE(dev);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
|
||||
|
||||
if (!iik->kcs.bmc) {
|
||||
error_setg(errp, "IPMI device requires a bmc attribute to be set");
|
||||
return;
|
||||
}
|
||||
|
||||
iik->kcs.bmc->intf = ii;
|
||||
|
||||
iic->init(ii, errp);
|
||||
if (*errp)
|
||||
return;
|
||||
|
||||
if (iik->isairq > 0) {
|
||||
isa_init_irq(isadev, &iik->kcs.irq, iik->isairq);
|
||||
iik->kcs.use_irq = 1;
|
||||
}
|
||||
|
||||
qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length);
|
||||
|
||||
isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
|
||||
|
||||
iik->fwinfo.interface_name = "kcs";
|
||||
iik->fwinfo.interface_type = IPMI_SMBIOS_KCS;
|
||||
iik->fwinfo.ipmi_spec_major_revision = 2;
|
||||
iik->fwinfo.ipmi_spec_minor_revision = 0;
|
||||
iik->fwinfo.base_address = iik->kcs.io_base;
|
||||
iik->fwinfo.i2c_slave_address = iik->kcs.bmc->slave_addr;
|
||||
iik->fwinfo.register_length = iik->kcs.io_length;
|
||||
iik->fwinfo.register_spacing = 1;
|
||||
iik->fwinfo.memspace = IPMI_MEMSPACE_IO;
|
||||
iik->fwinfo.irq_type = IPMI_LEVEL_IRQ;
|
||||
iik->fwinfo.interrupt_number = iik->isairq;
|
||||
iik->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
|
||||
ipmi_add_fwinfo(&iik->fwinfo, errp);
|
||||
}
|
||||
|
||||
const VMStateDescription vmstate_ISAIPMIKCSDevice = {
|
||||
.name = TYPE_IPMI_INTERFACE,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice),
|
||||
VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice),
|
||||
VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice),
|
||||
VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice),
|
||||
VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice),
|
||||
VMSTATE_VBUFFER_UINT32(kcs.outmsg, ISAIPMIKCSDevice, 1, NULL, 0,
|
||||
kcs.outlen),
|
||||
VMSTATE_VBUFFER_UINT32(kcs.inmsg, ISAIPMIKCSDevice, 1, NULL, 0,
|
||||
kcs.inlen),
|
||||
VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice),
|
||||
VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice),
|
||||
VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice),
|
||||
VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice),
|
||||
VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice),
|
||||
VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void isa_ipmi_kcs_init(Object *obj)
|
||||
{
|
||||
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj);
|
||||
|
||||
ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc);
|
||||
|
||||
vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik);
|
||||
}
|
||||
|
||||
static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii)
|
||||
{
|
||||
ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);
|
||||
|
||||
return &iik->kcs;
|
||||
}
|
||||
|
||||
static Property ipmi_isa_properties[] = {
|
||||
DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base, 0xca2),
|
||||
DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
|
||||
|
||||
dc->realize = ipmi_isa_realize;
|
||||
dc->props = ipmi_isa_properties;
|
||||
|
||||
iic->get_backend_data = isa_ipmi_kcs_get_backend_data;
|
||||
ipmi_kcs_class_init(iic);
|
||||
}
|
||||
|
||||
static const TypeInfo isa_ipmi_kcs_info = {
|
||||
.name = TYPE_ISA_IPMI_KCS,
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(ISAIPMIKCSDevice),
|
||||
.instance_init = isa_ipmi_kcs_init,
|
||||
.class_init = isa_ipmi_kcs_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_IPMI_INTERFACE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
static void ipmi_register_types(void)
|
||||
{
|
||||
type_register_static(&isa_ipmi_kcs_info);
|
||||
}
|
||||
|
||||
type_init(ipmi_register_types)
|
||||
@@ -1 +1,2 @@
|
||||
common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o
|
||||
common-obj-$(CONFIG_NVDIMM) += nvdimm.o
|
||||
|
||||
46
hw/mem/nvdimm.c
Normal file
46
hw/mem/nvdimm.c
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Non-Volatile Dual In-line Memory Module Virtualization Implementation
|
||||
*
|
||||
* Copyright(C) 2015 Intel Corporation.
|
||||
*
|
||||
* Author:
|
||||
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
|
||||
*
|
||||
* Currently, it only supports PMEM Virtualization.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "hw/mem/nvdimm.h"
|
||||
|
||||
static void nvdimm_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
/* nvdimm hotplug has not been supported yet. */
|
||||
dc->hotpluggable = false;
|
||||
}
|
||||
|
||||
static TypeInfo nvdimm_info = {
|
||||
.name = TYPE_NVDIMM,
|
||||
.parent = TYPE_PC_DIMM,
|
||||
.class_init = nvdimm_class_init,
|
||||
};
|
||||
|
||||
static void nvdimm_register_types(void)
|
||||
{
|
||||
type_register_static(&nvdimm_info);
|
||||
}
|
||||
|
||||
type_init(nvdimm_register_types)
|
||||
@@ -81,6 +81,7 @@ petalogix_ml605_init(MachineState *machine)
|
||||
|
||||
/* init CPUs */
|
||||
cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
|
||||
object_property_set_str(OBJECT(cpu), "8.10.a", "version", &error_abort);
|
||||
/* Use FPU but don't use floating point conversion and square
|
||||
* root instructions
|
||||
*/
|
||||
|
||||
@@ -66,6 +66,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
|
||||
cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
|
||||
object_property_set_str(OBJECT(cpu), "7.10.d", "version", &error_abort);
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &error_abort);
|
||||
|
||||
/* Attach emulated BRAM through the LMB. */
|
||||
|
||||
@@ -26,6 +26,8 @@ obj-$(CONFIG_NSERIES) += cbus.o
|
||||
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
|
||||
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
|
||||
obj-$(CONFIG_IMX) += imx_ccm.o
|
||||
obj-$(CONFIG_IMX) += imx31_ccm.o
|
||||
obj-$(CONFIG_IMX) += imx25_ccm.o
|
||||
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
|
||||
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
|
||||
obj-$(CONFIG_MAINSTONE) += mst_fpga.o
|
||||
@@ -41,3 +43,4 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
|
||||
|
||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||
obj-$(CONFIG_EDU) += edu.o
|
||||
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
|
||||
|
||||
167
hw/misc/hyperv_testdev.c
Normal file
167
hw/misc/hyperv_testdev.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
|
||||
*
|
||||
* Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
|
||||
*
|
||||
* Authors:
|
||||
* Andrey Smetanin <asmetanin@virtuozzo.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/qdev.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "linux/kvm.h"
|
||||
#include "target-i386/hyperv.h"
|
||||
#include "kvm_i386.h"
|
||||
|
||||
#define HV_TEST_DEV_MAX_SINT_ROUTES 64
|
||||
|
||||
struct HypervTestDev {
|
||||
ISADevice parent_obj;
|
||||
MemoryRegion sint_control;
|
||||
HvSintRoute *sint_route[HV_TEST_DEV_MAX_SINT_ROUTES];
|
||||
};
|
||||
typedef struct HypervTestDev HypervTestDev;
|
||||
|
||||
#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
|
||||
#define HYPERV_TEST_DEV(obj) \
|
||||
OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
|
||||
|
||||
enum {
|
||||
HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
|
||||
HV_TEST_DEV_SINT_ROUTE_DESTROY,
|
||||
HV_TEST_DEV_SINT_ROUTE_SET_SINT
|
||||
};
|
||||
|
||||
static int alloc_sint_route_index(HypervTestDev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
|
||||
if (dev->sint_route[i] == NULL) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void free_sint_route_index(HypervTestDev *dev, int i)
|
||||
{
|
||||
assert(i >= 0 && i < ARRAY_SIZE(dev->sint_route));
|
||||
dev->sint_route[i] = NULL;
|
||||
}
|
||||
|
||||
static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id,
|
||||
uint32_t sint)
|
||||
{
|
||||
HvSintRoute *sint_route;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
|
||||
sint_route = dev->sint_route[i];
|
||||
if (sint_route && sint_route->vcpu_id == vcpu_id &&
|
||||
sint_route->sint == sint) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl,
|
||||
uint32_t vcpu_id, uint32_t sint)
|
||||
{
|
||||
int i;
|
||||
HvSintRoute *sint_route;
|
||||
|
||||
switch (ctl) {
|
||||
case HV_TEST_DEV_SINT_ROUTE_CREATE:
|
||||
i = alloc_sint_route_index(dev);
|
||||
assert(i >= 0);
|
||||
sint_route = kvm_hv_sint_route_create(vcpu_id, sint, NULL);
|
||||
assert(sint_route);
|
||||
dev->sint_route[i] = sint_route;
|
||||
break;
|
||||
case HV_TEST_DEV_SINT_ROUTE_DESTROY:
|
||||
i = find_sint_route_index(dev, vcpu_id, sint);
|
||||
assert(i >= 0);
|
||||
sint_route = dev->sint_route[i];
|
||||
kvm_hv_sint_route_destroy(sint_route);
|
||||
free_sint_route_index(dev, i);
|
||||
break;
|
||||
case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
|
||||
i = find_sint_route_index(dev, vcpu_id, sint);
|
||||
assert(i >= 0);
|
||||
sint_route = dev->sint_route[i];
|
||||
kvm_hv_sint_route_set_sint(sint_route);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data,
|
||||
uint32_t len)
|
||||
{
|
||||
HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
|
||||
uint8_t ctl;
|
||||
|
||||
ctl = (data >> 16ULL) & 0xFF;
|
||||
switch (ctl) {
|
||||
case HV_TEST_DEV_SINT_ROUTE_CREATE:
|
||||
case HV_TEST_DEV_SINT_ROUTE_DESTROY:
|
||||
case HV_TEST_DEV_SINT_ROUTE_SET_SINT: {
|
||||
uint8_t sint = data & 0xFF;
|
||||
uint8_t vcpu_id = (data >> 8ULL) & 0xFF;
|
||||
hv_synic_test_dev_control(dev, ctl, vcpu_id, sint);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps synic_test_sint_ops = {
|
||||
.write = hv_test_dev_control,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
};
|
||||
|
||||
static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
|
||||
{
|
||||
ISADevice *isa = ISA_DEVICE(d);
|
||||
HypervTestDev *dev = HYPERV_TEST_DEV(d);
|
||||
MemoryRegion *io = isa_address_space_io(isa);
|
||||
|
||||
memset(dev->sint_route, 0, sizeof(dev->sint_route));
|
||||
memory_region_init_io(&dev->sint_control, OBJECT(dev),
|
||||
&synic_test_sint_ops, dev,
|
||||
"hyperv-testdev-ctl", 4);
|
||||
memory_region_add_subregion(io, 0x3000, &dev->sint_control);
|
||||
}
|
||||
|
||||
static void hv_test_dev_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||
dc->realize = hv_test_dev_realizefn;
|
||||
}
|
||||
|
||||
static const TypeInfo hv_test_dev_info = {
|
||||
.name = TYPE_HYPERV_TEST_DEV,
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(HypervTestDev),
|
||||
.class_init = hv_test_dev_class_init,
|
||||
};
|
||||
|
||||
static void hv_test_dev_register_types(void)
|
||||
{
|
||||
type_register_static(&hv_test_dev_info);
|
||||
}
|
||||
type_init(hv_test_dev_register_types);
|
||||
341
hw/misc/imx25_ccm.c
Normal file
341
hw/misc/imx25_ccm.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* IMX25 Clock Control Module
|
||||
*
|
||||
* Copyright (C) 2012 NICTA
|
||||
* Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* To get the timer frequencies right, we need to emulate at least part of
|
||||
* the CCM.
|
||||
*/
|
||||
|
||||
#include "hw/misc/imx25_ccm.h"
|
||||
|
||||
#ifndef DEBUG_IMX25_CCM
|
||||
#define DEBUG_IMX25_CCM 0
|
||||
#endif
|
||||
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { \
|
||||
if (DEBUG_IMX25_CCM) { \
|
||||
fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \
|
||||
__func__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static char const *imx25_ccm_reg_name(uint32_t reg)
|
||||
{
|
||||
static char unknown[20];
|
||||
|
||||
switch (reg) {
|
||||
case IMX25_CCM_MPCTL_REG:
|
||||
return "mpctl";
|
||||
case IMX25_CCM_UPCTL_REG:
|
||||
return "upctl";
|
||||
case IMX25_CCM_CCTL_REG:
|
||||
return "cctl";
|
||||
case IMX25_CCM_CGCR0_REG:
|
||||
return "cgcr0";
|
||||
case IMX25_CCM_CGCR1_REG:
|
||||
return "cgcr1";
|
||||
case IMX25_CCM_CGCR2_REG:
|
||||
return "cgcr2";
|
||||
case IMX25_CCM_PCDR0_REG:
|
||||
return "pcdr0";
|
||||
case IMX25_CCM_PCDR1_REG:
|
||||
return "pcdr1";
|
||||
case IMX25_CCM_PCDR2_REG:
|
||||
return "pcdr2";
|
||||
case IMX25_CCM_PCDR3_REG:
|
||||
return "pcdr3";
|
||||
case IMX25_CCM_RCSR_REG:
|
||||
return "rcsr";
|
||||
case IMX25_CCM_CRDR_REG:
|
||||
return "crdr";
|
||||
case IMX25_CCM_DCVR0_REG:
|
||||
return "dcvr0";
|
||||
case IMX25_CCM_DCVR1_REG:
|
||||
return "dcvr1";
|
||||
case IMX25_CCM_DCVR2_REG:
|
||||
return "dcvr2";
|
||||
case IMX25_CCM_DCVR3_REG:
|
||||
return "dcvr3";
|
||||
case IMX25_CCM_LTR0_REG:
|
||||
return "ltr0";
|
||||
case IMX25_CCM_LTR1_REG:
|
||||
return "ltr1";
|
||||
case IMX25_CCM_LTR2_REG:
|
||||
return "ltr2";
|
||||
case IMX25_CCM_LTR3_REG:
|
||||
return "ltr3";
|
||||
case IMX25_CCM_LTBR0_REG:
|
||||
return "ltbr0";
|
||||
case IMX25_CCM_LTBR1_REG:
|
||||
return "ltbr1";
|
||||
case IMX25_CCM_PMCR0_REG:
|
||||
return "pmcr0";
|
||||
case IMX25_CCM_PMCR1_REG:
|
||||
return "pmcr1";
|
||||
case IMX25_CCM_PMCR2_REG:
|
||||
return "pmcr2";
|
||||
case IMX25_CCM_MCR_REG:
|
||||
return "mcr";
|
||||
case IMX25_CCM_LPIMR0_REG:
|
||||
return "lpimr0";
|
||||
case IMX25_CCM_LPIMR1_REG:
|
||||
return "lpimr1";
|
||||
default:
|
||||
sprintf(unknown, "[%d ?]", reg);
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
#define CKIH_FREQ 24000000 /* 24MHz crystal input */
|
||||
|
||||
static const VMStateDescription vmstate_imx25_ccm = {
|
||||
.name = TYPE_IMX25_CCM,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX25CCMState *s = IMX25_CCM(dev);
|
||||
|
||||
if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], MPLL_BYPASS)) {
|
||||
freq = CKIH_FREQ;
|
||||
} else {
|
||||
freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ);
|
||||
}
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx25_ccm_get_upll_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq = 0;
|
||||
IMX25CCMState *s = IMX25_CCM(dev);
|
||||
|
||||
if (!EXTRACT(s->reg[IMX25_CCM_CCTL_REG], UPLL_DIS)) {
|
||||
freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_UPCTL_REG], CKIH_FREQ);
|
||||
}
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX25CCMState *s = IMX25_CCM(dev);
|
||||
|
||||
freq = imx25_ccm_get_mpll_clk(dev);
|
||||
|
||||
if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_SRC)) {
|
||||
freq = (freq * 3 / 4);
|
||||
}
|
||||
|
||||
freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX25CCMState *s = IMX25_CCM(dev);
|
||||
|
||||
freq = imx25_ccm_get_mcu_clk(dev)
|
||||
/ (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
|
||||
freq = imx25_ccm_get_ahb_clk(dev) / 2;
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
|
||||
{
|
||||
uint32_t freq = 0;
|
||||
DPRINTF("Clock = %d)\n", clock);
|
||||
|
||||
switch (clock) {
|
||||
case NOCLK:
|
||||
break;
|
||||
case CLK_MPLL:
|
||||
freq = imx25_ccm_get_mpll_clk(dev);
|
||||
break;
|
||||
case CLK_UPLL:
|
||||
freq = imx25_ccm_get_upll_clk(dev);
|
||||
break;
|
||||
case CLK_MCU:
|
||||
freq = imx25_ccm_get_mcu_clk(dev);
|
||||
break;
|
||||
case CLK_AHB:
|
||||
freq = imx25_ccm_get_ahb_clk(dev);
|
||||
break;
|
||||
case CLK_IPG:
|
||||
freq = imx25_ccm_get_ipg_clk(dev);
|
||||
break;
|
||||
case CLK_32k:
|
||||
freq = CKIL_FREQ;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
|
||||
TYPE_IMX25_CCM, __func__, clock);
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("Clock = %d) = %d\n", clock, freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static void imx25_ccm_reset(DeviceState *dev)
|
||||
{
|
||||
IMX25CCMState *s = IMX25_CCM(dev);
|
||||
|
||||
DPRINTF("\n");
|
||||
|
||||
memset(s->reg, 0, IMX25_CCM_MAX_REG * sizeof(uint32_t));
|
||||
s->reg[IMX25_CCM_MPCTL_REG] = 0x800b2c01;
|
||||
s->reg[IMX25_CCM_UPCTL_REG] = 0x84042800;
|
||||
/*
|
||||
* The value below gives:
|
||||
* CPU = 133 MHz, AHB = 66,5 MHz, IPG = 33 MHz.
|
||||
*/
|
||||
s->reg[IMX25_CCM_CCTL_REG] = 0xd0030000;
|
||||
s->reg[IMX25_CCM_CGCR0_REG] = 0x028A0100;
|
||||
s->reg[IMX25_CCM_CGCR1_REG] = 0x04008100;
|
||||
s->reg[IMX25_CCM_CGCR2_REG] = 0x00000438;
|
||||
s->reg[IMX25_CCM_PCDR0_REG] = 0x01010101;
|
||||
s->reg[IMX25_CCM_PCDR1_REG] = 0x01010101;
|
||||
s->reg[IMX25_CCM_PCDR2_REG] = 0x01010101;
|
||||
s->reg[IMX25_CCM_PCDR3_REG] = 0x01010101;
|
||||
s->reg[IMX25_CCM_PMCR0_REG] = 0x00A00000;
|
||||
s->reg[IMX25_CCM_PMCR1_REG] = 0x0000A030;
|
||||
s->reg[IMX25_CCM_PMCR2_REG] = 0x0000A030;
|
||||
s->reg[IMX25_CCM_MCR_REG] = 0x43000000;
|
||||
|
||||
/*
|
||||
* default boot will change the reset values to allow:
|
||||
* CPU = 399 MHz, AHB = 133 MHz, IPG = 66,5 MHz.
|
||||
* For some reason, this doesn't work. With the value below, linux
|
||||
* detects a 88 MHz IPG CLK instead of 66,5 MHz.
|
||||
s->reg[IMX25_CCM_CCTL_REG] = 0x20032000;
|
||||
*/
|
||||
}
|
||||
|
||||
static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
uint32 value = 0;
|
||||
IMX25CCMState *s = (IMX25CCMState *)opaque;
|
||||
|
||||
if (offset < 0x70) {
|
||||
value = s->reg[offset >> 2];
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
||||
HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
|
||||
}
|
||||
|
||||
DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
|
||||
value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
IMX25CCMState *s = (IMX25CCMState *)opaque;
|
||||
|
||||
DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
|
||||
(uint32_t)value);
|
||||
|
||||
if (offset < 0x70) {
|
||||
/*
|
||||
* We will do a better implementation later. In particular some bits
|
||||
* cannot be written to.
|
||||
*/
|
||||
s->reg[offset >> 2] = value;
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
||||
HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct MemoryRegionOps imx25_ccm_ops = {
|
||||
.read = imx25_ccm_read,
|
||||
.write = imx25_ccm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
/*
|
||||
* Our device would not work correctly if the guest was doing
|
||||
* unaligned access. This might not be a limitation on the real
|
||||
* device but in practice there is no reason for a guest to access
|
||||
* this device unaligned.
|
||||
*/
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
.unaligned = false,
|
||||
},
|
||||
};
|
||||
|
||||
static void imx25_ccm_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
|
||||
IMX25CCMState *s = IMX25_CCM(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(dev), &imx25_ccm_ops, s,
|
||||
TYPE_IMX25_CCM, 0x1000);
|
||||
sysbus_init_mmio(sd, &s->iomem);
|
||||
}
|
||||
|
||||
static void imx25_ccm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
|
||||
|
||||
dc->reset = imx25_ccm_reset;
|
||||
dc->vmsd = &vmstate_imx25_ccm;
|
||||
dc->desc = "i.MX25 Clock Control Module";
|
||||
|
||||
ccm->get_clock_frequency = imx25_ccm_get_clock_frequency;
|
||||
}
|
||||
|
||||
static const TypeInfo imx25_ccm_info = {
|
||||
.name = TYPE_IMX25_CCM,
|
||||
.parent = TYPE_IMX_CCM,
|
||||
.instance_size = sizeof(IMX25CCMState),
|
||||
.instance_init = imx25_ccm_init,
|
||||
.class_init = imx25_ccm_class_init,
|
||||
};
|
||||
|
||||
static void imx25_ccm_register_types(void)
|
||||
{
|
||||
type_register_static(&imx25_ccm_info);
|
||||
}
|
||||
|
||||
type_init(imx25_ccm_register_types)
|
||||
392
hw/misc/imx31_ccm.c
Normal file
392
hw/misc/imx31_ccm.c
Normal file
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* IMX31 Clock Control Module
|
||||
*
|
||||
* Copyright (C) 2012 NICTA
|
||||
* Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* To get the timer frequencies right, we need to emulate at least part of
|
||||
* the i.MX31 CCM.
|
||||
*/
|
||||
|
||||
#include "hw/misc/imx31_ccm.h"
|
||||
|
||||
#define CKIH_FREQ 26000000 /* 26MHz crystal input */
|
||||
|
||||
#ifndef DEBUG_IMX31_CCM
|
||||
#define DEBUG_IMX31_CCM 0
|
||||
#endif
|
||||
|
||||
#define DPRINTF(fmt, args...) \
|
||||
do { \
|
||||
if (DEBUG_IMX31_CCM) { \
|
||||
fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX31_CCM, \
|
||||
__func__, ##args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static char const *imx31_ccm_reg_name(uint32_t reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case 0:
|
||||
return "CCMR";
|
||||
case 1:
|
||||
return "PDR0";
|
||||
case 2:
|
||||
return "PDR1";
|
||||
case 3:
|
||||
return "RCSR";
|
||||
case 4:
|
||||
return "MPCTL";
|
||||
case 5:
|
||||
return "UPCTL";
|
||||
case 6:
|
||||
return "SPCTL";
|
||||
case 7:
|
||||
return "COSR";
|
||||
case 8:
|
||||
return "CGR0";
|
||||
case 9:
|
||||
return "CGR1";
|
||||
case 10:
|
||||
return "CGR2";
|
||||
case 11:
|
||||
return "WIMR";
|
||||
case 12:
|
||||
return "LDC";
|
||||
case 13:
|
||||
return "DCVR0";
|
||||
case 14:
|
||||
return "DCVR1";
|
||||
case 15:
|
||||
return "DCVR2";
|
||||
case 16:
|
||||
return "DCVR3";
|
||||
case 17:
|
||||
return "LTR0";
|
||||
case 18:
|
||||
return "LTR1";
|
||||
case 19:
|
||||
return "LTR2";
|
||||
case 20:
|
||||
return "LTR3";
|
||||
case 21:
|
||||
return "LTBR0";
|
||||
case 22:
|
||||
return "LTBR1";
|
||||
case 23:
|
||||
return "PMCR0";
|
||||
case 24:
|
||||
return "PMCR1";
|
||||
case 25:
|
||||
return "PDR2";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_imx31_ccm = {
|
||||
.name = TYPE_IMX31_CCM,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(ccmr, IMX31CCMState),
|
||||
VMSTATE_UINT32(pdr0, IMX31CCMState),
|
||||
VMSTATE_UINT32(pdr1, IMX31CCMState),
|
||||
VMSTATE_UINT32(mpctl, IMX31CCMState),
|
||||
VMSTATE_UINT32(spctl, IMX31CCMState),
|
||||
VMSTATE_UINT32_ARRAY(cgr, IMX31CCMState, 3),
|
||||
VMSTATE_UINT32(pmcr0, IMX31CCMState),
|
||||
VMSTATE_UINT32(pmcr1, IMX31CCMState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq = 0;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
if ((s->ccmr & CCMR_PRCS) == 2) {
|
||||
if (s->ccmr & CCMR_FPME) {
|
||||
freq = CKIL_FREQ;
|
||||
if (s->ccmr & CCMR_FPMF) {
|
||||
freq *= 1024;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
freq = CKIH_FREQ;
|
||||
}
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
freq = imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_pll_ref_clk(dev));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
|
||||
freq = imx31_ccm_get_pll_ref_clk(dev);
|
||||
} else {
|
||||
freq = imx31_ccm_get_mpll_clk(dev);
|
||||
}
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MCU));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, HSP));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MAX));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
|
||||
{
|
||||
uint32_t freq;
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
freq = imx31_ccm_get_hclk_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
|
||||
|
||||
DPRINTF("freq = %d\n", freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
|
||||
{
|
||||
uint32_t freq = 0;
|
||||
|
||||
switch (clock) {
|
||||
case NOCLK:
|
||||
break;
|
||||
case CLK_MCU:
|
||||
freq = imx31_ccm_get_mcu_clk(dev);
|
||||
break;
|
||||
case CLK_HSP:
|
||||
freq = imx31_ccm_get_hsp_clk(dev);
|
||||
break;
|
||||
case CLK_IPG:
|
||||
freq = imx31_ccm_get_ipg_clk(dev);
|
||||
break;
|
||||
case CLK_32k:
|
||||
freq = CKIL_FREQ;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
|
||||
TYPE_IMX31_CCM, __func__, clock);
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("Clock = %d) = %d\n", clock, freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
static void imx31_ccm_reset(DeviceState *dev)
|
||||
{
|
||||
IMX31CCMState *s = IMX31_CCM(dev);
|
||||
|
||||
DPRINTF("()\n");
|
||||
|
||||
s->ccmr = 0x074b0b7d;
|
||||
s->pdr0 = 0xff870b48;
|
||||
s->pdr1 = 0x49fcfe7f;
|
||||
s->mpctl = 0x04001800;
|
||||
s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
|
||||
s->spctl = 0x04043001;
|
||||
s->pmcr0 = 0x80209828;
|
||||
s->pmcr1 = 0x00aa0000;
|
||||
}
|
||||
|
||||
static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
uint32 value = 0;
|
||||
IMX31CCMState *s = (IMX31CCMState *)opaque;
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 0: /* CCMR */
|
||||
value = s->ccmr;
|
||||
break;
|
||||
case 1:
|
||||
value = s->pdr0;
|
||||
break;
|
||||
case 2:
|
||||
value = s->pdr1;
|
||||
break;
|
||||
case 4:
|
||||
value = s->mpctl;
|
||||
break;
|
||||
case 6:
|
||||
value = s->spctl;
|
||||
break;
|
||||
case 8:
|
||||
value = s->cgr[0];
|
||||
break;
|
||||
case 9:
|
||||
value = s->cgr[1];
|
||||
break;
|
||||
case 10:
|
||||
value = s->cgr[2];
|
||||
break;
|
||||
case 18: /* LTR1 */
|
||||
value = 0x00004040;
|
||||
break;
|
||||
case 23:
|
||||
value = s->pmcr0;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
||||
HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
|
||||
value);
|
||||
|
||||
return (uint64_t)value;
|
||||
}
|
||||
|
||||
static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
IMX31CCMState *s = (IMX31CCMState *)opaque;
|
||||
|
||||
DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
|
||||
(uint32_t)value);
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 0:
|
||||
s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
|
||||
break;
|
||||
case 1:
|
||||
s->pdr0 = value & 0xff9f3fff;
|
||||
break;
|
||||
case 2:
|
||||
s->pdr1 = value;
|
||||
break;
|
||||
case 4:
|
||||
s->mpctl = value & 0xbfff3fff;
|
||||
break;
|
||||
case 6:
|
||||
s->spctl = value & 0xbfff3fff;
|
||||
break;
|
||||
case 8:
|
||||
s->cgr[0] = value;
|
||||
break;
|
||||
case 9:
|
||||
s->cgr[1] = value;
|
||||
break;
|
||||
case 10:
|
||||
s->cgr[2] = value;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
||||
HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct MemoryRegionOps imx31_ccm_ops = {
|
||||
.read = imx31_ccm_read,
|
||||
.write = imx31_ccm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
/*
|
||||
* Our device would not work correctly if the guest was doing
|
||||
* unaligned access. This might not be a limitation on the real
|
||||
* device but in practice there is no reason for a guest to access
|
||||
* this device unaligned.
|
||||
*/
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
.unaligned = false,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static void imx31_ccm_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
|
||||
IMX31CCMState *s = IMX31_CCM(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(dev), &imx31_ccm_ops, s,
|
||||
TYPE_IMX31_CCM, 0x1000);
|
||||
sysbus_init_mmio(sd, &s->iomem);
|
||||
}
|
||||
|
||||
static void imx31_ccm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
|
||||
|
||||
dc->reset = imx31_ccm_reset;
|
||||
dc->vmsd = &vmstate_imx31_ccm;
|
||||
dc->desc = "i.MX31 Clock Control Module";
|
||||
|
||||
ccm->get_clock_frequency = imx31_ccm_get_clock_frequency;
|
||||
}
|
||||
|
||||
static const TypeInfo imx31_ccm_info = {
|
||||
.name = TYPE_IMX31_CCM,
|
||||
.parent = TYPE_IMX_CCM,
|
||||
.instance_size = sizeof(IMX31CCMState),
|
||||
.instance_init = imx31_ccm_init,
|
||||
.class_init = imx31_ccm_class_init,
|
||||
};
|
||||
|
||||
static void imx31_ccm_register_types(void)
|
||||
{
|
||||
type_register_static(&imx31_ccm_info);
|
||||
}
|
||||
|
||||
type_init(imx31_ccm_register_types)
|
||||
@@ -7,15 +7,12 @@
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
* To get the timer frequencies right, we need to emulate at least part of
|
||||
* the CCM.
|
||||
* This is an abstract base class used to get a common interface to
|
||||
* retrieve the CCM frequencies from the various i.MX SOC.
|
||||
*/
|
||||
|
||||
#include "hw/misc/imx_ccm.h"
|
||||
|
||||
#define CKIH_FREQ 26000000 /* 26MHz crystal input */
|
||||
#define CKIL_FREQ 32768 /* nominal 32khz clock */
|
||||
|
||||
#ifndef DEBUG_IMX_CCM
|
||||
#define DEBUG_IMX_CCM 0
|
||||
#endif
|
||||
@@ -28,51 +25,27 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int imx_ccm_post_load(void *opaque, int version_id);
|
||||
|
||||
static const VMStateDescription vmstate_imx_ccm = {
|
||||
.name = TYPE_IMX_CCM,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(ccmr, IMXCCMState),
|
||||
VMSTATE_UINT32(pdr0, IMXCCMState),
|
||||
VMSTATE_UINT32(pdr1, IMXCCMState),
|
||||
VMSTATE_UINT32(mpctl, IMXCCMState),
|
||||
VMSTATE_UINT32(spctl, IMXCCMState),
|
||||
VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3),
|
||||
VMSTATE_UINT32(pmcr0, IMXCCMState),
|
||||
VMSTATE_UINT32(pmcr1, IMXCCMState),
|
||||
VMSTATE_UINT32(pll_refclk_freq, IMXCCMState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.post_load = imx_ccm_post_load,
|
||||
};
|
||||
|
||||
uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock)
|
||||
uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
|
||||
{
|
||||
IMXCCMState *s = IMX_CCM(dev);
|
||||
uint32_t freq = 0;
|
||||
IMXCCMClass *klass = IMX_GET_CLASS(dev);
|
||||
|
||||
switch (clock) {
|
||||
case NOCLK:
|
||||
return 0;
|
||||
case MCU:
|
||||
return s->mcu_clk_freq;
|
||||
case HSP:
|
||||
return s->hsp_clk_freq;
|
||||
case IPG:
|
||||
return s->ipg_clk_freq;
|
||||
case CLK_32k:
|
||||
return CKIL_FREQ;
|
||||
if (klass->get_clock_frequency) {
|
||||
freq = klass->get_clock_frequency(dev, clock);
|
||||
}
|
||||
return 0;
|
||||
|
||||
DPRINTF("(clock = %d) = %d\n", clock, freq);
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate PLL output frequency
|
||||
*/
|
||||
static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
|
||||
uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq)
|
||||
{
|
||||
int32_t freq;
|
||||
int32_t mfn = MFN(pllreg); /* Numerator */
|
||||
uint32_t mfi = MFI(pllreg); /* Integer part */
|
||||
uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
|
||||
@@ -81,186 +54,26 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
|
||||
if (mfi < 5) {
|
||||
mfi = 5;
|
||||
}
|
||||
|
||||
/* mfn is 10-bit signed twos-complement */
|
||||
mfn <<= 32 - 10;
|
||||
mfn >>= 32 - 10;
|
||||
|
||||
return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
|
||||
freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
|
||||
(mfd * pd)) << 10;
|
||||
}
|
||||
|
||||
static void update_clocks(IMXCCMState *s)
|
||||
{
|
||||
/*
|
||||
* If we ever emulate more clocks, this should switch to a data-driven
|
||||
* approach
|
||||
*/
|
||||
DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
|
||||
freq);
|
||||
|
||||
if ((s->ccmr & CCMR_PRCS) == 2) {
|
||||
s->pll_refclk_freq = CKIL_FREQ * 1024;
|
||||
} else {
|
||||
s->pll_refclk_freq = CKIH_FREQ;
|
||||
}
|
||||
|
||||
/* ipg_clk_arm aka MCU clock */
|
||||
if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
|
||||
s->mcu_clk_freq = s->pll_refclk_freq;
|
||||
} else {
|
||||
s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq);
|
||||
}
|
||||
|
||||
/* High-speed clock */
|
||||
s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
|
||||
s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
|
||||
|
||||
DPRINTF("mcu %uMHz, HSP %uMHz, IPG %uHz\n",
|
||||
s->mcu_clk_freq / 1000000,
|
||||
s->hsp_clk_freq / 1000000,
|
||||
s->ipg_clk_freq);
|
||||
}
|
||||
|
||||
static void imx_ccm_reset(DeviceState *dev)
|
||||
{
|
||||
IMXCCMState *s = IMX_CCM(dev);
|
||||
|
||||
s->ccmr = 0x074b0b7b;
|
||||
s->pdr0 = 0xff870b48;
|
||||
s->pdr1 = 0x49fcfe7f;
|
||||
s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
|
||||
s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
|
||||
s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1);
|
||||
s->pmcr0 = 0x80209828;
|
||||
|
||||
update_clocks(s);
|
||||
}
|
||||
|
||||
static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
|
||||
unsigned size)
|
||||
{
|
||||
IMXCCMState *s = (IMXCCMState *)opaque;
|
||||
|
||||
DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset);
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 0: /* CCMR */
|
||||
DPRINTF(" ccmr = 0x%x\n", s->ccmr);
|
||||
return s->ccmr;
|
||||
case 1:
|
||||
DPRINTF(" pdr0 = 0x%x\n", s->pdr0);
|
||||
return s->pdr0;
|
||||
case 2:
|
||||
DPRINTF(" pdr1 = 0x%x\n", s->pdr1);
|
||||
return s->pdr1;
|
||||
case 4:
|
||||
DPRINTF(" mpctl = 0x%x\n", s->mpctl);
|
||||
return s->mpctl;
|
||||
case 6:
|
||||
DPRINTF(" spctl = 0x%x\n", s->spctl);
|
||||
return s->spctl;
|
||||
case 8:
|
||||
DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]);
|
||||
return s->cgr[0];
|
||||
case 9:
|
||||
DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]);
|
||||
return s->cgr[1];
|
||||
case 10:
|
||||
DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]);
|
||||
return s->cgr[2];
|
||||
case 18: /* LTR1 */
|
||||
return 0x00004040;
|
||||
case 23:
|
||||
DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
|
||||
return s->pmcr0;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
||||
HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_ccm_write(void *opaque, hwaddr offset,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
IMXCCMState *s = (IMXCCMState *)opaque;
|
||||
|
||||
DPRINTF("(offset=0x%" HWADDR_PRIx ", value = 0x%x)\n",
|
||||
offset, (unsigned int)value);
|
||||
|
||||
switch (offset >> 2) {
|
||||
case 0:
|
||||
s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
|
||||
break;
|
||||
case 1:
|
||||
s->pdr0 = value & 0xff9f3fff;
|
||||
break;
|
||||
case 2:
|
||||
s->pdr1 = value;
|
||||
break;
|
||||
case 4:
|
||||
s->mpctl = value & 0xbfff3fff;
|
||||
break;
|
||||
case 6:
|
||||
s->spctl = value & 0xbfff3fff;
|
||||
break;
|
||||
case 8:
|
||||
s->cgr[0] = value;
|
||||
return;
|
||||
case 9:
|
||||
s->cgr[1] = value;
|
||||
return;
|
||||
case 10:
|
||||
s->cgr[2] = value;
|
||||
return;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
|
||||
HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
|
||||
return;
|
||||
}
|
||||
update_clocks(s);
|
||||
}
|
||||
|
||||
static const struct MemoryRegionOps imx_ccm_ops = {
|
||||
.read = imx_ccm_read,
|
||||
.write = imx_ccm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static int imx_ccm_init(SysBusDevice *dev)
|
||||
{
|
||||
IMXCCMState *s = IMX_CCM(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s,
|
||||
TYPE_IMX_CCM, 0x1000);
|
||||
sysbus_init_mmio(dev, &s->iomem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_ccm_post_load(void *opaque, int version_id)
|
||||
{
|
||||
IMXCCMState *s = (IMXCCMState *)opaque;
|
||||
|
||||
update_clocks(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void imx_ccm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
|
||||
|
||||
sbc->init = imx_ccm_init;
|
||||
dc->reset = imx_ccm_reset;
|
||||
dc->vmsd = &vmstate_imx_ccm;
|
||||
dc->desc = "i.MX Clock Control Module";
|
||||
return freq;
|
||||
}
|
||||
|
||||
static const TypeInfo imx_ccm_info = {
|
||||
.name = TYPE_IMX_CCM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.name = TYPE_IMX_CCM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IMXCCMState),
|
||||
.class_init = imx_ccm_class_init,
|
||||
.class_size = sizeof(IMXCCMClass),
|
||||
.abstract = true,
|
||||
};
|
||||
|
||||
static void imx_ccm_register_types(void)
|
||||
|
||||
@@ -252,7 +252,8 @@ static void fw_cfg_write(FWCfgState *s, uint8_t value)
|
||||
|
||||
static int fw_cfg_select(FWCfgState *s, uint16_t key)
|
||||
{
|
||||
int ret;
|
||||
int arch, ret;
|
||||
FWCfgEntry *e;
|
||||
|
||||
s->cur_offset = 0;
|
||||
if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
|
||||
@@ -261,41 +262,45 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key)
|
||||
} else {
|
||||
s->cur_entry = key;
|
||||
ret = 1;
|
||||
/* entry successfully selected, now run callback if present */
|
||||
arch = !!(key & FW_CFG_ARCH_LOCAL);
|
||||
e = &s->entries[arch][key & FW_CFG_ENTRY_MASK];
|
||||
if (e->read_callback) {
|
||||
e->read_callback(e->callback_opaque);
|
||||
}
|
||||
}
|
||||
|
||||
trace_fw_cfg_select(s, key, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint8_t fw_cfg_read(FWCfgState *s)
|
||||
{
|
||||
int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
|
||||
FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
|
||||
uint8_t ret;
|
||||
|
||||
if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
|
||||
ret = 0;
|
||||
else {
|
||||
if (e->read_callback) {
|
||||
e->read_callback(e->callback_opaque, s->cur_offset);
|
||||
}
|
||||
ret = e->data[s->cur_offset++];
|
||||
}
|
||||
|
||||
trace_fw_cfg_read(s, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
static uint64_t fw_cfg_data_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
FWCfgState *s = opaque;
|
||||
int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
|
||||
FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
|
||||
&s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
|
||||
uint64_t value = 0;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
value = (value << 8) | fw_cfg_read(s);
|
||||
assert(size > 0 && size <= sizeof(value));
|
||||
if (s->cur_entry != FW_CFG_INVALID && e->data && s->cur_offset < e->len) {
|
||||
/* The least significant 'size' bytes of the return value are
|
||||
* expected to contain a string preserving portion of the item
|
||||
* data, padded with zeros on the right in case we run out early.
|
||||
* In technical terms, we're composing the host-endian representation
|
||||
* of the big endian interpretation of the fw_cfg string.
|
||||
*/
|
||||
do {
|
||||
value = (value << 8) | e->data[s->cur_offset++];
|
||||
} while (--size && s->cur_offset < e->len);
|
||||
/* If size is still not zero, we *did* run out early, so continue
|
||||
* left-shifting, to add the appropriate number of padding zeros
|
||||
* on the right.
|
||||
*/
|
||||
value <<= 8 * size;
|
||||
}
|
||||
|
||||
trace_fw_cfg_read(s, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -338,7 +343,8 @@ static void fw_cfg_dma_transfer(FWCfgState *s)
|
||||
}
|
||||
|
||||
arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
|
||||
e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
|
||||
e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
|
||||
&s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
|
||||
|
||||
if (dma.control & FW_CFG_DMA_CTL_READ) {
|
||||
read = 1;
|
||||
@@ -371,10 +377,6 @@ static void fw_cfg_dma_transfer(FWCfgState *s)
|
||||
len = (e->len - s->cur_offset);
|
||||
}
|
||||
|
||||
if (e->read_callback) {
|
||||
e->read_callback(e->callback_opaque, s->cur_offset);
|
||||
}
|
||||
|
||||
/* If the access is not a read access, it will be a skip access,
|
||||
* tested before.
|
||||
*/
|
||||
@@ -451,12 +453,6 @@ static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr,
|
||||
return is_write && size == 2;
|
||||
}
|
||||
|
||||
static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
return fw_cfg_read(opaque);
|
||||
}
|
||||
|
||||
static void fw_cfg_comb_write(void *opaque, hwaddr addr,
|
||||
uint64_t value, unsigned size)
|
||||
{
|
||||
@@ -483,7 +479,7 @@ static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
|
||||
};
|
||||
|
||||
static const MemoryRegionOps fw_cfg_data_mem_ops = {
|
||||
.read = fw_cfg_data_mem_read,
|
||||
.read = fw_cfg_data_read,
|
||||
.write = fw_cfg_data_mem_write,
|
||||
.endianness = DEVICE_BIG_ENDIAN,
|
||||
.valid = {
|
||||
@@ -494,7 +490,7 @@ static const MemoryRegionOps fw_cfg_data_mem_ops = {
|
||||
};
|
||||
|
||||
static const MemoryRegionOps fw_cfg_comb_mem_ops = {
|
||||
.read = fw_cfg_comb_read,
|
||||
.read = fw_cfg_data_read,
|
||||
.write = fw_cfg_comb_write,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.valid.accepts = fw_cfg_comb_valid,
|
||||
@@ -513,7 +509,8 @@ static void fw_cfg_reset(DeviceState *d)
|
||||
{
|
||||
FWCfgState *s = FW_CFG(d);
|
||||
|
||||
fw_cfg_select(s, 0);
|
||||
/* we never register a read callback for FW_CFG_SIGNATURE */
|
||||
fw_cfg_select(s, FW_CFG_SIGNATURE);
|
||||
}
|
||||
|
||||
/* Save restore 32 bit int as uint16_t
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
#define TYPE_PXB_BUS "pxb-bus"
|
||||
#define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS)
|
||||
|
||||
#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus"
|
||||
#define PXB_PCIE_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_PCIE_BUS)
|
||||
|
||||
typedef struct PXBBus {
|
||||
/*< private >*/
|
||||
PCIBus parent_obj;
|
||||
@@ -34,6 +37,9 @@ typedef struct PXBBus {
|
||||
#define TYPE_PXB_DEVICE "pxb"
|
||||
#define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE)
|
||||
|
||||
#define TYPE_PXB_PCIE_DEVICE "pxb-pcie"
|
||||
#define PXB_PCIE_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_PCIE_DEVICE)
|
||||
|
||||
typedef struct PXBDev {
|
||||
/*< private >*/
|
||||
PCIDevice parent_obj;
|
||||
@@ -43,13 +49,18 @@ typedef struct PXBDev {
|
||||
uint16_t numa_node;
|
||||
} PXBDev;
|
||||
|
||||
static PXBDev *convert_to_pxb(PCIDevice *dev)
|
||||
{
|
||||
return pci_bus_is_express(dev->bus) ? PXB_PCIE_DEV(dev) : PXB_DEV(dev);
|
||||
}
|
||||
|
||||
static GList *pxb_dev_list;
|
||||
|
||||
#define TYPE_PXB_HOST "pxb-host"
|
||||
|
||||
static int pxb_bus_num(PCIBus *bus)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(bus->parent_dev);
|
||||
PXBDev *pxb = convert_to_pxb(bus->parent_dev);
|
||||
|
||||
return pxb->bus_nr;
|
||||
}
|
||||
@@ -61,7 +72,7 @@ static bool pxb_is_root(PCIBus *bus)
|
||||
|
||||
static uint16_t pxb_bus_numa_node(PCIBus *bus)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(bus->parent_dev);
|
||||
PXBDev *pxb = convert_to_pxb(bus->parent_dev);
|
||||
|
||||
return pxb->numa_node;
|
||||
}
|
||||
@@ -82,10 +93,18 @@ static const TypeInfo pxb_bus_info = {
|
||||
.class_init = pxb_bus_class_init,
|
||||
};
|
||||
|
||||
static const TypeInfo pxb_pcie_bus_info = {
|
||||
.name = TYPE_PXB_PCIE_BUS,
|
||||
.parent = TYPE_PCIE_BUS,
|
||||
.instance_size = sizeof(PXBBus),
|
||||
.class_init = pxb_bus_class_init,
|
||||
};
|
||||
|
||||
static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
|
||||
PCIBus *rootbus)
|
||||
{
|
||||
PXBBus *bus = PXB_BUS(rootbus);
|
||||
PXBBus *bus = pci_bus_is_express(rootbus) ?
|
||||
PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus);
|
||||
|
||||
snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
|
||||
return bus->bus_path;
|
||||
@@ -103,7 +122,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev)
|
||||
|
||||
pxb_host = PCI_HOST_BRIDGE(dev);
|
||||
pxb_bus = pxb_host->bus;
|
||||
pxb_dev = PXB_DEV(pxb_bus->parent_dev);
|
||||
pxb_dev = convert_to_pxb(pxb_bus->parent_dev);
|
||||
position = g_list_index(pxb_dev_list, pxb_dev);
|
||||
assert(position >= 0);
|
||||
|
||||
@@ -193,10 +212,10 @@ static gint pxb_compare(gconstpointer a, gconstpointer b)
|
||||
0;
|
||||
}
|
||||
|
||||
static int pxb_dev_initfn(PCIDevice *dev)
|
||||
static int pxb_dev_init_common(PCIDevice *dev, bool pcie)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(dev);
|
||||
DeviceState *ds, *bds;
|
||||
PXBDev *pxb = convert_to_pxb(dev);
|
||||
DeviceState *ds, *bds = NULL;
|
||||
PCIBus *bus;
|
||||
const char *dev_name = NULL;
|
||||
|
||||
@@ -211,18 +230,21 @@ static int pxb_dev_initfn(PCIDevice *dev)
|
||||
}
|
||||
|
||||
ds = qdev_create(NULL, TYPE_PXB_HOST);
|
||||
bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
|
||||
if (pcie) {
|
||||
bus = pci_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS);
|
||||
} else {
|
||||
bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
|
||||
bds = qdev_create(BUS(bus), "pci-bridge");
|
||||
bds->id = dev_name;
|
||||
qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
|
||||
qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
|
||||
}
|
||||
|
||||
bus->parent_dev = dev;
|
||||
bus->address_space_mem = dev->bus->address_space_mem;
|
||||
bus->address_space_io = dev->bus->address_space_io;
|
||||
bus->map_irq = pxb_map_irq_fn;
|
||||
|
||||
bds = qdev_create(BUS(bus), "pci-bridge");
|
||||
bds->id = dev_name;
|
||||
qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
|
||||
qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
|
||||
|
||||
PCI_HOST_BRIDGE(ds)->bus = bus;
|
||||
|
||||
if (pxb_register_bus(dev, bus)) {
|
||||
@@ -230,7 +252,9 @@ static int pxb_dev_initfn(PCIDevice *dev)
|
||||
}
|
||||
|
||||
qdev_init_nofail(ds);
|
||||
qdev_init_nofail(bds);
|
||||
if (bds) {
|
||||
qdev_init_nofail(bds);
|
||||
}
|
||||
|
||||
pci_word_test_and_set_mask(dev->config + PCI_STATUS,
|
||||
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
|
||||
@@ -240,9 +264,19 @@ static int pxb_dev_initfn(PCIDevice *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pxb_dev_initfn(PCIDevice *dev)
|
||||
{
|
||||
if (pci_bus_is_express(dev->bus)) {
|
||||
error_report("pxb devices cannot reside on a PCIe bus!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return pxb_dev_init_common(dev, false);
|
||||
}
|
||||
|
||||
static void pxb_dev_exitfn(PCIDevice *pci_dev)
|
||||
{
|
||||
PXBDev *pxb = PXB_DEV(pci_dev);
|
||||
PXBDev *pxb = convert_to_pxb(pci_dev);
|
||||
|
||||
pxb_dev_list = g_list_remove(pxb_dev_list, pxb);
|
||||
}
|
||||
@@ -276,11 +310,45 @@ static const TypeInfo pxb_dev_info = {
|
||||
.class_init = pxb_dev_class_init,
|
||||
};
|
||||
|
||||
static int pxb_pcie_dev_initfn(PCIDevice *dev)
|
||||
{
|
||||
if (!pci_bus_is_express(dev->bus)) {
|
||||
error_report("pxb-pcie devices cannot reside on a PCI bus!");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return pxb_dev_init_common(dev, true);
|
||||
}
|
||||
|
||||
static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->init = pxb_pcie_dev_initfn;
|
||||
k->exit = pxb_dev_exitfn;
|
||||
k->vendor_id = PCI_VENDOR_ID_REDHAT;
|
||||
k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE;
|
||||
k->class_id = PCI_CLASS_BRIDGE_HOST;
|
||||
|
||||
dc->desc = "PCI Express Expander Bridge";
|
||||
dc->props = pxb_dev_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo pxb_pcie_dev_info = {
|
||||
.name = TYPE_PXB_PCIE_DEVICE,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(PXBDev),
|
||||
.class_init = pxb_pcie_dev_class_init,
|
||||
};
|
||||
|
||||
static void pxb_register_types(void)
|
||||
{
|
||||
type_register_static(&pxb_bus_info);
|
||||
type_register_static(&pxb_pcie_bus_info);
|
||||
type_register_static(&pxb_host_info);
|
||||
type_register_static(&pxb_dev_info);
|
||||
type_register_static(&pxb_pcie_dev_info);
|
||||
}
|
||||
|
||||
type_init(pxb_register_types)
|
||||
|
||||
@@ -1759,9 +1759,6 @@ void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier)
|
||||
if (notifier) {
|
||||
notifier_list_add(&req->cancel_notifiers, notifier);
|
||||
}
|
||||
if (req->io_canceled) {
|
||||
return;
|
||||
}
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
req->io_canceled = true;
|
||||
@@ -1841,11 +1838,13 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
|
||||
{
|
||||
SCSIRequest *req;
|
||||
|
||||
aio_context_acquire(blk_get_aio_context(sdev->conf.blk));
|
||||
while (!QTAILQ_EMPTY(&sdev->requests)) {
|
||||
req = QTAILQ_FIRST(&sdev->requests);
|
||||
scsi_req_cancel(req);
|
||||
scsi_req_cancel_async(req, NULL);
|
||||
}
|
||||
|
||||
blk_drain(sdev->conf.blk);
|
||||
aio_context_release(blk_get_aio_context(sdev->conf.blk));
|
||||
scsi_device_set_ua(sdev, sense);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
#define PVSCSI_MSI_OFFSET (0x50)
|
||||
#define PVSCSI_USE_64BIT (true)
|
||||
#define PVSCSI_PER_VECTOR_MASK (false)
|
||||
|
||||
@@ -49,9 +48,33 @@
|
||||
(stl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \
|
||||
(m)->rs_pa + offsetof(struct PVSCSIRingsState, field), val))
|
||||
|
||||
typedef struct PVSCSIClass {
|
||||
PCIDeviceClass parent_class;
|
||||
DeviceRealize parent_dc_realize;
|
||||
} PVSCSIClass;
|
||||
|
||||
#define TYPE_PVSCSI "pvscsi"
|
||||
#define PVSCSI(obj) OBJECT_CHECK(PVSCSIState, (obj), TYPE_PVSCSI)
|
||||
|
||||
#define PVSCSI_DEVICE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(PVSCSIClass, (klass), TYPE_PVSCSI)
|
||||
#define PVSCSI_DEVICE_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(PVSCSIClass, (obj), TYPE_PVSCSI)
|
||||
|
||||
/* Compatability flags for migration */
|
||||
#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0
|
||||
#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \
|
||||
(1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT)
|
||||
#define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1
|
||||
#define PVSCSI_COMPAT_DISABLE_PCIE \
|
||||
(1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT)
|
||||
|
||||
#define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \
|
||||
((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION)
|
||||
#define PVSCSI_MSI_OFFSET(s) \
|
||||
(PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c)
|
||||
#define PVSCSI_EXP_EP_OFFSET (0x40)
|
||||
|
||||
typedef struct PVSCSIRingInfo {
|
||||
uint64_t rs_pa;
|
||||
uint32_t txr_len_mask;
|
||||
@@ -100,6 +123,8 @@ typedef struct {
|
||||
|
||||
PVSCSIRingInfo rings; /* Data transfer rings manager */
|
||||
uint32_t resetting; /* Reset in progress */
|
||||
|
||||
uint32_t compat_flags;
|
||||
} PVSCSIState;
|
||||
|
||||
typedef struct PVSCSIRequest {
|
||||
@@ -1019,7 +1044,7 @@ pvscsi_init_msi(PVSCSIState *s)
|
||||
int res;
|
||||
PCIDevice *d = PCI_DEVICE(s);
|
||||
|
||||
res = msi_init(d, PVSCSI_MSI_OFFSET, PVSCSI_MSIX_NUM_VECTORS,
|
||||
res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS,
|
||||
PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK);
|
||||
if (res < 0) {
|
||||
trace_pvscsi_init_msi_fail(res);
|
||||
@@ -1069,9 +1094,16 @@ pvscsi_init(PCIDevice *pci_dev)
|
||||
|
||||
trace_pvscsi_state("init");
|
||||
|
||||
/* PCI subsystem ID */
|
||||
pci_dev->config[PCI_SUBSYSTEM_ID] = 0x00;
|
||||
pci_dev->config[PCI_SUBSYSTEM_ID + 1] = 0x10;
|
||||
/* PCI subsystem ID, subsystem vendor ID, revision */
|
||||
if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) {
|
||||
pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000);
|
||||
} else {
|
||||
pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
|
||||
PCI_VENDOR_ID_VMWARE);
|
||||
pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
|
||||
PCI_DEVICE_ID_VMWARE_PVSCSI);
|
||||
pci_config_set_revision(pci_dev->config, 0x2);
|
||||
}
|
||||
|
||||
/* PCI latency timer = 255 */
|
||||
pci_dev->config[PCI_LATENCY_TIMER] = 0xff;
|
||||
@@ -1085,6 +1117,10 @@ pvscsi_init(PCIDevice *pci_dev)
|
||||
|
||||
pvscsi_init_msi(s);
|
||||
|
||||
if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus)) {
|
||||
pcie_endpoint_cap_init(pci_dev, PVSCSI_EXP_EP_OFFSET);
|
||||
}
|
||||
|
||||
s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s);
|
||||
if (!s->completion_worker) {
|
||||
pvscsi_cleanup_msi(s);
|
||||
@@ -1139,6 +1175,27 @@ pvscsi_post_load(void *opaque, int version_id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pvscsi_vmstate_need_pcie_device(void *opaque)
|
||||
{
|
||||
PVSCSIState *s = PVSCSI(opaque);
|
||||
|
||||
return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE);
|
||||
}
|
||||
|
||||
static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id)
|
||||
{
|
||||
return !pvscsi_vmstate_need_pcie_device(opaque);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_pvscsi_pcie_device = {
|
||||
.name = "pvscsi/pcie",
|
||||
.needed = pvscsi_vmstate_need_pcie_device,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCIE_DEVICE(parent_obj, PVSCSIState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_pvscsi = {
|
||||
.name = "pvscsi",
|
||||
.version_id = 0,
|
||||
@@ -1146,7 +1203,9 @@ static const VMStateDescription vmstate_pvscsi = {
|
||||
.pre_save = pvscsi_pre_save,
|
||||
.post_load = pvscsi_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(parent_obj, PVSCSIState),
|
||||
VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState,
|
||||
pvscsi_vmstate_test_pci_device, 0,
|
||||
vmstate_pci_device, PCIDevice),
|
||||
VMSTATE_UINT8(msi_used, PVSCSIState),
|
||||
VMSTATE_UINT32(resetting, PVSCSIState),
|
||||
VMSTATE_UINT64(reg_interrupt_status, PVSCSIState),
|
||||
@@ -1171,18 +1230,40 @@ static const VMStateDescription vmstate_pvscsi = {
|
||||
VMSTATE_UINT64(rings.filled_cmp_ptr, PVSCSIState),
|
||||
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription*[]) {
|
||||
&vmstate_pvscsi_pcie_device,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
static Property pvscsi_properties[] = {
|
||||
DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1),
|
||||
DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags,
|
||||
PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false),
|
||||
DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags,
|
||||
PVSCSI_COMPAT_DISABLE_PCIE_BIT, false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void pvscsi_realize(DeviceState *qdev, Error **errp)
|
||||
{
|
||||
PVSCSIClass *pvs_c = PVSCSI_DEVICE_GET_CLASS(qdev);
|
||||
PCIDevice *pci_dev = PCI_DEVICE(qdev);
|
||||
PVSCSIState *s = PVSCSI(qdev);
|
||||
|
||||
if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) {
|
||||
pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
|
||||
}
|
||||
|
||||
pvs_c->parent_dc_realize(qdev, errp);
|
||||
}
|
||||
|
||||
static void pvscsi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
PVSCSIClass *pvs_k = PVSCSI_DEVICE_CLASS(klass);
|
||||
HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
|
||||
|
||||
k->init = pvscsi_init;
|
||||
@@ -1191,6 +1272,8 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
|
||||
k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI;
|
||||
k->class_id = PCI_CLASS_STORAGE_SCSI;
|
||||
k->subsystem_id = 0x1000;
|
||||
pvs_k->parent_dc_realize = dc->realize;
|
||||
dc->realize = pvscsi_realize;
|
||||
dc->reset = pvscsi_reset;
|
||||
dc->vmsd = &vmstate_pvscsi;
|
||||
dc->props = pvscsi_properties;
|
||||
@@ -1202,6 +1285,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
|
||||
static const TypeInfo pvscsi_info = {
|
||||
.name = TYPE_PVSCSI,
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.class_size = sizeof(PVSCSIClass),
|
||||
.instance_size = sizeof(PVSCSIState),
|
||||
.class_init = pvscsi_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
|
||||
@@ -193,7 +193,9 @@ static void sdhci_reset(SDHCIState *s)
|
||||
* initialization */
|
||||
memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
|
||||
|
||||
sd_set_cb(s->card, s->ro_cb, s->eject_cb);
|
||||
if (!s->noeject_quirk) {
|
||||
sd_set_cb(s->card, s->ro_cb, s->eject_cb);
|
||||
}
|
||||
s->data_count = 0;
|
||||
s->stopped_state = sdhc_not_stopped;
|
||||
}
|
||||
@@ -243,9 +245,6 @@ static void sdhci_send_command(SDHCIState *s)
|
||||
(s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) {
|
||||
s->norintsts |= SDHC_NIS_TRSCMP;
|
||||
}
|
||||
} else if (rlen != 0 && (s->errintstsen & SDHC_EISEN_CMDIDX)) {
|
||||
s->errintsts |= SDHC_EIS_CMDIDX;
|
||||
s->norintsts |= SDHC_NIS_ERR;
|
||||
}
|
||||
|
||||
if (s->norintstsen & SDHC_NISEN_CMDCMP) {
|
||||
@@ -831,7 +830,7 @@ static void sdhci_data_transfer(void *opaque)
|
||||
|
||||
static bool sdhci_can_issue_command(SDHCIState *s)
|
||||
{
|
||||
if (!SDHC_CLOCK_IS_ON(s->clkcon) || !(s->pwrcon & SDHC_POWER_ON) ||
|
||||
if (!SDHC_CLOCK_IS_ON(s->clkcon) ||
|
||||
(((s->prnsts & SDHC_DATA_INHIBIT) || s->stopped_state) &&
|
||||
((s->cmdreg & SDHC_CMD_DATA_PRESENT) ||
|
||||
((s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY &&
|
||||
@@ -1279,6 +1278,7 @@ static Property sdhci_sysbus_properties[] = {
|
||||
DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
|
||||
SDHC_CAPAB_REG_DEFAULT),
|
||||
DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
|
||||
DEFINE_PROP_BOOL("noeject-quirk", SDHCIState, noeject_quirk, false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
@@ -363,6 +363,8 @@ void cpu_put_timer(QEMUFile *f, CPUTimer *s)
|
||||
qemu_put_be32s(f, &s->frequency);
|
||||
qemu_put_be32s(f, &s->disabled);
|
||||
qemu_put_be64s(f, &s->disabled_mask);
|
||||
qemu_put_be32s(f, &s->npt);
|
||||
qemu_put_be64s(f, &s->npt_mask);
|
||||
qemu_put_sbe64s(f, &s->clock_offset);
|
||||
|
||||
timer_put(f, s->qtimer);
|
||||
@@ -373,6 +375,8 @@ void cpu_get_timer(QEMUFile *f, CPUTimer *s)
|
||||
qemu_get_be32s(f, &s->frequency);
|
||||
qemu_get_be32s(f, &s->disabled);
|
||||
qemu_get_be64s(f, &s->disabled_mask);
|
||||
qemu_get_be32s(f, &s->npt);
|
||||
qemu_get_be64s(f, &s->npt_mask);
|
||||
qemu_get_sbe64s(f, &s->clock_offset);
|
||||
|
||||
timer_get(f, s->qtimer);
|
||||
@@ -380,15 +384,17 @@ void cpu_get_timer(QEMUFile *f, CPUTimer *s)
|
||||
|
||||
static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu,
|
||||
QEMUBHFunc *cb, uint32_t frequency,
|
||||
uint64_t disabled_mask)
|
||||
uint64_t disabled_mask, uint64_t npt_mask)
|
||||
{
|
||||
CPUTimer *timer = g_malloc0(sizeof (CPUTimer));
|
||||
|
||||
timer->name = name;
|
||||
timer->frequency = frequency;
|
||||
timer->disabled_mask = disabled_mask;
|
||||
timer->npt_mask = npt_mask;
|
||||
|
||||
timer->disabled = 1;
|
||||
timer->npt = 1;
|
||||
timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu);
|
||||
@@ -494,17 +500,17 @@ static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency)
|
||||
|
||||
void cpu_tick_set_count(CPUTimer *timer, uint64_t count)
|
||||
{
|
||||
uint64_t real_count = count & ~timer->disabled_mask;
|
||||
uint64_t disabled_bit = count & timer->disabled_mask;
|
||||
uint64_t real_count = count & ~timer->npt_mask;
|
||||
uint64_t npt_bit = count & timer->npt_mask;
|
||||
|
||||
int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
|
||||
cpu_to_timer_ticks(real_count, timer->frequency);
|
||||
|
||||
TIMER_DPRINTF("%s set_count count=0x%016lx (%s) p=%p\n",
|
||||
TIMER_DPRINTF("%s set_count count=0x%016lx (npt %s) p=%p\n",
|
||||
timer->name, real_count,
|
||||
timer->disabled?"disabled":"enabled", timer);
|
||||
timer->npt ? "disabled" : "enabled", timer);
|
||||
|
||||
timer->disabled = disabled_bit ? 1 : 0;
|
||||
timer->npt = npt_bit ? 1 : 0;
|
||||
timer->clock_offset = vm_clock_offset;
|
||||
}
|
||||
|
||||
@@ -514,12 +520,13 @@ uint64_t cpu_tick_get_count(CPUTimer *timer)
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset,
|
||||
timer->frequency);
|
||||
|
||||
TIMER_DPRINTF("%s get_count count=0x%016lx (%s) p=%p\n",
|
||||
TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n",
|
||||
timer->name, real_count,
|
||||
timer->disabled?"disabled":"enabled", timer);
|
||||
timer->npt ? "disabled" : "enabled", timer);
|
||||
|
||||
if (timer->disabled)
|
||||
real_count |= timer->disabled_mask;
|
||||
if (timer->npt) {
|
||||
real_count |= timer->npt_mask;
|
||||
}
|
||||
|
||||
return real_count;
|
||||
}
|
||||
@@ -799,13 +806,16 @@ static SPARCCPU *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef)
|
||||
env = &cpu->env;
|
||||
|
||||
env->tick = cpu_timer_create("tick", cpu, tick_irq,
|
||||
tick_frequency, TICK_NPT_MASK);
|
||||
tick_frequency, TICK_INT_DIS,
|
||||
TICK_NPT_MASK);
|
||||
|
||||
env->stick = cpu_timer_create("stick", cpu, stick_irq,
|
||||
stick_frequency, TICK_INT_DIS);
|
||||
stick_frequency, TICK_INT_DIS,
|
||||
TICK_NPT_MASK);
|
||||
|
||||
env->hstick = cpu_timer_create("hstick", cpu, hstick_irq,
|
||||
hstick_frequency, TICK_INT_DIS);
|
||||
hstick_frequency, TICK_INT_DIS,
|
||||
TICK_NPT_MASK);
|
||||
|
||||
reset_info = g_malloc0(sizeof(ResetData));
|
||||
reset_info->cpu = cpu;
|
||||
|
||||
@@ -51,9 +51,9 @@ static char const *imx_epit_reg_name(uint32_t reg)
|
||||
* These are typical.
|
||||
*/
|
||||
static const IMXClk imx_epit_clocks[] = {
|
||||
0, /* 00 disabled */
|
||||
IPG, /* 01 ipg_clk, ~532MHz */
|
||||
IPG, /* 10 ipg_clk_highfreq */
|
||||
NOCLK, /* 00 disabled */
|
||||
CLK_IPG, /* 01 ipg_clk, ~532MHz */
|
||||
CLK_IPG, /* 10 ipg_clk_highfreq */
|
||||
CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */
|
||||
};
|
||||
|
||||
@@ -73,20 +73,18 @@ static void imx_epit_set_freq(IMXEPITState *s)
|
||||
{
|
||||
uint32_t clksrc;
|
||||
uint32_t prescaler;
|
||||
uint32_t freq;
|
||||
|
||||
clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2);
|
||||
prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12);
|
||||
|
||||
freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler;
|
||||
s->freq = imx_ccm_get_clock_frequency(s->ccm,
|
||||
imx_epit_clocks[clksrc]) / prescaler;
|
||||
|
||||
s->freq = freq;
|
||||
DPRINTF("Setting ptimer frequency to %u\n", s->freq);
|
||||
|
||||
DPRINTF("Setting ptimer frequency to %u\n", freq);
|
||||
|
||||
if (freq) {
|
||||
ptimer_set_freq(s->timer_reload, freq);
|
||||
ptimer_set_freq(s->timer_cmp, freq);
|
||||
if (s->freq) {
|
||||
ptimer_set_freq(s->timer_reload, s->freq);
|
||||
ptimer_set_freq(s->timer_cmp, s->freq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,8 +81,8 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
|
||||
|
||||
static const IMXClk imx_gpt_clocks[] = {
|
||||
NOCLK, /* 000 No clock source */
|
||||
IPG, /* 001 ipg_clk, 532MHz*/
|
||||
IPG, /* 010 ipg_clk_highfreq */
|
||||
CLK_IPG, /* 001 ipg_clk, 532MHz*/
|
||||
CLK_IPG, /* 010 ipg_clk_highfreq */
|
||||
NOCLK, /* 011 not defined */
|
||||
CLK_32k, /* 100 ipg_clk_32k */
|
||||
NOCLK, /* 101 not defined */
|
||||
@@ -93,14 +93,14 @@ static const IMXClk imx_gpt_clocks[] = {
|
||||
static void imx_gpt_set_freq(IMXGPTState *s)
|
||||
{
|
||||
uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
|
||||
uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc])
|
||||
/ (1 + s->pr);
|
||||
s->freq = freq;
|
||||
|
||||
DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq);
|
||||
s->freq = imx_ccm_get_clock_frequency(s->ccm,
|
||||
imx_gpt_clocks[clksrc]) / (1 + s->pr);
|
||||
|
||||
if (freq) {
|
||||
ptimer_set_freq(s->timer, freq);
|
||||
DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq);
|
||||
|
||||
if (s->freq) {
|
||||
ptimer_set_freq(s->timer, s->freq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
315
hw/usb/dev-mtp.c
315
hw/usb/dev-mtp.c
@@ -15,6 +15,10 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/inotify.h>
|
||||
#include "qemu/main-loop.h"
|
||||
#endif
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/iov.h"
|
||||
@@ -62,6 +66,11 @@ enum mtp_code {
|
||||
/* format codes */
|
||||
FMT_UNDEFINED_OBJECT = 0x3000,
|
||||
FMT_ASSOCIATION = 0x3001,
|
||||
|
||||
/* event codes */
|
||||
EVT_OBJ_ADDED = 0x4002,
|
||||
EVT_OBJ_REMOVED = 0x4003,
|
||||
EVT_OBJ_INFO_CHANGED = 0x4007,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -84,6 +93,17 @@ enum {
|
||||
EP_EVENT,
|
||||
};
|
||||
|
||||
#ifdef __linux__
|
||||
typedef struct MTPMonEntry MTPMonEntry;
|
||||
|
||||
struct MTPMonEntry {
|
||||
uint32_t event;
|
||||
uint32_t handle;
|
||||
|
||||
QTAILQ_ENTRY(MTPMonEntry) next;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct MTPControl {
|
||||
uint16_t code;
|
||||
uint32_t trans;
|
||||
@@ -108,9 +128,14 @@ struct MTPObject {
|
||||
char *name;
|
||||
char *path;
|
||||
struct stat stat;
|
||||
#ifdef __linux__
|
||||
/* inotify watch cookie */
|
||||
int watchfd;
|
||||
#endif
|
||||
MTPObject *parent;
|
||||
MTPObject **children;
|
||||
uint32_t nchildren;
|
||||
QLIST_HEAD(, MTPObject) children;
|
||||
QLIST_ENTRY(MTPObject) list;
|
||||
bool have_children;
|
||||
QTAILQ_ENTRY(MTPObject) next;
|
||||
};
|
||||
@@ -128,6 +153,11 @@ struct MTPState {
|
||||
uint32_t next_handle;
|
||||
|
||||
QTAILQ_HEAD(, MTPObject) objects;
|
||||
#ifdef __linux__
|
||||
/* inotify descriptor */
|
||||
int inotifyfd;
|
||||
QTAILQ_HEAD(events, MTPMonEntry) events;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define TYPE_USB_MTP "usb-mtp"
|
||||
@@ -183,7 +213,7 @@ static const USBDescIface desc_iface_full = {
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_IN | EP_EVENT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = 8,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 0x0a,
|
||||
},
|
||||
}
|
||||
@@ -225,7 +255,7 @@ static const USBDescIface desc_iface_high = {
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_IN | EP_EVENT,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = 8,
|
||||
.wMaxPacketSize = 64,
|
||||
.bInterval = 0x0a,
|
||||
},
|
||||
}
|
||||
@@ -317,15 +347,24 @@ ignore:
|
||||
|
||||
static void usb_mtp_object_free(MTPState *s, MTPObject *o)
|
||||
{
|
||||
int i;
|
||||
MTPObject *iter;
|
||||
|
||||
if (!o) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path);
|
||||
|
||||
QTAILQ_REMOVE(&s->objects, o, next);
|
||||
for (i = 0; i < o->nchildren; i++) {
|
||||
usb_mtp_object_free(s, o->children[i]);
|
||||
if (o->parent) {
|
||||
QLIST_REMOVE(o, list);
|
||||
o->parent->nchildren--;
|
||||
}
|
||||
|
||||
while (!QLIST_EMPTY(&o->children)) {
|
||||
iter = QLIST_FIRST(&o->children);
|
||||
usb_mtp_object_free(s, iter);
|
||||
}
|
||||
g_free(o->children);
|
||||
g_free(o->name);
|
||||
g_free(o->path);
|
||||
g_free(o);
|
||||
@@ -343,6 +382,204 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
|
||||
char *name)
|
||||
{
|
||||
MTPObject *child =
|
||||
usb_mtp_object_alloc(s, s->next_handle++, o, name);
|
||||
|
||||
if (child) {
|
||||
trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path);
|
||||
QLIST_INSERT_HEAD(&o->children, child, list);
|
||||
o->nchildren++;
|
||||
|
||||
if (child->format == FMT_ASSOCIATION) {
|
||||
QLIST_INIT(&child->children);
|
||||
}
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
|
||||
char *name, int len)
|
||||
{
|
||||
MTPObject *iter;
|
||||
|
||||
QLIST_FOREACH(iter, &parent->children, list) {
|
||||
if (strncmp(iter->name, name, len) == 0) {
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
|
||||
{
|
||||
MTPObject *iter;
|
||||
|
||||
QTAILQ_FOREACH(iter, &s->objects, next) {
|
||||
if (iter->watchfd == wd) {
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void inotify_watchfn(void *arg)
|
||||
{
|
||||
MTPState *s = arg;
|
||||
ssize_t bytes;
|
||||
/* From the man page: atleast one event can be read */
|
||||
int len = sizeof(struct inotify_event) + NAME_MAX + 1;
|
||||
int pos;
|
||||
char buf[len];
|
||||
|
||||
for (;;) {
|
||||
bytes = read(s->inotifyfd, buf, len);
|
||||
pos = 0;
|
||||
|
||||
if (bytes <= 0) {
|
||||
/* Better luck next time */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Ignore initiator initiated events.
|
||||
* For now we are good because the store is RO
|
||||
*/
|
||||
while (bytes > 0) {
|
||||
char *p = buf + pos;
|
||||
struct inotify_event *event = (struct inotify_event *)p;
|
||||
int watchfd = 0;
|
||||
uint32_t mask = event->mask & (IN_CREATE | IN_DELETE |
|
||||
IN_MODIFY | IN_IGNORED);
|
||||
MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd);
|
||||
MTPMonEntry *entry = NULL;
|
||||
MTPObject *o;
|
||||
|
||||
pos = pos + sizeof(struct inotify_event) + event->len;
|
||||
bytes = bytes - pos;
|
||||
|
||||
if (!parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
case IN_CREATE:
|
||||
if (usb_mtp_object_lookup_name
|
||||
(parent, event->name, event->len)) {
|
||||
/* Duplicate create event */
|
||||
continue;
|
||||
}
|
||||
entry = g_new0(MTPMonEntry, 1);
|
||||
entry->handle = s->next_handle;
|
||||
entry->event = EVT_OBJ_ADDED;
|
||||
o = usb_mtp_add_child(s, parent, event->name);
|
||||
if (!o) {
|
||||
g_free(entry);
|
||||
continue;
|
||||
}
|
||||
o->watchfd = watchfd;
|
||||
trace_usb_mtp_inotify_event(s->dev.addr, event->name,
|
||||
event->mask, "Obj Added");
|
||||
break;
|
||||
|
||||
case IN_DELETE:
|
||||
/*
|
||||
* The kernel issues a IN_IGNORED event
|
||||
* when a dir containing a watchpoint is
|
||||
* deleted, so we don't have to delete the
|
||||
* watchpoint
|
||||
*/
|
||||
o = usb_mtp_object_lookup_name(parent, event->name, event->len);
|
||||
if (!o) {
|
||||
continue;
|
||||
}
|
||||
entry = g_new0(MTPMonEntry, 1);
|
||||
entry->handle = o->handle;
|
||||
entry->event = EVT_OBJ_REMOVED;
|
||||
usb_mtp_object_free(s, o);
|
||||
trace_usb_mtp_inotify_event(s->dev.addr, o->path,
|
||||
event->mask, "Obj Deleted");
|
||||
break;
|
||||
|
||||
case IN_MODIFY:
|
||||
o = usb_mtp_object_lookup_name(parent, event->name, event->len);
|
||||
if (!o) {
|
||||
continue;
|
||||
}
|
||||
entry = g_new0(MTPMonEntry, 1);
|
||||
entry->handle = o->handle;
|
||||
entry->event = EVT_OBJ_INFO_CHANGED;
|
||||
trace_usb_mtp_inotify_event(s->dev.addr, o->path,
|
||||
event->mask, "Obj Modified");
|
||||
break;
|
||||
|
||||
case IN_IGNORED:
|
||||
o = usb_mtp_object_lookup_name(parent, event->name, event->len);
|
||||
trace_usb_mtp_inotify_event(s->dev.addr, o->path,
|
||||
event->mask, "Obj ignored");
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "usb-mtp: failed to parse inotify event\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
QTAILQ_INSERT_HEAD(&s->events, entry, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_mtp_inotify_init(MTPState *s)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = inotify_init1(IN_NONBLOCK);
|
||||
if (fd == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
QTAILQ_INIT(&s->events);
|
||||
s->inotifyfd = fd;
|
||||
|
||||
qemu_set_fd_handler(fd, inotify_watchfn, NULL, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_mtp_inotify_cleanup(MTPState *s)
|
||||
{
|
||||
MTPMonEntry *e;
|
||||
|
||||
if (!s->inotifyfd) {
|
||||
return;
|
||||
}
|
||||
|
||||
qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s);
|
||||
close(s->inotifyfd);
|
||||
|
||||
QTAILQ_FOREACH(e, &s->events, next) {
|
||||
QTAILQ_REMOVE(&s->events, e, next);
|
||||
g_free(e);
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_mtp_add_watch(int inotifyfd, char *path)
|
||||
{
|
||||
uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY |
|
||||
IN_ISDIR;
|
||||
|
||||
return inotify_add_watch(inotifyfd, path, mask);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
|
||||
{
|
||||
struct dirent *entry;
|
||||
@@ -357,15 +594,18 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
|
||||
if (!dir) {
|
||||
return;
|
||||
}
|
||||
#ifdef __linux__
|
||||
int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path);
|
||||
if (watchfd == -1) {
|
||||
fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path);
|
||||
} else {
|
||||
trace_usb_mtp_inotify_event(s->dev.addr, o->path,
|
||||
0, "Watch Added");
|
||||
o->watchfd = watchfd;
|
||||
}
|
||||
#endif
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if ((o->nchildren % 32) == 0) {
|
||||
o->children = g_renew(MTPObject *, o->children, o->nchildren + 32);
|
||||
}
|
||||
o->children[o->nchildren] =
|
||||
usb_mtp_object_alloc(s, s->next_handle++, o, entry->d_name);
|
||||
if (o->children[o->nchildren] != NULL) {
|
||||
o->nchildren++;
|
||||
}
|
||||
usb_mtp_add_child(s, o, entry->d_name);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
@@ -617,13 +857,15 @@ static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c,
|
||||
MTPObject *o)
|
||||
{
|
||||
MTPData *d = usb_mtp_data_alloc(c);
|
||||
uint32_t i, handles[o->nchildren];
|
||||
uint32_t i = 0, handles[o->nchildren];
|
||||
MTPObject *iter;
|
||||
|
||||
trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path);
|
||||
|
||||
for (i = 0; i < o->nchildren; i++) {
|
||||
handles[i] = o->children[i]->handle;
|
||||
QLIST_FOREACH(iter, &o->children, list) {
|
||||
handles[i++] = iter->handle;
|
||||
}
|
||||
assert(i == o->nchildren);
|
||||
usb_mtp_add_u32_array(d, o->nchildren, handles);
|
||||
|
||||
return d;
|
||||
@@ -754,11 +996,19 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
|
||||
trace_usb_mtp_op_open_session(s->dev.addr);
|
||||
s->session = c->argv[0];
|
||||
usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
|
||||
#ifdef __linux__
|
||||
if (usb_mtp_inotify_init(s)) {
|
||||
fprintf(stderr, "usb-mtp: file monitoring init failed\n");
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case CMD_CLOSE_SESSION:
|
||||
trace_usb_mtp_op_close_session(s->dev.addr);
|
||||
s->session = 0;
|
||||
s->next_handle = 0;
|
||||
#ifdef __linux__
|
||||
usb_mtp_inotify_cleanup(s);
|
||||
#endif
|
||||
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
|
||||
assert(QTAILQ_EMPTY(&s->objects));
|
||||
break;
|
||||
@@ -884,6 +1134,10 @@ static void usb_mtp_handle_reset(USBDevice *dev)
|
||||
|
||||
trace_usb_mtp_reset(s->dev.addr);
|
||||
|
||||
#ifdef __linux__
|
||||
usb_mtp_inotify_cleanup(s);
|
||||
#endif
|
||||
usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
|
||||
s->session = 0;
|
||||
usb_mtp_data_free(s->data_in);
|
||||
s->data_in = NULL;
|
||||
@@ -1043,6 +1297,31 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
|
||||
}
|
||||
break;
|
||||
case EP_EVENT:
|
||||
#ifdef __linux__
|
||||
if (!QTAILQ_EMPTY(&s->events)) {
|
||||
struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events);
|
||||
uint32_t handle;
|
||||
int len = sizeof(container) + sizeof(uint32_t);
|
||||
|
||||
if (p->iov.size < len) {
|
||||
trace_usb_mtp_stall(s->dev.addr,
|
||||
"packet too small to send event");
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(&s->events, e, next);
|
||||
container.length = cpu_to_le32(len);
|
||||
container.type = cpu_to_le32(TYPE_EVENT);
|
||||
container.code = cpu_to_le16(e->event);
|
||||
container.trans = 0; /* no trans specific events */
|
||||
handle = cpu_to_le32(e->handle);
|
||||
usb_packet_copy(p, &container, sizeof(container));
|
||||
usb_packet_copy(p, &handle, sizeof(uint32_t));
|
||||
g_free(e);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
p->status = USB_RET_NAK;
|
||||
return;
|
||||
default:
|
||||
|
||||
@@ -1389,7 +1389,7 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
{
|
||||
USBDevice *dev;
|
||||
USBEndpoint *ep;
|
||||
uint32_t i, len, pid, dir, devaddr, endp;
|
||||
uint32_t i, len, pid, dir, devaddr, endp, xfers = 0;
|
||||
uint32_t pg, off, ptr1, ptr2, max, mult;
|
||||
|
||||
ehci->periodic_sched_active = PERIODIC_ACTIVE;
|
||||
@@ -1479,9 +1479,10 @@ static int ehci_process_itd(EHCIState *ehci,
|
||||
ehci_raise_irq(ehci, USBSTS_INT);
|
||||
}
|
||||
itd->transact[i] &= ~ITD_XACT_ACTIVE;
|
||||
xfers++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return xfers ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user