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,13 +537,7 @@ 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, | ||||
|     status = coreaudio_get_framesizerange(core->outputDeviceID, | ||||
|                                           &frameRange); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
| @@ -347,14 +558,7 @@ 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, | ||||
|     status = coreaudio_set_framesize(core->outputDeviceID, | ||||
|                                      &core->audioDevicePropertyBufferFrameSize); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
| @@ -364,13 +568,7 @@ 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, | ||||
|     status = coreaudio_get_framesize(core->outputDeviceID, | ||||
|                                      &core->audioDevicePropertyBufferFrameSize); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
| @@ -380,13 +578,7 @@ 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, | ||||
|     status = coreaudio_get_streamformat(core->outputDeviceID, | ||||
|                                         &core->outputStreamBasicDescription); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, | ||||
| @@ -397,14 +589,7 @@ 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, | ||||
|     status = coreaudio_set_streamformat(core->outputDeviceID, | ||||
|                                         &core->outputStreamBasicDescription); | ||||
|     if (status != kAudioHardwareNoError) { | ||||
|         coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", | ||||
| @@ -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"); | ||||
|                 } | ||||
|   | ||||
							
								
								
									
										461
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										461
									
								
								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, | ||||
| static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, | ||||
|                                                  BlockDriverState *bs, | ||||
|                                     QDict *options, int flags) | ||||
|                                                  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,21 +4171,40 @@ 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")) | ||||
|         /* 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; | ||||
|   | ||||
							
								
								
									
										13
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								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,11 +2622,12 @@ 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); | ||||
|         } | ||||
|     } | ||||
|     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; | ||||
| } | ||||
|   | ||||
							
								
								
									
										215
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										215
									
								
								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) { | ||||
|     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) { | ||||
|             /* 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; | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|     } | ||||
|     if (bs->file) { | ||||
|         return bdrv_snapshot_delete(bs->file->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)); | ||||
|     return -ENOTSUP; | ||||
|         ret = -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. | ||||
							
								
								
									
										223
									
								
								exec.c
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								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); | ||||
|         } | ||||
|         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()) { | ||||
|  | ||||
|     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); | ||||
|     } 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; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         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,24 +2457,20 @@ 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)) { | ||||
|     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 | ||||
| @@ -2568,8 +2510,60 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, | ||||
|             memcpy(ptr, buf, l); | ||||
|             invalidate_and_set_dirty(mr, addr1, l); | ||||
|         } | ||||
|         } else { | ||||
|             if (!memory_access_is_direct(mr, is_write)) { | ||||
|  | ||||
|         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, true); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, | ||||
|                                 const 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, true); | ||||
|         result = address_space_write_continue(as, addr, attrs, buf, len, | ||||
|                                               addr1, l, mr); | ||||
|         rcu_read_unlock(); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| /* 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) | ||||
| { | ||||
|     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); | ||||
| @@ -2606,7 +2600,6 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, | ||||
|             ptr = qemu_get_ram_ptr(mr->ram_addr + addr1); | ||||
|             memcpy(buf, ptr, l); | ||||
|         } | ||||
|         } | ||||
|  | ||||
|         if (release_lock) { | ||||
|             qemu_mutex_unlock_iothread(); | ||||
| @@ -2616,25 +2609,48 @@ 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, false); | ||||
|     } | ||||
|     rcu_read_unlock(); | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, | ||||
|                                 const uint8_t *buf, int len) | ||||
| MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, | ||||
|                                     MemTxAttrs attrs, 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, false); | ||||
|         result = address_space_read_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) | ||||
| MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, | ||||
|                              uint8_t *buf, int len, bool is_write) | ||||
| { | ||||
|     return address_space_rw(as, addr, attrs, buf, len, false); | ||||
|     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); | ||||
|     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); | ||||
|  | ||||
|     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", | ||||
|     object_property_add(obj, "kernel-irqchip", "OnOffSplit", | ||||
|                         NULL, | ||||
|                         machine_set_kernel_irqchip, | ||||
|                              NULL); | ||||
|                         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,19 +888,35 @@ static Aml *build_crs(PCIHostState *host, | ||||
|              * that do not support multiple root buses | ||||
|              */ | ||||
|             if (range_base && 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, | ||||
|                                             range_base, | ||||
|                                             range_limit, | ||||
|                                             0, | ||||
|                                             range_limit - range_base + 1)); | ||||
|                 crs_range_insert(mem_ranges, range_base, range_limit); | ||||
|             } | ||||
|         } | ||||
|                                     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, | ||||
| @@ -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); | ||||
|     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, | ||||
|     .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); | ||||
|     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); | ||||
|     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); | ||||
|  | ||||
|     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