Compare commits
	
		
			314 Commits
		
	
	
		
			v2.5.0-rc4
			...
			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 | ||
| 
						 | 
					a8c40fa2d6 | ||
| 
						 | 
					6c8d56a2e9 | ||
| 
						 | 
					38bf20931a | ||
| 
						 | 
					66f8fd9dda | ||
| 
						 | 
					3f8752b4e5 | ||
| 
						 | 
					3bef7e8aab | ||
| 
						 | 
					9c4a5c55f5 | ||
| 
						 | 
					2f79a18fdd | ||
| 
						 | 
					2d99f6299b | ||
| 
						 | 
					95a860f62e | ||
| 
						 | 
					624d1fc30f | ||
| 
						 | 
					88a0f8300b | ||
| 
						 | 
					156a2e4dbf | ||
| 
						 | 
					93d592e3d1 | ||
| 
						 | 
					8e3e3897ce | ||
| 
						 | 
					b3c4d4250f | ||
| 
						 | 
					4c7a67f5cd | ||
| 
						 | 
					fc3e493bc8 | ||
| 
						 | 
					55c8672c2e | ||
| 
						 | 
					bdfe5159cb | ||
| 
						 | 
					f0ada3608a | 
							
								
								
									
										15
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								MAINTAINERS
									
									
									
									
									
								
							@@ -941,6 +941,13 @@ M: Jiri Pirko <jiri@resnulli.us>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: hw/net/rocker/
 | 
			
		||||
 | 
			
		||||
NVDIMM
 | 
			
		||||
M: Xiao Guangrong <guangrong.xiao@linux.intel.com>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: hw/acpi/nvdimm.c
 | 
			
		||||
F: hw/mem/nvdimm.c
 | 
			
		||||
F: include/hw/mem/nvdimm.h
 | 
			
		||||
 | 
			
		||||
Subsystems
 | 
			
		||||
----------
 | 
			
		||||
Audio
 | 
			
		||||
@@ -1243,6 +1250,13 @@ S: Odd fixes
 | 
			
		||||
F: util/buffer.c
 | 
			
		||||
F: include/qemu/buffer.h
 | 
			
		||||
 | 
			
		||||
I/O Channels
 | 
			
		||||
M: Daniel P. Berrange <berrange@redhat.com>
 | 
			
		||||
S: Maintained
 | 
			
		||||
F: io/
 | 
			
		||||
F: include/io/
 | 
			
		||||
F: tests/test-io-*
 | 
			
		||||
 | 
			
		||||
Usermode Emulation
 | 
			
		||||
------------------
 | 
			
		||||
Overall
 | 
			
		||||
@@ -1483,6 +1497,7 @@ M: Denis V. Lunev <den@openvz.org>
 | 
			
		||||
L: qemu-block@nongnu.org
 | 
			
		||||
S: Supported
 | 
			
		||||
F: block/parallels.c
 | 
			
		||||
F: docs/specs/parallels.txt
 | 
			
		||||
 | 
			
		||||
qed
 | 
			
		||||
M: Stefan Hajnoczi <stefanha@redhat.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								Makefile
									
									
									
									
									
								
							@@ -159,6 +159,7 @@ dummy := $(call unnest-vars,, \
 | 
			
		||||
                crypto-obj-y \
 | 
			
		||||
                crypto-aes-obj-y \
 | 
			
		||||
                qom-obj-y \
 | 
			
		||||
                io-obj-y \
 | 
			
		||||
                common-obj-y \
 | 
			
		||||
                common-obj-m)
 | 
			
		||||
 | 
			
		||||
@@ -178,6 +179,7 @@ SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES))
 | 
			
		||||
 | 
			
		||||
$(SOFTMMU_SUBDIR_RULES): $(block-obj-y)
 | 
			
		||||
$(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y)
 | 
			
		||||
$(SOFTMMU_SUBDIR_RULES): $(io-obj-y)
 | 
			
		||||
$(SOFTMMU_SUBDIR_RULES): config-all-devices.mak
 | 
			
		||||
 | 
			
		||||
subdir-%:
 | 
			
		||||
@@ -269,7 +271,8 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 | 
			
		||||
 | 
			
		||||
qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json
 | 
			
		||||
               $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json \
 | 
			
		||||
               $(SRC_PATH)/qapi/crypto.json
 | 
			
		||||
 | 
			
		||||
qapi-types.c qapi-types.h :\
 | 
			
		||||
$(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,11 @@ crypto-aes-obj-y = crypto/
 | 
			
		||||
 | 
			
		||||
qom-obj-y = qom/
 | 
			
		||||
 | 
			
		||||
#######################################################################
 | 
			
		||||
# io-obj-y is code used by both qemu system emulation and qemu-img
 | 
			
		||||
 | 
			
		||||
io-obj-y = io/
 | 
			
		||||
 | 
			
		||||
######################################################################
 | 
			
		||||
# Target independent part of system emulation. The long term path is to
 | 
			
		||||
# suppress *all* target specific code in case of system emulation, i.e. a
 | 
			
		||||
 
 | 
			
		||||
@@ -176,6 +176,7 @@ dummy := $(call unnest-vars,.., \
 | 
			
		||||
               crypto-obj-y \
 | 
			
		||||
               crypto-aes-obj-y \
 | 
			
		||||
               qom-obj-y \
 | 
			
		||||
               io-obj-y \
 | 
			
		||||
               common-obj-y \
 | 
			
		||||
               common-obj-m)
 | 
			
		||||
target-obj-y := $(target-obj-y-save)
 | 
			
		||||
@@ -185,6 +186,7 @@ all-obj-y += $(qom-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_USER_ONLY) += $(crypto-aes-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
 | 
			
		||||
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
 | 
			
		||||
 | 
			
		||||
$(QEMU_PROG_BUILD): config-devices.mak
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,10 @@
 | 
			
		||||
#define AUDIO_CAP "coreaudio"
 | 
			
		||||
#include "audio_int.h"
 | 
			
		||||
 | 
			
		||||
#ifndef MAC_OS_X_VERSION_10_6
 | 
			
		||||
#define MAC_OS_X_VERSION_10_6 1060
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int isAtexit;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -45,11 +49,233 @@ typedef struct coreaudioVoiceOut {
 | 
			
		||||
    AudioDeviceID outputDeviceID;
 | 
			
		||||
    UInt32 audioDevicePropertyBufferFrameSize;
 | 
			
		||||
    AudioStreamBasicDescription outputStreamBasicDescription;
 | 
			
		||||
    AudioDeviceIOProcID ioprocid;
 | 
			
		||||
    int live;
 | 
			
		||||
    int decr;
 | 
			
		||||
    int rpos;
 | 
			
		||||
} coreaudioVoiceOut;
 | 
			
		||||
 | 
			
		||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
 | 
			
		||||
/* The APIs used here only become available from 10.6 */
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_voice(AudioDeviceID *id)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*id);
 | 
			
		||||
    AudioObjectPropertyAddress addr = {
 | 
			
		||||
        kAudioHardwarePropertyDefaultOutputDevice,
 | 
			
		||||
        kAudioObjectPropertyScopeGlobal,
 | 
			
		||||
        kAudioObjectPropertyElementMaster
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return AudioObjectGetPropertyData(kAudioObjectSystemObject,
 | 
			
		||||
                                      &addr,
 | 
			
		||||
                                      0,
 | 
			
		||||
                                      NULL,
 | 
			
		||||
                                      &size,
 | 
			
		||||
                                      id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
 | 
			
		||||
                                             AudioValueRange *framerange)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*framerange);
 | 
			
		||||
    AudioObjectPropertyAddress addr = {
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSizeRange,
 | 
			
		||||
        kAudioDevicePropertyScopeOutput,
 | 
			
		||||
        kAudioObjectPropertyElementMaster
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return AudioObjectGetPropertyData(id,
 | 
			
		||||
                                      &addr,
 | 
			
		||||
                                      0,
 | 
			
		||||
                                      NULL,
 | 
			
		||||
                                      &size,
 | 
			
		||||
                                      framerange);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*framesize);
 | 
			
		||||
    AudioObjectPropertyAddress addr = {
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSize,
 | 
			
		||||
        kAudioDevicePropertyScopeOutput,
 | 
			
		||||
        kAudioObjectPropertyElementMaster
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return AudioObjectGetPropertyData(id,
 | 
			
		||||
                                      &addr,
 | 
			
		||||
                                      0,
 | 
			
		||||
                                      NULL,
 | 
			
		||||
                                      &size,
 | 
			
		||||
                                      framesize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*framesize);
 | 
			
		||||
    AudioObjectPropertyAddress addr = {
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSize,
 | 
			
		||||
        kAudioDevicePropertyScopeOutput,
 | 
			
		||||
        kAudioObjectPropertyElementMaster
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return AudioObjectSetPropertyData(id,
 | 
			
		||||
                                      &addr,
 | 
			
		||||
                                      0,
 | 
			
		||||
                                      NULL,
 | 
			
		||||
                                      size,
 | 
			
		||||
                                      framesize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
 | 
			
		||||
                                           AudioStreamBasicDescription *d)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*d);
 | 
			
		||||
    AudioObjectPropertyAddress addr = {
 | 
			
		||||
        kAudioDevicePropertyStreamFormat,
 | 
			
		||||
        kAudioDevicePropertyScopeOutput,
 | 
			
		||||
        kAudioObjectPropertyElementMaster
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return AudioObjectGetPropertyData(id,
 | 
			
		||||
                                      &addr,
 | 
			
		||||
                                      0,
 | 
			
		||||
                                      NULL,
 | 
			
		||||
                                      &size,
 | 
			
		||||
                                      d);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
 | 
			
		||||
                                           AudioStreamBasicDescription *d)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*d);
 | 
			
		||||
    AudioObjectPropertyAddress addr = {
 | 
			
		||||
        kAudioDevicePropertyStreamFormat,
 | 
			
		||||
        kAudioDevicePropertyScopeOutput,
 | 
			
		||||
        kAudioObjectPropertyElementMaster
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return AudioObjectSetPropertyData(id,
 | 
			
		||||
                                      &addr,
 | 
			
		||||
                                      0,
 | 
			
		||||
                                      NULL,
 | 
			
		||||
                                      size,
 | 
			
		||||
                                      d);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*result);
 | 
			
		||||
    AudioObjectPropertyAddress addr = {
 | 
			
		||||
        kAudioDevicePropertyDeviceIsRunning,
 | 
			
		||||
        kAudioDevicePropertyScopeOutput,
 | 
			
		||||
        kAudioObjectPropertyElementMaster
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return AudioObjectGetPropertyData(id,
 | 
			
		||||
                                      &addr,
 | 
			
		||||
                                      0,
 | 
			
		||||
                                      NULL,
 | 
			
		||||
                                      &size,
 | 
			
		||||
                                      result);
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
/* Legacy versions of functions using deprecated APIs */
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_voice(AudioDeviceID *id)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*id);
 | 
			
		||||
 | 
			
		||||
    return AudioHardwareGetProperty(
 | 
			
		||||
        kAudioHardwarePropertyDefaultOutputDevice,
 | 
			
		||||
        &size,
 | 
			
		||||
        id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
 | 
			
		||||
                                             AudioValueRange *framerange)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*framerange);
 | 
			
		||||
 | 
			
		||||
    return AudioDeviceGetProperty(
 | 
			
		||||
        id,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSizeRange,
 | 
			
		||||
        &size,
 | 
			
		||||
        framerange);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*framesize);
 | 
			
		||||
 | 
			
		||||
    return AudioDeviceGetProperty(
 | 
			
		||||
        id,
 | 
			
		||||
        0,
 | 
			
		||||
        false,
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSize,
 | 
			
		||||
        &size,
 | 
			
		||||
        framesize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*framesize);
 | 
			
		||||
 | 
			
		||||
    return AudioDeviceSetProperty(
 | 
			
		||||
        id,
 | 
			
		||||
        NULL,
 | 
			
		||||
        0,
 | 
			
		||||
        false,
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSize,
 | 
			
		||||
        size,
 | 
			
		||||
        framesize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
 | 
			
		||||
                                           AudioStreamBasicDescription *d)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*d);
 | 
			
		||||
 | 
			
		||||
    return AudioDeviceGetProperty(
 | 
			
		||||
        id,
 | 
			
		||||
        0,
 | 
			
		||||
        false,
 | 
			
		||||
        kAudioDevicePropertyStreamFormat,
 | 
			
		||||
        &size,
 | 
			
		||||
        d);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
 | 
			
		||||
                                           AudioStreamBasicDescription *d)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*d);
 | 
			
		||||
 | 
			
		||||
    return AudioDeviceSetProperty(
 | 
			
		||||
        id,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        kAudioDevicePropertyStreamFormat,
 | 
			
		||||
        size,
 | 
			
		||||
        d);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
 | 
			
		||||
{
 | 
			
		||||
    UInt32 size = sizeof(*result);
 | 
			
		||||
 | 
			
		||||
    return AudioDeviceGetProperty(
 | 
			
		||||
        id,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        kAudioDevicePropertyDeviceIsRunning,
 | 
			
		||||
        &size,
 | 
			
		||||
        result);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void coreaudio_logstatus (OSStatus status)
 | 
			
		||||
{
 | 
			
		||||
    const char *str = "BUG";
 | 
			
		||||
@@ -144,10 +370,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
 | 
			
		||||
{
 | 
			
		||||
    OSStatus status;
 | 
			
		||||
    UInt32 result = 0;
 | 
			
		||||
    UInt32 propertySize = sizeof(outputDeviceID);
 | 
			
		||||
    status = AudioDeviceGetProperty(
 | 
			
		||||
        outputDeviceID, 0, 0,
 | 
			
		||||
        kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
 | 
			
		||||
    status = coreaudio_get_isrunning(outputDeviceID, &result);
 | 
			
		||||
    if (status != kAudioHardwareNoError) {
 | 
			
		||||
        coreaudio_logerr(status,
 | 
			
		||||
                         "Could not determine whether Device is playing\n");
 | 
			
		||||
@@ -288,7 +511,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
{
 | 
			
		||||
    OSStatus status;
 | 
			
		||||
    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 | 
			
		||||
    UInt32 propertySize;
 | 
			
		||||
    int err;
 | 
			
		||||
    const char *typ = "playback";
 | 
			
		||||
    AudioValueRange frameRange;
 | 
			
		||||
@@ -303,12 +525,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
 | 
			
		||||
    audio_pcm_init_info (&hw->info, as);
 | 
			
		||||
 | 
			
		||||
    /* open default output device */
 | 
			
		||||
    propertySize = sizeof(core->outputDeviceID);
 | 
			
		||||
    status = AudioHardwareGetProperty(
 | 
			
		||||
        kAudioHardwarePropertyDefaultOutputDevice,
 | 
			
		||||
        &propertySize,
 | 
			
		||||
        &core->outputDeviceID);
 | 
			
		||||
    status = coreaudio_get_voice(&core->outputDeviceID);
 | 
			
		||||
    if (status != kAudioHardwareNoError) {
 | 
			
		||||
        coreaudio_logerr2 (status, typ,
 | 
			
		||||
                           "Could not get default output Device\n");
 | 
			
		||||
@@ -320,14 +537,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* get minimum and maximum buffer frame sizes */
 | 
			
		||||
    propertySize = sizeof(frameRange);
 | 
			
		||||
    status = AudioDeviceGetProperty(
 | 
			
		||||
        core->outputDeviceID,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSizeRange,
 | 
			
		||||
        &propertySize,
 | 
			
		||||
        &frameRange);
 | 
			
		||||
    status = coreaudio_get_framesizerange(core->outputDeviceID,
 | 
			
		||||
                                          &frameRange);
 | 
			
		||||
    if (status != kAudioHardwareNoError) {
 | 
			
		||||
        coreaudio_logerr2 (status, typ,
 | 
			
		||||
                           "Could not get device buffer frame range\n");
 | 
			
		||||
@@ -347,15 +558,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* set Buffer Frame Size */
 | 
			
		||||
    propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
 | 
			
		||||
    status = AudioDeviceSetProperty(
 | 
			
		||||
        core->outputDeviceID,
 | 
			
		||||
        NULL,
 | 
			
		||||
        0,
 | 
			
		||||
        false,
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSize,
 | 
			
		||||
        propertySize,
 | 
			
		||||
        &core->audioDevicePropertyBufferFrameSize);
 | 
			
		||||
    status = coreaudio_set_framesize(core->outputDeviceID,
 | 
			
		||||
                                     &core->audioDevicePropertyBufferFrameSize);
 | 
			
		||||
    if (status != kAudioHardwareNoError) {
 | 
			
		||||
        coreaudio_logerr2 (status, typ,
 | 
			
		||||
                           "Could not set device buffer frame size %" PRIu32 "\n",
 | 
			
		||||
@@ -364,14 +568,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* get Buffer Frame Size */
 | 
			
		||||
    propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
 | 
			
		||||
    status = AudioDeviceGetProperty(
 | 
			
		||||
        core->outputDeviceID,
 | 
			
		||||
        0,
 | 
			
		||||
        false,
 | 
			
		||||
        kAudioDevicePropertyBufferFrameSize,
 | 
			
		||||
        &propertySize,
 | 
			
		||||
        &core->audioDevicePropertyBufferFrameSize);
 | 
			
		||||
    status = coreaudio_get_framesize(core->outputDeviceID,
 | 
			
		||||
                                     &core->audioDevicePropertyBufferFrameSize);
 | 
			
		||||
    if (status != kAudioHardwareNoError) {
 | 
			
		||||
        coreaudio_logerr2 (status, typ,
 | 
			
		||||
                           "Could not get device buffer frame size\n");
 | 
			
		||||
@@ -380,14 +578,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
 | 
			
		||||
 | 
			
		||||
    /* get StreamFormat */
 | 
			
		||||
    propertySize = sizeof(core->outputStreamBasicDescription);
 | 
			
		||||
    status = AudioDeviceGetProperty(
 | 
			
		||||
        core->outputDeviceID,
 | 
			
		||||
        0,
 | 
			
		||||
        false,
 | 
			
		||||
        kAudioDevicePropertyStreamFormat,
 | 
			
		||||
        &propertySize,
 | 
			
		||||
        &core->outputStreamBasicDescription);
 | 
			
		||||
    status = coreaudio_get_streamformat(core->outputDeviceID,
 | 
			
		||||
                                        &core->outputStreamBasicDescription);
 | 
			
		||||
    if (status != kAudioHardwareNoError) {
 | 
			
		||||
        coreaudio_logerr2 (status, typ,
 | 
			
		||||
                           "Could not get Device Stream properties\n");
 | 
			
		||||
@@ -397,15 +589,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
 | 
			
		||||
    /* set Samplerate */
 | 
			
		||||
    core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
 | 
			
		||||
    propertySize = sizeof(core->outputStreamBasicDescription);
 | 
			
		||||
    status = AudioDeviceSetProperty(
 | 
			
		||||
        core->outputDeviceID,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        0,
 | 
			
		||||
        kAudioDevicePropertyStreamFormat,
 | 
			
		||||
        propertySize,
 | 
			
		||||
        &core->outputStreamBasicDescription);
 | 
			
		||||
    status = coreaudio_set_streamformat(core->outputDeviceID,
 | 
			
		||||
                                        &core->outputStreamBasicDescription);
 | 
			
		||||
    if (status != kAudioHardwareNoError) {
 | 
			
		||||
        coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
 | 
			
		||||
                           as->freq);
 | 
			
		||||
@@ -414,8 +599,12 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* set Callback */
 | 
			
		||||
    status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
 | 
			
		||||
    if (status != kAudioHardwareNoError) {
 | 
			
		||||
    core->ioprocid = NULL;
 | 
			
		||||
    status = AudioDeviceCreateIOProcID(core->outputDeviceID,
 | 
			
		||||
                                       audioDeviceIOProc,
 | 
			
		||||
                                       hw,
 | 
			
		||||
                                       &core->ioprocid);
 | 
			
		||||
    if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
 | 
			
		||||
        coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
 | 
			
		||||
        core->outputDeviceID = kAudioDeviceUnknown;
 | 
			
		||||
        return -1;
 | 
			
		||||
@@ -423,10 +612,10 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 | 
			
		||||
 | 
			
		||||
    /* start Playback */
 | 
			
		||||
    if (!isPlaying(core->outputDeviceID)) {
 | 
			
		||||
        status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
 | 
			
		||||
        status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
 | 
			
		||||
        if (status != kAudioHardwareNoError) {
 | 
			
		||||
            coreaudio_logerr2 (status, typ, "Could not start playback\n");
 | 
			
		||||
            AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
 | 
			
		||||
            AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid);
 | 
			
		||||
            core->outputDeviceID = kAudioDeviceUnknown;
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
@@ -444,15 +633,15 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
 | 
			
		||||
    if (!isAtexit) {
 | 
			
		||||
        /* stop playback */
 | 
			
		||||
        if (isPlaying(core->outputDeviceID)) {
 | 
			
		||||
            status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
 | 
			
		||||
            status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
 | 
			
		||||
            if (status != kAudioHardwareNoError) {
 | 
			
		||||
                coreaudio_logerr (status, "Could not stop playback\n");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* remove callback */
 | 
			
		||||
        status = AudioDeviceRemoveIOProc(core->outputDeviceID,
 | 
			
		||||
                                         audioDeviceIOProc);
 | 
			
		||||
        status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
 | 
			
		||||
                                            core->ioprocid);
 | 
			
		||||
        if (status != kAudioHardwareNoError) {
 | 
			
		||||
            coreaudio_logerr (status, "Could not remove IOProc\n");
 | 
			
		||||
        }
 | 
			
		||||
@@ -475,7 +664,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
    case VOICE_ENABLE:
 | 
			
		||||
        /* start playback */
 | 
			
		||||
        if (!isPlaying(core->outputDeviceID)) {
 | 
			
		||||
            status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
 | 
			
		||||
            status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
 | 
			
		||||
            if (status != kAudioHardwareNoError) {
 | 
			
		||||
                coreaudio_logerr (status, "Could not resume playback\n");
 | 
			
		||||
            }
 | 
			
		||||
@@ -486,7 +675,8 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | 
			
		||||
        /* stop playback */
 | 
			
		||||
        if (!isAtexit) {
 | 
			
		||||
            if (isPlaying(core->outputDeviceID)) {
 | 
			
		||||
                status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
 | 
			
		||||
                status = AudioDeviceStop(core->outputDeviceID,
 | 
			
		||||
                                         core->ioprocid);
 | 
			
		||||
                if (status != kAudioHardwareNoError) {
 | 
			
		||||
                    coreaudio_logerr (status, "Could not pause playback\n");
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										469
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										469
									
								
								block.c
									
									
									
									
									
								
							@@ -29,6 +29,7 @@
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
#include "qemu/module.h"
 | 
			
		||||
#include "qapi/qmp/qerror.h"
 | 
			
		||||
#include "qapi/qmp/qbool.h"
 | 
			
		||||
#include "qapi/qmp/qjson.h"
 | 
			
		||||
#include "sysemu/block-backend.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
@@ -623,6 +624,20 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Combines a QDict of new block driver @options with any missing options taken
 | 
			
		||||
 * from @old_options, so that leaving out an option defaults to its old value.
 | 
			
		||||
 */
 | 
			
		||||
static void bdrv_join_options(BlockDriverState *bs, QDict *options,
 | 
			
		||||
                              QDict *old_options)
 | 
			
		||||
{
 | 
			
		||||
    if (bs->drv && bs->drv->bdrv_join_options) {
 | 
			
		||||
        bs->drv->bdrv_join_options(options, old_options);
 | 
			
		||||
    } else {
 | 
			
		||||
        qdict_join(options, old_options, false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set open flags for a given discard mode
 | 
			
		||||
 *
 | 
			
		||||
@@ -681,60 +696,81 @@ static int bdrv_temp_snapshot_flags(int flags)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns the flags that bs->file should get if a protocol driver is expected,
 | 
			
		||||
 * based on the given flags for the parent BDS
 | 
			
		||||
 * Returns the options and flags that bs->file should get if a protocol driver
 | 
			
		||||
 * is expected, based on the given options and flags for the parent BDS
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_inherited_flags(int flags)
 | 
			
		||||
static void bdrv_inherited_options(int *child_flags, QDict *child_options,
 | 
			
		||||
                                   int parent_flags, QDict *parent_options)
 | 
			
		||||
{
 | 
			
		||||
    int flags = parent_flags;
 | 
			
		||||
 | 
			
		||||
    /* Enable protocol handling, disable format probing for bs->file */
 | 
			
		||||
    flags |= BDRV_O_PROTOCOL;
 | 
			
		||||
 | 
			
		||||
    /* If the cache mode isn't explicitly set, inherit direct and no-flush from
 | 
			
		||||
     * the parent. */
 | 
			
		||||
    qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
 | 
			
		||||
    qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
 | 
			
		||||
 | 
			
		||||
    /* Our block drivers take care to send flushes and respect unmap policy,
 | 
			
		||||
     * so we can enable both unconditionally on lower layers. */
 | 
			
		||||
    flags |= BDRV_O_CACHE_WB | BDRV_O_UNMAP;
 | 
			
		||||
     * so we can default to enable both on lower layers regardless of the
 | 
			
		||||
     * corresponding parent options. */
 | 
			
		||||
    qdict_set_default_str(child_options, BDRV_OPT_CACHE_WB, "on");
 | 
			
		||||
    flags |= BDRV_O_UNMAP;
 | 
			
		||||
 | 
			
		||||
    /* Clear flags that only apply to the top layer */
 | 
			
		||||
    flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ);
 | 
			
		||||
 | 
			
		||||
    return flags;
 | 
			
		||||
    *child_flags = flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const BdrvChildRole child_file = {
 | 
			
		||||
    .inherit_flags = bdrv_inherited_flags,
 | 
			
		||||
    .inherit_options = bdrv_inherited_options,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns the flags that bs->file should get if the use of formats (and not
 | 
			
		||||
 * only protocols) is permitted for it, based on the given flags for the parent
 | 
			
		||||
 * BDS
 | 
			
		||||
 * Returns the options and flags that bs->file should get if the use of formats
 | 
			
		||||
 * (and not only protocols) is permitted for it, based on the given options and
 | 
			
		||||
 * flags for the parent BDS
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_inherited_fmt_flags(int parent_flags)
 | 
			
		||||
static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options,
 | 
			
		||||
                                       int parent_flags, QDict *parent_options)
 | 
			
		||||
{
 | 
			
		||||
    int flags = child_file.inherit_flags(parent_flags);
 | 
			
		||||
    return flags & ~BDRV_O_PROTOCOL;
 | 
			
		||||
    child_file.inherit_options(child_flags, child_options,
 | 
			
		||||
                               parent_flags, parent_options);
 | 
			
		||||
 | 
			
		||||
    *child_flags &= ~BDRV_O_PROTOCOL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const BdrvChildRole child_format = {
 | 
			
		||||
    .inherit_flags = bdrv_inherited_fmt_flags,
 | 
			
		||||
    .inherit_options = bdrv_inherited_fmt_options,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Returns the flags that bs->backing should get, based on the given flags
 | 
			
		||||
 * for the parent BDS
 | 
			
		||||
 * Returns the options and flags that bs->backing should get, based on the
 | 
			
		||||
 * given options and flags for the parent BDS
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_backing_flags(int flags)
 | 
			
		||||
static void bdrv_backing_options(int *child_flags, QDict *child_options,
 | 
			
		||||
                                 int parent_flags, QDict *parent_options)
 | 
			
		||||
{
 | 
			
		||||
    int flags = parent_flags;
 | 
			
		||||
 | 
			
		||||
    /* The cache mode is inherited unmodified for backing files */
 | 
			
		||||
    qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_WB);
 | 
			
		||||
    qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
 | 
			
		||||
    qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
 | 
			
		||||
 | 
			
		||||
    /* backing files always opened read-only */
 | 
			
		||||
    flags &= ~(BDRV_O_RDWR | BDRV_O_COPY_ON_READ);
 | 
			
		||||
 | 
			
		||||
    /* snapshot=on is handled on the top layer */
 | 
			
		||||
    flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_TEMPORARY);
 | 
			
		||||
 | 
			
		||||
    return flags;
 | 
			
		||||
    *child_flags = flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const BdrvChildRole child_backing = {
 | 
			
		||||
    .inherit_flags = bdrv_backing_flags,
 | 
			
		||||
    .inherit_options = bdrv_backing_options,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int bdrv_open_flags(BlockDriverState *bs, int flags)
 | 
			
		||||
@@ -757,6 +793,42 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
 | 
			
		||||
    return open_flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_flags_from_options(int *flags, QemuOpts *opts)
 | 
			
		||||
{
 | 
			
		||||
    *flags &= ~BDRV_O_CACHE_MASK;
 | 
			
		||||
 | 
			
		||||
    assert(qemu_opt_find(opts, BDRV_OPT_CACHE_WB));
 | 
			
		||||
    if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, false)) {
 | 
			
		||||
        *flags |= BDRV_O_CACHE_WB;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH));
 | 
			
		||||
    if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
 | 
			
		||||
        *flags |= BDRV_O_NO_FLUSH;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(qemu_opt_find(opts, BDRV_OPT_CACHE_DIRECT));
 | 
			
		||||
    if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
 | 
			
		||||
        *flags |= BDRV_O_NOCACHE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_options_from_flags(QDict *options, int flags)
 | 
			
		||||
{
 | 
			
		||||
    if (!qdict_haskey(options, BDRV_OPT_CACHE_WB)) {
 | 
			
		||||
        qdict_put(options, BDRV_OPT_CACHE_WB,
 | 
			
		||||
                  qbool_from_bool(flags & BDRV_O_CACHE_WB));
 | 
			
		||||
    }
 | 
			
		||||
    if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) {
 | 
			
		||||
        qdict_put(options, BDRV_OPT_CACHE_DIRECT,
 | 
			
		||||
                  qbool_from_bool(flags & BDRV_O_NOCACHE));
 | 
			
		||||
    }
 | 
			
		||||
    if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) {
 | 
			
		||||
        qdict_put(options, BDRV_OPT_CACHE_NO_FLUSH,
 | 
			
		||||
                  qbool_from_bool(flags & BDRV_O_NO_FLUSH));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bdrv_assign_node_name(BlockDriverState *bs,
 | 
			
		||||
                                  const char *node_name,
 | 
			
		||||
                                  Error **errp)
 | 
			
		||||
@@ -803,6 +875,26 @@ static QemuOptsList bdrv_runtime_opts = {
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "Node name of the block device node",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = "driver",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "Block driver to use for the node",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = BDRV_OPT_CACHE_WB,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "Enable writeback mode",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = BDRV_OPT_CACHE_DIRECT,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "Bypass software writeback cache on the host",
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            .name = BDRV_OPT_CACHE_NO_FLUSH,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "Ignore flush requests",
 | 
			
		||||
        },
 | 
			
		||||
        { /* end of list */ }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
@@ -813,18 +905,31 @@ static QemuOptsList bdrv_runtime_opts = {
 | 
			
		||||
 * Removes all processed options from *options.
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
 | 
			
		||||
    QDict *options, int flags, BlockDriver *drv, Error **errp)
 | 
			
		||||
                            QDict *options, int flags, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    int ret, open_flags;
 | 
			
		||||
    const char *filename;
 | 
			
		||||
    const char *driver_name = NULL;
 | 
			
		||||
    const char *node_name = NULL;
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
    BlockDriver *drv;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    assert(drv != NULL);
 | 
			
		||||
    assert(bs->file == NULL);
 | 
			
		||||
    assert(options != NULL && bs->options != options);
 | 
			
		||||
 | 
			
		||||
    opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
 | 
			
		||||
    qemu_opts_absorb_qdict(opts, options, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail_opts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    driver_name = qemu_opt_get(opts, "driver");
 | 
			
		||||
    drv = bdrv_find_format(driver_name);
 | 
			
		||||
    assert(drv != NULL);
 | 
			
		||||
 | 
			
		||||
    if (file != NULL) {
 | 
			
		||||
        filename = file->bs->filename;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -834,19 +939,12 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
 | 
			
		||||
    if (drv->bdrv_needs_filename && !filename) {
 | 
			
		||||
        error_setg(errp, "The '%s' block driver requires a file name",
 | 
			
		||||
                   drv->format_name);
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
 | 
			
		||||
 | 
			
		||||
    opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
 | 
			
		||||
    qemu_opts_absorb_qdict(opts, options, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail_opts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name);
 | 
			
		||||
 | 
			
		||||
    node_name = qemu_opt_get(opts, "node-name");
 | 
			
		||||
    bdrv_assign_node_name(bs, node_name, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
@@ -891,7 +989,9 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
 | 
			
		||||
    bs->drv = drv;
 | 
			
		||||
    bs->opaque = g_malloc0(drv->instance_size);
 | 
			
		||||
 | 
			
		||||
    bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
 | 
			
		||||
    /* Apply cache mode options */
 | 
			
		||||
    update_flags_from_options(&bs->open_flags, opts);
 | 
			
		||||
    bdrv_set_enable_write_cache(bs, bs->open_flags & BDRV_O_CACHE_WB);
 | 
			
		||||
 | 
			
		||||
    /* Open the image, either directly or using a protocol */
 | 
			
		||||
    if (drv->bdrv_file_open) {
 | 
			
		||||
@@ -984,37 +1084,45 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
 | 
			
		||||
    return options;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void parse_json_protocol(QDict *options, const char **pfilename,
 | 
			
		||||
                                Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QDict *json_options;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Parse json: pseudo-protocol */
 | 
			
		||||
    if (!*pfilename || !g_str_has_prefix(*pfilename, "json:")) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    json_options = parse_json_filename(*pfilename, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Options given in the filename have lower priority than options
 | 
			
		||||
     * specified directly */
 | 
			
		||||
    qdict_join(options, json_options, false);
 | 
			
		||||
    QDECREF(json_options);
 | 
			
		||||
    *pfilename = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Fills in default options for opening images and converts the legacy
 | 
			
		||||
 * filename/flags pair to option QDict entries.
 | 
			
		||||
 * The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a
 | 
			
		||||
 * block driver has been specified explicitly.
 | 
			
		||||
 */
 | 
			
		||||
static int bdrv_fill_options(QDict **options, const char **pfilename,
 | 
			
		||||
static int bdrv_fill_options(QDict **options, const char *filename,
 | 
			
		||||
                             int *flags, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    const char *filename = *pfilename;
 | 
			
		||||
    const char *drvname;
 | 
			
		||||
    bool protocol = *flags & BDRV_O_PROTOCOL;
 | 
			
		||||
    bool parse_filename = false;
 | 
			
		||||
    BlockDriver *drv = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Parse json: pseudo-protocol */
 | 
			
		||||
    if (filename && g_str_has_prefix(filename, "json:")) {
 | 
			
		||||
        QDict *json_options = parse_json_filename(filename, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Options given in the filename have lower priority than options
 | 
			
		||||
         * specified directly */
 | 
			
		||||
        qdict_join(*options, json_options, false);
 | 
			
		||||
        QDECREF(json_options);
 | 
			
		||||
        *pfilename = filename = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drvname = qdict_get_try_str(*options, "driver");
 | 
			
		||||
    if (drvname) {
 | 
			
		||||
        drv = bdrv_find_format(drvname);
 | 
			
		||||
@@ -1033,6 +1141,9 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
 | 
			
		||||
        *flags &= ~BDRV_O_PROTOCOL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Translate cache options from flags into options */
 | 
			
		||||
    update_options_from_flags(*options, *flags);
 | 
			
		||||
 | 
			
		||||
    /* Fetch the file name from the options QDict if necessary */
 | 
			
		||||
    if (protocol && filename) {
 | 
			
		||||
        if (!qdict_haskey(*options, "filename")) {
 | 
			
		||||
@@ -1087,11 +1198,13 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
 | 
			
		||||
 | 
			
		||||
static BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
 | 
			
		||||
                                    BlockDriverState *child_bs,
 | 
			
		||||
                                    const char *child_name,
 | 
			
		||||
                                    const BdrvChildRole *child_role)
 | 
			
		||||
{
 | 
			
		||||
    BdrvChild *child = g_new(BdrvChild, 1);
 | 
			
		||||
    *child = (BdrvChild) {
 | 
			
		||||
        .bs     = child_bs,
 | 
			
		||||
        .name   = g_strdup(child_name),
 | 
			
		||||
        .role   = child_role,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -1105,6 +1218,7 @@ static void bdrv_detach_child(BdrvChild *child)
 | 
			
		||||
{
 | 
			
		||||
    QLIST_REMOVE(child, next);
 | 
			
		||||
    QLIST_REMOVE(child, next_parent);
 | 
			
		||||
    g_free(child->name);
 | 
			
		||||
    g_free(child);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1151,7 +1265,7 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
 | 
			
		||||
        bs->backing = NULL;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    bs->backing = bdrv_attach_child(bs, backing_hd, &child_backing);
 | 
			
		||||
    bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing);
 | 
			
		||||
    bs->open_flags &= ~BDRV_O_NO_BACKING;
 | 
			
		||||
    pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_hd->filename);
 | 
			
		||||
    pstrcpy(bs->backing_format, sizeof(bs->backing_format),
 | 
			
		||||
@@ -1168,30 +1282,43 @@ out:
 | 
			
		||||
/*
 | 
			
		||||
 * Opens the backing file for a BlockDriverState if not yet open
 | 
			
		||||
 *
 | 
			
		||||
 * options is a QDict of options to pass to the block drivers, or NULL for an
 | 
			
		||||
 * empty set of options. The reference to the QDict is transferred to this
 | 
			
		||||
 * function (even on failure), so if the caller intends to reuse the dictionary,
 | 
			
		||||
 * it needs to use QINCREF() before calling bdrv_file_open.
 | 
			
		||||
 * bdref_key specifies the key for the image's BlockdevRef in the options QDict.
 | 
			
		||||
 * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict
 | 
			
		||||
 * itself, all options starting with "${bdref_key}." are considered part of the
 | 
			
		||||
 * BlockdevRef.
 | 
			
		||||
 *
 | 
			
		||||
 * TODO Can this be unified with bdrv_open_image()?
 | 
			
		||||
 */
 | 
			
		||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
 | 
			
		||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
 | 
			
		||||
                           const char *bdref_key, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    char *backing_filename = g_malloc0(PATH_MAX);
 | 
			
		||||
    char *bdref_key_dot;
 | 
			
		||||
    const char *reference = NULL;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    BlockDriverState *backing_hd;
 | 
			
		||||
    QDict *options;
 | 
			
		||||
    QDict *tmp_parent_options = NULL;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
 | 
			
		||||
    if (bs->backing != NULL) {
 | 
			
		||||
        QDECREF(options);
 | 
			
		||||
        goto free_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* NULL means an empty set of options */
 | 
			
		||||
    if (options == NULL) {
 | 
			
		||||
        options = qdict_new();
 | 
			
		||||
    if (parent_options == NULL) {
 | 
			
		||||
        tmp_parent_options = qdict_new();
 | 
			
		||||
        parent_options = tmp_parent_options;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->open_flags &= ~BDRV_O_NO_BACKING;
 | 
			
		||||
    if (qdict_haskey(options, "file.filename")) {
 | 
			
		||||
 | 
			
		||||
    bdref_key_dot = g_strdup_printf("%s.", bdref_key);
 | 
			
		||||
    qdict_extract_subqdict(parent_options, &options, bdref_key_dot);
 | 
			
		||||
    g_free(bdref_key_dot);
 | 
			
		||||
 | 
			
		||||
    reference = qdict_get_try_str(parent_options, bdref_key);
 | 
			
		||||
    if (reference || qdict_haskey(options, "file.filename")) {
 | 
			
		||||
        backing_filename[0] = '\0';
 | 
			
		||||
    } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
 | 
			
		||||
        QDECREF(options);
 | 
			
		||||
@@ -1214,19 +1341,16 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
 | 
			
		||||
        goto free_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    backing_hd = bdrv_new();
 | 
			
		||||
 | 
			
		||||
    if (bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) {
 | 
			
		||||
        qdict_put(options, "driver", qstring_from_str(bs->backing_format));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(bs->backing == NULL);
 | 
			
		||||
    backing_hd = NULL;
 | 
			
		||||
    ret = bdrv_open_inherit(&backing_hd,
 | 
			
		||||
                            *backing_filename ? backing_filename : NULL,
 | 
			
		||||
                            NULL, options, 0, bs, &child_backing, &local_err);
 | 
			
		||||
                            reference, options, 0, bs, &child_backing,
 | 
			
		||||
                            &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        bdrv_unref(backing_hd);
 | 
			
		||||
        backing_hd = NULL;
 | 
			
		||||
        bs->open_flags |= BDRV_O_NO_BACKING;
 | 
			
		||||
        error_setg(errp, "Could not open backing file: %s",
 | 
			
		||||
                   error_get_pretty(local_err));
 | 
			
		||||
@@ -1239,8 +1363,11 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
 | 
			
		||||
    bdrv_set_backing_hd(bs, backing_hd);
 | 
			
		||||
    bdrv_unref(backing_hd);
 | 
			
		||||
 | 
			
		||||
    qdict_del(parent_options, bdref_key);
 | 
			
		||||
 | 
			
		||||
free_exit:
 | 
			
		||||
    g_free(backing_filename);
 | 
			
		||||
    QDECREF(tmp_parent_options);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1294,7 +1421,7 @@ BdrvChild *bdrv_open_child(const char *filename,
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    c = bdrv_attach_child(parent, bs, child_role);
 | 
			
		||||
    c = bdrv_attach_child(parent, bs, bdref_key, child_role);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    qdict_del(options, bdref_key);
 | 
			
		||||
@@ -1437,21 +1564,34 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
 | 
			
		||||
        options = qdict_new();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (child_role) {
 | 
			
		||||
        bs->inherits_from = parent;
 | 
			
		||||
        flags = child_role->inherit_flags(parent->open_flags);
 | 
			
		||||
    /* json: syntax counts as explicit options, as if in the QDict */
 | 
			
		||||
    parse_json_protocol(options, &filename, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_fill_options(&options, &filename, &flags, &local_err);
 | 
			
		||||
    bs->explicit_options = qdict_clone_shallow(options);
 | 
			
		||||
 | 
			
		||||
    if (child_role) {
 | 
			
		||||
        bs->inherits_from = parent;
 | 
			
		||||
        child_role->inherit_options(&flags, options,
 | 
			
		||||
                                    parent->open_flags, parent->options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_fill_options(&options, filename, &flags, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->open_flags = flags;
 | 
			
		||||
    bs->options = options;
 | 
			
		||||
    options = qdict_clone_shallow(options);
 | 
			
		||||
 | 
			
		||||
    /* Find the right image format driver */
 | 
			
		||||
    drvname = qdict_get_try_str(options, "driver");
 | 
			
		||||
    if (drvname) {
 | 
			
		||||
        drv = bdrv_find_format(drvname);
 | 
			
		||||
        qdict_del(options, "driver");
 | 
			
		||||
        if (!drv) {
 | 
			
		||||
            error_setg(errp, "Unknown driver: '%s'", drvname);
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
@@ -1467,10 +1607,6 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
 | 
			
		||||
        qdict_del(options, "backing");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs->open_flags = flags;
 | 
			
		||||
    bs->options = options;
 | 
			
		||||
    options = qdict_clone_shallow(options);
 | 
			
		||||
 | 
			
		||||
    /* Open image file without format layer */
 | 
			
		||||
    if ((flags & BDRV_O_PROTOCOL) == 0) {
 | 
			
		||||
        if (flags & BDRV_O_RDWR) {
 | 
			
		||||
@@ -1478,7 +1614,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
 | 
			
		||||
        }
 | 
			
		||||
        if (flags & BDRV_O_SNAPSHOT) {
 | 
			
		||||
            snapshot_flags = bdrv_temp_snapshot_flags(flags);
 | 
			
		||||
            flags = bdrv_backing_flags(flags);
 | 
			
		||||
            bdrv_backing_options(&flags, options, flags, options);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bs->open_flags = flags;
 | 
			
		||||
@@ -1498,6 +1634,19 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
        /*
 | 
			
		||||
         * This option update would logically belong in bdrv_fill_options(),
 | 
			
		||||
         * but we first need to open bs->file for the probing to work, while
 | 
			
		||||
         * opening bs->file already requires the (mostly) final set of options
 | 
			
		||||
         * so that cache mode etc. can be inherited.
 | 
			
		||||
         *
 | 
			
		||||
         * Adding the driver later is somewhat ugly, but it's not an option
 | 
			
		||||
         * that would ever be inherited, so it's correct. We just need to make
 | 
			
		||||
         * sure to update both bs->options (which has the full effective
 | 
			
		||||
         * options for bs) and options (which has file.* already removed).
 | 
			
		||||
         */
 | 
			
		||||
        qdict_put(bs->options, "driver", qstring_from_str(drv->format_name));
 | 
			
		||||
        qdict_put(options, "driver", qstring_from_str(drv->format_name));
 | 
			
		||||
    } else if (!drv) {
 | 
			
		||||
        error_setg(errp, "Must specify either driver or file");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
@@ -1511,7 +1660,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
 | 
			
		||||
    assert(!(flags & BDRV_O_PROTOCOL) || !file);
 | 
			
		||||
 | 
			
		||||
    /* Open the image */
 | 
			
		||||
    ret = bdrv_open_common(bs, file, options, flags, drv, &local_err);
 | 
			
		||||
    ret = bdrv_open_common(bs, file, options, flags, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1523,10 +1672,7 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
 | 
			
		||||
 | 
			
		||||
    /* If there is a backing file, use it */
 | 
			
		||||
    if ((flags & BDRV_O_NO_BACKING) == 0) {
 | 
			
		||||
        QDict *backing_options;
 | 
			
		||||
 | 
			
		||||
        qdict_extract_subqdict(options, &backing_options, "backing.");
 | 
			
		||||
        ret = bdrv_open_backing_file(bs, backing_options, &local_err);
 | 
			
		||||
        ret = bdrv_open_backing_file(bs, options, "backing", &local_err);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto close_and_fail;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1581,6 +1727,7 @@ fail:
 | 
			
		||||
    if (file != NULL) {
 | 
			
		||||
        bdrv_unref_child(bs, file);
 | 
			
		||||
    }
 | 
			
		||||
    QDECREF(bs->explicit_options);
 | 
			
		||||
    QDECREF(bs->options);
 | 
			
		||||
    QDECREF(options);
 | 
			
		||||
    bs->options = NULL;
 | 
			
		||||
@@ -1643,15 +1790,19 @@ typedef struct BlockReopenQueueEntry {
 | 
			
		||||
 * bs_queue, or the existing bs_queue being used.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
 | 
			
		||||
                                    BlockDriverState *bs,
 | 
			
		||||
                                    QDict *options, int flags)
 | 
			
		||||
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
 | 
			
		||||
                                                 BlockDriverState *bs,
 | 
			
		||||
                                                 QDict *options,
 | 
			
		||||
                                                 int flags,
 | 
			
		||||
                                                 const BdrvChildRole *role,
 | 
			
		||||
                                                 QDict *parent_options,
 | 
			
		||||
                                                 int parent_flags)
 | 
			
		||||
{
 | 
			
		||||
    assert(bs != NULL);
 | 
			
		||||
 | 
			
		||||
    BlockReopenQueueEntry *bs_entry;
 | 
			
		||||
    BdrvChild *child;
 | 
			
		||||
    QDict *old_options;
 | 
			
		||||
    QDict *old_options, *explicit_options;
 | 
			
		||||
 | 
			
		||||
    if (bs_queue == NULL) {
 | 
			
		||||
        bs_queue = g_new0(BlockReopenQueue, 1);
 | 
			
		||||
@@ -1662,23 +1813,63 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
 | 
			
		||||
        options = qdict_new();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Precedence of options:
 | 
			
		||||
     * 1. Explicitly passed in options (highest)
 | 
			
		||||
     * 2. Set in flags (only for top level)
 | 
			
		||||
     * 3. Retained from explicitly set options of bs
 | 
			
		||||
     * 4. Inherited from parent node
 | 
			
		||||
     * 5. Retained from effective options of bs
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    if (!parent_options) {
 | 
			
		||||
        /*
 | 
			
		||||
         * Any setting represented by flags is always updated. If the
 | 
			
		||||
         * corresponding QDict option is set, it takes precedence. Otherwise
 | 
			
		||||
         * the flag is translated into a QDict option. The old setting of bs is
 | 
			
		||||
         * not considered.
 | 
			
		||||
         */
 | 
			
		||||
        update_options_from_flags(options, flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Old explicitly set values (don't overwrite by inherited value) */
 | 
			
		||||
    old_options = qdict_clone_shallow(bs->explicit_options);
 | 
			
		||||
    bdrv_join_options(bs, options, old_options);
 | 
			
		||||
    QDECREF(old_options);
 | 
			
		||||
 | 
			
		||||
    explicit_options = qdict_clone_shallow(options);
 | 
			
		||||
 | 
			
		||||
    /* Inherit from parent node */
 | 
			
		||||
    if (parent_options) {
 | 
			
		||||
        assert(!flags);
 | 
			
		||||
        role->inherit_options(&flags, options, parent_flags, parent_options);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Old values are used for options that aren't set yet */
 | 
			
		||||
    old_options = qdict_clone_shallow(bs->options);
 | 
			
		||||
    qdict_join(options, old_options, false);
 | 
			
		||||
    bdrv_join_options(bs, options, old_options);
 | 
			
		||||
    QDECREF(old_options);
 | 
			
		||||
 | 
			
		||||
    /* bdrv_open() masks this flag out */
 | 
			
		||||
    flags &= ~BDRV_O_PROTOCOL;
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(child, &bs->children, next) {
 | 
			
		||||
        int child_flags;
 | 
			
		||||
        QDict *new_child_options;
 | 
			
		||||
        char *child_key_dot;
 | 
			
		||||
 | 
			
		||||
        /* reopen can only change the options of block devices that were
 | 
			
		||||
         * implicitly created and inherited options. For other (referenced)
 | 
			
		||||
         * block devices, a syntax like "backing.foo" results in an error. */
 | 
			
		||||
        if (child->bs->inherits_from != bs) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        child_flags = child->role->inherit_flags(flags);
 | 
			
		||||
        /* TODO Pass down child flags (backing.*, extents.*, ...) */
 | 
			
		||||
        bdrv_reopen_queue(bs_queue, child->bs, NULL, child_flags);
 | 
			
		||||
        child_key_dot = g_strdup_printf("%s.", child->name);
 | 
			
		||||
        qdict_extract_subqdict(options, &new_child_options, child_key_dot);
 | 
			
		||||
        g_free(child_key_dot);
 | 
			
		||||
 | 
			
		||||
        bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, 0,
 | 
			
		||||
                                child->role, options, flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs_entry = g_new0(BlockReopenQueueEntry, 1);
 | 
			
		||||
@@ -1686,11 +1877,20 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
 | 
			
		||||
 | 
			
		||||
    bs_entry->state.bs = bs;
 | 
			
		||||
    bs_entry->state.options = options;
 | 
			
		||||
    bs_entry->state.explicit_options = explicit_options;
 | 
			
		||||
    bs_entry->state.flags = flags;
 | 
			
		||||
 | 
			
		||||
    return bs_queue;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
 | 
			
		||||
                                    BlockDriverState *bs,
 | 
			
		||||
                                    QDict *options, int flags)
 | 
			
		||||
{
 | 
			
		||||
    return bdrv_reopen_queue_child(bs_queue, bs, options, flags,
 | 
			
		||||
                                   NULL, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Reopen multiple BlockDriverStates atomically & transactionally.
 | 
			
		||||
 *
 | 
			
		||||
@@ -1737,6 +1937,8 @@ cleanup:
 | 
			
		||||
    QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
 | 
			
		||||
        if (ret && bs_entry->prepared) {
 | 
			
		||||
            bdrv_reopen_abort(&bs_entry->state);
 | 
			
		||||
        } else if (ret) {
 | 
			
		||||
            QDECREF(bs_entry->state.explicit_options);
 | 
			
		||||
        }
 | 
			
		||||
        QDECREF(bs_entry->state.options);
 | 
			
		||||
        g_free(bs_entry);
 | 
			
		||||
@@ -1784,11 +1986,47 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
 | 
			
		||||
    int ret = -1;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    BlockDriver *drv;
 | 
			
		||||
    QemuOpts *opts;
 | 
			
		||||
    const char *value;
 | 
			
		||||
 | 
			
		||||
    assert(reopen_state != NULL);
 | 
			
		||||
    assert(reopen_state->bs->drv != NULL);
 | 
			
		||||
    drv = reopen_state->bs->drv;
 | 
			
		||||
 | 
			
		||||
    /* Process generic block layer options */
 | 
			
		||||
    opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
 | 
			
		||||
    qemu_opts_absorb_qdict(opts, reopen_state->options, &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    update_flags_from_options(&reopen_state->flags, opts);
 | 
			
		||||
 | 
			
		||||
    /* If a guest device is attached, it owns WCE */
 | 
			
		||||
    if (reopen_state->bs->blk && blk_get_attached_dev(reopen_state->bs->blk)) {
 | 
			
		||||
        bool old_wce = bdrv_enable_write_cache(reopen_state->bs);
 | 
			
		||||
        bool new_wce = (reopen_state->flags & BDRV_O_CACHE_WB);
 | 
			
		||||
        if (old_wce != new_wce) {
 | 
			
		||||
            error_setg(errp, "Cannot change cache.writeback: Device attached");
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
            goto error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* node-name and driver must be unchanged. Put them back into the QDict, so
 | 
			
		||||
     * that they are checked at the end of this function. */
 | 
			
		||||
    value = qemu_opt_get(opts, "node-name");
 | 
			
		||||
    if (value) {
 | 
			
		||||
        qdict_put(reopen_state->options, "node-name", qstring_from_str(value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    value = qemu_opt_get(opts, "driver");
 | 
			
		||||
    if (value) {
 | 
			
		||||
        qdict_put(reopen_state->options, "driver", qstring_from_str(value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* if we are to stay read-only, do not allow permission change
 | 
			
		||||
     * to r/w */
 | 
			
		||||
    if (!(reopen_state->bs->open_flags & BDRV_O_ALLOW_RDWR) &&
 | 
			
		||||
@@ -1849,6 +2087,7 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
 | 
			
		||||
    ret = 0;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1871,6 +2110,9 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* set BDS specific flags now */
 | 
			
		||||
    QDECREF(reopen_state->bs->explicit_options);
 | 
			
		||||
 | 
			
		||||
    reopen_state->bs->explicit_options   = reopen_state->explicit_options;
 | 
			
		||||
    reopen_state->bs->open_flags         = reopen_state->flags;
 | 
			
		||||
    reopen_state->bs->enable_write_cache = !!(reopen_state->flags &
 | 
			
		||||
                                              BDRV_O_CACHE_WB);
 | 
			
		||||
@@ -1894,6 +2136,8 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
 | 
			
		||||
    if (drv->bdrv_reopen_abort) {
 | 
			
		||||
        drv->bdrv_reopen_abort(reopen_state);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QDECREF(reopen_state->explicit_options);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1952,6 +2196,7 @@ void bdrv_close(BlockDriverState *bs)
 | 
			
		||||
        bs->sg = 0;
 | 
			
		||||
        bs->zero_beyond_eof = false;
 | 
			
		||||
        QDECREF(bs->options);
 | 
			
		||||
        QDECREF(bs->explicit_options);
 | 
			
		||||
        bs->options = NULL;
 | 
			
		||||
        QDECREF(bs->full_open_options);
 | 
			
		||||
        bs->full_open_options = NULL;
 | 
			
		||||
@@ -2851,7 +3096,7 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs)
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
 | 
			
		||||
void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
 | 
			
		||||
{
 | 
			
		||||
    if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
 | 
			
		||||
        return;
 | 
			
		||||
@@ -3823,12 +4068,12 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
                       BlockDriverAmendStatusCB *status_cb)
 | 
			
		||||
                       BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
 | 
			
		||||
{
 | 
			
		||||
    if (!bs->drv->bdrv_amend_options) {
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
    return bs->drv->bdrv_amend_options(bs, opts, status_cb);
 | 
			
		||||
    return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* This function will be called by the bdrv_recurse_is_first_non_filter method
 | 
			
		||||
@@ -3926,20 +4171,39 @@ out:
 | 
			
		||||
static bool append_open_options(QDict *d, BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    const QDictEntry *entry;
 | 
			
		||||
    QemuOptDesc *desc;
 | 
			
		||||
    BdrvChild *child;
 | 
			
		||||
    bool found_any = false;
 | 
			
		||||
    const char *p;
 | 
			
		||||
 | 
			
		||||
    for (entry = qdict_first(bs->options); entry;
 | 
			
		||||
         entry = qdict_next(bs->options, entry))
 | 
			
		||||
    {
 | 
			
		||||
        /* Only take options for this level and exclude all non-driver-specific
 | 
			
		||||
         * options */
 | 
			
		||||
        if (!strchr(qdict_entry_key(entry), '.') &&
 | 
			
		||||
            strcmp(qdict_entry_key(entry), "node-name"))
 | 
			
		||||
        {
 | 
			
		||||
            qobject_incref(qdict_entry_value(entry));
 | 
			
		||||
            qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry));
 | 
			
		||||
            found_any = true;
 | 
			
		||||
        /* Exclude options for children */
 | 
			
		||||
        QLIST_FOREACH(child, &bs->children, next) {
 | 
			
		||||
            if (strstart(qdict_entry_key(entry), child->name, &p)
 | 
			
		||||
                && (!*p || *p == '.'))
 | 
			
		||||
            {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (child) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* And exclude all non-driver-specific options */
 | 
			
		||||
        for (desc = bdrv_runtime_opts.desc; desc->name; desc++) {
 | 
			
		||||
            if (!strcmp(qdict_entry_key(entry), desc->name)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (desc->name) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        qobject_incref(qdict_entry_value(entry));
 | 
			
		||||
        qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry));
 | 
			
		||||
        found_any = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return found_any;
 | 
			
		||||
@@ -3981,7 +4245,10 @@ void bdrv_refresh_filename(BlockDriverState *bs)
 | 
			
		||||
            bs->full_open_options = NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        drv->bdrv_refresh_filename(bs);
 | 
			
		||||
        opts = qdict_new();
 | 
			
		||||
        append_open_options(opts, bs);
 | 
			
		||||
        drv->bdrv_refresh_filename(bs, opts);
 | 
			
		||||
        QDECREF(opts);
 | 
			
		||||
    } else if (bs->file) {
 | 
			
		||||
        /* Try to reconstruct valid information from the underlying file */
 | 
			
		||||
        bool has_open_options;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										103
									
								
								block/blkdebug.c
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								block/blkdebug.c
									
									
									
									
									
								
							@@ -36,7 +36,7 @@ typedef struct BDRVBlkdebugState {
 | 
			
		||||
    int state;
 | 
			
		||||
    int new_state;
 | 
			
		||||
 | 
			
		||||
    QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
 | 
			
		||||
    QLIST_HEAD(, BlkdebugRule) rules[BLKDBG__MAX];
 | 
			
		||||
    QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
 | 
			
		||||
    QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
 | 
			
		||||
} BDRVBlkdebugState;
 | 
			
		||||
@@ -64,7 +64,7 @@ enum {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct BlkdebugRule {
 | 
			
		||||
    BlkDebugEvent event;
 | 
			
		||||
    BlkdebugEvent event;
 | 
			
		||||
    int action;
 | 
			
		||||
    int state;
 | 
			
		||||
    union {
 | 
			
		||||
@@ -143,69 +143,12 @@ static QemuOptsList *config_groups[] = {
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *event_names[BLKDBG_EVENT_MAX] = {
 | 
			
		||||
    [BLKDBG_L1_UPDATE]                      = "l1_update",
 | 
			
		||||
    [BLKDBG_L1_GROW_ALLOC_TABLE]            = "l1_grow.alloc_table",
 | 
			
		||||
    [BLKDBG_L1_GROW_WRITE_TABLE]            = "l1_grow.write_table",
 | 
			
		||||
    [BLKDBG_L1_GROW_ACTIVATE_TABLE]         = "l1_grow.activate_table",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_L2_LOAD]                        = "l2_load",
 | 
			
		||||
    [BLKDBG_L2_UPDATE]                      = "l2_update",
 | 
			
		||||
    [BLKDBG_L2_UPDATE_COMPRESSED]           = "l2_update_compressed",
 | 
			
		||||
    [BLKDBG_L2_ALLOC_COW_READ]              = "l2_alloc.cow_read",
 | 
			
		||||
    [BLKDBG_L2_ALLOC_WRITE]                 = "l2_alloc.write",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_READ_AIO]                       = "read_aio",
 | 
			
		||||
    [BLKDBG_READ_BACKING_AIO]               = "read_backing_aio",
 | 
			
		||||
    [BLKDBG_READ_COMPRESSED]                = "read_compressed",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_WRITE_AIO]                      = "write_aio",
 | 
			
		||||
    [BLKDBG_WRITE_COMPRESSED]               = "write_compressed",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_VMSTATE_LOAD]                   = "vmstate_load",
 | 
			
		||||
    [BLKDBG_VMSTATE_SAVE]                   = "vmstate_save",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_COW_READ]                       = "cow_read",
 | 
			
		||||
    [BLKDBG_COW_WRITE]                      = "cow_write",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_REFTABLE_LOAD]                  = "reftable_load",
 | 
			
		||||
    [BLKDBG_REFTABLE_GROW]                  = "reftable_grow",
 | 
			
		||||
    [BLKDBG_REFTABLE_UPDATE]                = "reftable_update",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_REFBLOCK_LOAD]                  = "refblock_load",
 | 
			
		||||
    [BLKDBG_REFBLOCK_UPDATE]                = "refblock_update",
 | 
			
		||||
    [BLKDBG_REFBLOCK_UPDATE_PART]           = "refblock_update_part",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC]                 = "refblock_alloc",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_HOOKUP]          = "refblock_alloc.hookup",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_WRITE]           = "refblock_alloc.write",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS]    = "refblock_alloc.write_blocks",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE]     = "refblock_alloc.write_table",
 | 
			
		||||
    [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE]    = "refblock_alloc.switch_table",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_CLUSTER_ALLOC]                  = "cluster_alloc",
 | 
			
		||||
    [BLKDBG_CLUSTER_ALLOC_BYTES]            = "cluster_alloc_bytes",
 | 
			
		||||
    [BLKDBG_CLUSTER_FREE]                   = "cluster_free",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_FLUSH_TO_OS]                    = "flush_to_os",
 | 
			
		||||
    [BLKDBG_FLUSH_TO_DISK]                  = "flush_to_disk",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_PWRITEV_RMW_HEAD]               = "pwritev_rmw.head",
 | 
			
		||||
    [BLKDBG_PWRITEV_RMW_AFTER_HEAD]         = "pwritev_rmw.after_head",
 | 
			
		||||
    [BLKDBG_PWRITEV_RMW_TAIL]               = "pwritev_rmw.tail",
 | 
			
		||||
    [BLKDBG_PWRITEV_RMW_AFTER_TAIL]         = "pwritev_rmw.after_tail",
 | 
			
		||||
    [BLKDBG_PWRITEV]                        = "pwritev",
 | 
			
		||||
    [BLKDBG_PWRITEV_ZERO]                   = "pwritev_zero",
 | 
			
		||||
    [BLKDBG_PWRITEV_DONE]                   = "pwritev_done",
 | 
			
		||||
 | 
			
		||||
    [BLKDBG_EMPTY_IMAGE_PREPARE]            = "empty_image_prepare",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int get_event_by_name(const char *name, BlkDebugEvent *event)
 | 
			
		||||
static int get_event_by_name(const char *name, BlkdebugEvent *event)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
 | 
			
		||||
        if (!strcmp(event_names[i], name)) {
 | 
			
		||||
    for (i = 0; i < BLKDBG__MAX; i++) {
 | 
			
		||||
        if (!strcmp(BlkdebugEvent_lookup[i], name)) {
 | 
			
		||||
            *event = i;
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
@@ -224,7 +167,7 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
 | 
			
		||||
    struct add_rule_data *d = opaque;
 | 
			
		||||
    BDRVBlkdebugState *s = d->s;
 | 
			
		||||
    const char* event_name;
 | 
			
		||||
    BlkDebugEvent event;
 | 
			
		||||
    BlkdebugEvent event;
 | 
			
		||||
    struct BlkdebugRule *rule;
 | 
			
		||||
 | 
			
		||||
    /* Find the right event for the rule */
 | 
			
		||||
@@ -564,7 +507,7 @@ static void blkdebug_close(BlockDriverState *bs)
 | 
			
		||||
    BlkdebugRule *rule, *next;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
 | 
			
		||||
    for (i = 0; i < BLKDBG__MAX; i++) {
 | 
			
		||||
        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
 | 
			
		||||
            remove_rule(rule);
 | 
			
		||||
        }
 | 
			
		||||
@@ -627,13 +570,13 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
 | 
			
		||||
    return injected;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
 | 
			
		||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
    struct BlkdebugRule *rule, *next;
 | 
			
		||||
    bool injected;
 | 
			
		||||
 | 
			
		||||
    assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
 | 
			
		||||
    assert((int)event >= 0 && event < BLKDBG__MAX);
 | 
			
		||||
 | 
			
		||||
    injected = false;
 | 
			
		||||
    s->new_state = s->state;
 | 
			
		||||
@@ -648,7 +591,7 @@ static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkdebugState *s = bs->opaque;
 | 
			
		||||
    struct BlkdebugRule *rule;
 | 
			
		||||
    BlkDebugEvent blkdebug_event;
 | 
			
		||||
    BlkdebugEvent blkdebug_event;
 | 
			
		||||
 | 
			
		||||
    if (get_event_by_name(event, &blkdebug_event) < 0) {
 | 
			
		||||
        return -ENOENT;
 | 
			
		||||
@@ -690,7 +633,7 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
 | 
			
		||||
    BlkdebugRule *rule, *next;
 | 
			
		||||
    int i, ret = -ENOENT;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
 | 
			
		||||
    for (i = 0; i < BLKDBG__MAX; i++) {
 | 
			
		||||
        QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
 | 
			
		||||
            if (rule->action == ACTION_SUSPEND &&
 | 
			
		||||
                !strcmp(rule->options.suspend.tag, tag)) {
 | 
			
		||||
@@ -731,17 +674,15 @@ static int blkdebug_truncate(BlockDriverState *bs, int64_t offset)
 | 
			
		||||
    return bdrv_truncate(bs->file->bs, offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blkdebug_refresh_filename(BlockDriverState *bs)
 | 
			
		||||
static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
 | 
			
		||||
{
 | 
			
		||||
    QDict *opts;
 | 
			
		||||
    const QDictEntry *e;
 | 
			
		||||
    bool force_json = false;
 | 
			
		||||
 | 
			
		||||
    for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
 | 
			
		||||
    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
 | 
			
		||||
        if (strcmp(qdict_entry_key(e), "config") &&
 | 
			
		||||
            strcmp(qdict_entry_key(e), "x-image") &&
 | 
			
		||||
            strcmp(qdict_entry_key(e), "image") &&
 | 
			
		||||
            strncmp(qdict_entry_key(e), "image.", strlen("image.")))
 | 
			
		||||
            strcmp(qdict_entry_key(e), "x-image"))
 | 
			
		||||
        {
 | 
			
		||||
            force_json = true;
 | 
			
		||||
            break;
 | 
			
		||||
@@ -757,7 +698,7 @@ static void blkdebug_refresh_filename(BlockDriverState *bs)
 | 
			
		||||
    if (!force_json && bs->file->bs->exact_filename[0]) {
 | 
			
		||||
        snprintf(bs->exact_filename, sizeof(bs->exact_filename),
 | 
			
		||||
                 "blkdebug:%s:%s",
 | 
			
		||||
                 qdict_get_try_str(bs->options, "config") ?: "",
 | 
			
		||||
                 qdict_get_try_str(options, "config") ?: "",
 | 
			
		||||
                 bs->file->bs->exact_filename);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -767,11 +708,8 @@ static void blkdebug_refresh_filename(BlockDriverState *bs)
 | 
			
		||||
    QINCREF(bs->file->bs->full_open_options);
 | 
			
		||||
    qdict_put_obj(opts, "image", QOBJECT(bs->file->bs->full_open_options));
 | 
			
		||||
 | 
			
		||||
    for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) {
 | 
			
		||||
        if (strcmp(qdict_entry_key(e), "x-image") &&
 | 
			
		||||
            strcmp(qdict_entry_key(e), "image") &&
 | 
			
		||||
            strncmp(qdict_entry_key(e), "image.", strlen("image.")))
 | 
			
		||||
        {
 | 
			
		||||
    for (e = qdict_first(options); e; e = qdict_next(options, e)) {
 | 
			
		||||
        if (strcmp(qdict_entry_key(e), "x-image")) {
 | 
			
		||||
            qobject_incref(qdict_entry_value(e));
 | 
			
		||||
            qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
 | 
			
		||||
        }
 | 
			
		||||
@@ -780,6 +718,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs)
 | 
			
		||||
    bs->full_open_options = opts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
 | 
			
		||||
                                   BlockReopenQueue *queue, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriver bdrv_blkdebug = {
 | 
			
		||||
    .format_name            = "blkdebug",
 | 
			
		||||
    .protocol_name          = "blkdebug",
 | 
			
		||||
@@ -788,6 +732,7 @@ static BlockDriver bdrv_blkdebug = {
 | 
			
		||||
    .bdrv_parse_filename    = blkdebug_parse_filename,
 | 
			
		||||
    .bdrv_file_open         = blkdebug_open,
 | 
			
		||||
    .bdrv_close             = blkdebug_close,
 | 
			
		||||
    .bdrv_reopen_prepare    = blkdebug_reopen_prepare,
 | 
			
		||||
    .bdrv_getlength         = blkdebug_getlength,
 | 
			
		||||
    .bdrv_truncate          = blkdebug_truncate,
 | 
			
		||||
    .bdrv_refresh_filename  = blkdebug_refresh_filename,
 | 
			
		||||
 
 | 
			
		||||
@@ -307,7 +307,7 @@ static void blkverify_attach_aio_context(BlockDriverState *bs,
 | 
			
		||||
    bdrv_attach_aio_context(s->test_file->bs, new_context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void blkverify_refresh_filename(BlockDriverState *bs)
 | 
			
		||||
static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
 | 
			
		||||
{
 | 
			
		||||
    BDRVBlkverifyState *s = bs->opaque;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1023,6 +1023,11 @@ int blk_get_max_transfer_length(BlockBackend *blk)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blk_get_max_iov(BlockBackend *blk)
 | 
			
		||||
{
 | 
			
		||||
    return blk->bs->bl.max_iov;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blk_set_guest_block_size(BlockBackend *blk, int align)
 | 
			
		||||
{
 | 
			
		||||
    blk->guest_block_size = align;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								block/io.c
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								block/io.c
									
									
									
									
									
								
							@@ -166,9 +166,13 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
        bs->bl.max_transfer_length = bs->file->bs->bl.max_transfer_length;
 | 
			
		||||
        bs->bl.min_mem_alignment = bs->file->bs->bl.min_mem_alignment;
 | 
			
		||||
        bs->bl.opt_mem_alignment = bs->file->bs->bl.opt_mem_alignment;
 | 
			
		||||
        bs->bl.max_iov = bs->file->bs->bl.max_iov;
 | 
			
		||||
    } else {
 | 
			
		||||
        bs->bl.min_mem_alignment = 512;
 | 
			
		||||
        bs->bl.opt_mem_alignment = getpagesize();
 | 
			
		||||
 | 
			
		||||
        /* Safe default since most protocols use readv()/writev()/etc */
 | 
			
		||||
        bs->bl.max_iov = IOV_MAX;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (bs->backing) {
 | 
			
		||||
@@ -189,6 +193,9 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp)
 | 
			
		||||
        bs->bl.min_mem_alignment =
 | 
			
		||||
            MAX(bs->bl.min_mem_alignment,
 | 
			
		||||
                bs->backing->bs->bl.min_mem_alignment);
 | 
			
		||||
        bs->bl.max_iov =
 | 
			
		||||
            MIN(bs->bl.max_iov,
 | 
			
		||||
                bs->backing->bs->bl.max_iov);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Then let the driver override it */
 | 
			
		||||
@@ -1882,7 +1889,8 @@ static int multiwrite_merge(BlockDriverState *bs, BlockRequest *reqs,
 | 
			
		||||
            merge = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 > IOV_MAX) {
 | 
			
		||||
        if (reqs[outidx].qiov->niov + reqs[i].qiov->niov + 1 >
 | 
			
		||||
            bs->bl.max_iov) {
 | 
			
		||||
            merge = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -2614,10 +2622,11 @@ int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
 | 
			
		||||
        bdrv_co_ioctl_entry(&data);
 | 
			
		||||
    } else {
 | 
			
		||||
        Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry);
 | 
			
		||||
 | 
			
		||||
        qemu_coroutine_enter(co, &data);
 | 
			
		||||
    }
 | 
			
		||||
    while (data.ret == -EINPROGRESS) {
 | 
			
		||||
        aio_poll(bdrv_get_aio_context(bs), true);
 | 
			
		||||
        while (data.ret == -EINPROGRESS) {
 | 
			
		||||
            aio_poll(bdrv_get_aio_context(bs), true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return data.ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
#include "qapi/qmp/qerror.h"
 | 
			
		||||
#include "qemu/ratelimit.h"
 | 
			
		||||
#include "qemu/bitmap.h"
 | 
			
		||||
#include "qemu/error-report.h"
 | 
			
		||||
 | 
			
		||||
#define SLICE_TIME    100000000ULL /* ns */
 | 
			
		||||
#define MAX_IN_FLIGHT 16
 | 
			
		||||
@@ -160,13 +161,15 @@ static void mirror_read_complete(void *opaque, int ret)
 | 
			
		||||
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *source = s->common.bs;
 | 
			
		||||
    int nb_sectors, sectors_per_chunk, nb_chunks;
 | 
			
		||||
    int nb_sectors, sectors_per_chunk, nb_chunks, max_iov;
 | 
			
		||||
    int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector;
 | 
			
		||||
    uint64_t delay_ns = 0;
 | 
			
		||||
    MirrorOp *op;
 | 
			
		||||
    int pnum;
 | 
			
		||||
    int64_t ret;
 | 
			
		||||
 | 
			
		||||
    max_iov = MIN(source->bl.max_iov, s->target->bl.max_iov);
 | 
			
		||||
 | 
			
		||||
    s->sector_num = hbitmap_iter_next(&s->hbi);
 | 
			
		||||
    if (s->sector_num < 0) {
 | 
			
		||||
        bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
 | 
			
		||||
@@ -247,7 +250,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
 | 
			
		||||
            trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (IOV_MAX < nb_chunks + added_chunks) {
 | 
			
		||||
        if (max_iov < nb_chunks + added_chunks) {
 | 
			
		||||
            trace_mirror_break_iov_max(s, nb_chunks, added_chunks);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -370,11 +373,22 @@ static void mirror_exit(BlockJob *job, void *opaque)
 | 
			
		||||
        if (s->to_replace) {
 | 
			
		||||
            to_replace = s->to_replace;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* This was checked in mirror_start_job(), but meanwhile one of the
 | 
			
		||||
         * nodes could have been newly attached to a BlockBackend. */
 | 
			
		||||
        if (to_replace->blk && s->target->blk) {
 | 
			
		||||
            error_report("block job: Can't create node with two BlockBackends");
 | 
			
		||||
            data->ret = -EINVAL;
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (bdrv_get_flags(s->target) != bdrv_get_flags(to_replace)) {
 | 
			
		||||
            bdrv_reopen(s->target, bdrv_get_flags(to_replace), NULL);
 | 
			
		||||
        }
 | 
			
		||||
        bdrv_replace_in_backing_chain(to_replace, s->target);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
    if (s->to_replace) {
 | 
			
		||||
        bdrv_op_unblock_all(s->to_replace, s->replace_blocker);
 | 
			
		||||
        error_free(s->replace_blocker);
 | 
			
		||||
@@ -640,7 +654,7 @@ static void mirror_complete(BlockJob *job, Error **errp)
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_open_backing_file(s->target, NULL, &local_err);
 | 
			
		||||
    ret = bdrv_open_backing_file(s->target, NULL, "backing", &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        return;
 | 
			
		||||
@@ -705,6 +719,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
 | 
			
		||||
                             bool is_none_mode, BlockDriverState *base)
 | 
			
		||||
{
 | 
			
		||||
    MirrorBlockJob *s;
 | 
			
		||||
    BlockDriverState *replaced_bs;
 | 
			
		||||
 | 
			
		||||
    if (granularity == 0) {
 | 
			
		||||
        granularity = bdrv_get_default_bitmap_granularity(target);
 | 
			
		||||
@@ -728,6 +743,21 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
 | 
			
		||||
        buf_size = DEFAULT_MIRROR_BUF_SIZE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* We can't support this case as long as the block layer can't handle
 | 
			
		||||
     * multiple BlockBackends per BlockDriverState. */
 | 
			
		||||
    if (replaces) {
 | 
			
		||||
        replaced_bs = bdrv_lookup_bs(replaces, replaces, errp);
 | 
			
		||||
        if (replaced_bs == NULL) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        replaced_bs = bs;
 | 
			
		||||
    }
 | 
			
		||||
    if (replaced_bs->blk && target->blk) {
 | 
			
		||||
        error_setg(errp, "Can't create node with two BlockBackends");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s = block_job_create(driver, bs, speed, cb, opaque, errp);
 | 
			
		||||
    if (!s) {
 | 
			
		||||
        return;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								block/nbd.c
									
									
									
									
									
								
							@@ -342,13 +342,13 @@ static void nbd_attach_aio_context(BlockDriverState *bs,
 | 
			
		||||
    nbd_client_attach_aio_context(bs, new_context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nbd_refresh_filename(BlockDriverState *bs)
 | 
			
		||||
static void nbd_refresh_filename(BlockDriverState *bs, QDict *options)
 | 
			
		||||
{
 | 
			
		||||
    QDict *opts = qdict_new();
 | 
			
		||||
    const char *path   = qdict_get_try_str(bs->options, "path");
 | 
			
		||||
    const char *host   = qdict_get_try_str(bs->options, "host");
 | 
			
		||||
    const char *port   = qdict_get_try_str(bs->options, "port");
 | 
			
		||||
    const char *export = qdict_get_try_str(bs->options, "export");
 | 
			
		||||
    const char *path   = qdict_get_try_str(options, "path");
 | 
			
		||||
    const char *host   = qdict_get_try_str(options, "host");
 | 
			
		||||
    const char *port   = qdict_get_try_str(options, "port");
 | 
			
		||||
    const char *export = qdict_get_try_str(options, "export");
 | 
			
		||||
 | 
			
		||||
    qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd")));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,7 @@ typedef struct ParallelsHeader {
 | 
			
		||||
typedef enum ParallelsPreallocMode {
 | 
			
		||||
    PRL_PREALLOC_MODE_FALLOCATE = 0,
 | 
			
		||||
    PRL_PREALLOC_MODE_TRUNCATE = 1,
 | 
			
		||||
    PRL_PREALLOC_MODE_MAX = 2,
 | 
			
		||||
    PRL_PREALLOC_MODE__MAX = 2,
 | 
			
		||||
} ParallelsPreallocMode;
 | 
			
		||||
 | 
			
		||||
static const char *prealloc_mode_lookup[] = {
 | 
			
		||||
@@ -660,7 +660,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
 | 
			
		||||
    s->prealloc_size = MAX(s->tracks, s->prealloc_size >> BDRV_SECTOR_BITS);
 | 
			
		||||
    buf = qemu_opt_get_del(opts, PARALLELS_OPT_PREALLOC_MODE);
 | 
			
		||||
    s->prealloc_mode = qapi_enum_parse(prealloc_mode_lookup, buf,
 | 
			
		||||
            PRL_PREALLOC_MODE_MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err);
 | 
			
		||||
            PRL_PREALLOC_MODE__MAX, PRL_PREALLOC_MODE_FALLOCATE, &local_err);
 | 
			
		||||
    g_free(buf);
 | 
			
		||||
    if (local_err != NULL) {
 | 
			
		||||
        goto fail_options;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								block/qapi.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								block/qapi.c
									
									
									
									
									
								
							@@ -245,15 +245,17 @@ void bdrv_query_image_info(BlockDriverState *bs,
 | 
			
		||||
        info->has_backing_filename = true;
 | 
			
		||||
        bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err);
 | 
			
		||||
        if (err) {
 | 
			
		||||
            error_propagate(errp, err);
 | 
			
		||||
            qapi_free_ImageInfo(info);
 | 
			
		||||
            /* Can't reconstruct the full backing filename, so we must omit
 | 
			
		||||
             * this field and apply a Best Effort to this query. */
 | 
			
		||||
            g_free(backing_filename2);
 | 
			
		||||
            return;
 | 
			
		||||
            backing_filename2 = NULL;
 | 
			
		||||
            error_free(err);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (strcmp(backing_filename, backing_filename2) != 0) {
 | 
			
		||||
            info->full_backing_filename =
 | 
			
		||||
                        g_strdup(backing_filename2);
 | 
			
		||||
        /* Always report the full_backing_filename if present, even if it's the
 | 
			
		||||
         * same as backing_filename. That they are same is useful info. */
 | 
			
		||||
        if (backing_filename2) {
 | 
			
		||||
            info->full_backing_filename = g_strdup(backing_filename2);
 | 
			
		||||
            info->has_full_backing_filename = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -588,7 +590,7 @@ static void dump_qlist(fprintf_function func_fprintf, void *f, int indentation,
 | 
			
		||||
    int i = 0;
 | 
			
		||||
 | 
			
		||||
    for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) {
 | 
			
		||||
        qtype_code type = qobject_type(entry->value);
 | 
			
		||||
        QType type = qobject_type(entry->value);
 | 
			
		||||
        bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
 | 
			
		||||
        const char *format = composite ? "%*s[%i]:\n" : "%*s[%i]: ";
 | 
			
		||||
 | 
			
		||||
@@ -606,7 +608,7 @@ static void dump_qdict(fprintf_function func_fprintf, void *f, int indentation,
 | 
			
		||||
    const QDictEntry *entry;
 | 
			
		||||
 | 
			
		||||
    for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) {
 | 
			
		||||
        qtype_code type = qobject_type(entry->value);
 | 
			
		||||
        QType type = qobject_type(entry->value);
 | 
			
		||||
        bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST);
 | 
			
		||||
        const char *format = composite ? "%*s%s:\n" : "%*s%s: ";
 | 
			
		||||
        char key[strlen(entry->key) + 1];
 | 
			
		||||
@@ -676,7 +678,10 @@ void bdrv_image_info_dump(fprintf_function func_fprintf, void *f,
 | 
			
		||||
 | 
			
		||||
    if (info->has_backing_filename) {
 | 
			
		||||
        func_fprintf(f, "backing file: %s", info->backing_filename);
 | 
			
		||||
        if (info->has_full_backing_filename) {
 | 
			
		||||
        if (!info->has_full_backing_filename) {
 | 
			
		||||
            func_fprintf(f, " (cannot determine actual path)");
 | 
			
		||||
        } else if (strcmp(info->backing_filename,
 | 
			
		||||
                          info->full_backing_filename) != 0) {
 | 
			
		||||
            func_fprintf(f, " (actual path: %s)", info->full_backing_filename);
 | 
			
		||||
        }
 | 
			
		||||
        func_fprintf(f, "\n");
 | 
			
		||||
 
 | 
			
		||||
@@ -1641,7 +1641,8 @@ fail:
 | 
			
		||||
static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
 | 
			
		||||
                                      int l1_size, int64_t *visited_l1_entries,
 | 
			
		||||
                                      int64_t l1_entries,
 | 
			
		||||
                                      BlockDriverAmendStatusCB *status_cb)
 | 
			
		||||
                                      BlockDriverAmendStatusCB *status_cb,
 | 
			
		||||
                                      void *cb_opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    bool is_active_l1 = (l1_table == s->l1_table);
 | 
			
		||||
@@ -1667,7 +1668,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
 | 
			
		||||
            /* unallocated */
 | 
			
		||||
            (*visited_l1_entries)++;
 | 
			
		||||
            if (status_cb) {
 | 
			
		||||
                status_cb(bs, *visited_l1_entries, l1_entries);
 | 
			
		||||
                status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque);
 | 
			
		||||
            }
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1804,7 +1805,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
 | 
			
		||||
 | 
			
		||||
        (*visited_l1_entries)++;
 | 
			
		||||
        if (status_cb) {
 | 
			
		||||
            status_cb(bs, *visited_l1_entries, l1_entries);
 | 
			
		||||
            status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1828,7 +1829,8 @@ fail:
 | 
			
		||||
 * qcow2 version which doesn't yet support metadata zero clusters.
 | 
			
		||||
 */
 | 
			
		||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
 | 
			
		||||
                               BlockDriverAmendStatusCB *status_cb)
 | 
			
		||||
                               BlockDriverAmendStatusCB *status_cb,
 | 
			
		||||
                               void *cb_opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    uint64_t *l1_table = NULL;
 | 
			
		||||
@@ -1845,7 +1847,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
    ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size,
 | 
			
		||||
                                     &visited_l1_entries, l1_entries,
 | 
			
		||||
                                     status_cb);
 | 
			
		||||
                                     status_cb, cb_opaque);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1881,7 +1883,7 @@ int qcow2_expand_zero_clusters(BlockDriverState *bs,
 | 
			
		||||
 | 
			
		||||
        ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size,
 | 
			
		||||
                                         &visited_l1_entries, l1_entries,
 | 
			
		||||
                                         status_cb);
 | 
			
		||||
                                         status_cb, cb_opaque);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto fail;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1345,6 +1345,9 @@ static int inc_refcounts(BlockDriverState *bs,
 | 
			
		||||
        if (refcount == s->refcount_max) {
 | 
			
		||||
            fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64
 | 
			
		||||
                    "\n", cluster_offset);
 | 
			
		||||
            fprintf(stderr, "Use qemu-img amend to increase the refcount entry "
 | 
			
		||||
                    "width or qemu-img convert to create a clean copy if the "
 | 
			
		||||
                    "image cannot be opened for writing\n");
 | 
			
		||||
            res->corruptions++;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
@@ -2467,3 +2470,450 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* A pointer to a function of this type is given to walk_over_reftable(). That
 | 
			
		||||
 * function will create refblocks and pass them to a RefblockFinishOp once they
 | 
			
		||||
 * are completed (@refblock). @refblock_empty is set if the refblock is
 | 
			
		||||
 * completely empty.
 | 
			
		||||
 *
 | 
			
		||||
 * Along with the refblock, a corresponding reftable entry is passed, in the
 | 
			
		||||
 * reftable @reftable (which may be reallocated) at @reftable_index.
 | 
			
		||||
 *
 | 
			
		||||
 * @allocated should be set to true if a new cluster has been allocated.
 | 
			
		||||
 */
 | 
			
		||||
typedef int (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable,
 | 
			
		||||
                               uint64_t reftable_index, uint64_t *reftable_size,
 | 
			
		||||
                               void *refblock, bool refblock_empty,
 | 
			
		||||
                               bool *allocated, Error **errp);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This "operation" for walk_over_reftable() allocates the refblock on disk (if
 | 
			
		||||
 * it is not empty) and inserts its offset into the new reftable. The size of
 | 
			
		||||
 * this new reftable is increased as required.
 | 
			
		||||
 */
 | 
			
		||||
static int alloc_refblock(BlockDriverState *bs, uint64_t **reftable,
 | 
			
		||||
                          uint64_t reftable_index, uint64_t *reftable_size,
 | 
			
		||||
                          void *refblock, bool refblock_empty, bool *allocated,
 | 
			
		||||
                          Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    int64_t offset;
 | 
			
		||||
 | 
			
		||||
    if (!refblock_empty && reftable_index >= *reftable_size) {
 | 
			
		||||
        uint64_t *new_reftable;
 | 
			
		||||
        uint64_t new_reftable_size;
 | 
			
		||||
 | 
			
		||||
        new_reftable_size = ROUND_UP(reftable_index + 1,
 | 
			
		||||
                                     s->cluster_size / sizeof(uint64_t));
 | 
			
		||||
        if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
 | 
			
		||||
            error_setg(errp,
 | 
			
		||||
                       "This operation would make the refcount table grow "
 | 
			
		||||
                       "beyond the maximum size supported by QEMU, aborting");
 | 
			
		||||
            return -ENOTSUP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        new_reftable = g_try_realloc(*reftable, new_reftable_size *
 | 
			
		||||
                                                sizeof(uint64_t));
 | 
			
		||||
        if (!new_reftable) {
 | 
			
		||||
            error_setg(errp, "Failed to increase reftable buffer size");
 | 
			
		||||
            return -ENOMEM;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        memset(new_reftable + *reftable_size, 0,
 | 
			
		||||
               (new_reftable_size - *reftable_size) * sizeof(uint64_t));
 | 
			
		||||
 | 
			
		||||
        *reftable      = new_reftable;
 | 
			
		||||
        *reftable_size = new_reftable_size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!refblock_empty && !(*reftable)[reftable_index]) {
 | 
			
		||||
        offset = qcow2_alloc_clusters(bs, s->cluster_size);
 | 
			
		||||
        if (offset < 0) {
 | 
			
		||||
            error_setg_errno(errp, -offset, "Failed to allocate refblock");
 | 
			
		||||
            return offset;
 | 
			
		||||
        }
 | 
			
		||||
        (*reftable)[reftable_index] = offset;
 | 
			
		||||
        *allocated = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This "operation" for walk_over_reftable() writes the refblock to disk at the
 | 
			
		||||
 * offset specified by the new reftable's entry. It does not modify the new
 | 
			
		||||
 * reftable or change any refcounts.
 | 
			
		||||
 */
 | 
			
		||||
static int flush_refblock(BlockDriverState *bs, uint64_t **reftable,
 | 
			
		||||
                          uint64_t reftable_index, uint64_t *reftable_size,
 | 
			
		||||
                          void *refblock, bool refblock_empty, bool *allocated,
 | 
			
		||||
                          Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    int64_t offset;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (reftable_index < *reftable_size && (*reftable)[reftable_index]) {
 | 
			
		||||
        offset = (*reftable)[reftable_index];
 | 
			
		||||
 | 
			
		||||
        ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg_errno(errp, -ret, "Overlap check failed");
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = bdrv_pwrite(bs->file->bs, offset, refblock, s->cluster_size);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg_errno(errp, -ret, "Failed to write refblock");
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        assert(refblock_empty);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This function walks over the existing reftable and every referenced refblock;
 | 
			
		||||
 * if @new_set_refcount is non-NULL, it is called for every refcount entry to
 | 
			
		||||
 * create an equal new entry in the passed @new_refblock. Once that
 | 
			
		||||
 * @new_refblock is completely filled, @operation will be called.
 | 
			
		||||
 *
 | 
			
		||||
 * @status_cb and @cb_opaque are used for the amend operation's status callback.
 | 
			
		||||
 * @index is the index of the walk_over_reftable() calls and @total is the total
 | 
			
		||||
 * number of walk_over_reftable() calls per amend operation. Both are used for
 | 
			
		||||
 * calculating the parameters for the status callback.
 | 
			
		||||
 *
 | 
			
		||||
 * @allocated is set to true if a new cluster has been allocated.
 | 
			
		||||
 */
 | 
			
		||||
static int walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable,
 | 
			
		||||
                              uint64_t *new_reftable_index,
 | 
			
		||||
                              uint64_t *new_reftable_size,
 | 
			
		||||
                              void *new_refblock, int new_refblock_size,
 | 
			
		||||
                              int new_refcount_bits,
 | 
			
		||||
                              RefblockFinishOp *operation, bool *allocated,
 | 
			
		||||
                              Qcow2SetRefcountFunc *new_set_refcount,
 | 
			
		||||
                              BlockDriverAmendStatusCB *status_cb,
 | 
			
		||||
                              void *cb_opaque, int index, int total,
 | 
			
		||||
                              Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    uint64_t reftable_index;
 | 
			
		||||
    bool new_refblock_empty = true;
 | 
			
		||||
    int refblock_index;
 | 
			
		||||
    int new_refblock_index = 0;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    for (reftable_index = 0; reftable_index < s->refcount_table_size;
 | 
			
		||||
         reftable_index++)
 | 
			
		||||
    {
 | 
			
		||||
        uint64_t refblock_offset = s->refcount_table[reftable_index]
 | 
			
		||||
                                 & REFT_OFFSET_MASK;
 | 
			
		||||
 | 
			
		||||
        status_cb(bs, (uint64_t)index * s->refcount_table_size + reftable_index,
 | 
			
		||||
                  (uint64_t)total * s->refcount_table_size, cb_opaque);
 | 
			
		||||
 | 
			
		||||
        if (refblock_offset) {
 | 
			
		||||
            void *refblock;
 | 
			
		||||
 | 
			
		||||
            if (offset_into_cluster(s, refblock_offset)) {
 | 
			
		||||
                qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#"
 | 
			
		||||
                                        PRIx64 " unaligned (reftable index: %#"
 | 
			
		||||
                                        PRIx64 ")", refblock_offset,
 | 
			
		||||
                                        reftable_index);
 | 
			
		||||
                error_setg(errp,
 | 
			
		||||
                           "Image is corrupt (unaligned refblock offset)");
 | 
			
		||||
                return -EIO;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offset,
 | 
			
		||||
                                  &refblock);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                error_setg_errno(errp, -ret, "Failed to retrieve refblock");
 | 
			
		||||
                return ret;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (refblock_index = 0; refblock_index < s->refcount_block_size;
 | 
			
		||||
                 refblock_index++)
 | 
			
		||||
            {
 | 
			
		||||
                uint64_t refcount;
 | 
			
		||||
 | 
			
		||||
                if (new_refblock_index >= new_refblock_size) {
 | 
			
		||||
                    /* new_refblock is now complete */
 | 
			
		||||
                    ret = operation(bs, new_reftable, *new_reftable_index,
 | 
			
		||||
                                    new_reftable_size, new_refblock,
 | 
			
		||||
                                    new_refblock_empty, allocated, errp);
 | 
			
		||||
                    if (ret < 0) {
 | 
			
		||||
                        qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
 | 
			
		||||
                        return ret;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    (*new_reftable_index)++;
 | 
			
		||||
                    new_refblock_index = 0;
 | 
			
		||||
                    new_refblock_empty = true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                refcount = s->get_refcount(refblock, refblock_index);
 | 
			
		||||
                if (new_refcount_bits < 64 && refcount >> new_refcount_bits) {
 | 
			
		||||
                    uint64_t offset;
 | 
			
		||||
 | 
			
		||||
                    qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
 | 
			
		||||
 | 
			
		||||
                    offset = ((reftable_index << s->refcount_block_bits)
 | 
			
		||||
                              + refblock_index) << s->cluster_bits;
 | 
			
		||||
 | 
			
		||||
                    error_setg(errp, "Cannot decrease refcount entry width to "
 | 
			
		||||
                               "%i bits: Cluster at offset %#" PRIx64 " has a "
 | 
			
		||||
                               "refcount of %" PRIu64, new_refcount_bits,
 | 
			
		||||
                               offset, refcount);
 | 
			
		||||
                    return -EINVAL;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (new_set_refcount) {
 | 
			
		||||
                    new_set_refcount(new_refblock, new_refblock_index++,
 | 
			
		||||
                                     refcount);
 | 
			
		||||
                } else {
 | 
			
		||||
                    new_refblock_index++;
 | 
			
		||||
                }
 | 
			
		||||
                new_refblock_empty = new_refblock_empty && refcount == 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            qcow2_cache_put(bs, s->refcount_block_cache, &refblock);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* No refblock means every refcount is 0 */
 | 
			
		||||
            for (refblock_index = 0; refblock_index < s->refcount_block_size;
 | 
			
		||||
                 refblock_index++)
 | 
			
		||||
            {
 | 
			
		||||
                if (new_refblock_index >= new_refblock_size) {
 | 
			
		||||
                    /* new_refblock is now complete */
 | 
			
		||||
                    ret = operation(bs, new_reftable, *new_reftable_index,
 | 
			
		||||
                                    new_reftable_size, new_refblock,
 | 
			
		||||
                                    new_refblock_empty, allocated, errp);
 | 
			
		||||
                    if (ret < 0) {
 | 
			
		||||
                        return ret;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    (*new_reftable_index)++;
 | 
			
		||||
                    new_refblock_index = 0;
 | 
			
		||||
                    new_refblock_empty = true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (new_set_refcount) {
 | 
			
		||||
                    new_set_refcount(new_refblock, new_refblock_index++, 0);
 | 
			
		||||
                } else {
 | 
			
		||||
                    new_refblock_index++;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (new_refblock_index > 0) {
 | 
			
		||||
        /* Complete the potentially existing partially filled final refblock */
 | 
			
		||||
        if (new_set_refcount) {
 | 
			
		||||
            for (; new_refblock_index < new_refblock_size;
 | 
			
		||||
                 new_refblock_index++)
 | 
			
		||||
            {
 | 
			
		||||
                new_set_refcount(new_refblock, new_refblock_index, 0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = operation(bs, new_reftable, *new_reftable_index,
 | 
			
		||||
                        new_reftable_size, new_refblock, new_refblock_empty,
 | 
			
		||||
                        allocated, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        (*new_reftable_index)++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    status_cb(bs, (uint64_t)(index + 1) * s->refcount_table_size,
 | 
			
		||||
              (uint64_t)total * s->refcount_table_size, cb_opaque);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
 | 
			
		||||
                                BlockDriverAmendStatusCB *status_cb,
 | 
			
		||||
                                void *cb_opaque, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    Qcow2GetRefcountFunc *new_get_refcount;
 | 
			
		||||
    Qcow2SetRefcountFunc *new_set_refcount;
 | 
			
		||||
    void *new_refblock = qemu_blockalign(bs->file->bs, s->cluster_size);
 | 
			
		||||
    uint64_t *new_reftable = NULL, new_reftable_size = 0;
 | 
			
		||||
    uint64_t *old_reftable, old_reftable_size, old_reftable_offset;
 | 
			
		||||
    uint64_t new_reftable_index = 0;
 | 
			
		||||
    uint64_t i;
 | 
			
		||||
    int64_t new_reftable_offset = 0, allocated_reftable_size = 0;
 | 
			
		||||
    int new_refblock_size, new_refcount_bits = 1 << refcount_order;
 | 
			
		||||
    int old_refcount_order;
 | 
			
		||||
    int walk_index = 0;
 | 
			
		||||
    int ret;
 | 
			
		||||
    bool new_allocation;
 | 
			
		||||
 | 
			
		||||
    assert(s->qcow_version >= 3);
 | 
			
		||||
    assert(refcount_order >= 0 && refcount_order <= 6);
 | 
			
		||||
 | 
			
		||||
    /* see qcow2_open() */
 | 
			
		||||
    new_refblock_size = 1 << (s->cluster_bits - (refcount_order - 3));
 | 
			
		||||
 | 
			
		||||
    new_get_refcount = get_refcount_funcs[refcount_order];
 | 
			
		||||
    new_set_refcount = set_refcount_funcs[refcount_order];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        int total_walks;
 | 
			
		||||
 | 
			
		||||
        new_allocation = false;
 | 
			
		||||
 | 
			
		||||
        /* At least we have to do this walk and the one which writes the
 | 
			
		||||
         * refblocks; also, at least we have to do this loop here at least
 | 
			
		||||
         * twice (normally), first to do the allocations, and second to
 | 
			
		||||
         * determine that everything is correctly allocated, this then makes
 | 
			
		||||
         * three walks in total */
 | 
			
		||||
        total_walks = MAX(walk_index + 2, 3);
 | 
			
		||||
 | 
			
		||||
        /* First, allocate the structures so they are present in the refcount
 | 
			
		||||
         * structures */
 | 
			
		||||
        ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index,
 | 
			
		||||
                                 &new_reftable_size, NULL, new_refblock_size,
 | 
			
		||||
                                 new_refcount_bits, &alloc_refblock,
 | 
			
		||||
                                 &new_allocation, NULL, status_cb, cb_opaque,
 | 
			
		||||
                                 walk_index++, total_walks, errp);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            goto done;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        new_reftable_index = 0;
 | 
			
		||||
 | 
			
		||||
        if (new_allocation) {
 | 
			
		||||
            if (new_reftable_offset) {
 | 
			
		||||
                qcow2_free_clusters(bs, new_reftable_offset,
 | 
			
		||||
                                    allocated_reftable_size * sizeof(uint64_t),
 | 
			
		||||
                                    QCOW2_DISCARD_NEVER);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size *
 | 
			
		||||
                                                           sizeof(uint64_t));
 | 
			
		||||
            if (new_reftable_offset < 0) {
 | 
			
		||||
                error_setg_errno(errp, -new_reftable_offset,
 | 
			
		||||
                                 "Failed to allocate the new reftable");
 | 
			
		||||
                ret = new_reftable_offset;
 | 
			
		||||
                goto done;
 | 
			
		||||
            }
 | 
			
		||||
            allocated_reftable_size = new_reftable_size;
 | 
			
		||||
        }
 | 
			
		||||
    } while (new_allocation);
 | 
			
		||||
 | 
			
		||||
    /* Second, write the new refblocks */
 | 
			
		||||
    ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index,
 | 
			
		||||
                             &new_reftable_size, new_refblock,
 | 
			
		||||
                             new_refblock_size, new_refcount_bits,
 | 
			
		||||
                             &flush_refblock, &new_allocation, new_set_refcount,
 | 
			
		||||
                             status_cb, cb_opaque, walk_index, walk_index + 1,
 | 
			
		||||
                             errp);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
    assert(!new_allocation);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /* Write the new reftable */
 | 
			
		||||
    ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset,
 | 
			
		||||
                                        new_reftable_size * sizeof(uint64_t));
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Overlap check failed");
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < new_reftable_size; i++) {
 | 
			
		||||
        cpu_to_be64s(&new_reftable[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_pwrite(bs->file->bs, new_reftable_offset, new_reftable,
 | 
			
		||||
                      new_reftable_size * sizeof(uint64_t));
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < new_reftable_size; i++) {
 | 
			
		||||
        be64_to_cpus(&new_reftable[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Failed to write the new reftable");
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /* Empty the refcount cache */
 | 
			
		||||
    ret = qcow2_cache_flush(bs, s->refcount_block_cache);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Failed to flush the refblock cache");
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Update the image header to point to the new reftable; this only updates
 | 
			
		||||
     * the fields which are relevant to qcow2_update_header(); other fields
 | 
			
		||||
     * such as s->refcount_table or s->refcount_bits stay stale for now
 | 
			
		||||
     * (because we have to restore everything if qcow2_update_header() fails) */
 | 
			
		||||
    old_refcount_order  = s->refcount_order;
 | 
			
		||||
    old_reftable_size   = s->refcount_table_size;
 | 
			
		||||
    old_reftable_offset = s->refcount_table_offset;
 | 
			
		||||
 | 
			
		||||
    s->refcount_order        = refcount_order;
 | 
			
		||||
    s->refcount_table_size   = new_reftable_size;
 | 
			
		||||
    s->refcount_table_offset = new_reftable_offset;
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_update_header(bs);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        s->refcount_order        = old_refcount_order;
 | 
			
		||||
        s->refcount_table_size   = old_reftable_size;
 | 
			
		||||
        s->refcount_table_offset = old_reftable_offset;
 | 
			
		||||
        error_setg_errno(errp, -ret, "Failed to update the qcow2 header");
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Now update the rest of the in-memory information */
 | 
			
		||||
    old_reftable = s->refcount_table;
 | 
			
		||||
    s->refcount_table = new_reftable;
 | 
			
		||||
 | 
			
		||||
    s->refcount_bits = 1 << refcount_order;
 | 
			
		||||
    s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1);
 | 
			
		||||
    s->refcount_max += s->refcount_max - 1;
 | 
			
		||||
 | 
			
		||||
    s->refcount_block_bits = s->cluster_bits - (refcount_order - 3);
 | 
			
		||||
    s->refcount_block_size = 1 << s->refcount_block_bits;
 | 
			
		||||
 | 
			
		||||
    s->get_refcount = new_get_refcount;
 | 
			
		||||
    s->set_refcount = new_set_refcount;
 | 
			
		||||
 | 
			
		||||
    /* For cleaning up all old refblocks and the old reftable below the "done"
 | 
			
		||||
     * label */
 | 
			
		||||
    new_reftable        = old_reftable;
 | 
			
		||||
    new_reftable_size   = old_reftable_size;
 | 
			
		||||
    new_reftable_offset = old_reftable_offset;
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
    if (new_reftable) {
 | 
			
		||||
        /* On success, new_reftable actually points to the old reftable (and
 | 
			
		||||
         * new_reftable_size is the old reftable's size); but that is just
 | 
			
		||||
         * fine */
 | 
			
		||||
        for (i = 0; i < new_reftable_size; i++) {
 | 
			
		||||
            uint64_t offset = new_reftable[i] & REFT_OFFSET_MASK;
 | 
			
		||||
            if (offset) {
 | 
			
		||||
                qcow2_free_clusters(bs, offset, s->cluster_size,
 | 
			
		||||
                                    QCOW2_DISCARD_OTHER);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        g_free(new_reftable);
 | 
			
		||||
 | 
			
		||||
        if (new_reftable_offset > 0) {
 | 
			
		||||
            qcow2_free_clusters(bs, new_reftable_offset,
 | 
			
		||||
                                new_reftable_size * sizeof(uint64_t),
 | 
			
		||||
                                QCOW2_DISCARD_OTHER);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_vfree(new_refblock);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										231
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										231
									
								
								block/qcow2.c
									
									
									
									
									
								
							@@ -1282,6 +1282,52 @@ static void qcow2_reopen_abort(BDRVReopenState *state)
 | 
			
		||||
    g_free(state->opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void qcow2_join_options(QDict *options, QDict *old_options)
 | 
			
		||||
{
 | 
			
		||||
    bool has_new_overlap_template =
 | 
			
		||||
        qdict_haskey(options, QCOW2_OPT_OVERLAP) ||
 | 
			
		||||
        qdict_haskey(options, QCOW2_OPT_OVERLAP_TEMPLATE);
 | 
			
		||||
    bool has_new_total_cache_size =
 | 
			
		||||
        qdict_haskey(options, QCOW2_OPT_CACHE_SIZE);
 | 
			
		||||
    bool has_all_cache_options;
 | 
			
		||||
 | 
			
		||||
    /* New overlap template overrides all old overlap options */
 | 
			
		||||
    if (has_new_overlap_template) {
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_TEMPLATE);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_MAIN_HEADER);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L1);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L2);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_TABLE);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L1);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* New total cache size overrides all old options */
 | 
			
		||||
    if (qdict_haskey(options, QCOW2_OPT_CACHE_SIZE)) {
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_L2_CACHE_SIZE);
 | 
			
		||||
        qdict_del(old_options, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qdict_join(options, old_options, false);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * If after merging all cache size options are set, an old total size is
 | 
			
		||||
     * overwritten. Do keep all options, however, if all three are new. The
 | 
			
		||||
     * resulting error message is what we want to happen.
 | 
			
		||||
     */
 | 
			
		||||
    has_all_cache_options =
 | 
			
		||||
        qdict_haskey(options, QCOW2_OPT_CACHE_SIZE) ||
 | 
			
		||||
        qdict_haskey(options, QCOW2_OPT_L2_CACHE_SIZE) ||
 | 
			
		||||
        qdict_haskey(options, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
 | 
			
		||||
 | 
			
		||||
    if (has_all_cache_options && !has_new_total_cache_size) {
 | 
			
		||||
        qdict_del(options, QCOW2_OPT_CACHE_SIZE);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
 | 
			
		||||
        int64_t sector_num, int nb_sectors, int *pnum)
 | 
			
		||||
{
 | 
			
		||||
@@ -2269,7 +2315,7 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
 | 
			
		||||
                                         DEFAULT_CLUSTER_SIZE);
 | 
			
		||||
    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
 | 
			
		||||
    prealloc = qapi_enum_parse(PreallocMode_lookup, buf,
 | 
			
		||||
                               PREALLOC_MODE_MAX, PREALLOC_MODE_OFF,
 | 
			
		||||
                               PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
 | 
			
		||||
                               &local_err);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
@@ -2757,6 +2803,10 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
 | 
			
		||||
            .has_corrupt        = true,
 | 
			
		||||
            .refcount_bits      = s->refcount_bits,
 | 
			
		||||
        };
 | 
			
		||||
    } else {
 | 
			
		||||
        /* if this assertion fails, this probably means a new version was
 | 
			
		||||
         * added without having it covered here */
 | 
			
		||||
        assert(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return spec_info;
 | 
			
		||||
@@ -2824,7 +2874,7 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
 | 
			
		||||
 * have to be removed.
 | 
			
		||||
 */
 | 
			
		||||
static int qcow2_downgrade(BlockDriverState *bs, int target_version,
 | 
			
		||||
                           BlockDriverAmendStatusCB *status_cb)
 | 
			
		||||
                           BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    int current_version = s->qcow_version;
 | 
			
		||||
@@ -2839,13 +2889,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->refcount_order != 4) {
 | 
			
		||||
        /* we would have to convert the image to a refcount_order == 4 image
 | 
			
		||||
         * here; however, since qemu (at the time of writing this) does not
 | 
			
		||||
         * support anything different than 4 anyway, there is no point in doing
 | 
			
		||||
         * so right now; however, we should error out (if qemu supports this in
 | 
			
		||||
         * the future and this code has not been adapted) */
 | 
			
		||||
        error_report("qcow2_downgrade: Image refcount orders other than 4 are "
 | 
			
		||||
                     "currently not supported.");
 | 
			
		||||
        error_report("compat=0.10 requires refcount_bits=16");
 | 
			
		||||
        return -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2873,7 +2917,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
 | 
			
		||||
    /* clearing autoclear features is trivial */
 | 
			
		||||
    s->autoclear_features = 0;
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_expand_zero_clusters(bs, status_cb);
 | 
			
		||||
    ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
@@ -2887,8 +2931,79 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef enum Qcow2AmendOperation {
 | 
			
		||||
    /* This is the value Qcow2AmendHelperCBInfo::last_operation will be
 | 
			
		||||
     * statically initialized to so that the helper CB can discern the first
 | 
			
		||||
     * invocation from an operation change */
 | 
			
		||||
    QCOW2_NO_OPERATION = 0,
 | 
			
		||||
 | 
			
		||||
    QCOW2_CHANGING_REFCOUNT_ORDER,
 | 
			
		||||
    QCOW2_DOWNGRADING,
 | 
			
		||||
} Qcow2AmendOperation;
 | 
			
		||||
 | 
			
		||||
typedef struct Qcow2AmendHelperCBInfo {
 | 
			
		||||
    /* The code coordinating the amend operations should only modify
 | 
			
		||||
     * these four fields; the rest will be managed by the CB */
 | 
			
		||||
    BlockDriverAmendStatusCB *original_status_cb;
 | 
			
		||||
    void *original_cb_opaque;
 | 
			
		||||
 | 
			
		||||
    Qcow2AmendOperation current_operation;
 | 
			
		||||
 | 
			
		||||
    /* Total number of operations to perform (only set once) */
 | 
			
		||||
    int total_operations;
 | 
			
		||||
 | 
			
		||||
    /* The following fields are managed by the CB */
 | 
			
		||||
 | 
			
		||||
    /* Number of operations completed */
 | 
			
		||||
    int operations_completed;
 | 
			
		||||
 | 
			
		||||
    /* Cumulative offset of all completed operations */
 | 
			
		||||
    int64_t offset_completed;
 | 
			
		||||
 | 
			
		||||
    Qcow2AmendOperation last_operation;
 | 
			
		||||
    int64_t last_work_size;
 | 
			
		||||
} Qcow2AmendHelperCBInfo;
 | 
			
		||||
 | 
			
		||||
static void qcow2_amend_helper_cb(BlockDriverState *bs,
 | 
			
		||||
                                  int64_t operation_offset,
 | 
			
		||||
                                  int64_t operation_work_size, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    Qcow2AmendHelperCBInfo *info = opaque;
 | 
			
		||||
    int64_t current_work_size;
 | 
			
		||||
    int64_t projected_work_size;
 | 
			
		||||
 | 
			
		||||
    if (info->current_operation != info->last_operation) {
 | 
			
		||||
        if (info->last_operation != QCOW2_NO_OPERATION) {
 | 
			
		||||
            info->offset_completed += info->last_work_size;
 | 
			
		||||
            info->operations_completed++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        info->last_operation = info->current_operation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert(info->total_operations > 0);
 | 
			
		||||
    assert(info->operations_completed < info->total_operations);
 | 
			
		||||
 | 
			
		||||
    info->last_work_size = operation_work_size;
 | 
			
		||||
 | 
			
		||||
    current_work_size = info->offset_completed + operation_work_size;
 | 
			
		||||
 | 
			
		||||
    /* current_work_size is the total work size for (operations_completed + 1)
 | 
			
		||||
     * operations (which includes this one), so multiply it by the number of
 | 
			
		||||
     * operations not covered and divide it by the number of operations
 | 
			
		||||
     * covered to get a projection for the operations not covered */
 | 
			
		||||
    projected_work_size = current_work_size * (info->total_operations -
 | 
			
		||||
                                               info->operations_completed - 1)
 | 
			
		||||
                                            / (info->operations_completed + 1);
 | 
			
		||||
 | 
			
		||||
    info->original_status_cb(bs, info->offset_completed + operation_offset,
 | 
			
		||||
                             current_work_size + projected_work_size,
 | 
			
		||||
                             info->original_cb_opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
                               BlockDriverAmendStatusCB *status_cb)
 | 
			
		||||
                               BlockDriverAmendStatusCB *status_cb,
 | 
			
		||||
                               void *cb_opaque)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQcow2State *s = bs->opaque;
 | 
			
		||||
    int old_version = s->qcow_version, new_version = old_version;
 | 
			
		||||
@@ -2898,8 +3013,10 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
    const char *compat = NULL;
 | 
			
		||||
    uint64_t cluster_size = s->cluster_size;
 | 
			
		||||
    bool encrypt;
 | 
			
		||||
    int refcount_bits = s->refcount_bits;
 | 
			
		||||
    int ret;
 | 
			
		||||
    QemuOptDesc *desc = opts->list->desc;
 | 
			
		||||
    Qcow2AmendHelperCBInfo helper_cb_info;
 | 
			
		||||
 | 
			
		||||
    while (desc && desc->name) {
 | 
			
		||||
        if (!qemu_opt_find(opts, desc->name)) {
 | 
			
		||||
@@ -2917,11 +3034,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
            } else if (!strcmp(compat, "1.1")) {
 | 
			
		||||
                new_version = 3;
 | 
			
		||||
            } else {
 | 
			
		||||
                fprintf(stderr, "Unknown compatibility level %s.\n", compat);
 | 
			
		||||
                error_report("Unknown compatibility level %s", compat);
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
 | 
			
		||||
            fprintf(stderr, "Cannot change preallocation mode.\n");
 | 
			
		||||
            error_report("Cannot change preallocation mode");
 | 
			
		||||
            return -ENOTSUP;
 | 
			
		||||
        } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
 | 
			
		||||
            new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
 | 
			
		||||
@@ -2934,47 +3051,74 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
                                        !!s->cipher);
 | 
			
		||||
 | 
			
		||||
            if (encrypt != !!s->cipher) {
 | 
			
		||||
                fprintf(stderr, "Changing the encryption flag is not "
 | 
			
		||||
                        "supported.\n");
 | 
			
		||||
                error_report("Changing the encryption flag is not supported");
 | 
			
		||||
                return -ENOTSUP;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
 | 
			
		||||
            cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
 | 
			
		||||
                                             cluster_size);
 | 
			
		||||
            if (cluster_size != s->cluster_size) {
 | 
			
		||||
                fprintf(stderr, "Changing the cluster size is not "
 | 
			
		||||
                        "supported.\n");
 | 
			
		||||
                error_report("Changing the cluster size is not supported");
 | 
			
		||||
                return -ENOTSUP;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
 | 
			
		||||
            lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS,
 | 
			
		||||
                                               lazy_refcounts);
 | 
			
		||||
        } else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) {
 | 
			
		||||
            error_report("Cannot change refcount entry width");
 | 
			
		||||
            return -ENOTSUP;
 | 
			
		||||
            refcount_bits = qemu_opt_get_number(opts, BLOCK_OPT_REFCOUNT_BITS,
 | 
			
		||||
                                                refcount_bits);
 | 
			
		||||
 | 
			
		||||
            if (refcount_bits <= 0 || refcount_bits > 64 ||
 | 
			
		||||
                !is_power_of_2(refcount_bits))
 | 
			
		||||
            {
 | 
			
		||||
                error_report("Refcount width must be a power of two and may "
 | 
			
		||||
                             "not exceed 64 bits");
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            /* if this assertion fails, this probably means a new option was
 | 
			
		||||
            /* if this point is reached, this probably means a new option was
 | 
			
		||||
             * added without having it covered here */
 | 
			
		||||
            assert(false);
 | 
			
		||||
            abort();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        desc++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (new_version != old_version) {
 | 
			
		||||
        if (new_version > old_version) {
 | 
			
		||||
            /* Upgrade */
 | 
			
		||||
            s->qcow_version = new_version;
 | 
			
		||||
            ret = qcow2_update_header(bs);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                s->qcow_version = old_version;
 | 
			
		||||
                return ret;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = qcow2_downgrade(bs, new_version, status_cb);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                return ret;
 | 
			
		||||
            }
 | 
			
		||||
    helper_cb_info = (Qcow2AmendHelperCBInfo){
 | 
			
		||||
        .original_status_cb = status_cb,
 | 
			
		||||
        .original_cb_opaque = cb_opaque,
 | 
			
		||||
        .total_operations = (new_version < old_version)
 | 
			
		||||
                          + (s->refcount_bits != refcount_bits)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* Upgrade first (some features may require compat=1.1) */
 | 
			
		||||
    if (new_version > old_version) {
 | 
			
		||||
        s->qcow_version = new_version;
 | 
			
		||||
        ret = qcow2_update_header(bs);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            s->qcow_version = old_version;
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->refcount_bits != refcount_bits) {
 | 
			
		||||
        int refcount_order = ctz32(refcount_bits);
 | 
			
		||||
        Error *local_error = NULL;
 | 
			
		||||
 | 
			
		||||
        if (new_version < 3 && refcount_bits != 16) {
 | 
			
		||||
            error_report("Different refcount widths than 16 bits require "
 | 
			
		||||
                         "compatibility level 1.1 or above (use compat=1.1 or "
 | 
			
		||||
                         "greater)");
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER;
 | 
			
		||||
        ret = qcow2_change_refcount_order(bs, refcount_order,
 | 
			
		||||
                                          &qcow2_amend_helper_cb,
 | 
			
		||||
                                          &helper_cb_info, &local_error);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_report_err(local_error);
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -2989,9 +3133,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
 | 
			
		||||
    if (s->use_lazy_refcounts != lazy_refcounts) {
 | 
			
		||||
        if (lazy_refcounts) {
 | 
			
		||||
            if (s->qcow_version < 3) {
 | 
			
		||||
                fprintf(stderr, "Lazy refcounts only supported with compatibility "
 | 
			
		||||
                        "level 1.1 and above (use compat=1.1 or greater)\n");
 | 
			
		||||
            if (new_version < 3) {
 | 
			
		||||
                error_report("Lazy refcounts only supported with compatibility "
 | 
			
		||||
                             "level 1.1 and above (use compat=1.1 or greater)");
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
            s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
 | 
			
		||||
@@ -3025,6 +3169,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Downgrade last (so unsupported features can be removed before) */
 | 
			
		||||
    if (new_version < old_version) {
 | 
			
		||||
        helper_cb_info.current_operation = QCOW2_DOWNGRADING;
 | 
			
		||||
        ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb,
 | 
			
		||||
                              &helper_cb_info);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -3145,6 +3299,7 @@ BlockDriver bdrv_qcow2 = {
 | 
			
		||||
    .bdrv_reopen_prepare  = qcow2_reopen_prepare,
 | 
			
		||||
    .bdrv_reopen_commit   = qcow2_reopen_commit,
 | 
			
		||||
    .bdrv_reopen_abort    = qcow2_reopen_abort,
 | 
			
		||||
    .bdrv_join_options    = qcow2_join_options,
 | 
			
		||||
    .bdrv_create        = qcow2_create,
 | 
			
		||||
    .bdrv_has_zero_init = bdrv_has_zero_init_1,
 | 
			
		||||
    .bdrv_co_get_block_status = qcow2_co_get_block_status,
 | 
			
		||||
 
 | 
			
		||||
@@ -529,6 +529,10 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
 | 
			
		||||
int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset,
 | 
			
		||||
                                  int64_t size);
 | 
			
		||||
 | 
			
		||||
int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
 | 
			
		||||
                                BlockDriverAmendStatusCB *status_cb,
 | 
			
		||||
                                void *cb_opaque, Error **errp);
 | 
			
		||||
 | 
			
		||||
/* qcow2-cluster.c functions */
 | 
			
		||||
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
 | 
			
		||||
                        bool exact_size);
 | 
			
		||||
@@ -553,7 +557,8 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
 | 
			
		||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
 | 
			
		||||
 | 
			
		||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
 | 
			
		||||
                               BlockDriverAmendStatusCB *status_cb);
 | 
			
		||||
                               BlockDriverAmendStatusCB *status_cb,
 | 
			
		||||
                               void *cb_opaque);
 | 
			
		||||
 | 
			
		||||
/* qcow2-snapshot.c functions */
 | 
			
		||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
 | 
			
		||||
 
 | 
			
		||||
@@ -847,7 +847,7 @@ static int parse_read_pattern(const char *opt)
 | 
			
		||||
        return QUORUM_READ_PATTERN_QUORUM;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < QUORUM_READ_PATTERN_MAX; i++) {
 | 
			
		||||
    for (i = 0; i < QUORUM_READ_PATTERN__MAX; i++) {
 | 
			
		||||
        if (!strcmp(opt, QuorumReadPattern_lookup[i])) {
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
@@ -997,7 +997,7 @@ static void quorum_attach_aio_context(BlockDriverState *bs,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void quorum_refresh_filename(BlockDriverState *bs)
 | 
			
		||||
static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
 | 
			
		||||
{
 | 
			
		||||
    BDRVQuorumState *s = bs->opaque;
 | 
			
		||||
    QDict *opts;
 | 
			
		||||
 
 | 
			
		||||
@@ -500,21 +500,17 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
    if (!s->use_aio && (bdrv_flags & BDRV_O_NATIVE_AIO)) {
 | 
			
		||||
        error_printf("WARNING: aio=native was specified for '%s', but "
 | 
			
		||||
                     "it requires cache.direct=on, which was not "
 | 
			
		||||
                     "specified. Falling back to aio=threads.\n"
 | 
			
		||||
                     "         This will become an error condition in "
 | 
			
		||||
                     "future QEMU versions.\n",
 | 
			
		||||
                     bs->filename);
 | 
			
		||||
        error_setg(errp, "aio=native was specified, but it requires "
 | 
			
		||||
                         "cache.direct=on, which was not specified.");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    if (bdrv_flags & BDRV_O_NATIVE_AIO) {
 | 
			
		||||
        error_printf("WARNING: aio=native was specified for '%s', but "
 | 
			
		||||
                     "is not supported in this build. Falling back to "
 | 
			
		||||
                     "aio=threads.\n"
 | 
			
		||||
                     "         This will become an error condition in "
 | 
			
		||||
                     "future QEMU versions.\n",
 | 
			
		||||
                     bs->filename);
 | 
			
		||||
        error_setg(errp, "aio=native was specified, but is not supported "
 | 
			
		||||
                         "in this build.");
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
#endif /* !defined(CONFIG_LINUX_AIO) */
 | 
			
		||||
 | 
			
		||||
@@ -1636,7 +1632,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
 | 
			
		||||
    nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false);
 | 
			
		||||
    buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
 | 
			
		||||
    prealloc = qapi_enum_parse(PreallocMode_lookup, buf,
 | 
			
		||||
                               PREALLOC_MODE_MAX, PREALLOC_MODE_OFF,
 | 
			
		||||
                               PREALLOC_MODE__MAX, PREALLOC_MODE_OFF,
 | 
			
		||||
                               &local_err);
 | 
			
		||||
    g_free(buf);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
 
 | 
			
		||||
@@ -229,6 +229,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
 | 
			
		||||
                         Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriver *drv = bs->drv;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (!drv) {
 | 
			
		||||
        error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
 | 
			
		||||
        return -ENOMEDIUM;
 | 
			
		||||
@@ -239,18 +241,21 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* drain all pending i/o before deleting snapshot */
 | 
			
		||||
    bdrv_drain(bs);
 | 
			
		||||
    bdrv_drained_begin(bs);
 | 
			
		||||
 | 
			
		||||
    if (drv->bdrv_snapshot_delete) {
 | 
			
		||||
        return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
 | 
			
		||||
        ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
 | 
			
		||||
    } else if (bs->file) {
 | 
			
		||||
        ret = bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp);
 | 
			
		||||
    } else {
 | 
			
		||||
        error_setg(errp, "Block format '%s' used by device '%s' "
 | 
			
		||||
                   "does not support internal snapshot deletion",
 | 
			
		||||
                   drv->format_name, bdrv_get_device_name(bs));
 | 
			
		||||
        ret = -ENOTSUP;
 | 
			
		||||
    }
 | 
			
		||||
    if (bs->file) {
 | 
			
		||||
        return bdrv_snapshot_delete(bs->file->bs, snapshot_id, name, errp);
 | 
			
		||||
    }
 | 
			
		||||
    error_setg(errp, "Block format '%s' used by device '%s' "
 | 
			
		||||
               "does not support internal snapshot deletion",
 | 
			
		||||
               drv->format_name, bdrv_get_device_name(bs));
 | 
			
		||||
    return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
    bdrv_drained_end(bs);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								blockdev.c
									
									
									
									
									
								
							@@ -387,16 +387,6 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) {
 | 
			
		||||
            *bdrv_flags |= BDRV_O_CACHE_WB;
 | 
			
		||||
        }
 | 
			
		||||
        if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
 | 
			
		||||
            *bdrv_flags |= BDRV_O_NOCACHE;
 | 
			
		||||
        }
 | 
			
		||||
        if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
 | 
			
		||||
            *bdrv_flags |= BDRV_O_NO_FLUSH;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((aio = qemu_opt_get(opts, "aio")) != NULL) {
 | 
			
		||||
            if (!strcmp(aio, "native")) {
 | 
			
		||||
                *bdrv_flags |= BDRV_O_NATIVE_AIO;
 | 
			
		||||
@@ -454,7 +444,7 @@ static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags,
 | 
			
		||||
        *detect_zeroes =
 | 
			
		||||
            qapi_enum_parse(BlockdevDetectZeroesOptions_lookup,
 | 
			
		||||
                            qemu_opt_get(opts, "detect-zeroes"),
 | 
			
		||||
                            BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
 | 
			
		||||
                            BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX,
 | 
			
		||||
                            BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
 | 
			
		||||
                            &local_error);
 | 
			
		||||
        if (local_error) {
 | 
			
		||||
@@ -490,7 +480,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 | 
			
		||||
    QDict *interval_dict = NULL;
 | 
			
		||||
    QList *interval_list = NULL;
 | 
			
		||||
    const char *id;
 | 
			
		||||
    bool has_driver_specific_opts;
 | 
			
		||||
    BlockdevDetectZeroesOptions detect_zeroes =
 | 
			
		||||
        BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
 | 
			
		||||
    const char *throttling_group = NULL;
 | 
			
		||||
@@ -514,8 +503,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 | 
			
		||||
        qdict_del(bs_opts, "id");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    has_driver_specific_opts = !!qdict_size(bs_opts);
 | 
			
		||||
 | 
			
		||||
    /* extract parameters */
 | 
			
		||||
    snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
 | 
			
		||||
 | 
			
		||||
@@ -572,13 +559,11 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (snapshot) {
 | 
			
		||||
        /* always use cache=unsafe with snapshot */
 | 
			
		||||
        bdrv_flags &= ~BDRV_O_CACHE_MASK;
 | 
			
		||||
        bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
 | 
			
		||||
        bdrv_flags |= BDRV_O_SNAPSHOT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* init */
 | 
			
		||||
    if ((!file || !*file) && !has_driver_specific_opts) {
 | 
			
		||||
    if ((!file || !*file) && !qdict_size(bs_opts)) {
 | 
			
		||||
        BlockBackendRootState *blk_rs;
 | 
			
		||||
 | 
			
		||||
        blk = blk_new(qemu_opts_id(opts), errp);
 | 
			
		||||
@@ -606,6 +591,20 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
 | 
			
		||||
            file = NULL;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* bdrv_open() defaults to the values in bdrv_flags (for compatibility
 | 
			
		||||
         * with other callers) rather than what we want as the real defaults.
 | 
			
		||||
         * Apply the defaults here instead. */
 | 
			
		||||
        qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_WB, "on");
 | 
			
		||||
        qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off");
 | 
			
		||||
        qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
 | 
			
		||||
 | 
			
		||||
        if (snapshot) {
 | 
			
		||||
            /* always use cache=unsafe with snapshot */
 | 
			
		||||
            qdict_put(bs_opts, BDRV_OPT_CACHE_WB, qstring_from_str("on"));
 | 
			
		||||
            qdict_put(bs_opts, BDRV_OPT_CACHE_DIRECT, qstring_from_str("off"));
 | 
			
		||||
            qdict_put(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, qstring_from_str("on"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags,
 | 
			
		||||
                           errp);
 | 
			
		||||
        if (!blk) {
 | 
			
		||||
@@ -3872,18 +3871,6 @@ QemuOptsList qemu_common_drive_opts = {
 | 
			
		||||
            .name = "discard",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "discard operation (ignore/off, unmap/on)",
 | 
			
		||||
        },{
 | 
			
		||||
            .name = BDRV_OPT_CACHE_WB,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "enables writeback mode for any caches",
 | 
			
		||||
        },{
 | 
			
		||||
            .name = BDRV_OPT_CACHE_DIRECT,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "enables use of O_DIRECT (bypass the host page cache)",
 | 
			
		||||
        },{
 | 
			
		||||
            .name = BDRV_OPT_CACHE_NO_FLUSH,
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "ignore any flush requests for the device",
 | 
			
		||||
        },{
 | 
			
		||||
            .name = "aio",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
@@ -3991,18 +3978,6 @@ static QemuOptsList qemu_root_bds_opts = {
 | 
			
		||||
            .name = "discard",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
            .help = "discard operation (ignore/off, unmap/on)",
 | 
			
		||||
        },{
 | 
			
		||||
            .name = "cache.writeback",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "enables writeback mode for any caches",
 | 
			
		||||
        },{
 | 
			
		||||
            .name = "cache.direct",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "enables use of O_DIRECT (bypass the host page cache)",
 | 
			
		||||
        },{
 | 
			
		||||
            .name = "cache.no-flush",
 | 
			
		||||
            .type = QEMU_OPT_BOOL,
 | 
			
		||||
            .help = "ignore any flush requests for the device",
 | 
			
		||||
        },{
 | 
			
		||||
            .name = "aio",
 | 
			
		||||
            .type = QEMU_OPT_STRING,
 | 
			
		||||
 
 | 
			
		||||
@@ -938,7 +938,7 @@ int main(int argc, char **argv)
 | 
			
		||||
            unsigned long tmp;
 | 
			
		||||
            if (fscanf(fp, "%lu", &tmp) == 1) {
 | 
			
		||||
                mmap_min_addr = tmp;
 | 
			
		||||
                qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr);
 | 
			
		||||
                qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n", mmap_min_addr);
 | 
			
		||||
            }
 | 
			
		||||
            fclose(fp);
 | 
			
		||||
        }
 | 
			
		||||
@@ -955,7 +955,7 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
    free(target_environ);
 | 
			
		||||
 | 
			
		||||
    if (qemu_log_enabled()) {
 | 
			
		||||
    if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
 | 
			
		||||
        qemu_log("guest_base  0x%lx\n", guest_base);
 | 
			
		||||
        log_page_dump();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,6 @@
 | 
			
		||||
#include "qemu.h"
 | 
			
		||||
#include "target_signal.h"
 | 
			
		||||
 | 
			
		||||
//#define DEBUG_SIGNAL
 | 
			
		||||
 | 
			
		||||
void signal_init(void)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								configure
									
									
									
									
										vendored
									
									
								
							@@ -2426,6 +2426,14 @@ else
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# getifaddrs (for tests/test-io-channel-socket )
 | 
			
		||||
 | 
			
		||||
have_ifaddrs_h=yes
 | 
			
		||||
if ! check_include "ifaddrs.h" ; then
 | 
			
		||||
  have_ifaddrs_h=no
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
##########################################
 | 
			
		||||
# VTE probe
 | 
			
		||||
 | 
			
		||||
@@ -4758,7 +4766,11 @@ echo "GTK GL support    $gtk_gl"
 | 
			
		||||
echo "GNUTLS support    $gnutls"
 | 
			
		||||
echo "GNUTLS hash       $gnutls_hash"
 | 
			
		||||
echo "libgcrypt         $gcrypt"
 | 
			
		||||
echo "nettle            $nettle ${nettle+($nettle_version)}"
 | 
			
		||||
if test "$nettle" = "yes"; then
 | 
			
		||||
    echo "nettle            $nettle ($nettle_version)"
 | 
			
		||||
else
 | 
			
		||||
    echo "nettle            $nettle"
 | 
			
		||||
fi
 | 
			
		||||
echo "libtasn1          $tasn1"
 | 
			
		||||
echo "VTE support       $vte"
 | 
			
		||||
echo "curses support    $curses"
 | 
			
		||||
@@ -5137,6 +5149,9 @@ fi
 | 
			
		||||
if test "$tasn1" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_TASN1=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$have_ifaddrs_h" = "yes" ; then
 | 
			
		||||
    echo "HAVE_IFADDRS_H=y" >> $config_host_mak
 | 
			
		||||
fi
 | 
			
		||||
if test "$vte" = "yes" ; then
 | 
			
		||||
  echo "CONFIG_VTE=y" >> $config_host_mak
 | 
			
		||||
  echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								cpus.c
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								cpus.c
									
									
									
									
									
								
							@@ -1558,22 +1558,29 @@ CpuInfoList *qmp_query_cpus(Error **errp)
 | 
			
		||||
        info->value->qom_path = object_get_canonical_path(OBJECT(cpu));
 | 
			
		||||
        info->value->thread_id = cpu->thread_id;
 | 
			
		||||
#if defined(TARGET_I386)
 | 
			
		||||
        info->value->has_pc = true;
 | 
			
		||||
        info->value->pc = env->eip + env->segs[R_CS].base;
 | 
			
		||||
        info->value->arch = CPU_INFO_ARCH_X86;
 | 
			
		||||
        info->value->u.x86 = g_new0(CpuInfoX86, 1);
 | 
			
		||||
        info->value->u.x86->pc = env->eip + env->segs[R_CS].base;
 | 
			
		||||
#elif defined(TARGET_PPC)
 | 
			
		||||
        info->value->has_nip = true;
 | 
			
		||||
        info->value->nip = env->nip;
 | 
			
		||||
        info->value->arch = CPU_INFO_ARCH_PPC;
 | 
			
		||||
        info->value->u.ppc = g_new0(CpuInfoPPC, 1);
 | 
			
		||||
        info->value->u.ppc->nip = env->nip;
 | 
			
		||||
#elif defined(TARGET_SPARC)
 | 
			
		||||
        info->value->has_pc = true;
 | 
			
		||||
        info->value->pc = env->pc;
 | 
			
		||||
        info->value->has_npc = true;
 | 
			
		||||
        info->value->npc = env->npc;
 | 
			
		||||
        info->value->arch = CPU_INFO_ARCH_SPARC;
 | 
			
		||||
        info->value->u.sparc = g_new0(CpuInfoSPARC, 1);
 | 
			
		||||
        info->value->u.sparc->pc = env->pc;
 | 
			
		||||
        info->value->u.sparc->npc = env->npc;
 | 
			
		||||
#elif defined(TARGET_MIPS)
 | 
			
		||||
        info->value->has_PC = true;
 | 
			
		||||
        info->value->PC = env->active_tc.PC;
 | 
			
		||||
        info->value->arch = CPU_INFO_ARCH_MIPS;
 | 
			
		||||
        info->value->u.mips = g_new0(CpuInfoMIPS, 1);
 | 
			
		||||
        info->value->u.mips->PC = env->active_tc.PC;
 | 
			
		||||
#elif defined(TARGET_TRICORE)
 | 
			
		||||
        info->value->has_PC = true;
 | 
			
		||||
        info->value->PC = env->PC;
 | 
			
		||||
        info->value->arch = CPU_INFO_ARCH_TRICORE;
 | 
			
		||||
        info->value->u.tricore = g_new0(CpuInfoTricore, 1);
 | 
			
		||||
        info->value->u.tricore->PC = env->PC;
 | 
			
		||||
#else
 | 
			
		||||
        info->value->arch = CPU_INFO_ARCH_OTHER;
 | 
			
		||||
        info->value->u.other = g_new0(CpuInfoOther, 1);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        /* XXX: waiting for the qapi to support GSList */
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ crypto-obj-y += tlscreds.o
 | 
			
		||||
crypto-obj-y += tlscredsanon.o
 | 
			
		||||
crypto-obj-y += tlscredsx509.o
 | 
			
		||||
crypto-obj-y += tlssession.o
 | 
			
		||||
crypto-obj-y += secret.o
 | 
			
		||||
 | 
			
		||||
# Let the userspace emulators avoid linking gnutls/etc
 | 
			
		||||
crypto-aes-obj-y = aes.o
 | 
			
		||||
 
 | 
			
		||||
@@ -21,19 +21,67 @@
 | 
			
		||||
#include "crypto/cipher.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static size_t alg_key_len[QCRYPTO_CIPHER_ALG_LAST] = {
 | 
			
		||||
static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = {
 | 
			
		||||
    [QCRYPTO_CIPHER_ALG_AES_128] = 16,
 | 
			
		||||
    [QCRYPTO_CIPHER_ALG_AES_192] = 24,
 | 
			
		||||
    [QCRYPTO_CIPHER_ALG_AES_256] = 32,
 | 
			
		||||
    [QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {
 | 
			
		||||
    [QCRYPTO_CIPHER_ALG_AES_128] = 16,
 | 
			
		||||
    [QCRYPTO_CIPHER_ALG_AES_192] = 16,
 | 
			
		||||
    [QCRYPTO_CIPHER_ALG_AES_256] = 16,
 | 
			
		||||
    [QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = {
 | 
			
		||||
    [QCRYPTO_CIPHER_MODE_ECB] = false,
 | 
			
		||||
    [QCRYPTO_CIPHER_MODE_CBC] = true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg)
 | 
			
		||||
{
 | 
			
		||||
    if (alg >= G_N_ELEMENTS(alg_key_len)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return alg_block_len[alg];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg)
 | 
			
		||||
{
 | 
			
		||||
    if (alg >= G_N_ELEMENTS(alg_key_len)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return alg_key_len[alg];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg,
 | 
			
		||||
                                 QCryptoCipherMode mode)
 | 
			
		||||
{
 | 
			
		||||
    if (alg >= G_N_ELEMENTS(alg_block_len)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (mode >= G_N_ELEMENTS(mode_need_iv)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (mode_need_iv[mode]) {
 | 
			
		||||
        return alg_block_len[alg];
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
 | 
			
		||||
                                   size_t nkey,
 | 
			
		||||
                                   Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    if ((unsigned)alg >= QCRYPTO_CIPHER_ALG_LAST) {
 | 
			
		||||
    if ((unsigned)alg >= QCRYPTO_CIPHER_ALG__MAX) {
 | 
			
		||||
        error_setg(errp, "Cipher algorithm %d out of range",
 | 
			
		||||
                   alg);
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -41,7 +89,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
 | 
			
		||||
 | 
			
		||||
    if (alg_key_len[alg] != nkey) {
 | 
			
		||||
        error_setg(errp, "Cipher key length %zu should be %zu",
 | 
			
		||||
                   alg_key_len[alg], nkey);
 | 
			
		||||
                   nkey, alg_key_len[alg]);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
 
 | 
			
		||||
@@ -24,12 +24,18 @@
 | 
			
		||||
#include <gnutls/gnutls.h>
 | 
			
		||||
#include <gnutls/crypto.h>
 | 
			
		||||
 | 
			
		||||
static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = {
 | 
			
		||||
static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
 | 
			
		||||
    [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
 | 
			
		||||
    [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
 | 
			
		||||
    [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = {
 | 
			
		||||
    [QCRYPTO_HASH_ALG_MD5] = 16,
 | 
			
		||||
    [QCRYPTO_HASH_ALG_SHA1] = 20,
 | 
			
		||||
    [QCRYPTO_HASH_ALG_SHA256] = 32,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
 | 
			
		||||
{
 | 
			
		||||
    if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) {
 | 
			
		||||
@@ -38,6 +44,15 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg)
 | 
			
		||||
{
 | 
			
		||||
    if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_size)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return qcrypto_hash_alg_size[alg];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
 | 
			
		||||
                        const struct iovec *iov,
 | 
			
		||||
                        size_t niov,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										513
									
								
								crypto/secret.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										513
									
								
								crypto/secret.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,513 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU crypto secret support
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2015 Red Hat, Inc.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "crypto/secret.h"
 | 
			
		||||
#include "crypto/cipher.h"
 | 
			
		||||
#include "qom/object_interfaces.h"
 | 
			
		||||
#include "qemu/base64.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_load_data(QCryptoSecret *secret,
 | 
			
		||||
                         uint8_t **output,
 | 
			
		||||
                         size_t *outputlen,
 | 
			
		||||
                         Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    char *data = NULL;
 | 
			
		||||
    size_t length = 0;
 | 
			
		||||
    GError *gerr = NULL;
 | 
			
		||||
 | 
			
		||||
    *output = NULL;
 | 
			
		||||
    *outputlen = 0;
 | 
			
		||||
 | 
			
		||||
    if (secret->file) {
 | 
			
		||||
        if (secret->data) {
 | 
			
		||||
            error_setg(errp,
 | 
			
		||||
                       "'file' and 'data' are mutually exclusive");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!g_file_get_contents(secret->file, &data, &length, &gerr)) {
 | 
			
		||||
            error_setg(errp,
 | 
			
		||||
                       "Unable to read %s: %s",
 | 
			
		||||
                       secret->file, gerr->message);
 | 
			
		||||
            g_error_free(gerr);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        *output = (uint8_t *)data;
 | 
			
		||||
        *outputlen = length;
 | 
			
		||||
    } else if (secret->data) {
 | 
			
		||||
        *outputlen = strlen(secret->data);
 | 
			
		||||
        *output = (uint8_t *)g_strdup(secret->data);
 | 
			
		||||
    } else {
 | 
			
		||||
        error_setg(errp, "Either 'file' or 'data' must be provided");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void qcrypto_secret_decrypt(QCryptoSecret *secret,
 | 
			
		||||
                                   const uint8_t *input,
 | 
			
		||||
                                   size_t inputlen,
 | 
			
		||||
                                   uint8_t **output,
 | 
			
		||||
                                   size_t *outputlen,
 | 
			
		||||
                                   Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL;
 | 
			
		||||
    size_t keylen, ciphertextlen, ivlen;
 | 
			
		||||
    QCryptoCipher *aes = NULL;
 | 
			
		||||
    uint8_t *plaintext = NULL;
 | 
			
		||||
 | 
			
		||||
    *output = NULL;
 | 
			
		||||
    *outputlen = 0;
 | 
			
		||||
 | 
			
		||||
    if (qcrypto_secret_lookup(secret->keyid,
 | 
			
		||||
                              &key, &keylen,
 | 
			
		||||
                              errp) < 0) {
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (keylen != 32) {
 | 
			
		||||
        error_setg(errp, "Key should be 32 bytes in length");
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!secret->iv) {
 | 
			
		||||
        error_setg(errp, "IV is required to decrypt secret");
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iv = qbase64_decode(secret->iv, -1, &ivlen, errp);
 | 
			
		||||
    if (!iv) {
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
    if (ivlen != 16) {
 | 
			
		||||
        error_setg(errp, "IV should be 16 bytes in length not %zu",
 | 
			
		||||
                   ivlen);
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
 | 
			
		||||
                             QCRYPTO_CIPHER_MODE_CBC,
 | 
			
		||||
                             key, keylen,
 | 
			
		||||
                             errp);
 | 
			
		||||
    if (!aes) {
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
 | 
			
		||||
        ciphertext = qbase64_decode((const gchar*)input,
 | 
			
		||||
                                    inputlen,
 | 
			
		||||
                                    &ciphertextlen,
 | 
			
		||||
                                    errp);
 | 
			
		||||
        if (!ciphertext) {
 | 
			
		||||
            goto cleanup;
 | 
			
		||||
        }
 | 
			
		||||
        plaintext = g_new0(uint8_t, ciphertextlen + 1);
 | 
			
		||||
    } else {
 | 
			
		||||
        ciphertextlen = inputlen;
 | 
			
		||||
        plaintext = g_new0(uint8_t, inputlen + 1);
 | 
			
		||||
    }
 | 
			
		||||
    if (qcrypto_cipher_decrypt(aes,
 | 
			
		||||
                               ciphertext ? ciphertext : input,
 | 
			
		||||
                               plaintext,
 | 
			
		||||
                               ciphertextlen,
 | 
			
		||||
                               errp) < 0) {
 | 
			
		||||
        plaintext = NULL;
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (plaintext[ciphertextlen - 1] > 16 ||
 | 
			
		||||
        plaintext[ciphertextlen - 1] > ciphertextlen) {
 | 
			
		||||
        error_setg(errp, "Incorrect number of padding bytes (%d) "
 | 
			
		||||
                   "found on decrypted data",
 | 
			
		||||
                   (int)plaintext[ciphertextlen - 1]);
 | 
			
		||||
        g_free(plaintext);
 | 
			
		||||
        plaintext = NULL;
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Even though plaintext may contain arbitrary NUL
 | 
			
		||||
     * ensure it is explicitly NUL terminated.
 | 
			
		||||
     */
 | 
			
		||||
    ciphertextlen -= plaintext[ciphertextlen - 1];
 | 
			
		||||
    plaintext[ciphertextlen] = '\0';
 | 
			
		||||
 | 
			
		||||
    *output = plaintext;
 | 
			
		||||
    *outputlen = ciphertextlen;
 | 
			
		||||
 | 
			
		||||
 cleanup:
 | 
			
		||||
    g_free(ciphertext);
 | 
			
		||||
    g_free(iv);
 | 
			
		||||
    g_free(key);
 | 
			
		||||
    qcrypto_cipher_free(aes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void qcrypto_secret_decode(const uint8_t *input,
 | 
			
		||||
                                  size_t inputlen,
 | 
			
		||||
                                  uint8_t **output,
 | 
			
		||||
                                  size_t *outputlen,
 | 
			
		||||
                                  Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    *output = qbase64_decode((const gchar*)input,
 | 
			
		||||
                             inputlen,
 | 
			
		||||
                             outputlen,
 | 
			
		||||
                             errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_prop_set_loaded(Object *obj,
 | 
			
		||||
                               bool value,
 | 
			
		||||
                               Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
 | 
			
		||||
    if (value) {
 | 
			
		||||
        Error *local_err = NULL;
 | 
			
		||||
        uint8_t *input = NULL;
 | 
			
		||||
        size_t inputlen = 0;
 | 
			
		||||
        uint8_t *output = NULL;
 | 
			
		||||
        size_t outputlen = 0;
 | 
			
		||||
 | 
			
		||||
        qcrypto_secret_load_data(secret, &input, &inputlen, &local_err);
 | 
			
		||||
        if (local_err) {
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (secret->keyid) {
 | 
			
		||||
            qcrypto_secret_decrypt(secret, input, inputlen,
 | 
			
		||||
                                   &output, &outputlen, &local_err);
 | 
			
		||||
            g_free(input);
 | 
			
		||||
            if (local_err) {
 | 
			
		||||
                error_propagate(errp, local_err);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            input = output;
 | 
			
		||||
            inputlen = outputlen;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) {
 | 
			
		||||
                qcrypto_secret_decode(input, inputlen,
 | 
			
		||||
                                      &output, &outputlen, &local_err);
 | 
			
		||||
                g_free(input);
 | 
			
		||||
                if (local_err) {
 | 
			
		||||
                    error_propagate(errp, local_err);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                input = output;
 | 
			
		||||
                inputlen = outputlen;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        secret->rawdata = input;
 | 
			
		||||
        secret->rawlen = inputlen;
 | 
			
		||||
    } else {
 | 
			
		||||
        g_free(secret->rawdata);
 | 
			
		||||
        secret->rawlen = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
qcrypto_secret_prop_get_loaded(Object *obj,
 | 
			
		||||
                               Error **errp G_GNUC_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
    return secret->data != NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_prop_set_format(Object *obj,
 | 
			
		||||
                               int value,
 | 
			
		||||
                               Error **errp G_GNUC_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *creds = QCRYPTO_SECRET(obj);
 | 
			
		||||
 | 
			
		||||
    creds->format = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
qcrypto_secret_prop_get_format(Object *obj,
 | 
			
		||||
                               Error **errp G_GNUC_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *creds = QCRYPTO_SECRET(obj);
 | 
			
		||||
 | 
			
		||||
    return creds->format;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_prop_set_data(Object *obj,
 | 
			
		||||
                             const char *value,
 | 
			
		||||
                             Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
 | 
			
		||||
    g_free(secret->data);
 | 
			
		||||
    secret->data = g_strdup(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
qcrypto_secret_prop_get_data(Object *obj,
 | 
			
		||||
                             Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
    return g_strdup(secret->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_prop_set_file(Object *obj,
 | 
			
		||||
                             const char *value,
 | 
			
		||||
                             Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
 | 
			
		||||
    g_free(secret->file);
 | 
			
		||||
    secret->file = g_strdup(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
qcrypto_secret_prop_get_file(Object *obj,
 | 
			
		||||
                             Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
    return g_strdup(secret->file);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_prop_set_iv(Object *obj,
 | 
			
		||||
                           const char *value,
 | 
			
		||||
                           Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
 | 
			
		||||
    g_free(secret->iv);
 | 
			
		||||
    secret->iv = g_strdup(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
qcrypto_secret_prop_get_iv(Object *obj,
 | 
			
		||||
                           Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
    return g_strdup(secret->iv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_prop_set_keyid(Object *obj,
 | 
			
		||||
                              const char *value,
 | 
			
		||||
                              Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
 | 
			
		||||
    g_free(secret->keyid);
 | 
			
		||||
    secret->keyid = g_strdup(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
qcrypto_secret_prop_get_keyid(Object *obj,
 | 
			
		||||
                              Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
    return g_strdup(secret->keyid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_complete(UserCreatable *uc, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    object_property_set_bool(OBJECT(uc), true, "loaded", errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_init(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    object_property_add_bool(obj, "loaded",
 | 
			
		||||
                             qcrypto_secret_prop_get_loaded,
 | 
			
		||||
                             qcrypto_secret_prop_set_loaded,
 | 
			
		||||
                             NULL);
 | 
			
		||||
    object_property_add_enum(obj, "format",
 | 
			
		||||
                             "QCryptoSecretFormat",
 | 
			
		||||
                             QCryptoSecretFormat_lookup,
 | 
			
		||||
                             qcrypto_secret_prop_get_format,
 | 
			
		||||
                             qcrypto_secret_prop_set_format,
 | 
			
		||||
                             NULL);
 | 
			
		||||
    object_property_add_str(obj, "data",
 | 
			
		||||
                            qcrypto_secret_prop_get_data,
 | 
			
		||||
                            qcrypto_secret_prop_set_data,
 | 
			
		||||
                            NULL);
 | 
			
		||||
    object_property_add_str(obj, "file",
 | 
			
		||||
                            qcrypto_secret_prop_get_file,
 | 
			
		||||
                            qcrypto_secret_prop_set_file,
 | 
			
		||||
                            NULL);
 | 
			
		||||
    object_property_add_str(obj, "keyid",
 | 
			
		||||
                            qcrypto_secret_prop_get_keyid,
 | 
			
		||||
                            qcrypto_secret_prop_set_keyid,
 | 
			
		||||
                            NULL);
 | 
			
		||||
    object_property_add_str(obj, "iv",
 | 
			
		||||
                            qcrypto_secret_prop_get_iv,
 | 
			
		||||
                            qcrypto_secret_prop_set_iv,
 | 
			
		||||
                            NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_finalize(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoSecret *secret = QCRYPTO_SECRET(obj);
 | 
			
		||||
 | 
			
		||||
    g_free(secret->iv);
 | 
			
		||||
    g_free(secret->file);
 | 
			
		||||
    g_free(secret->keyid);
 | 
			
		||||
    g_free(secret->rawdata);
 | 
			
		||||
    g_free(secret->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
{
 | 
			
		||||
    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
 | 
			
		||||
 | 
			
		||||
    ucc->complete = qcrypto_secret_complete;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int qcrypto_secret_lookup(const char *secretid,
 | 
			
		||||
                          uint8_t **data,
 | 
			
		||||
                          size_t *datalen,
 | 
			
		||||
                          Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    Object *obj;
 | 
			
		||||
    QCryptoSecret *secret;
 | 
			
		||||
 | 
			
		||||
    obj = object_resolve_path_component(
 | 
			
		||||
        object_get_objects_root(), secretid);
 | 
			
		||||
    if (!obj) {
 | 
			
		||||
        error_setg(errp, "No secret with id '%s'", secretid);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    secret = (QCryptoSecret *)
 | 
			
		||||
        object_dynamic_cast(obj,
 | 
			
		||||
                            TYPE_QCRYPTO_SECRET);
 | 
			
		||||
    if (!secret) {
 | 
			
		||||
        error_setg(errp, "Object with id '%s' is not a secret",
 | 
			
		||||
                   secretid);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!secret->rawdata) {
 | 
			
		||||
        error_setg(errp, "Secret with id '%s' has no data",
 | 
			
		||||
                   secretid);
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    *data = g_new0(uint8, secret->rawlen + 1);
 | 
			
		||||
    memcpy(*data, secret->rawdata, secret->rawlen);
 | 
			
		||||
    (*data)[secret->rawlen] = '\0';
 | 
			
		||||
    *datalen = secret->rawlen;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
char *qcrypto_secret_lookup_as_utf8(const char *secretid,
 | 
			
		||||
                                    Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t *data;
 | 
			
		||||
    size_t datalen;
 | 
			
		||||
 | 
			
		||||
    if (qcrypto_secret_lookup(secretid,
 | 
			
		||||
                              &data,
 | 
			
		||||
                              &datalen,
 | 
			
		||||
                              errp) < 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!g_utf8_validate((const gchar*)data, datalen, NULL)) {
 | 
			
		||||
        error_setg(errp,
 | 
			
		||||
                   "Data from secret %s is not valid UTF-8",
 | 
			
		||||
                   secretid);
 | 
			
		||||
        g_free(data);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (char *)data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
char *qcrypto_secret_lookup_as_base64(const char *secretid,
 | 
			
		||||
                                      Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t *data;
 | 
			
		||||
    size_t datalen;
 | 
			
		||||
    char *ret;
 | 
			
		||||
 | 
			
		||||
    if (qcrypto_secret_lookup(secretid,
 | 
			
		||||
                              &data,
 | 
			
		||||
                              &datalen,
 | 
			
		||||
                              errp) < 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = g_base64_encode(data, datalen);
 | 
			
		||||
    g_free(data);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const TypeInfo qcrypto_secret_info = {
 | 
			
		||||
    .parent = TYPE_OBJECT,
 | 
			
		||||
    .name = TYPE_QCRYPTO_SECRET,
 | 
			
		||||
    .instance_size = sizeof(QCryptoSecret),
 | 
			
		||||
    .instance_init = qcrypto_secret_init,
 | 
			
		||||
    .instance_finalize = qcrypto_secret_finalize,
 | 
			
		||||
    .class_size = sizeof(QCryptoSecretClass),
 | 
			
		||||
    .class_init = qcrypto_secret_class_init,
 | 
			
		||||
    .interfaces = (InterfaceInfo[]) {
 | 
			
		||||
        { TYPE_USER_CREATABLE },
 | 
			
		||||
        { }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_secret_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&qcrypto_secret_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type_init(qcrypto_secret_register_types);
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
 | 
			
		||||
#include "crypto/tlscredsx509.h"
 | 
			
		||||
#include "crypto/tlscredspriv.h"
 | 
			
		||||
#include "crypto/secret.h"
 | 
			
		||||
#include "qom/object_interfaces.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
@@ -607,9 +608,30 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (cert != NULL && key != NULL) {
 | 
			
		||||
#if GNUTLS_VERSION_NUMBER >= 0x030111
 | 
			
		||||
        char *password = NULL;
 | 
			
		||||
        if (creds->passwordid) {
 | 
			
		||||
            password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
 | 
			
		||||
                                                     errp);
 | 
			
		||||
            if (!password) {
 | 
			
		||||
                goto cleanup;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ret = gnutls_certificate_set_x509_key_file2(creds->data,
 | 
			
		||||
                                                    cert, key,
 | 
			
		||||
                                                    GNUTLS_X509_FMT_PEM,
 | 
			
		||||
                                                    password,
 | 
			
		||||
                                                    0);
 | 
			
		||||
        g_free(password);
 | 
			
		||||
#else /* GNUTLS_VERSION_NUMBER < 0x030111 */
 | 
			
		||||
        if (creds->passwordid) {
 | 
			
		||||
            error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11");
 | 
			
		||||
            goto cleanup;
 | 
			
		||||
        }
 | 
			
		||||
        ret = gnutls_certificate_set_x509_key_file(creds->data,
 | 
			
		||||
                                                   cert, key,
 | 
			
		||||
                                                   GNUTLS_X509_FMT_PEM);
 | 
			
		||||
#endif /* GNUTLS_VERSION_NUMBER < 0x030111 */
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
 | 
			
		||||
                       cert, key, gnutls_strerror(ret));
 | 
			
		||||
@@ -737,6 +759,27 @@ qcrypto_tls_creds_x509_prop_set_sanity(Object *obj,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
qcrypto_tls_creds_x509_prop_set_passwordid(Object *obj,
 | 
			
		||||
                                           const char *value,
 | 
			
		||||
                                           Error **errp G_GNUC_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
 | 
			
		||||
 | 
			
		||||
    creds->passwordid = g_strdup(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
qcrypto_tls_creds_x509_prop_get_passwordid(Object *obj,
 | 
			
		||||
                                           Error **errp G_GNUC_UNUSED)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
 | 
			
		||||
 | 
			
		||||
    return g_strdup(creds->passwordid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
qcrypto_tls_creds_x509_prop_get_sanity(Object *obj,
 | 
			
		||||
                                       Error **errp G_GNUC_UNUSED)
 | 
			
		||||
@@ -769,6 +812,10 @@ qcrypto_tls_creds_x509_init(Object *obj)
 | 
			
		||||
                             qcrypto_tls_creds_x509_prop_get_sanity,
 | 
			
		||||
                             qcrypto_tls_creds_x509_prop_set_sanity,
 | 
			
		||||
                             NULL);
 | 
			
		||||
    object_property_add_str(obj, "passwordid",
 | 
			
		||||
                            qcrypto_tls_creds_x509_prop_get_passwordid,
 | 
			
		||||
                            qcrypto_tls_creds_x509_prop_set_passwordid,
 | 
			
		||||
                            NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -777,6 +824,7 @@ qcrypto_tls_creds_x509_finalize(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj);
 | 
			
		||||
 | 
			
		||||
    g_free(creds->passwordid);
 | 
			
		||||
    qcrypto_tls_creds_x509_unload(creds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,11 @@ CONFIG_VGA_CIRRUS=y
 | 
			
		||||
CONFIG_VMWARE_VGA=y
 | 
			
		||||
CONFIG_VIRTIO_VGA=y
 | 
			
		||||
CONFIG_VMMOUSE=y
 | 
			
		||||
CONFIG_IPMI=y
 | 
			
		||||
CONFIG_IPMI_LOCAL=y
 | 
			
		||||
CONFIG_IPMI_EXTERN=y
 | 
			
		||||
CONFIG_ISA_IPMI_KCS=y
 | 
			
		||||
CONFIG_ISA_IPMI_BT=y
 | 
			
		||||
CONFIG_SERIAL=y
 | 
			
		||||
CONFIG_PARALLEL=y
 | 
			
		||||
CONFIG_I8254=y
 | 
			
		||||
@@ -46,7 +51,10 @@ CONFIG_APIC=y
 | 
			
		||||
CONFIG_IOAPIC=y
 | 
			
		||||
CONFIG_PVPANIC=y
 | 
			
		||||
CONFIG_MEM_HOTPLUG=y
 | 
			
		||||
CONFIG_NVDIMM=y
 | 
			
		||||
CONFIG_ACPI_NVDIMM=y
 | 
			
		||||
CONFIG_XIO3130=y
 | 
			
		||||
CONFIG_IOH3420=y
 | 
			
		||||
CONFIG_I82801B11=y
 | 
			
		||||
CONFIG_SMBIOS=y
 | 
			
		||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,11 @@ CONFIG_VGA_CIRRUS=y
 | 
			
		||||
CONFIG_VMWARE_VGA=y
 | 
			
		||||
CONFIG_VIRTIO_VGA=y
 | 
			
		||||
CONFIG_VMMOUSE=y
 | 
			
		||||
CONFIG_IPMI=y
 | 
			
		||||
CONFIG_IPMI_LOCAL=y
 | 
			
		||||
CONFIG_IPMI_EXTERN=y
 | 
			
		||||
CONFIG_ISA_IPMI_KCS=y
 | 
			
		||||
CONFIG_ISA_IPMI_BT=y
 | 
			
		||||
CONFIG_SERIAL=y
 | 
			
		||||
CONFIG_PARALLEL=y
 | 
			
		||||
CONFIG_I8254=y
 | 
			
		||||
@@ -46,7 +51,10 @@ CONFIG_APIC=y
 | 
			
		||||
CONFIG_IOAPIC=y
 | 
			
		||||
CONFIG_PVPANIC=y
 | 
			
		||||
CONFIG_MEM_HOTPLUG=y
 | 
			
		||||
CONFIG_NVDIMM=y
 | 
			
		||||
CONFIG_ACPI_NVDIMM=y
 | 
			
		||||
CONFIG_XIO3130=y
 | 
			
		||||
CONFIG_IOH3420=y
 | 
			
		||||
CONFIG_I82801B11=y
 | 
			
		||||
CONFIG_SMBIOS=y
 | 
			
		||||
CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
Block I/O error injection using blkdebug
 | 
			
		||||
----------------------------------------
 | 
			
		||||
Copyright (C) 2014 Red Hat Inc
 | 
			
		||||
Copyright (C) 2014-2015 Red Hat Inc
 | 
			
		||||
 | 
			
		||||
This work is licensed under the terms of the GNU GPL, version 2 or later.  See
 | 
			
		||||
the COPYING file in the top-level directory.
 | 
			
		||||
@@ -92,8 +92,9 @@ The core events are:
 | 
			
		||||
 | 
			
		||||
  flush_to_disk - flush the host block device's disk cache
 | 
			
		||||
 | 
			
		||||
See block/blkdebug.c:event_names[] for the full list of events.  You may need
 | 
			
		||||
to grep block driver source code to understand the meaning of specific events.
 | 
			
		||||
See qapi/block-core.json:BlkdebugEvent for the full list of events.
 | 
			
		||||
You may need to grep block driver source code to understand the
 | 
			
		||||
meaning of specific events.
 | 
			
		||||
 | 
			
		||||
State transitions
 | 
			
		||||
-----------------
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,9 @@ A detailed command line would be:
 | 
			
		||||
-m 2G
 | 
			
		||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0
 | 
			
		||||
-object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1
 | 
			
		||||
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd-device e1000,bus=bridge1,addr=0x4,netdev=nd
 | 
			
		||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8,bus=pci.0 -device e1000,bus=bridge2,addr=0x3
 | 
			
		||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40,bus=pci.0 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
 | 
			
		||||
-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd
 | 
			
		||||
-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8, -device e1000,bus=bridge2,addr=0x3
 | 
			
		||||
-device pxb,id=bridge3,bus=pci.0,bus_nr=40, -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1
 | 
			
		||||
 | 
			
		||||
Here you have:
 | 
			
		||||
 - 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes)
 | 
			
		||||
 
 | 
			
		||||
@@ -118,17 +118,17 @@ tracking optional fields.
 | 
			
		||||
 | 
			
		||||
Any name (command, event, type, field, or enum value) beginning with
 | 
			
		||||
"x-" is marked experimental, and may be withdrawn or changed
 | 
			
		||||
incompatibly in a future release.  Downstream vendors may add
 | 
			
		||||
extensions; such extensions should begin with a prefix matching
 | 
			
		||||
"__RFQDN_" (for the reverse-fully-qualified-domain-name of the
 | 
			
		||||
vendor), even if the rest of the name uses dash (example:
 | 
			
		||||
__com.redhat_drive-mirror).  Other than downstream extensions (with
 | 
			
		||||
leading underscore and the use of dots), all names should begin with a
 | 
			
		||||
letter, and contain only ASCII letters, digits, dash, and underscore.
 | 
			
		||||
Names beginning with 'q_' are reserved for the generator: QMP names
 | 
			
		||||
that resemble C keywords or other problematic strings will be munged
 | 
			
		||||
in C to use this prefix.  For example, a field named "default" in
 | 
			
		||||
qapi becomes "q_default" in the generated C code.
 | 
			
		||||
incompatibly in a future release.  All names must begin with a letter,
 | 
			
		||||
and contain only ASCII letters, digits, dash, and underscore.  There
 | 
			
		||||
are two exceptions: enum values may start with a digit, and any
 | 
			
		||||
extensions added by downstream vendors should start with a prefix
 | 
			
		||||
matching "__RFQDN_" (for the reverse-fully-qualified-domain-name of
 | 
			
		||||
the vendor), even if the rest of the name uses dash (example:
 | 
			
		||||
__com.redhat_drive-mirror).  Names beginning with 'q_' are reserved
 | 
			
		||||
for the generator: QMP names that resemble C keywords or other
 | 
			
		||||
problematic strings will be munged in C to use this prefix.  For
 | 
			
		||||
example, a field named "default" in qapi becomes "q_default" in the
 | 
			
		||||
generated C code.
 | 
			
		||||
 | 
			
		||||
In the rest of this document, usage lines are given for each
 | 
			
		||||
expression type, with literal strings written in lower case and
 | 
			
		||||
@@ -160,6 +160,7 @@ The following types are predefined, and map to C as follows:
 | 
			
		||||
                       accepts size suffixes
 | 
			
		||||
  bool      bool       JSON true or false
 | 
			
		||||
  any       QObject *  any JSON value
 | 
			
		||||
  QType     QType      JSON string matching enum QType values
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Includes ===
 | 
			
		||||
@@ -383,9 +384,6 @@ where each branch of the union names a QAPI type.  For example:
 | 
			
		||||
   'data': { 'definition': 'BlockdevOptions',
 | 
			
		||||
             'reference': 'str' } }
 | 
			
		||||
 | 
			
		||||
Just like for a simple union, an implicit C enum 'NameKind' is created
 | 
			
		||||
to enumerate the branches for the alternate 'Name'.
 | 
			
		||||
 | 
			
		||||
Unlike a union, the discriminator string is never passed on the wire
 | 
			
		||||
for the Client JSON Protocol.  Instead, the value's JSON type serves
 | 
			
		||||
as an implicit discriminator, which in turn means that an alternate
 | 
			
		||||
@@ -1053,7 +1051,7 @@ Example:
 | 
			
		||||
 | 
			
		||||
    const char *const example_QAPIEvent_lookup[] = {
 | 
			
		||||
        [EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT",
 | 
			
		||||
        [EXAMPLE_QAPI_EVENT_MAX] = NULL,
 | 
			
		||||
        [EXAMPLE_QAPI_EVENT__MAX] = NULL,
 | 
			
		||||
    };
 | 
			
		||||
    $ cat qapi-generated/example-qapi-event.h
 | 
			
		||||
[Uninteresting stuff omitted...]
 | 
			
		||||
@@ -1070,7 +1068,7 @@ Example:
 | 
			
		||||
 | 
			
		||||
    typedef enum example_QAPIEvent {
 | 
			
		||||
        EXAMPLE_QAPI_EVENT_MY_EVENT = 0,
 | 
			
		||||
        EXAMPLE_QAPI_EVENT_MAX = 1,
 | 
			
		||||
        EXAMPLE_QAPI_EVENT__MAX = 1,
 | 
			
		||||
    } example_QAPIEvent;
 | 
			
		||||
 | 
			
		||||
    extern const char *const example_QAPIEvent_lookup[];
 | 
			
		||||
 
 | 
			
		||||
@@ -192,90 +192,7 @@ To check the result, read the "control" field:
 | 
			
		||||
                            today due to implementation not being async,
 | 
			
		||||
                            but may in the future).
 | 
			
		||||
 | 
			
		||||
= Host-side API =
 | 
			
		||||
 | 
			
		||||
The following functions are available to the QEMU programmer for adding
 | 
			
		||||
data to a fw_cfg device during guest initialization (see fw_cfg.h for
 | 
			
		||||
each function's complete prototype):
 | 
			
		||||
 | 
			
		||||
== fw_cfg_add_bytes() ==
 | 
			
		||||
 | 
			
		||||
Given a selector key value, starting pointer, and size, create an item
 | 
			
		||||
as a raw "blob" of the given size, available by selecting the given key.
 | 
			
		||||
The data referenced by the starting pointer is only linked, NOT copied,
 | 
			
		||||
into the data structure of the fw_cfg device.
 | 
			
		||||
 | 
			
		||||
== fw_cfg_add_string() ==
 | 
			
		||||
 | 
			
		||||
Instead of a starting pointer and size, this function accepts a pointer
 | 
			
		||||
to a NUL-terminated ascii string, and inserts a newly allocated copy of
 | 
			
		||||
the string (including the NUL terminator) into the fw_cfg device data
 | 
			
		||||
structure.
 | 
			
		||||
 | 
			
		||||
== fw_cfg_add_iXX() ==
 | 
			
		||||
 | 
			
		||||
Insert an XX-bit item, where XX may be 16, 32, or 64. These functions
 | 
			
		||||
will convert a 16-, 32-, or 64-bit integer to little-endian, then add
 | 
			
		||||
a dynamically allocated copy of the appropriately sized item to fw_cfg
 | 
			
		||||
under the given selector key value.
 | 
			
		||||
 | 
			
		||||
== fw_cfg_modify_iXX() ==
 | 
			
		||||
 | 
			
		||||
Modify the value of an XX-bit item (where XX may be 16, 32, or 64).
 | 
			
		||||
Similarly to the corresponding fw_cfg_add_iXX() function set, convert
 | 
			
		||||
a 16-, 32-, or 64-bit integer to little endian, create a dynamically
 | 
			
		||||
allocated copy of the required size, and replace the existing item at
 | 
			
		||||
the given selector key value with the newly allocated one. The previous
 | 
			
		||||
item, assumed to have been allocated during an earlier call to
 | 
			
		||||
fw_cfg_add_iXX() or fw_cfg_modify_iXX() (of the same width XX), is freed
 | 
			
		||||
before the function returns.
 | 
			
		||||
 | 
			
		||||
== fw_cfg_add_file() ==
 | 
			
		||||
 | 
			
		||||
Given a filename (i.e., fw_cfg item name), starting pointer, and size,
 | 
			
		||||
create an item as a raw "blob" of the given size. Unlike fw_cfg_add_bytes()
 | 
			
		||||
above, the next available selector key (above 0x0020, FW_CFG_FILE_FIRST)
 | 
			
		||||
will be used, and a new entry will be added to the file directory structure
 | 
			
		||||
(at key 0x0019), containing the item name, blob size, and automatically
 | 
			
		||||
assigned selector key value. The data referenced by the starting pointer
 | 
			
		||||
is only linked, NOT copied, into the fw_cfg data structure.
 | 
			
		||||
 | 
			
		||||
== fw_cfg_add_file_callback() ==
 | 
			
		||||
 | 
			
		||||
Like fw_cfg_add_file(), but additionally sets pointers to a callback
 | 
			
		||||
function (and opaque argument), which will be executed host-side by
 | 
			
		||||
QEMU each time a byte is read by the guest from this particular item.
 | 
			
		||||
 | 
			
		||||
NOTE: The callback function is given the opaque argument set by
 | 
			
		||||
fw_cfg_add_file_callback(), but also the current data offset,
 | 
			
		||||
allowing it the option of only acting upon specific offset values
 | 
			
		||||
(e.g., 0, before the first data byte of the selected item is
 | 
			
		||||
returned to the guest).
 | 
			
		||||
 | 
			
		||||
== fw_cfg_modify_file() ==
 | 
			
		||||
 | 
			
		||||
Given a filename (i.e., fw_cfg item name), starting pointer, and size,
 | 
			
		||||
completely replace the configuration item referenced by the given item
 | 
			
		||||
name with the new given blob. If an existing blob is found, its
 | 
			
		||||
callback information is removed, and a pointer to the old data is
 | 
			
		||||
returned to allow the caller to free it, helping avoid memory leaks.
 | 
			
		||||
If a configuration item does not already exist under the given item
 | 
			
		||||
name, a new item will be created as with fw_cfg_add_file(), and NULL
 | 
			
		||||
is returned to the caller. In any case, the data referenced by the
 | 
			
		||||
starting pointer is only linked, NOT copied, into the fw_cfg data
 | 
			
		||||
structure.
 | 
			
		||||
 | 
			
		||||
== fw_cfg_add_callback() ==
 | 
			
		||||
 | 
			
		||||
Like fw_cfg_add_bytes(), but additionally sets pointers to a callback
 | 
			
		||||
function (and opaque argument), which will be executed host-side by
 | 
			
		||||
QEMU each time a guest-side write operation to this particular item
 | 
			
		||||
completes fully overwriting the item's data.
 | 
			
		||||
 | 
			
		||||
NOTE: This function is deprecated, and will be completely removed
 | 
			
		||||
starting with QEMU v2.4.
 | 
			
		||||
 | 
			
		||||
== Externally Provided Items ==
 | 
			
		||||
= Externally Provided Items =
 | 
			
		||||
 | 
			
		||||
As of v2.4, "file" fw_cfg items (i.e., items with selector keys above
 | 
			
		||||
FW_CFG_FILE_FIRST, and with a corresponding entry in the fw_cfg file
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										228
									
								
								docs/specs/parallels.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								docs/specs/parallels.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,228 @@
 | 
			
		||||
= License =
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2015 Denis Lunev
 | 
			
		||||
Copyright (c) 2015 Vladimir Sementsov-Ogievskiy
 | 
			
		||||
 | 
			
		||||
This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
See the COPYING file in the top-level directory.
 | 
			
		||||
 | 
			
		||||
= Parallels Expandable Image File Format =
 | 
			
		||||
 | 
			
		||||
A Parallels expandable image file consists of three consecutive parts:
 | 
			
		||||
    * header
 | 
			
		||||
    * BAT
 | 
			
		||||
    * data area
 | 
			
		||||
 | 
			
		||||
All numbers in a Parallels expandable image are stored in little-endian byte
 | 
			
		||||
order.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
== Definitions ==
 | 
			
		||||
 | 
			
		||||
    Sector    A 512-byte data chunk.
 | 
			
		||||
 | 
			
		||||
    Cluster   A data chunk of the size specified in the image header.
 | 
			
		||||
              Currently, the default size is 1MiB (2048 sectors). In previous
 | 
			
		||||
              versions, cluster sizes of 63 sectors, 256 and 252 kilobytes were
 | 
			
		||||
              used.
 | 
			
		||||
 | 
			
		||||
    BAT       Block Allocation Table, an entity that contains information for
 | 
			
		||||
              guest-to-host I/O data address translation.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
== Header ==
 | 
			
		||||
 | 
			
		||||
The header is placed at the start of an image and contains the following
 | 
			
		||||
fields:
 | 
			
		||||
 | 
			
		||||
Bytes:
 | 
			
		||||
   0 - 15:    magic
 | 
			
		||||
              Must contain "WithoutFreeSpace" or "WithouFreSpacExt".
 | 
			
		||||
 | 
			
		||||
  16 - 19:    version
 | 
			
		||||
              Must be 2.
 | 
			
		||||
 | 
			
		||||
  20 - 23:    heads
 | 
			
		||||
              Disk geometry parameter for guest.
 | 
			
		||||
 | 
			
		||||
  24 - 27:    cylinders
 | 
			
		||||
              Disk geometry parameter for guest.
 | 
			
		||||
 | 
			
		||||
  28 - 31:    tracks
 | 
			
		||||
              Cluster size, in sectors.
 | 
			
		||||
 | 
			
		||||
  32 - 35:    nb_bat_entries
 | 
			
		||||
              Disk size, in clusters (BAT size).
 | 
			
		||||
 | 
			
		||||
  36 - 43:    nb_sectors
 | 
			
		||||
              Disk size, in sectors.
 | 
			
		||||
 | 
			
		||||
              For "WithoutFreeSpace" images:
 | 
			
		||||
              Only the lowest 4 bytes are used. The highest 4 bytes must be
 | 
			
		||||
              cleared in this case.
 | 
			
		||||
 | 
			
		||||
              For "WithouFreSpacExt" images, there are no such
 | 
			
		||||
              restrictions.
 | 
			
		||||
 | 
			
		||||
  44 - 47:    in_use
 | 
			
		||||
              Set to 0x746F6E59 when the image is opened by software in R/W
 | 
			
		||||
              mode; set to 0x312e3276 when the image is closed.
 | 
			
		||||
 | 
			
		||||
              A zero in this field means that the image was opened by an old
 | 
			
		||||
              version of the software that doesn't support Format Extension
 | 
			
		||||
              (see below).
 | 
			
		||||
 | 
			
		||||
              Other values are not allowed.
 | 
			
		||||
 | 
			
		||||
  48 - 51:    data_off
 | 
			
		||||
              An offset, in sectors, from the start of the file to the start of
 | 
			
		||||
              the data area.
 | 
			
		||||
 | 
			
		||||
              For "WithoutFreeSpace" images:
 | 
			
		||||
              - If data_off is zero, the offset is calculated as the end of BAT
 | 
			
		||||
                table plus some padding to ensure sector size alignment.
 | 
			
		||||
              - If data_off is non-zero, the offset should be aligned to sector
 | 
			
		||||
                size. However it is recommended to align it to cluster size for
 | 
			
		||||
                newly created images.
 | 
			
		||||
 | 
			
		||||
              For "WithouFreSpacExt" images:
 | 
			
		||||
              data_off must be non-zero and aligned to cluster size.
 | 
			
		||||
 | 
			
		||||
  52 - 55:    flags
 | 
			
		||||
              Miscellaneous flags.
 | 
			
		||||
 | 
			
		||||
              Bit 0: Empty Image bit. If set, the image should be
 | 
			
		||||
                     considered clear.
 | 
			
		||||
 | 
			
		||||
              Bits 2-31: Unused.
 | 
			
		||||
 | 
			
		||||
  56 - 63:    ext_off
 | 
			
		||||
              Format Extension offset, an offset, in sectors, from the start of
 | 
			
		||||
              the file to the start of the Format Extension Cluster.
 | 
			
		||||
 | 
			
		||||
              ext_off must meet the same requirements as cluster offsets
 | 
			
		||||
              defined by BAT entries (see below).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
== BAT ==
 | 
			
		||||
 | 
			
		||||
BAT is placed immediately after the image header. In the file, BAT is a
 | 
			
		||||
contiguous array of 32-bit unsigned little-endian integers with
 | 
			
		||||
(bat_entries * 4) bytes size.
 | 
			
		||||
 | 
			
		||||
Each BAT entry contains an offset from the start of the file to the
 | 
			
		||||
corresponding cluster. The offset set in clusters for "WithouFreSpacExt" images
 | 
			
		||||
and in sectors for "WithoutFreeSpace" images.
 | 
			
		||||
 | 
			
		||||
If a BAT entry is zero, the corresponding cluster is not allocated and should
 | 
			
		||||
be considered as filled with zeroes.
 | 
			
		||||
 | 
			
		||||
Cluster offsets specified by BAT entries must meet the following requirements:
 | 
			
		||||
    - the value must not be lower than data offset (provided by header.data_off
 | 
			
		||||
      or calculated as specified above),
 | 
			
		||||
    - the value must be lower than the desired file size,
 | 
			
		||||
    - the value must be unique among all BAT entries,
 | 
			
		||||
    - the result of (cluster offset - data offset) must be aligned to cluster
 | 
			
		||||
      size.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
== Data Area ==
 | 
			
		||||
 | 
			
		||||
The data area is an area from the data offset (provided by header.data_off or
 | 
			
		||||
calculated as specified above) to the end of the file. It represents a
 | 
			
		||||
contiguous array of clusters. Most of them are allocated by the BAT, some may
 | 
			
		||||
be allocated by the ext_off field in the header while other may be allocated by
 | 
			
		||||
extensions. All clusters allocated by ext_off and extensions should meet the
 | 
			
		||||
same requirements as clusters specified by BAT entries.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
== Format Extension ==
 | 
			
		||||
 | 
			
		||||
The Format Extension is an area 1 cluster in size that provides additional
 | 
			
		||||
format features. This cluster is addressed by the ext_off field in the header.
 | 
			
		||||
The format of the Format Extension area is the following:
 | 
			
		||||
 | 
			
		||||
   0 -  7:    magic
 | 
			
		||||
              Must be 0xAB234CEF23DCEA87
 | 
			
		||||
 | 
			
		||||
   8 - 23:    m_CheckSum
 | 
			
		||||
              The MD5 checksum of the entire Header Extension cluster except
 | 
			
		||||
              the first 24 bytes.
 | 
			
		||||
 | 
			
		||||
    The above are followed by feature sections or "extensions". The last
 | 
			
		||||
    extension must be "End of features" (see below).
 | 
			
		||||
 | 
			
		||||
Each feature section has the following format:
 | 
			
		||||
 | 
			
		||||
   0 -  7:    magic
 | 
			
		||||
              The identifier of the feature:
 | 
			
		||||
              0x0000000000000000 - End of features
 | 
			
		||||
              0x20385FAE252CB34A - Dirty bitmap
 | 
			
		||||
 | 
			
		||||
   8 - 15:    flags
 | 
			
		||||
              External flags for extension:
 | 
			
		||||
 | 
			
		||||
              Bit 0: NECESSARY
 | 
			
		||||
                     If the software cannot load the extension (due to an
 | 
			
		||||
                     unknown magic number or error), the file should not be
 | 
			
		||||
                     changed. If this flag is unset and there is an error on
 | 
			
		||||
                     loading the extension, said extension should be dropped.
 | 
			
		||||
 | 
			
		||||
              Bit 1: TRANSIT
 | 
			
		||||
                     If there is an unknown extension with this flag set,
 | 
			
		||||
                     said extension should be left as is.
 | 
			
		||||
 | 
			
		||||
              If neither NECESSARY nor TRANSIT are set, the extension should be
 | 
			
		||||
              dropped.
 | 
			
		||||
 | 
			
		||||
  16 - 19:    data_size
 | 
			
		||||
              The size of the following feature data, in bytes.
 | 
			
		||||
 | 
			
		||||
  20 - 23:    unused32
 | 
			
		||||
              Align header to 8 bytes boundary.
 | 
			
		||||
 | 
			
		||||
  variable:   data (data_size bytes)
 | 
			
		||||
 | 
			
		||||
    The above is followed by padding to the next 8 bytes boundary, then the
 | 
			
		||||
    next extension starts.
 | 
			
		||||
 | 
			
		||||
    The last extension must be "End of features" with all the fields set to 0.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=== Dirty bitmaps feature ===
 | 
			
		||||
 | 
			
		||||
This feature provides a way of storing dirty bitmaps in the image. The fields
 | 
			
		||||
of its data area are:
 | 
			
		||||
 | 
			
		||||
   0 -  7:    size
 | 
			
		||||
              The bitmap size, should be equal to disk size in sectors.
 | 
			
		||||
 | 
			
		||||
   8 - 23:    id
 | 
			
		||||
              An identifier for backup consistency checking.
 | 
			
		||||
 | 
			
		||||
  24 - 27:    granularity
 | 
			
		||||
              Bitmap granularity, in sectors. I.e., the number of sectors
 | 
			
		||||
              corresponding to one bit of the bitmap. Granularity must be
 | 
			
		||||
              a power of 2.
 | 
			
		||||
 | 
			
		||||
  28 - 31:    l1_size
 | 
			
		||||
              The number of entries in the L1 table of the bitmap.
 | 
			
		||||
 | 
			
		||||
  variable:   l1 (64 * l1_size bytes)
 | 
			
		||||
              L1 offset table (in bytes)
 | 
			
		||||
 | 
			
		||||
A dirty bitmap is stored using a one-level structure for the mapping to host
 | 
			
		||||
clusters - an L1 table.
 | 
			
		||||
 | 
			
		||||
Given an offset in bytes into the bitmap data, the offset in bytes into the
 | 
			
		||||
image file can be obtained as follows:
 | 
			
		||||
 | 
			
		||||
    offset = l1_table[offset / cluster_size] + (offset % cluster_size)
 | 
			
		||||
 | 
			
		||||
If an L1 table entry is 0, the corresponding cluster of the bitmap is assumed
 | 
			
		||||
to be zero.
 | 
			
		||||
 | 
			
		||||
If an L1 table entry is 1, the corresponding cluster of the bitmap is assumed
 | 
			
		||||
to have all bits set.
 | 
			
		||||
 | 
			
		||||
If an L1 table entry is not 0 or 1, it allocates a cluster from the data area.
 | 
			
		||||
							
								
								
									
										365
									
								
								exec.c
									
									
									
									
									
								
							
							
						
						
									
										365
									
								
								exec.c
									
									
									
									
									
								
							@@ -88,9 +88,6 @@ static MemoryRegion io_mem_unassigned;
 | 
			
		||||
 */
 | 
			
		||||
#define RAM_RESIZEABLE (1 << 2)
 | 
			
		||||
 | 
			
		||||
/* RAM is backed by an mmapped file.
 | 
			
		||||
 */
 | 
			
		||||
#define RAM_FILE (1 << 3)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
 | 
			
		||||
@@ -393,18 +390,6 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x
 | 
			
		||||
    return section;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
 | 
			
		||||
{
 | 
			
		||||
    if (memory_region_is_ram(mr)) {
 | 
			
		||||
        return !(is_write && mr->readonly);
 | 
			
		||||
    }
 | 
			
		||||
    if (memory_region_is_romd(mr)) {
 | 
			
		||||
        return !is_write;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called from RCU critical section */
 | 
			
		||||
MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
 | 
			
		||||
                                      hwaddr *xlat, hwaddr *plen,
 | 
			
		||||
@@ -873,7 +858,7 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...)
 | 
			
		||||
    vfprintf(stderr, fmt, ap);
 | 
			
		||||
    fprintf(stderr, "\n");
 | 
			
		||||
    cpu_dump_state(cpu, stderr, fprintf, CPU_DUMP_FPU | CPU_DUMP_CCOP);
 | 
			
		||||
    if (qemu_log_enabled()) {
 | 
			
		||||
    if (qemu_log_separate()) {
 | 
			
		||||
        qemu_log("qemu: fatal: ");
 | 
			
		||||
        qemu_log_vprintf(fmt, ap2);
 | 
			
		||||
        qemu_log("\n");
 | 
			
		||||
@@ -1601,7 +1586,6 @@ ram_addr_t qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
 | 
			
		||||
    new_block->used_length = size;
 | 
			
		||||
    new_block->max_length = size;
 | 
			
		||||
    new_block->flags = share ? RAM_SHARED : 0;
 | 
			
		||||
    new_block->flags |= RAM_FILE;
 | 
			
		||||
    new_block->host = file_ram_alloc(new_block, size,
 | 
			
		||||
                                     mem_path, errp);
 | 
			
		||||
    if (!new_block->host) {
 | 
			
		||||
@@ -1676,25 +1660,6 @@ ram_addr_t qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz,
 | 
			
		||||
    return qemu_ram_alloc_internal(size, maxsz, resized, NULL, true, mr, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void qemu_ram_free_from_ptr(ram_addr_t addr)
 | 
			
		||||
{
 | 
			
		||||
    RAMBlock *block;
 | 
			
		||||
 | 
			
		||||
    qemu_mutex_lock_ramlist();
 | 
			
		||||
    QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
 | 
			
		||||
        if (addr == block->offset) {
 | 
			
		||||
            QLIST_REMOVE_RCU(block, next);
 | 
			
		||||
            ram_list.mru_block = NULL;
 | 
			
		||||
            /* Write list before version */
 | 
			
		||||
            smp_wmb();
 | 
			
		||||
            ram_list.version++;
 | 
			
		||||
            g_free_rcu(block, rcu);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    qemu_mutex_unlock_ramlist();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void reclaim_ramblock(RAMBlock *block)
 | 
			
		||||
{
 | 
			
		||||
    if (block->flags & RAM_PREALLOC) {
 | 
			
		||||
@@ -1703,11 +1668,7 @@ static void reclaim_ramblock(RAMBlock *block)
 | 
			
		||||
        xen_invalidate_map_cache_entry(block->host);
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
    } else if (block->fd >= 0) {
 | 
			
		||||
        if (block->flags & RAM_FILE) {
 | 
			
		||||
            qemu_ram_munmap(block->host, block->max_length);
 | 
			
		||||
        } else {
 | 
			
		||||
            munmap(block->host, block->max_length);
 | 
			
		||||
        }
 | 
			
		||||
        qemu_ram_munmap(block->host, block->max_length);
 | 
			
		||||
        close(block->fd);
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -1813,19 +1774,11 @@ void *qemu_get_ram_block_host_ptr(ram_addr_t addr)
 | 
			
		||||
 * or address_space_rw instead. For local memory (e.g. video ram) that the
 | 
			
		||||
 * device owns, use memory_region_get_ram_ptr.
 | 
			
		||||
 *
 | 
			
		||||
 * By the time this function returns, the returned pointer is not protected
 | 
			
		||||
 * by RCU anymore.  If the caller is not within an RCU critical section and
 | 
			
		||||
 * does not hold the iothread lock, it must have other means of protecting the
 | 
			
		||||
 * pointer, such as a reference to the region that includes the incoming
 | 
			
		||||
 * ram_addr_t.
 | 
			
		||||
 * Called within RCU critical section.
 | 
			
		||||
 */
 | 
			
		||||
void *qemu_get_ram_ptr(ram_addr_t addr)
 | 
			
		||||
{
 | 
			
		||||
    RAMBlock *block;
 | 
			
		||||
    void *ptr;
 | 
			
		||||
 | 
			
		||||
    rcu_read_lock();
 | 
			
		||||
    block = qemu_get_ram_block(addr);
 | 
			
		||||
    RAMBlock *block = qemu_get_ram_block(addr);
 | 
			
		||||
 | 
			
		||||
    if (xen_enabled() && block->host == NULL) {
 | 
			
		||||
        /* We need to check if the requested address is in the RAM
 | 
			
		||||
@@ -1833,52 +1786,44 @@ void *qemu_get_ram_ptr(ram_addr_t addr)
 | 
			
		||||
         * In that case just map until the end of the page.
 | 
			
		||||
         */
 | 
			
		||||
        if (block->offset == 0) {
 | 
			
		||||
            ptr = xen_map_cache(addr, 0, 0);
 | 
			
		||||
            goto unlock;
 | 
			
		||||
            return xen_map_cache(addr, 0, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        block->host = xen_map_cache(block->offset, block->max_length, 1);
 | 
			
		||||
    }
 | 
			
		||||
    ptr = ramblock_ptr(block, addr - block->offset);
 | 
			
		||||
 | 
			
		||||
unlock:
 | 
			
		||||
    rcu_read_unlock();
 | 
			
		||||
    return ptr;
 | 
			
		||||
    return ramblock_ptr(block, addr - block->offset);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return a host pointer to guest's ram. Similar to qemu_get_ram_ptr
 | 
			
		||||
 * but takes a size argument.
 | 
			
		||||
 *
 | 
			
		||||
 * By the time this function returns, the returned pointer is not protected
 | 
			
		||||
 * by RCU anymore.  If the caller is not within an RCU critical section and
 | 
			
		||||
 * does not hold the iothread lock, it must have other means of protecting the
 | 
			
		||||
 * pointer, such as a reference to the region that includes the incoming
 | 
			
		||||
 * ram_addr_t.
 | 
			
		||||
 * Called within RCU critical section.
 | 
			
		||||
 */
 | 
			
		||||
static void *qemu_ram_ptr_length(ram_addr_t addr, hwaddr *size)
 | 
			
		||||
{
 | 
			
		||||
    void *ptr;
 | 
			
		||||
    RAMBlock *block;
 | 
			
		||||
    ram_addr_t offset_inside_block;
 | 
			
		||||
    if (*size == 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (xen_enabled()) {
 | 
			
		||||
        return xen_map_cache(addr, *size, 1);
 | 
			
		||||
    } else {
 | 
			
		||||
        RAMBlock *block;
 | 
			
		||||
        rcu_read_lock();
 | 
			
		||||
        QLIST_FOREACH_RCU(block, &ram_list.blocks, next) {
 | 
			
		||||
            if (addr - block->offset < block->max_length) {
 | 
			
		||||
                if (addr - block->offset + *size > block->max_length)
 | 
			
		||||
                    *size = block->max_length - addr + block->offset;
 | 
			
		||||
                ptr = ramblock_ptr(block, addr - block->offset);
 | 
			
		||||
                rcu_read_unlock();
 | 
			
		||||
                return ptr;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
    block = qemu_get_ram_block(addr);
 | 
			
		||||
    offset_inside_block = addr - block->offset;
 | 
			
		||||
    *size = MIN(*size, block->max_length - offset_inside_block);
 | 
			
		||||
 | 
			
		||||
    if (xen_enabled() && block->host == NULL) {
 | 
			
		||||
        /* We need to check if the requested address is in the RAM
 | 
			
		||||
         * because we don't want to map the entire memory in QEMU.
 | 
			
		||||
         * In that case just map the requested area.
 | 
			
		||||
         */
 | 
			
		||||
        if (block->offset == 0) {
 | 
			
		||||
            return xen_map_cache(addr, *size, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr);
 | 
			
		||||
        abort();
 | 
			
		||||
        block->host = xen_map_cache(block->offset, block->max_length, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ramblock_ptr(block, offset_inside_block);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -1981,6 +1926,7 @@ MemoryRegion *qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr)
 | 
			
		||||
    return block->mr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Called within RCU critical section.  */
 | 
			
		||||
static void notdirty_mem_write(void *opaque, hwaddr ram_addr,
 | 
			
		||||
                               uint64_t val, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
@@ -2511,101 +2457,58 @@ static bool prepare_mmio_access(MemoryRegion *mr)
 | 
			
		||||
    return release_lock;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
 | 
			
		||||
                             uint8_t *buf, int len, bool is_write)
 | 
			
		||||
/* Called within RCU critical section.  */
 | 
			
		||||
static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
 | 
			
		||||
                                                MemTxAttrs attrs,
 | 
			
		||||
                                                const uint8_t *buf,
 | 
			
		||||
                                                int len, hwaddr addr1,
 | 
			
		||||
                                                hwaddr l, MemoryRegion *mr)
 | 
			
		||||
{
 | 
			
		||||
    hwaddr l;
 | 
			
		||||
    uint8_t *ptr;
 | 
			
		||||
    uint64_t val;
 | 
			
		||||
    hwaddr addr1;
 | 
			
		||||
    MemoryRegion *mr;
 | 
			
		||||
    MemTxResult result = MEMTX_OK;
 | 
			
		||||
    bool release_lock = false;
 | 
			
		||||
 | 
			
		||||
    rcu_read_lock();
 | 
			
		||||
    while (len > 0) {
 | 
			
		||||
        l = len;
 | 
			
		||||
        mr = address_space_translate(as, addr, &addr1, &l, is_write);
 | 
			
		||||
 | 
			
		||||
        if (is_write) {
 | 
			
		||||
            if (!memory_access_is_direct(mr, is_write)) {
 | 
			
		||||
                release_lock |= prepare_mmio_access(mr);
 | 
			
		||||
                l = memory_access_size(mr, l, addr1);
 | 
			
		||||
                /* XXX: could force current_cpu to NULL to avoid
 | 
			
		||||
                   potential bugs */
 | 
			
		||||
                switch (l) {
 | 
			
		||||
                case 8:
 | 
			
		||||
                    /* 64 bit write access */
 | 
			
		||||
                    val = ldq_p(buf);
 | 
			
		||||
                    result |= memory_region_dispatch_write(mr, addr1, val, 8,
 | 
			
		||||
                                                           attrs);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 4:
 | 
			
		||||
                    /* 32 bit write access */
 | 
			
		||||
                    val = ldl_p(buf);
 | 
			
		||||
                    result |= memory_region_dispatch_write(mr, addr1, val, 4,
 | 
			
		||||
                                                           attrs);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    /* 16 bit write access */
 | 
			
		||||
                    val = lduw_p(buf);
 | 
			
		||||
                    result |= memory_region_dispatch_write(mr, addr1, val, 2,
 | 
			
		||||
                                                           attrs);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 1:
 | 
			
		||||
                    /* 8 bit write access */
 | 
			
		||||
                    val = ldub_p(buf);
 | 
			
		||||
                    result |= memory_region_dispatch_write(mr, addr1, val, 1,
 | 
			
		||||
                                                           attrs);
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    abort();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                addr1 += memory_region_get_ram_addr(mr);
 | 
			
		||||
                /* RAM case */
 | 
			
		||||
                ptr = qemu_get_ram_ptr(addr1);
 | 
			
		||||
                memcpy(ptr, buf, l);
 | 
			
		||||
                invalidate_and_set_dirty(mr, addr1, l);
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        if (!memory_access_is_direct(mr, true)) {
 | 
			
		||||
            release_lock |= prepare_mmio_access(mr);
 | 
			
		||||
            l = memory_access_size(mr, l, addr1);
 | 
			
		||||
            /* XXX: could force current_cpu to NULL to avoid
 | 
			
		||||
               potential bugs */
 | 
			
		||||
            switch (l) {
 | 
			
		||||
            case 8:
 | 
			
		||||
                /* 64 bit write access */
 | 
			
		||||
                val = ldq_p(buf);
 | 
			
		||||
                result |= memory_region_dispatch_write(mr, addr1, val, 8,
 | 
			
		||||
                                                       attrs);
 | 
			
		||||
                break;
 | 
			
		||||
            case 4:
 | 
			
		||||
                /* 32 bit write access */
 | 
			
		||||
                val = ldl_p(buf);
 | 
			
		||||
                result |= memory_region_dispatch_write(mr, addr1, val, 4,
 | 
			
		||||
                                                       attrs);
 | 
			
		||||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                /* 16 bit write access */
 | 
			
		||||
                val = lduw_p(buf);
 | 
			
		||||
                result |= memory_region_dispatch_write(mr, addr1, val, 2,
 | 
			
		||||
                                                       attrs);
 | 
			
		||||
                break;
 | 
			
		||||
            case 1:
 | 
			
		||||
                /* 8 bit write access */
 | 
			
		||||
                val = ldub_p(buf);
 | 
			
		||||
                result |= memory_region_dispatch_write(mr, addr1, val, 1,
 | 
			
		||||
                                                       attrs);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                abort();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!memory_access_is_direct(mr, is_write)) {
 | 
			
		||||
                /* I/O case */
 | 
			
		||||
                release_lock |= prepare_mmio_access(mr);
 | 
			
		||||
                l = memory_access_size(mr, l, addr1);
 | 
			
		||||
                switch (l) {
 | 
			
		||||
                case 8:
 | 
			
		||||
                    /* 64 bit read access */
 | 
			
		||||
                    result |= memory_region_dispatch_read(mr, addr1, &val, 8,
 | 
			
		||||
                                                          attrs);
 | 
			
		||||
                    stq_p(buf, val);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 4:
 | 
			
		||||
                    /* 32 bit read access */
 | 
			
		||||
                    result |= memory_region_dispatch_read(mr, addr1, &val, 4,
 | 
			
		||||
                                                          attrs);
 | 
			
		||||
                    stl_p(buf, val);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    /* 16 bit read access */
 | 
			
		||||
                    result |= memory_region_dispatch_read(mr, addr1, &val, 2,
 | 
			
		||||
                                                          attrs);
 | 
			
		||||
                    stw_p(buf, val);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 1:
 | 
			
		||||
                    /* 8 bit read access */
 | 
			
		||||
                    result |= memory_region_dispatch_read(mr, addr1, &val, 1,
 | 
			
		||||
                                                          attrs);
 | 
			
		||||
                    stb_p(buf, val);
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    abort();
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                /* RAM case */
 | 
			
		||||
                ptr = qemu_get_ram_ptr(mr->ram_addr + addr1);
 | 
			
		||||
                memcpy(buf, ptr, l);
 | 
			
		||||
            }
 | 
			
		||||
            addr1 += memory_region_get_ram_addr(mr);
 | 
			
		||||
            /* RAM case */
 | 
			
		||||
            ptr = qemu_get_ram_ptr(addr1);
 | 
			
		||||
            memcpy(ptr, buf, l);
 | 
			
		||||
            invalidate_and_set_dirty(mr, addr1, l);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (release_lock) {
 | 
			
		||||
@@ -2616,8 +2519,14 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
 | 
			
		||||
        len -= l;
 | 
			
		||||
        buf += l;
 | 
			
		||||
        addr += l;
 | 
			
		||||
 | 
			
		||||
        if (!len) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        l = len;
 | 
			
		||||
        mr = address_space_translate(as, addr, &addr1, &l, true);
 | 
			
		||||
    }
 | 
			
		||||
    rcu_read_unlock();
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
@@ -2625,15 +2534,122 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
 | 
			
		||||
MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
 | 
			
		||||
                                const uint8_t *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
    return address_space_rw(as, addr, attrs, (uint8_t *)buf, len, true);
 | 
			
		||||
    hwaddr l;
 | 
			
		||||
    hwaddr addr1;
 | 
			
		||||
    MemoryRegion *mr;
 | 
			
		||||
    MemTxResult result = MEMTX_OK;
 | 
			
		||||
 | 
			
		||||
    if (len > 0) {
 | 
			
		||||
        rcu_read_lock();
 | 
			
		||||
        l = len;
 | 
			
		||||
        mr = address_space_translate(as, addr, &addr1, &l, true);
 | 
			
		||||
        result = address_space_write_continue(as, addr, attrs, buf, len,
 | 
			
		||||
                                              addr1, l, mr);
 | 
			
		||||
        rcu_read_unlock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
 | 
			
		||||
                               uint8_t *buf, int len)
 | 
			
		||||
/* Called within RCU critical section.  */
 | 
			
		||||
MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
 | 
			
		||||
                                        MemTxAttrs attrs, uint8_t *buf,
 | 
			
		||||
                                        int len, hwaddr addr1, hwaddr l,
 | 
			
		||||
                                        MemoryRegion *mr)
 | 
			
		||||
{
 | 
			
		||||
    return address_space_rw(as, addr, attrs, buf, len, false);
 | 
			
		||||
    uint8_t *ptr;
 | 
			
		||||
    uint64_t val;
 | 
			
		||||
    MemTxResult result = MEMTX_OK;
 | 
			
		||||
    bool release_lock = false;
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        if (!memory_access_is_direct(mr, false)) {
 | 
			
		||||
            /* I/O case */
 | 
			
		||||
            release_lock |= prepare_mmio_access(mr);
 | 
			
		||||
            l = memory_access_size(mr, l, addr1);
 | 
			
		||||
            switch (l) {
 | 
			
		||||
            case 8:
 | 
			
		||||
                /* 64 bit read access */
 | 
			
		||||
                result |= memory_region_dispatch_read(mr, addr1, &val, 8,
 | 
			
		||||
                                                      attrs);
 | 
			
		||||
                stq_p(buf, val);
 | 
			
		||||
                break;
 | 
			
		||||
            case 4:
 | 
			
		||||
                /* 32 bit read access */
 | 
			
		||||
                result |= memory_region_dispatch_read(mr, addr1, &val, 4,
 | 
			
		||||
                                                      attrs);
 | 
			
		||||
                stl_p(buf, val);
 | 
			
		||||
                break;
 | 
			
		||||
            case 2:
 | 
			
		||||
                /* 16 bit read access */
 | 
			
		||||
                result |= memory_region_dispatch_read(mr, addr1, &val, 2,
 | 
			
		||||
                                                      attrs);
 | 
			
		||||
                stw_p(buf, val);
 | 
			
		||||
                break;
 | 
			
		||||
            case 1:
 | 
			
		||||
                /* 8 bit read access */
 | 
			
		||||
                result |= memory_region_dispatch_read(mr, addr1, &val, 1,
 | 
			
		||||
                                                      attrs);
 | 
			
		||||
                stb_p(buf, val);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                abort();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            /* RAM case */
 | 
			
		||||
            ptr = qemu_get_ram_ptr(mr->ram_addr + addr1);
 | 
			
		||||
            memcpy(buf, ptr, l);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (release_lock) {
 | 
			
		||||
            qemu_mutex_unlock_iothread();
 | 
			
		||||
            release_lock = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        len -= l;
 | 
			
		||||
        buf += l;
 | 
			
		||||
        addr += l;
 | 
			
		||||
 | 
			
		||||
        if (!len) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        l = len;
 | 
			
		||||
        mr = address_space_translate(as, addr, &addr1, &l, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
 | 
			
		||||
                                    MemTxAttrs attrs, uint8_t *buf, int len)
 | 
			
		||||
{
 | 
			
		||||
    hwaddr l;
 | 
			
		||||
    hwaddr addr1;
 | 
			
		||||
    MemoryRegion *mr;
 | 
			
		||||
    MemTxResult result = MEMTX_OK;
 | 
			
		||||
 | 
			
		||||
    if (len > 0) {
 | 
			
		||||
        rcu_read_lock();
 | 
			
		||||
        l = len;
 | 
			
		||||
        mr = address_space_translate(as, addr, &addr1, &l, false);
 | 
			
		||||
        result = address_space_read_continue(as, addr, attrs, buf, len,
 | 
			
		||||
                                             addr1, l, mr);
 | 
			
		||||
        rcu_read_unlock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
 | 
			
		||||
                             uint8_t *buf, int len, bool is_write)
 | 
			
		||||
{
 | 
			
		||||
    if (is_write) {
 | 
			
		||||
        return address_space_write(as, addr, attrs, (uint8_t *)buf, len);
 | 
			
		||||
    } else {
 | 
			
		||||
        return address_space_read(as, addr, attrs, (uint8_t *)buf, len);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf,
 | 
			
		||||
                            int len, int is_write)
 | 
			
		||||
@@ -2825,6 +2841,7 @@ void *address_space_map(AddressSpace *as,
 | 
			
		||||
    hwaddr l, xlat, base;
 | 
			
		||||
    MemoryRegion *mr, *this_mr;
 | 
			
		||||
    ram_addr_t raddr;
 | 
			
		||||
    void *ptr;
 | 
			
		||||
 | 
			
		||||
    if (len == 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
@@ -2876,9 +2893,11 @@ void *address_space_map(AddressSpace *as,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memory_region_ref(mr);
 | 
			
		||||
    rcu_read_unlock();
 | 
			
		||||
    *plen = done;
 | 
			
		||||
    return qemu_ram_ptr_length(raddr + base, plen);
 | 
			
		||||
    ptr = qemu_ram_ptr_length(raddr + base, plen);
 | 
			
		||||
    rcu_read_unlock();
 | 
			
		||||
 | 
			
		||||
    return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Unmaps a memory region previously mapped by address_space_map().
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										44
									
								
								hmp.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								hmp.c
									
									
									
									
									
								
							@@ -311,17 +311,25 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
 | 
			
		||||
 | 
			
		||||
        monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->CPU);
 | 
			
		||||
 | 
			
		||||
        if (cpu->value->has_pc) {
 | 
			
		||||
            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->pc);
 | 
			
		||||
        }
 | 
			
		||||
        if (cpu->value->has_nip) {
 | 
			
		||||
            monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->nip);
 | 
			
		||||
        }
 | 
			
		||||
        if (cpu->value->has_npc) {
 | 
			
		||||
            monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->npc);
 | 
			
		||||
        }
 | 
			
		||||
        if (cpu->value->has_PC) {
 | 
			
		||||
            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->PC);
 | 
			
		||||
        switch (cpu->value->arch) {
 | 
			
		||||
        case CPU_INFO_ARCH_X86:
 | 
			
		||||
            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.x86->pc);
 | 
			
		||||
            break;
 | 
			
		||||
        case CPU_INFO_ARCH_PPC:
 | 
			
		||||
            monitor_printf(mon, " nip=0x%016" PRIx64, cpu->value->u.ppc->nip);
 | 
			
		||||
            break;
 | 
			
		||||
        case CPU_INFO_ARCH_SPARC:
 | 
			
		||||
            monitor_printf(mon, " pc=0x%016" PRIx64, cpu->value->u.sparc->pc);
 | 
			
		||||
            monitor_printf(mon, " npc=0x%016" PRIx64, cpu->value->u.sparc->npc);
 | 
			
		||||
            break;
 | 
			
		||||
        case CPU_INFO_ARCH_MIPS:
 | 
			
		||||
            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.mips->PC);
 | 
			
		||||
            break;
 | 
			
		||||
        case CPU_INFO_ARCH_TRICORE:
 | 
			
		||||
            monitor_printf(mon, " PC=0x%016" PRIx64, cpu->value->u.tricore->PC);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cpu->value->halted) {
 | 
			
		||||
@@ -855,7 +863,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
 | 
			
		||||
                           tpo->has_cancel_path ? ",cancel-path=" : "",
 | 
			
		||||
                           tpo->has_cancel_path ? tpo->cancel_path : "");
 | 
			
		||||
            break;
 | 
			
		||||
        case TPM_TYPE_OPTIONS_KIND_MAX:
 | 
			
		||||
        case TPM_TYPE_OPTIONS_KIND__MAX:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        monitor_printf(mon, "\n");
 | 
			
		||||
@@ -1203,7 +1211,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
 | 
			
		||||
    MigrationCapabilityStatusList *caps = g_malloc0(sizeof(*caps));
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
 | 
			
		||||
    for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) {
 | 
			
		||||
        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
 | 
			
		||||
            caps->value = g_malloc0(sizeof(*caps->value));
 | 
			
		||||
            caps->value->capability = i;
 | 
			
		||||
@@ -1214,7 +1222,7 @@ void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (i == MIGRATION_CAPABILITY_MAX) {
 | 
			
		||||
    if (i == MIGRATION_CAPABILITY__MAX) {
 | 
			
		||||
        error_setg(&err, QERR_INVALID_PARAMETER, cap);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1239,7 +1247,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
 | 
			
		||||
    bool has_x_cpu_throttle_increment = false;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < MIGRATION_PARAMETER_MAX; i++) {
 | 
			
		||||
    for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) {
 | 
			
		||||
        if (strcmp(param, MigrationParameter_lookup[i]) == 0) {
 | 
			
		||||
            switch (i) {
 | 
			
		||||
            case MIGRATION_PARAMETER_COMPRESS_LEVEL:
 | 
			
		||||
@@ -1268,7 +1276,7 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (i == MIGRATION_PARAMETER_MAX) {
 | 
			
		||||
    if (i == MIGRATION_PARAMETER__MAX) {
 | 
			
		||||
        error_setg(&err, QERR_INVALID_PARAMETER, param);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1368,7 +1376,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
 | 
			
		||||
        if (read_only) {
 | 
			
		||||
            read_only_mode =
 | 
			
		||||
                qapi_enum_parse(BlockdevChangeReadOnlyMode_lookup,
 | 
			
		||||
                                read_only, BLOCKDEV_CHANGE_READ_ONLY_MODE_MAX,
 | 
			
		||||
                                read_only, BLOCKDEV_CHANGE_READ_ONLY_MODE__MAX,
 | 
			
		||||
                                BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, &err);
 | 
			
		||||
            if (err) {
 | 
			
		||||
                hmp_handle_error(mon, &err);
 | 
			
		||||
@@ -1771,7 +1779,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict)
 | 
			
		||||
            keylist->value->u.number = value;
 | 
			
		||||
        } else {
 | 
			
		||||
            int idx = index_from_key(keyname_buf);
 | 
			
		||||
            if (idx == Q_KEY_CODE_MAX) {
 | 
			
		||||
            if (idx == Q_KEY_CODE__MAX) {
 | 
			
		||||
                goto err_out;
 | 
			
		||||
            }
 | 
			
		||||
            keylist->value->type = KEY_VALUE_KIND_QCODE;
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,6 @@ static int coroutine_enter_func(void *arg)
 | 
			
		||||
void co_run_in_worker_bh(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    Coroutine *co = opaque;
 | 
			
		||||
    thread_pool_submit_aio(qemu_get_aio_context()->thread_pool,
 | 
			
		||||
    thread_pool_submit_aio(aio_get_thread_pool(qemu_get_aio_context()),
 | 
			
		||||
                           coroutine_enter_func, co, coroutine_enter_cb, co);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ide/
 | 
			
		||||
devices-dirs-$(CONFIG_SOFTMMU) += input/
 | 
			
		||||
devices-dirs-$(CONFIG_SOFTMMU) += intc/
 | 
			
		||||
devices-dirs-$(CONFIG_IPACK) += ipack/
 | 
			
		||||
devices-dirs-$(CONFIG_IPMI) += ipmi/
 | 
			
		||||
devices-dirs-$(CONFIG_SOFTMMU) += isa/
 | 
			
		||||
devices-dirs-$(CONFIG_SOFTMMU) += misc/
 | 
			
		||||
devices-dirs-$(CONFIG_SOFTMMU) += net/
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI) += acpi_interface.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI) += bios-linker-loader.o
 | 
			
		||||
common-obj-$(CONFIG_ACPI) += aml-build.o
 | 
			
		||||
 
 | 
			
		||||
@@ -427,6 +427,41 @@ Aml *aml_arg(int pos)
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToInteger */
 | 
			
		||||
Aml *aml_to_integer(Aml *arg)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x99 /* ToIntegerOp */);
 | 
			
		||||
    aml_append(var, arg);
 | 
			
		||||
    build_append_byte(var->buf, 0x00 /* NullNameOp */);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToHexString */
 | 
			
		||||
Aml *aml_to_hexstring(Aml *src, Aml *dst)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x98 /* ToHexStringOp */);
 | 
			
		||||
    aml_append(var, src);
 | 
			
		||||
    if (dst) {
 | 
			
		||||
        aml_append(var, dst);
 | 
			
		||||
    } else {
 | 
			
		||||
        build_append_byte(var->buf, 0x00 /* NullNameOp */);
 | 
			
		||||
    }
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToBuffer */
 | 
			
		||||
Aml *aml_to_buffer(Aml *src, Aml *dst)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x96 /* ToBufferOp */);
 | 
			
		||||
    aml_append(var, src);
 | 
			
		||||
    if (dst) {
 | 
			
		||||
        aml_append(var, dst);
 | 
			
		||||
    } else {
 | 
			
		||||
        build_append_byte(var->buf, 0x00 /* NullNameOp */);
 | 
			
		||||
    }
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */
 | 
			
		||||
Aml *aml_store(Aml *val, Aml *target)
 | 
			
		||||
{
 | 
			
		||||
@@ -436,44 +471,64 @@ Aml *aml_store(Aml *val, Aml *target)
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */
 | 
			
		||||
Aml *aml_and(Aml *arg1, Aml *arg2)
 | 
			
		||||
/**
 | 
			
		||||
 * build_opcode_2arg_dst:
 | 
			
		||||
 * @op: 1-byte opcode
 | 
			
		||||
 * @arg1: 1st operand
 | 
			
		||||
 * @arg2: 2nd operand
 | 
			
		||||
 * @dst: optional target to store to, set to NULL if it's not required
 | 
			
		||||
 *
 | 
			
		||||
 * An internal helper to compose AML terms that have
 | 
			
		||||
 *   "Op Operand Operand Target"
 | 
			
		||||
 * pattern.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: The newly allocated and composed according to patter Aml object.
 | 
			
		||||
 */
 | 
			
		||||
static Aml *
 | 
			
		||||
build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x7B /* AndOp */);
 | 
			
		||||
    Aml *var = aml_opcode(op);
 | 
			
		||||
    aml_append(var, arg1);
 | 
			
		||||
    aml_append(var, arg2);
 | 
			
		||||
    build_append_byte(var->buf, 0x00 /* NullNameOp */);
 | 
			
		||||
    if (dst) {
 | 
			
		||||
        aml_append(var, dst);
 | 
			
		||||
    } else {
 | 
			
		||||
        build_append_byte(var->buf, 0x00 /* NullNameOp */);
 | 
			
		||||
    }
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */
 | 
			
		||||
Aml *aml_or(Aml *arg1, Aml *arg2)
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */
 | 
			
		||||
Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x7D /* OrOp */);
 | 
			
		||||
    return build_opcode_2arg_dst(0x7B /* AndOp */, arg1, arg2, dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */
 | 
			
		||||
Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst)
 | 
			
		||||
{
 | 
			
		||||
    return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLOr */
 | 
			
		||||
Aml *aml_lor(Aml *arg1, Aml *arg2)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x91 /* LOrOp */);
 | 
			
		||||
    aml_append(var, arg1);
 | 
			
		||||
    aml_append(var, arg2);
 | 
			
		||||
    build_append_byte(var->buf, 0x00 /* NullNameOp */);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */
 | 
			
		||||
Aml *aml_shiftleft(Aml *arg1, Aml *count)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x79 /* ShiftLeftOp */);
 | 
			
		||||
    aml_append(var, arg1);
 | 
			
		||||
    aml_append(var, count);
 | 
			
		||||
    build_append_byte(var->buf, 0x00); /* NullNameOp */
 | 
			
		||||
    return var;
 | 
			
		||||
    return build_opcode_2arg_dst(0x79 /* ShiftLeftOp */, arg1, count, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftRight */
 | 
			
		||||
Aml *aml_shiftright(Aml *arg1, Aml *count)
 | 
			
		||||
Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x7A /* ShiftRightOp */);
 | 
			
		||||
    aml_append(var, arg1);
 | 
			
		||||
    aml_append(var, count);
 | 
			
		||||
    build_append_byte(var->buf, 0x00); /* NullNameOp */
 | 
			
		||||
    return var;
 | 
			
		||||
    return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */
 | 
			
		||||
@@ -486,13 +541,15 @@ Aml *aml_lless(Aml *arg1, Aml *arg2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */
 | 
			
		||||
Aml *aml_add(Aml *arg1, Aml *arg2)
 | 
			
		||||
Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x72 /* AddOp */);
 | 
			
		||||
    aml_append(var, arg1);
 | 
			
		||||
    aml_append(var, arg2);
 | 
			
		||||
    build_append_byte(var->buf, 0x00 /* NullNameOp */);
 | 
			
		||||
    return var;
 | 
			
		||||
    return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSubtract */
 | 
			
		||||
Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst)
 | 
			
		||||
{
 | 
			
		||||
    return build_opcode_2arg_dst(0x74 /* SubtractOp */, arg1, arg2, dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */
 | 
			
		||||
@@ -503,14 +560,18 @@ Aml *aml_increment(Aml *arg)
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDecrement */
 | 
			
		||||
Aml *aml_decrement(Aml *arg)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x76 /* DecrementOp */);
 | 
			
		||||
    aml_append(var, arg);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIndex */
 | 
			
		||||
Aml *aml_index(Aml *arg1, Aml *idx)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x88 /* IndexOp */);
 | 
			
		||||
    aml_append(var, arg1);
 | 
			
		||||
    aml_append(var, idx);
 | 
			
		||||
    build_append_byte(var->buf, 0x00 /* NullNameOp */);
 | 
			
		||||
    return var;
 | 
			
		||||
    return build_opcode_2arg_dst(0x88 /* IndexOp */, arg1, idx, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefNotify */
 | 
			
		||||
@@ -522,6 +583,14 @@ Aml *aml_notify(Aml *arg1, Aml *arg2)
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* helper to call method with 1 argument */
 | 
			
		||||
Aml *aml_call0(const char *method)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    build_append_namestring(var->buf, "%s", method);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* helper to call method with 1 argument */
 | 
			
		||||
Aml *aml_call1(const char *method, Aml *arg1)
 | 
			
		||||
{
 | 
			
		||||
@@ -564,6 +633,94 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4)
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ACPI 5.0: 6.4.3.8.1 GPIO Connection Descriptor
 | 
			
		||||
 * Type 1, Large Item Name 0xC
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static Aml *aml_gpio_connection(AmlGpioConnectionType type,
 | 
			
		||||
                                AmlConsumerAndProducer con_and_pro,
 | 
			
		||||
                                uint8_t flags, AmlPinConfig pin_config,
 | 
			
		||||
                                uint16_t output_drive,
 | 
			
		||||
                                uint16_t debounce_timeout,
 | 
			
		||||
                                const uint32_t pin_list[], uint32_t pin_count,
 | 
			
		||||
                                const char *resource_source_name,
 | 
			
		||||
                                const uint8_t *vendor_data,
 | 
			
		||||
                                uint16_t vendor_data_len)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    const uint16_t min_desc_len = 0x16;
 | 
			
		||||
    uint16_t resource_source_name_len, length;
 | 
			
		||||
    uint16_t pin_table_offset, resource_source_name_offset, vendor_data_offset;
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
 | 
			
		||||
    assert(resource_source_name);
 | 
			
		||||
    resource_source_name_len = strlen(resource_source_name) + 1;
 | 
			
		||||
    length = min_desc_len + resource_source_name_len + vendor_data_len;
 | 
			
		||||
    pin_table_offset = min_desc_len + 1;
 | 
			
		||||
    resource_source_name_offset = pin_table_offset + pin_count * 2;
 | 
			
		||||
    vendor_data_offset = resource_source_name_offset + resource_source_name_len;
 | 
			
		||||
 | 
			
		||||
    build_append_byte(var->buf, 0x8C);  /* GPIO Connection Descriptor */
 | 
			
		||||
    build_append_int_noprefix(var->buf, length, 2); /* Length */
 | 
			
		||||
    build_append_byte(var->buf, 1);     /* Revision ID */
 | 
			
		||||
    build_append_byte(var->buf, type);  /* GPIO Connection Type */
 | 
			
		||||
    /* General Flags (2 bytes) */
 | 
			
		||||
    build_append_int_noprefix(var->buf, con_and_pro, 2);
 | 
			
		||||
    /* Interrupt and IO Flags (2 bytes) */
 | 
			
		||||
    build_append_int_noprefix(var->buf, flags, 2);
 | 
			
		||||
    /* Pin Configuration 0 = Default 1 = Pull-up 2 = Pull-down 3 = No Pull */
 | 
			
		||||
    build_append_byte(var->buf, pin_config);
 | 
			
		||||
    /* Output Drive Strength (2 bytes) */
 | 
			
		||||
    build_append_int_noprefix(var->buf, output_drive, 2);
 | 
			
		||||
    /* Debounce Timeout (2 bytes) */
 | 
			
		||||
    build_append_int_noprefix(var->buf, debounce_timeout, 2);
 | 
			
		||||
    /* Pin Table Offset (2 bytes) */
 | 
			
		||||
    build_append_int_noprefix(var->buf, pin_table_offset, 2);
 | 
			
		||||
    build_append_byte(var->buf, 0);     /* Resource Source Index */
 | 
			
		||||
    /* Resource Source Name Offset (2 bytes) */
 | 
			
		||||
    build_append_int_noprefix(var->buf, resource_source_name_offset, 2);
 | 
			
		||||
    /* Vendor Data Offset (2 bytes) */
 | 
			
		||||
    build_append_int_noprefix(var->buf, vendor_data_offset, 2);
 | 
			
		||||
    /* Vendor Data Length (2 bytes) */
 | 
			
		||||
    build_append_int_noprefix(var->buf, vendor_data_len, 2);
 | 
			
		||||
    /* Pin Number (2n bytes)*/
 | 
			
		||||
    for (i = 0; i < pin_count; i++) {
 | 
			
		||||
        build_append_int_noprefix(var->buf, pin_list[i], 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Resource Source Name */
 | 
			
		||||
    build_append_namestring(var->buf, "%s", resource_source_name);
 | 
			
		||||
    build_append_byte(var->buf, '\0');
 | 
			
		||||
 | 
			
		||||
    /* Vendor-defined Data */
 | 
			
		||||
    if (vendor_data != NULL) {
 | 
			
		||||
        g_array_append_vals(var->buf, vendor_data, vendor_data_len);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ACPI 5.0: 19.5.53
 | 
			
		||||
 * GpioInt(GPIO Interrupt Connection Resource Descriptor Macro)
 | 
			
		||||
 */
 | 
			
		||||
Aml *aml_gpio_int(AmlConsumerAndProducer con_and_pro,
 | 
			
		||||
                  AmlLevelAndEdge edge_level,
 | 
			
		||||
                  AmlActiveHighAndLow active_level, AmlShared shared,
 | 
			
		||||
                  AmlPinConfig pin_config, uint16_t debounce_timeout,
 | 
			
		||||
                  const uint32_t pin_list[], uint32_t pin_count,
 | 
			
		||||
                  const char *resource_source_name,
 | 
			
		||||
                  const uint8_t *vendor_data, uint16_t vendor_data_len)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t flags = edge_level | (active_level << 1) | (shared << 3);
 | 
			
		||||
 | 
			
		||||
    return aml_gpio_connection(AML_INTERRUPT_CONNECTION, con_and_pro, flags,
 | 
			
		||||
                               pin_config, 0, debounce_timeout, pin_list,
 | 
			
		||||
                               pin_count, resource_source_name, vendor_data,
 | 
			
		||||
                               vendor_data_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ACPI 1.0b: 6.4.3.4 32-Bit Fixed Location Memory Range Descriptor
 | 
			
		||||
 * (Type 1, Large Item Name 0x6)
 | 
			
		||||
@@ -598,23 +755,27 @@ Aml *aml_memory32_fixed(uint32_t addr, uint32_t size,
 | 
			
		||||
Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro,
 | 
			
		||||
                   AmlLevelAndEdge level_and_edge,
 | 
			
		||||
                   AmlActiveHighAndLow high_and_low, AmlShared shared,
 | 
			
		||||
                   uint32_t irq)
 | 
			
		||||
                   uint32_t *irq_list, uint8_t irq_count)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    uint8_t irq_flags = con_and_pro | (level_and_edge << 1)
 | 
			
		||||
                        | (high_and_low << 2) | (shared << 3);
 | 
			
		||||
    const int header_bytes_in_len = 2;
 | 
			
		||||
    uint16_t len = header_bytes_in_len + irq_count * sizeof(uint32_t);
 | 
			
		||||
 | 
			
		||||
    assert(irq_count > 0);
 | 
			
		||||
 | 
			
		||||
    build_append_byte(var->buf, 0x89); /* Extended irq descriptor */
 | 
			
		||||
    build_append_byte(var->buf, 6); /* Length, bits[7:0] minimum value = 6 */
 | 
			
		||||
    build_append_byte(var->buf, 0); /* Length, bits[15:8] minimum value = 0 */
 | 
			
		||||
    build_append_byte(var->buf, len & 0xFF); /* Length, bits[7:0] */
 | 
			
		||||
    build_append_byte(var->buf, len >> 8); /* Length, bits[15:8] */
 | 
			
		||||
    build_append_byte(var->buf, irq_flags); /* Interrupt Vector Information. */
 | 
			
		||||
    build_append_byte(var->buf, 0x01);      /* Interrupt table length = 1 */
 | 
			
		||||
    build_append_byte(var->buf, irq_count);   /* Interrupt table length */
 | 
			
		||||
 | 
			
		||||
    /* Interrupt Number */
 | 
			
		||||
    build_append_byte(var->buf, extract32(irq, 0, 8));  /* bits[7:0] */
 | 
			
		||||
    build_append_byte(var->buf, extract32(irq, 8, 8));  /* bits[15:8] */
 | 
			
		||||
    build_append_byte(var->buf, extract32(irq, 16, 8)); /* bits[23:16] */
 | 
			
		||||
    build_append_byte(var->buf, extract32(irq, 24, 8)); /* bits[31:24] */
 | 
			
		||||
    /* Interrupt Number List */
 | 
			
		||||
    for (i = 0; i < irq_count; i++) {
 | 
			
		||||
        build_append_int_noprefix(var->buf, irq_list[i], 4);
 | 
			
		||||
    }
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -672,6 +833,26 @@ Aml *aml_equal(Aml *arg1, Aml *arg2)
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreater */
 | 
			
		||||
Aml *aml_lgreater(Aml *arg1, Aml *arg2)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x94 /* LGreaterOp */);
 | 
			
		||||
    aml_append(var, arg1);
 | 
			
		||||
    aml_append(var, arg2);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreaterEqual */
 | 
			
		||||
Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2)
 | 
			
		||||
{
 | 
			
		||||
    /* LGreaterEqualOp := LNotOp LLessOp */
 | 
			
		||||
    Aml *var = aml_opcode(0x92 /* LNotOp */);
 | 
			
		||||
    build_append_byte(var->buf, 0x95 /* LLessOp */);
 | 
			
		||||
    aml_append(var, arg1);
 | 
			
		||||
    aml_append(var, arg2);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefIfElse */
 | 
			
		||||
Aml *aml_if(Aml *predicate)
 | 
			
		||||
{
 | 
			
		||||
@@ -696,11 +877,24 @@ Aml *aml_while(Aml *predicate)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */
 | 
			
		||||
Aml *aml_method(const char *name, int arg_count)
 | 
			
		||||
Aml *aml_method(const char *name, int arg_count, AmlSerializeFlag sflag)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_bundle(0x14 /* MethodOp */, AML_PACKAGE);
 | 
			
		||||
    int methodflags;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * MethodFlags:
 | 
			
		||||
     *   bit 0-2: ArgCount (0-7)
 | 
			
		||||
     *   bit 3: SerializeFlag
 | 
			
		||||
     *     0: NotSerialized
 | 
			
		||||
     *     1: Serialized
 | 
			
		||||
     *   bit 4-7: reserved (must be 0)
 | 
			
		||||
     */
 | 
			
		||||
    assert(arg_count < 8);
 | 
			
		||||
    methodflags = arg_count | (sflag << 3);
 | 
			
		||||
 | 
			
		||||
    build_append_namestring(var->buf, "%s", name);
 | 
			
		||||
    build_append_byte(var->buf, arg_count); /* MethodFlags: ArgCount */
 | 
			
		||||
    build_append_byte(var->buf, methodflags); /* MethodFlags: ArgCount */
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -784,27 +978,43 @@ Aml *aml_reserved_field(unsigned length)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */
 | 
			
		||||
Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule)
 | 
			
		||||
Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock,
 | 
			
		||||
               AmlUpdateRule rule)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE);
 | 
			
		||||
    uint8_t flags = rule << 5 | type;
 | 
			
		||||
 | 
			
		||||
    flags |= lock << 4; /* LockRule at 4 bit offset */
 | 
			
		||||
 | 
			
		||||
    build_append_namestring(var->buf, "%s", name);
 | 
			
		||||
    build_append_byte(var->buf, flags);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
 | 
			
		||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
 | 
			
		||||
static
 | 
			
		||||
Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    build_append_byte(var->buf, 0x8A); /* CreateDWordFieldOp */
 | 
			
		||||
    Aml *var = aml_opcode(opcode);
 | 
			
		||||
    aml_append(var, srcbuf);
 | 
			
		||||
    aml_append(var, index);
 | 
			
		||||
    build_append_namestring(var->buf, "%s", name);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */
 | 
			
		||||
Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name)
 | 
			
		||||
{
 | 
			
		||||
    return create_field_common(0x8A /* CreateDWordFieldOp */,
 | 
			
		||||
                               srcbuf, index, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 2.0a: 17.2.4.2 Named Objects Encoding: DefCreateQWordField */
 | 
			
		||||
Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name)
 | 
			
		||||
{
 | 
			
		||||
    return create_field_common(0x8F /* CreateQWordFieldOp */,
 | 
			
		||||
                               srcbuf, index, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.3 Data Objects Encoding: String */
 | 
			
		||||
Aml *aml_string(const char *name_format, ...)
 | 
			
		||||
{
 | 
			
		||||
@@ -1065,6 +1275,30 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed,
 | 
			
		||||
                             addr_trans, len, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 6.4.2.2 DMA Format/6.4.2.2.1 ASL Macro for DMA Descriptor */
 | 
			
		||||
Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz,
 | 
			
		||||
             uint8_t channel)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    uint8_t flags = sz | bm << 2 | typ << 5;
 | 
			
		||||
 | 
			
		||||
    assert(channel < 8);
 | 
			
		||||
    build_append_byte(var->buf, 0x2A);    /* Byte 0: DMA Descriptor */
 | 
			
		||||
    build_append_byte(var->buf, 1U << channel); /* Byte 1: _DMA - DmaChannel */
 | 
			
		||||
    build_append_byte(var->buf, flags);   /* Byte 2 */
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefSleep */
 | 
			
		||||
Aml *aml_sleep(uint64_t msec)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
 | 
			
		||||
    build_append_byte(var->buf, 0x22); /* SleepOp */
 | 
			
		||||
    aml_append(var, aml_int(msec));
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t Hex2Byte(const char *src)
 | 
			
		||||
{
 | 
			
		||||
    int hi, lo;
 | 
			
		||||
@@ -1135,16 +1369,81 @@ Aml *aml_unicode(const char *str)
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDerefOf */
 | 
			
		||||
Aml *aml_derefof(Aml *arg)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x83 /* DerefOfOp */);
 | 
			
		||||
    aml_append(var, arg);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSizeOf */
 | 
			
		||||
Aml *aml_sizeof(Aml *arg)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x87 /* SizeOfOp */);
 | 
			
		||||
    aml_append(var, arg);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMutex */
 | 
			
		||||
Aml *aml_mutex(const char *name, uint8_t sync_level)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
 | 
			
		||||
    build_append_byte(var->buf, 0x01); /* MutexOp */
 | 
			
		||||
    build_append_namestring(var->buf, "%s", name);
 | 
			
		||||
    assert(!(sync_level & 0xF0));
 | 
			
		||||
    build_append_byte(var->buf, sync_level);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAcquire */
 | 
			
		||||
Aml *aml_acquire(Aml *mutex, uint16_t timeout)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
 | 
			
		||||
    build_append_byte(var->buf, 0x23); /* AcquireOp */
 | 
			
		||||
    aml_append(var, mutex);
 | 
			
		||||
    build_append_int_noprefix(var->buf, timeout, sizeof(timeout));
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefRelease */
 | 
			
		||||
Aml *aml_release(Aml *mutex)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_alloc();
 | 
			
		||||
    build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */
 | 
			
		||||
    build_append_byte(var->buf, 0x27); /* ReleaseOp */
 | 
			
		||||
    aml_append(var, mutex);
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 1.0b: 16.2.5.1 Name Space Modifier Objects Encoding: DefAlias */
 | 
			
		||||
Aml *aml_alias(const char *source_object, const char *alias_object)
 | 
			
		||||
{
 | 
			
		||||
    Aml *var = aml_opcode(0x06 /* AliasOp */);
 | 
			
		||||
    aml_append(var, aml_name("%s", source_object));
 | 
			
		||||
    aml_append(var, aml_name("%s", alias_object));
 | 
			
		||||
    return var;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
build_header(GArray *linker, GArray *table_data,
 | 
			
		||||
             AcpiTableHeader *h, const char *sig, int len, uint8_t rev)
 | 
			
		||||
             AcpiTableHeader *h, const char *sig, int len, uint8_t rev,
 | 
			
		||||
             const char *oem_table_id)
 | 
			
		||||
{
 | 
			
		||||
    memcpy(&h->signature, sig, 4);
 | 
			
		||||
    h->length = cpu_to_le32(len);
 | 
			
		||||
    h->revision = rev;
 | 
			
		||||
    memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6);
 | 
			
		||||
    memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
 | 
			
		||||
    memcpy(h->oem_table_id + 4, sig, 4);
 | 
			
		||||
 | 
			
		||||
    if (oem_table_id) {
 | 
			
		||||
        strncpy((char *)h->oem_table_id, oem_table_id, sizeof(h->oem_table_id));
 | 
			
		||||
    } else {
 | 
			
		||||
        memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4);
 | 
			
		||||
        memcpy(h->oem_table_id + 4, sig, 4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    h->oem_revision = cpu_to_le32(1);
 | 
			
		||||
    memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4);
 | 
			
		||||
    h->asl_compiler_revision = cpu_to_le32(1);
 | 
			
		||||
@@ -1211,5 +1510,5 @@ build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets)
 | 
			
		||||
                                       sizeof(uint32_t));
 | 
			
		||||
    }
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)rsdt, "RSDT", rsdt_len, 1);
 | 
			
		||||
                 (void *)rsdt, "RSDT", rsdt_len, 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -231,6 +231,11 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
 | 
			
		||||
                         DeviceState *dev, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    MemStatus *mdev;
 | 
			
		||||
    DeviceClass *dc = DEVICE_GET_CLASS(dev);
 | 
			
		||||
 | 
			
		||||
    if (!dc->hotpluggable) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mdev = acpi_memory_slot_status(mem_st, dev, errp);
 | 
			
		||||
    if (!mdev) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										488
									
								
								hw/acpi/nvdimm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										488
									
								
								hw/acpi/nvdimm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,488 @@
 | 
			
		||||
/*
 | 
			
		||||
 * NVDIMM ACPI Implementation
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright(C) 2015 Intel Corporation.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:
 | 
			
		||||
 *  Xiao Guangrong <guangrong.xiao@linux.intel.com>
 | 
			
		||||
 *
 | 
			
		||||
 * NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
 | 
			
		||||
 * and the DSM specification can be found at:
 | 
			
		||||
 *       http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf
 | 
			
		||||
 *
 | 
			
		||||
 * Currently, it only supports PMEM Virtualization.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library; if not, see <http://www.gnu.org/licenses/>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/acpi/acpi.h"
 | 
			
		||||
#include "hw/acpi/aml-build.h"
 | 
			
		||||
#include "hw/mem/nvdimm.h"
 | 
			
		||||
 | 
			
		||||
static int nvdimm_plugged_device_list(Object *obj, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    GSList **list = opaque;
 | 
			
		||||
 | 
			
		||||
    if (object_dynamic_cast(obj, TYPE_NVDIMM)) {
 | 
			
		||||
        DeviceState *dev = DEVICE(obj);
 | 
			
		||||
 | 
			
		||||
        if (dev->realized) { /* only realized NVDIMMs matter */
 | 
			
		||||
            *list = g_slist_append(*list, DEVICE(obj));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    object_child_foreach(obj, nvdimm_plugged_device_list, opaque);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * inquire plugged NVDIMM devices and link them into the list which is
 | 
			
		||||
 * returned to the caller.
 | 
			
		||||
 *
 | 
			
		||||
 * Note: it is the caller's responsibility to free the list to avoid
 | 
			
		||||
 * memory leak.
 | 
			
		||||
 */
 | 
			
		||||
static GSList *nvdimm_get_plugged_device_list(void)
 | 
			
		||||
{
 | 
			
		||||
    GSList *list = NULL;
 | 
			
		||||
 | 
			
		||||
    object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list,
 | 
			
		||||
                         &list);
 | 
			
		||||
    return list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7)             \
 | 
			
		||||
   { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
 | 
			
		||||
     (b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff,          \
 | 
			
		||||
     (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * define Byte Addressable Persistent Memory (PM) Region according to
 | 
			
		||||
 * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure.
 | 
			
		||||
 */
 | 
			
		||||
static const uint8_t nvdimm_nfit_spa_uuid[] =
 | 
			
		||||
      NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33,
 | 
			
		||||
                     0x18, 0xb7, 0x8c, 0xdb);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * NVDIMM Firmware Interface Table
 | 
			
		||||
 * @signature: "NFIT"
 | 
			
		||||
 *
 | 
			
		||||
 * It provides information that allows OSPM to enumerate NVDIMM present in
 | 
			
		||||
 * the platform and associate system physical address ranges created by the
 | 
			
		||||
 * NVDIMMs.
 | 
			
		||||
 *
 | 
			
		||||
 * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
 | 
			
		||||
 */
 | 
			
		||||
struct NvdimmNfitHeader {
 | 
			
		||||
    ACPI_TABLE_HEADER_DEF
 | 
			
		||||
    uint32_t reserved;
 | 
			
		||||
} QEMU_PACKED;
 | 
			
		||||
typedef struct NvdimmNfitHeader NvdimmNfitHeader;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware
 | 
			
		||||
 * Interface Table (NFIT).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * System Physical Address Range Structure
 | 
			
		||||
 *
 | 
			
		||||
 * It describes the system physical address ranges occupied by NVDIMMs and
 | 
			
		||||
 * the types of the regions.
 | 
			
		||||
 */
 | 
			
		||||
struct NvdimmNfitSpa {
 | 
			
		||||
    uint16_t type;
 | 
			
		||||
    uint16_t length;
 | 
			
		||||
    uint16_t spa_index;
 | 
			
		||||
    uint16_t flags;
 | 
			
		||||
    uint32_t reserved;
 | 
			
		||||
    uint32_t proximity_domain;
 | 
			
		||||
    uint8_t type_guid[16];
 | 
			
		||||
    uint64_t spa_base;
 | 
			
		||||
    uint64_t spa_length;
 | 
			
		||||
    uint64_t mem_attr;
 | 
			
		||||
} QEMU_PACKED;
 | 
			
		||||
typedef struct NvdimmNfitSpa NvdimmNfitSpa;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Memory Device to System Physical Address Range Mapping Structure
 | 
			
		||||
 *
 | 
			
		||||
 * It enables identifying each NVDIMM region and the corresponding SPA
 | 
			
		||||
 * describing the memory interleave
 | 
			
		||||
 */
 | 
			
		||||
struct NvdimmNfitMemDev {
 | 
			
		||||
    uint16_t type;
 | 
			
		||||
    uint16_t length;
 | 
			
		||||
    uint32_t nfit_handle;
 | 
			
		||||
    uint16_t phys_id;
 | 
			
		||||
    uint16_t region_id;
 | 
			
		||||
    uint16_t spa_index;
 | 
			
		||||
    uint16_t dcr_index;
 | 
			
		||||
    uint64_t region_len;
 | 
			
		||||
    uint64_t region_offset;
 | 
			
		||||
    uint64_t region_dpa;
 | 
			
		||||
    uint16_t interleave_index;
 | 
			
		||||
    uint16_t interleave_ways;
 | 
			
		||||
    uint16_t flags;
 | 
			
		||||
    uint16_t reserved;
 | 
			
		||||
} QEMU_PACKED;
 | 
			
		||||
typedef struct NvdimmNfitMemDev NvdimmNfitMemDev;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * NVDIMM Control Region Structure
 | 
			
		||||
 *
 | 
			
		||||
 * It describes the NVDIMM and if applicable, Block Control Window.
 | 
			
		||||
 */
 | 
			
		||||
struct NvdimmNfitControlRegion {
 | 
			
		||||
    uint16_t type;
 | 
			
		||||
    uint16_t length;
 | 
			
		||||
    uint16_t dcr_index;
 | 
			
		||||
    uint16_t vendor_id;
 | 
			
		||||
    uint16_t device_id;
 | 
			
		||||
    uint16_t revision_id;
 | 
			
		||||
    uint16_t sub_vendor_id;
 | 
			
		||||
    uint16_t sub_device_id;
 | 
			
		||||
    uint16_t sub_revision_id;
 | 
			
		||||
    uint8_t reserved[6];
 | 
			
		||||
    uint32_t serial_number;
 | 
			
		||||
    uint16_t fic;
 | 
			
		||||
    uint16_t num_bcw;
 | 
			
		||||
    uint64_t bcw_size;
 | 
			
		||||
    uint64_t cmd_offset;
 | 
			
		||||
    uint64_t cmd_size;
 | 
			
		||||
    uint64_t status_offset;
 | 
			
		||||
    uint64_t status_size;
 | 
			
		||||
    uint16_t flags;
 | 
			
		||||
    uint8_t reserved2[6];
 | 
			
		||||
} QEMU_PACKED;
 | 
			
		||||
typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Module serial number is a unique number for each device. We use the
 | 
			
		||||
 * slot id of NVDIMM device to generate this number so that each device
 | 
			
		||||
 * associates with a different number.
 | 
			
		||||
 *
 | 
			
		||||
 * 0x123456 is a magic number we arbitrarily chose.
 | 
			
		||||
 */
 | 
			
		||||
static uint32_t nvdimm_slot_to_sn(int slot)
 | 
			
		||||
{
 | 
			
		||||
    return 0x123456 + slot;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * handle is used to uniquely associate nfit_memdev structure with NVDIMM
 | 
			
		||||
 * ACPI device - nfit_memdev.nfit_handle matches with the value returned
 | 
			
		||||
 * by ACPI device _ADR method.
 | 
			
		||||
 *
 | 
			
		||||
 * We generate the handle with the slot id of NVDIMM device and reserve
 | 
			
		||||
 * 0 for NVDIMM root device.
 | 
			
		||||
 */
 | 
			
		||||
static uint32_t nvdimm_slot_to_handle(int slot)
 | 
			
		||||
{
 | 
			
		||||
    return slot + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * index uniquely identifies the structure, 0 is reserved which indicates
 | 
			
		||||
 * that the structure is not valid or the associated structure is not
 | 
			
		||||
 * present.
 | 
			
		||||
 *
 | 
			
		||||
 * Each NVDIMM device needs two indexes, one for nfit_spa and another for
 | 
			
		||||
 * nfit_dc which are generated by the slot id of NVDIMM device.
 | 
			
		||||
 */
 | 
			
		||||
static uint16_t nvdimm_slot_to_spa_index(int slot)
 | 
			
		||||
{
 | 
			
		||||
    return (slot + 1) << 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* See the comments of nvdimm_slot_to_spa_index(). */
 | 
			
		||||
static uint32_t nvdimm_slot_to_dcr_index(int slot)
 | 
			
		||||
{
 | 
			
		||||
    return nvdimm_slot_to_spa_index(slot) + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */
 | 
			
		||||
static void
 | 
			
		||||
nvdimm_build_structure_spa(GArray *structures, DeviceState *dev)
 | 
			
		||||
{
 | 
			
		||||
    NvdimmNfitSpa *nfit_spa;
 | 
			
		||||
    uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
 | 
			
		||||
                                            NULL);
 | 
			
		||||
    uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
 | 
			
		||||
                                            NULL);
 | 
			
		||||
    uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP,
 | 
			
		||||
                                            NULL);
 | 
			
		||||
    int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
 | 
			
		||||
                                            NULL);
 | 
			
		||||
 | 
			
		||||
    nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa));
 | 
			
		||||
 | 
			
		||||
    nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range
 | 
			
		||||
                                      Structure */);
 | 
			
		||||
    nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa));
 | 
			
		||||
    nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Control region is strict as all the device info, such as SN, index,
 | 
			
		||||
     * is associated with slot id.
 | 
			
		||||
     */
 | 
			
		||||
    nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for
 | 
			
		||||
                                       management during hot add/online
 | 
			
		||||
                                       operation */ |
 | 
			
		||||
                                  2 /* Data in Proximity Domain field is
 | 
			
		||||
                                       valid*/);
 | 
			
		||||
 | 
			
		||||
    /* NUMA node. */
 | 
			
		||||
    nfit_spa->proximity_domain = cpu_to_le32(node);
 | 
			
		||||
    /* the region reported as PMEM. */
 | 
			
		||||
    memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid,
 | 
			
		||||
           sizeof(nvdimm_nfit_spa_uuid));
 | 
			
		||||
 | 
			
		||||
    nfit_spa->spa_base = cpu_to_le64(addr);
 | 
			
		||||
    nfit_spa->spa_length = cpu_to_le64(size);
 | 
			
		||||
 | 
			
		||||
    /* It is the PMEM and can be cached as writeback. */
 | 
			
		||||
    nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ |
 | 
			
		||||
                                     0x8000ULL /* EFI_MEMORY_NV */);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping
 | 
			
		||||
 * Structure
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev)
 | 
			
		||||
{
 | 
			
		||||
    NvdimmNfitMemDev *nfit_memdev;
 | 
			
		||||
    uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP,
 | 
			
		||||
                                            NULL);
 | 
			
		||||
    uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP,
 | 
			
		||||
                                            NULL);
 | 
			
		||||
    int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
 | 
			
		||||
                                            NULL);
 | 
			
		||||
    uint32_t handle = nvdimm_slot_to_handle(slot);
 | 
			
		||||
 | 
			
		||||
    nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev));
 | 
			
		||||
 | 
			
		||||
    nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address
 | 
			
		||||
                                         Range Map Structure*/);
 | 
			
		||||
    nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev));
 | 
			
		||||
    nfit_memdev->nfit_handle = cpu_to_le32(handle);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * associate memory device with System Physical Address Range
 | 
			
		||||
     * Structure.
 | 
			
		||||
     */
 | 
			
		||||
    nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot));
 | 
			
		||||
    /* associate memory device with Control Region Structure. */
 | 
			
		||||
    nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));
 | 
			
		||||
 | 
			
		||||
    /* The memory region on the device. */
 | 
			
		||||
    nfit_memdev->region_len = cpu_to_le64(size);
 | 
			
		||||
    nfit_memdev->region_dpa = cpu_to_le64(addr);
 | 
			
		||||
 | 
			
		||||
    /* Only one interleave for PMEM. */
 | 
			
		||||
    nfit_memdev->interleave_ways = cpu_to_le16(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure.
 | 
			
		||||
 */
 | 
			
		||||
static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev)
 | 
			
		||||
{
 | 
			
		||||
    NvdimmNfitControlRegion *nfit_dcr;
 | 
			
		||||
    int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
 | 
			
		||||
                                       NULL);
 | 
			
		||||
    uint32_t sn = nvdimm_slot_to_sn(slot);
 | 
			
		||||
 | 
			
		||||
    nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr));
 | 
			
		||||
 | 
			
		||||
    nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */);
 | 
			
		||||
    nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr));
 | 
			
		||||
    nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot));
 | 
			
		||||
 | 
			
		||||
    /* vendor: Intel. */
 | 
			
		||||
    nfit_dcr->vendor_id = cpu_to_le16(0x8086);
 | 
			
		||||
    nfit_dcr->device_id = cpu_to_le16(1);
 | 
			
		||||
 | 
			
		||||
    /* The _DSM method is following Intel's DSM specification. */
 | 
			
		||||
    nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported
 | 
			
		||||
                                             in ACPI 6.0 is 1. */);
 | 
			
		||||
    nfit_dcr->serial_number = cpu_to_le32(sn);
 | 
			
		||||
    nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter
 | 
			
		||||
                                         2: NVDIMM Device Specific Method
 | 
			
		||||
                                         (DSM) in DSM Spec Rev1.*/);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GArray *nvdimm_build_device_structure(GSList *device_list)
 | 
			
		||||
{
 | 
			
		||||
    GArray *structures = g_array_new(false, true /* clear */, 1);
 | 
			
		||||
 | 
			
		||||
    for (; device_list; device_list = device_list->next) {
 | 
			
		||||
        DeviceState *dev = device_list->data;
 | 
			
		||||
 | 
			
		||||
        /* build System Physical Address Range Structure. */
 | 
			
		||||
        nvdimm_build_structure_spa(structures, dev);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * build Memory Device to System Physical Address Range Mapping
 | 
			
		||||
         * Structure.
 | 
			
		||||
         */
 | 
			
		||||
        nvdimm_build_structure_memdev(structures, dev);
 | 
			
		||||
 | 
			
		||||
        /* build NVDIMM Control Region Structure. */
 | 
			
		||||
        nvdimm_build_structure_dcr(structures, dev);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return structures;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets,
 | 
			
		||||
                              GArray *table_data, GArray *linker)
 | 
			
		||||
{
 | 
			
		||||
    GArray *structures = nvdimm_build_device_structure(device_list);
 | 
			
		||||
    void *header;
 | 
			
		||||
 | 
			
		||||
    acpi_add_table(table_offsets, table_data);
 | 
			
		||||
 | 
			
		||||
    /* NFIT header. */
 | 
			
		||||
    header = acpi_data_push(table_data, sizeof(NvdimmNfitHeader));
 | 
			
		||||
    /* NVDIMM device structures. */
 | 
			
		||||
    g_array_append_vals(table_data, structures->data, structures->len);
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data, header, "NFIT",
 | 
			
		||||
                 sizeof(NvdimmNfitHeader) + structures->len, 1, NULL);
 | 
			
		||||
    g_array_free(structures, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define NVDIMM_COMMON_DSM      "NCAL"
 | 
			
		||||
 | 
			
		||||
static void nvdimm_build_common_dsm(Aml *dev)
 | 
			
		||||
{
 | 
			
		||||
    Aml *method, *ifctx, *function;
 | 
			
		||||
    uint8_t byte_list[1];
 | 
			
		||||
 | 
			
		||||
    method = aml_method(NVDIMM_COMMON_DSM, 4, AML_NOTSERIALIZED);
 | 
			
		||||
    function = aml_arg(2);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * function 0 is called to inquire what functions are supported by
 | 
			
		||||
     * OSPM
 | 
			
		||||
     */
 | 
			
		||||
    ifctx = aml_if(aml_equal(function, aml_int(0)));
 | 
			
		||||
    byte_list[0] = 0 /* No function Supported */;
 | 
			
		||||
    aml_append(ifctx, aml_return(aml_buffer(1, byte_list)));
 | 
			
		||||
    aml_append(method, ifctx);
 | 
			
		||||
 | 
			
		||||
    /* No function is supported yet. */
 | 
			
		||||
    byte_list[0] = 1 /* Not Supported */;
 | 
			
		||||
    aml_append(method, aml_return(aml_buffer(1, byte_list)));
 | 
			
		||||
 | 
			
		||||
    aml_append(dev, method);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nvdimm_build_device_dsm(Aml *dev)
 | 
			
		||||
{
 | 
			
		||||
    Aml *method;
 | 
			
		||||
 | 
			
		||||
    method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
 | 
			
		||||
    aml_append(method, aml_return(aml_call4(NVDIMM_COMMON_DSM, aml_arg(0),
 | 
			
		||||
                                  aml_arg(1), aml_arg(2), aml_arg(3))));
 | 
			
		||||
    aml_append(dev, method);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev)
 | 
			
		||||
{
 | 
			
		||||
    for (; device_list; device_list = device_list->next) {
 | 
			
		||||
        DeviceState *dev = device_list->data;
 | 
			
		||||
        int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP,
 | 
			
		||||
                                           NULL);
 | 
			
		||||
        uint32_t handle = nvdimm_slot_to_handle(slot);
 | 
			
		||||
        Aml *nvdimm_dev;
 | 
			
		||||
 | 
			
		||||
        nvdimm_dev = aml_device("NV%02X", slot);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * ACPI 6.0: 9.20 NVDIMM Devices:
 | 
			
		||||
         *
 | 
			
		||||
         * _ADR object that is used to supply OSPM with unique address
 | 
			
		||||
         * of the NVDIMM device. This is done by returning the NFIT Device
 | 
			
		||||
         * handle that is used to identify the associated entries in ACPI
 | 
			
		||||
         * table NFIT or _FIT.
 | 
			
		||||
         */
 | 
			
		||||
        aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle)));
 | 
			
		||||
 | 
			
		||||
        nvdimm_build_device_dsm(nvdimm_dev);
 | 
			
		||||
        aml_append(root_dev, nvdimm_dev);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets,
 | 
			
		||||
                              GArray *table_data, GArray *linker)
 | 
			
		||||
{
 | 
			
		||||
    Aml *ssdt, *sb_scope, *dev;
 | 
			
		||||
 | 
			
		||||
    acpi_add_table(table_offsets, table_data);
 | 
			
		||||
 | 
			
		||||
    ssdt = init_aml_allocator();
 | 
			
		||||
    acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
 | 
			
		||||
 | 
			
		||||
    sb_scope = aml_scope("\\_SB");
 | 
			
		||||
 | 
			
		||||
    dev = aml_device("NVDR");
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * ACPI 6.0: 9.20 NVDIMM Devices:
 | 
			
		||||
     *
 | 
			
		||||
     * The ACPI Name Space device uses _HID of ACPI0012 to identify the root
 | 
			
		||||
     * NVDIMM interface device. Platform firmware is required to contain one
 | 
			
		||||
     * such device in _SB scope if NVDIMMs support is exposed by platform to
 | 
			
		||||
     * OSPM.
 | 
			
		||||
     * For each NVDIMM present or intended to be supported by platform,
 | 
			
		||||
     * platform firmware also exposes an ACPI Namespace Device under the
 | 
			
		||||
     * root device.
 | 
			
		||||
     */
 | 
			
		||||
    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012")));
 | 
			
		||||
 | 
			
		||||
    nvdimm_build_common_dsm(dev);
 | 
			
		||||
    nvdimm_build_device_dsm(dev);
 | 
			
		||||
 | 
			
		||||
    nvdimm_build_nvdimm_devices(device_list, dev);
 | 
			
		||||
 | 
			
		||||
    aml_append(sb_scope, dev);
 | 
			
		||||
 | 
			
		||||
    aml_append(ssdt, sb_scope);
 | 
			
		||||
    /* copy AML table into ACPI tables blob and patch header there */
 | 
			
		||||
    g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
        (void *)(table_data->data + table_data->len - ssdt->buf->len),
 | 
			
		||||
        "SSDT", ssdt->buf->len, 1, "NVDIMM");
 | 
			
		||||
    free_aml_allocator();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data,
 | 
			
		||||
                       GArray *linker)
 | 
			
		||||
{
 | 
			
		||||
    GSList *device_list;
 | 
			
		||||
 | 
			
		||||
    /* no NVDIMM device is plugged. */
 | 
			
		||||
    device_list = nvdimm_get_plugged_device_list();
 | 
			
		||||
    if (!device_list) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    nvdimm_build_nfit(device_list, table_offsets, table_data, linker);
 | 
			
		||||
    nvdimm_build_ssdt(device_list, table_offsets, table_data, linker);
 | 
			
		||||
    g_slist_free(device_list);
 | 
			
		||||
}
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
#include "alpha_sys.h"
 | 
			
		||||
#include "qemu/log.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Fallback for unassigned PCI I/O operations.  Avoids MCHK.  */
 | 
			
		||||
@@ -73,7 +74,7 @@ static uint64_t iack_read(void *opaque, hwaddr addr, unsigned size)
 | 
			
		||||
static void special_write(void *opaque, hwaddr addr,
 | 
			
		||||
                          uint64_t val, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    qemu_log("pci: special write cycle");
 | 
			
		||||
    trace_alpha_pci_iack_write();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const MemoryRegionOps alpha_pci_iack_ops = {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ static void fsl_imx25_init(Object *obj)
 | 
			
		||||
    object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
 | 
			
		||||
    qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
 | 
			
		||||
 | 
			
		||||
    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
 | 
			
		||||
    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM);
 | 
			
		||||
    qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) {
 | 
			
		||||
@@ -150,7 +150,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
            { FSL_IMX25_GPT4_ADDR, FSL_IMX25_GPT4_IRQ }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        s->gpt[i].ccm = DEVICE(&s->ccm);
 | 
			
		||||
        s->gpt[i].ccm = IMX_CCM(&s->ccm);
 | 
			
		||||
 | 
			
		||||
        object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", &err);
 | 
			
		||||
        if (err) {
 | 
			
		||||
@@ -173,7 +173,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
            { FSL_IMX25_EPIT2_ADDR, FSL_IMX25_EPIT2_IRQ }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        s->epit[i].ccm = DEVICE(&s->ccm);
 | 
			
		||||
        s->epit[i].ccm = IMX_CCM(&s->ccm);
 | 
			
		||||
 | 
			
		||||
        object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
 | 
			
		||||
        if (err) {
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ static void fsl_imx31_init(Object *obj)
 | 
			
		||||
    object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC);
 | 
			
		||||
    qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default());
 | 
			
		||||
 | 
			
		||||
    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX_CCM);
 | 
			
		||||
    object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM);
 | 
			
		||||
    qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default());
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) {
 | 
			
		||||
@@ -128,7 +128,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
                                            serial_table[i].irq));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->gpt.ccm = DEVICE(&s->ccm);
 | 
			
		||||
    s->gpt.ccm = IMX_CCM(&s->ccm);
 | 
			
		||||
 | 
			
		||||
    object_property_set_bool(OBJECT(&s->gpt), true, "realized", &err);
 | 
			
		||||
    if (err) {
 | 
			
		||||
@@ -150,7 +150,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
            { FSL_IMX31_EPIT2_ADDR, FSL_IMX31_EPIT2_IRQ },
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        s->epit[i].ccm = DEVICE(&s->ccm);
 | 
			
		||||
        s->epit[i].ccm = IMX_CCM(&s->ccm);
 | 
			
		||||
 | 
			
		||||
        object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", &err);
 | 
			
		||||
        if (err) {
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@
 | 
			
		||||
#include "hw/pci/pci.h"
 | 
			
		||||
 | 
			
		||||
#define ARM_SPI_BASE 32
 | 
			
		||||
#define ACPI_POWER_BUTTON_DEVICE "PWRB"
 | 
			
		||||
 | 
			
		||||
typedef struct VirtAcpiCpuInfo {
 | 
			
		||||
    DECLARE_BITMAP(found_cpus, VIRT_ACPI_CPU_ID_LIMIT);
 | 
			
		||||
@@ -71,7 +72,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, int smp_cpus)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
 | 
			
		||||
                                           int uart_irq)
 | 
			
		||||
                                           uint32_t uart_irq)
 | 
			
		||||
{
 | 
			
		||||
    Aml *dev = aml_device("COM0");
 | 
			
		||||
    aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011")));
 | 
			
		||||
@@ -82,7 +83,7 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
 | 
			
		||||
                                       uart_memmap->size, AML_READ_WRITE));
 | 
			
		||||
    aml_append(crs,
 | 
			
		||||
               aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
 | 
			
		||||
                             AML_EXCLUSIVE, uart_irq));
 | 
			
		||||
                             AML_EXCLUSIVE, &uart_irq, 1));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_CRS", crs));
 | 
			
		||||
 | 
			
		||||
    /* The _ADR entry is used to link this device to the UART described
 | 
			
		||||
@@ -94,7 +95,7 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
 | 
			
		||||
                                          int rtc_irq)
 | 
			
		||||
                                          uint32_t rtc_irq)
 | 
			
		||||
{
 | 
			
		||||
    Aml *dev = aml_device("RTC0");
 | 
			
		||||
    aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0013")));
 | 
			
		||||
@@ -105,7 +106,7 @@ static void acpi_dsdt_add_rtc(Aml *scope, const MemMapEntry *rtc_memmap,
 | 
			
		||||
                                       rtc_memmap->size, AML_READ_WRITE));
 | 
			
		||||
    aml_append(crs,
 | 
			
		||||
               aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
 | 
			
		||||
                             AML_EXCLUSIVE, rtc_irq));
 | 
			
		||||
                             AML_EXCLUSIVE, &rtc_irq, 1));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_CRS", crs));
 | 
			
		||||
    aml_append(scope, dev);
 | 
			
		||||
}
 | 
			
		||||
@@ -136,14 +137,14 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
 | 
			
		||||
 | 
			
		||||
static void acpi_dsdt_add_virtio(Aml *scope,
 | 
			
		||||
                                 const MemMapEntry *virtio_mmio_memmap,
 | 
			
		||||
                                 int mmio_irq, int num)
 | 
			
		||||
                                 uint32_t mmio_irq, int num)
 | 
			
		||||
{
 | 
			
		||||
    hwaddr base = virtio_mmio_memmap->base;
 | 
			
		||||
    hwaddr size = virtio_mmio_memmap->size;
 | 
			
		||||
    int irq = mmio_irq;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < num; i++) {
 | 
			
		||||
        uint32_t irq = mmio_irq + i;
 | 
			
		||||
        Aml *dev = aml_device("VR%02u", i);
 | 
			
		||||
        aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
 | 
			
		||||
        aml_append(dev, aml_name_decl("_UID", aml_int(i)));
 | 
			
		||||
@@ -152,15 +153,15 @@ static void acpi_dsdt_add_virtio(Aml *scope,
 | 
			
		||||
        aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
 | 
			
		||||
        aml_append(crs,
 | 
			
		||||
                   aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
 | 
			
		||||
                                 AML_EXCLUSIVE, irq + i));
 | 
			
		||||
                                 AML_EXCLUSIVE, &irq, 1));
 | 
			
		||||
        aml_append(dev, aml_name_decl("_CRS", crs));
 | 
			
		||||
        aml_append(scope, dev);
 | 
			
		||||
        base += size;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
 | 
			
		||||
                              bool use_highmem)
 | 
			
		||||
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
 | 
			
		||||
                              uint32_t irq, bool use_highmem)
 | 
			
		||||
{
 | 
			
		||||
    Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf;
 | 
			
		||||
    int i, bus_no;
 | 
			
		||||
@@ -199,29 +200,30 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
 | 
			
		||||
 | 
			
		||||
    /* Create GSI link device */
 | 
			
		||||
    for (i = 0; i < PCI_NUM_PINS; i++) {
 | 
			
		||||
        uint32_t irqs =  irq + i;
 | 
			
		||||
        Aml *dev_gsi = aml_device("GSI%d", i);
 | 
			
		||||
        aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F")));
 | 
			
		||||
        aml_append(dev_gsi, aml_name_decl("_UID", aml_int(0)));
 | 
			
		||||
        crs = aml_resource_template();
 | 
			
		||||
        aml_append(crs,
 | 
			
		||||
                   aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
 | 
			
		||||
                                 AML_EXCLUSIVE, irq + i));
 | 
			
		||||
                                 AML_EXCLUSIVE, &irqs, 1));
 | 
			
		||||
        aml_append(dev_gsi, aml_name_decl("_PRS", crs));
 | 
			
		||||
        crs = aml_resource_template();
 | 
			
		||||
        aml_append(crs,
 | 
			
		||||
                   aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
 | 
			
		||||
                                 AML_EXCLUSIVE, irq + i));
 | 
			
		||||
                                 AML_EXCLUSIVE, &irqs, 1));
 | 
			
		||||
        aml_append(dev_gsi, aml_name_decl("_CRS", crs));
 | 
			
		||||
        method = aml_method("_SRS", 1);
 | 
			
		||||
        method = aml_method("_SRS", 1, AML_NOTSERIALIZED);
 | 
			
		||||
        aml_append(dev_gsi, method);
 | 
			
		||||
        aml_append(dev, dev_gsi);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    method = aml_method("_CBA", 0);
 | 
			
		||||
    method = aml_method("_CBA", 0, AML_NOTSERIALIZED);
 | 
			
		||||
    aml_append(method, aml_return(aml_int(base_ecam)));
 | 
			
		||||
    aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
    method = aml_method("_CRS", 0);
 | 
			
		||||
    method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
 | 
			
		||||
    Aml *rbuf = aml_resource_template();
 | 
			
		||||
    aml_append(rbuf,
 | 
			
		||||
        aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
 | 
			
		||||
@@ -254,7 +256,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
 | 
			
		||||
    /* Declare an _OSC (OS Control Handoff) method */
 | 
			
		||||
    aml_append(dev, aml_name_decl("SUPP", aml_int(0)));
 | 
			
		||||
    aml_append(dev, aml_name_decl("CTRL", aml_int(0)));
 | 
			
		||||
    method = aml_method("_OSC", 4);
 | 
			
		||||
    method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
 | 
			
		||||
    aml_append(method,
 | 
			
		||||
        aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
 | 
			
		||||
 | 
			
		||||
@@ -272,16 +274,16 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
 | 
			
		||||
        aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
 | 
			
		||||
    aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP")));
 | 
			
		||||
    aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL")));
 | 
			
		||||
    aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D)),
 | 
			
		||||
    aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D), NULL),
 | 
			
		||||
                                aml_name("CTRL")));
 | 
			
		||||
 | 
			
		||||
    ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1))));
 | 
			
		||||
    aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08)),
 | 
			
		||||
    aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08), NULL),
 | 
			
		||||
                                 aml_name("CDW1")));
 | 
			
		||||
    aml_append(ifctx, ifctx1);
 | 
			
		||||
 | 
			
		||||
    ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL"))));
 | 
			
		||||
    aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10)),
 | 
			
		||||
    aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10), NULL),
 | 
			
		||||
                                 aml_name("CDW1")));
 | 
			
		||||
    aml_append(ifctx, ifctx1);
 | 
			
		||||
 | 
			
		||||
@@ -290,13 +292,13 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
 | 
			
		||||
    aml_append(method, ifctx);
 | 
			
		||||
 | 
			
		||||
    elsectx = aml_else();
 | 
			
		||||
    aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4)),
 | 
			
		||||
    aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4), NULL),
 | 
			
		||||
                                  aml_name("CDW1")));
 | 
			
		||||
    aml_append(elsectx, aml_return(aml_arg(3)));
 | 
			
		||||
    aml_append(method, elsectx);
 | 
			
		||||
    aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
    method = aml_method("_DSM", 4);
 | 
			
		||||
    method = aml_method("_DSM", 4, AML_NOTSERIALIZED);
 | 
			
		||||
 | 
			
		||||
    /* PCI Firmware Specification 3.0
 | 
			
		||||
     * 4.6.1. _DSM for PCI Express Slot Information
 | 
			
		||||
@@ -323,6 +325,46 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int irq,
 | 
			
		||||
    aml_append(scope, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap,
 | 
			
		||||
                                           uint32_t gpio_irq)
 | 
			
		||||
{
 | 
			
		||||
    Aml *dev = aml_device("GPO0");
 | 
			
		||||
    aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0061")));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
 | 
			
		||||
 | 
			
		||||
    Aml *crs = aml_resource_template();
 | 
			
		||||
    aml_append(crs, aml_memory32_fixed(gpio_memmap->base, gpio_memmap->size,
 | 
			
		||||
                                       AML_READ_WRITE));
 | 
			
		||||
    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
 | 
			
		||||
                                  AML_EXCLUSIVE, &gpio_irq, 1));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_CRS", crs));
 | 
			
		||||
 | 
			
		||||
    Aml *aei = aml_resource_template();
 | 
			
		||||
    /* Pin 3 for power button */
 | 
			
		||||
    const uint32_t pin_list[1] = {3};
 | 
			
		||||
    aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
 | 
			
		||||
                                 AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1,
 | 
			
		||||
                                 "GPO0", NULL, 0));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_AEI", aei));
 | 
			
		||||
 | 
			
		||||
    /* _E03 is handle for power button */
 | 
			
		||||
    Aml *method = aml_method("_E03", 0, AML_NOTSERIALIZED);
 | 
			
		||||
    aml_append(method, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE),
 | 
			
		||||
                                  aml_int(0x80)));
 | 
			
		||||
    aml_append(dev, method);
 | 
			
		||||
    aml_append(scope, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void acpi_dsdt_add_power_button(Aml *scope)
 | 
			
		||||
{
 | 
			
		||||
    Aml *dev = aml_device(ACPI_POWER_BUTTON_DEVICE);
 | 
			
		||||
    aml_append(dev, aml_name_decl("_HID", aml_string("PNP0C0C")));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
 | 
			
		||||
    aml_append(dev, aml_name_decl("_UID", aml_int(0)));
 | 
			
		||||
    aml_append(scope, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* RSDP */
 | 
			
		||||
static GArray *
 | 
			
		||||
build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt)
 | 
			
		||||
@@ -381,7 +423,8 @@ build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
 | 
			
		||||
    spcr->pci_device_id = 0xffff;  /* PCI Device ID: not a PCI device */
 | 
			
		||||
    spcr->pci_vendor_id = 0xffff;  /* PCI Vendor ID: not a PCI device */
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2);
 | 
			
		||||
    build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2,
 | 
			
		||||
                 NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -400,7 +443,7 @@ build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
 | 
			
		||||
    mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
 | 
			
		||||
                                          / PCIE_MMCFG_SIZE_MIN) - 1;
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1);
 | 
			
		||||
    build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* GTDT */
 | 
			
		||||
@@ -426,7 +469,7 @@ build_gtdt(GArray *table_data, GArray *linker)
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)(table_data->data + gtdt_start), "GTDT",
 | 
			
		||||
                 table_data->len - gtdt_start, 2);
 | 
			
		||||
                 table_data->len - gtdt_start, 2, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* MADT */
 | 
			
		||||
@@ -488,7 +531,7 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)(table_data->data + madt_start), "APIC",
 | 
			
		||||
                 table_data->len - madt_start, 3);
 | 
			
		||||
                 table_data->len - madt_start, 3, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* FADT */
 | 
			
		||||
@@ -513,7 +556,7 @@ build_fadt(GArray *table_data, GArray *linker, unsigned dsdt)
 | 
			
		||||
                                   sizeof fadt->dsdt);
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)fadt, "FACP", sizeof(*fadt), 5);
 | 
			
		||||
                 (void *)fadt, "FACP", sizeof(*fadt), 5, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* DSDT */
 | 
			
		||||
@@ -539,6 +582,9 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
 | 
			
		||||
                    (irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
 | 
			
		||||
    acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
 | 
			
		||||
                      guest_info->use_highmem);
 | 
			
		||||
    acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO],
 | 
			
		||||
                       (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
 | 
			
		||||
    acpi_dsdt_add_power_button(scope);
 | 
			
		||||
 | 
			
		||||
    aml_append(dsdt, scope);
 | 
			
		||||
 | 
			
		||||
@@ -546,7 +592,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info)
 | 
			
		||||
    g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
        (void *)(table_data->data + table_data->len - dsdt->buf->len),
 | 
			
		||||
        "DSDT", dsdt->buf->len, 2);
 | 
			
		||||
        "DSDT", dsdt->buf->len, 2, NULL);
 | 
			
		||||
    free_aml_allocator();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -631,7 +677,7 @@ static void acpi_ram_update(MemoryRegion *mr, GArray *data)
 | 
			
		||||
    memory_region_set_dirty(mr, 0, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void virt_acpi_build_update(void *build_opaque, uint32_t offset)
 | 
			
		||||
static void virt_acpi_build_update(void *build_opaque)
 | 
			
		||||
{
 | 
			
		||||
    AcpiBuildState *build_state = build_opaque;
 | 
			
		||||
    AcpiBuildTables tables;
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,7 @@
 | 
			
		||||
#include "kvm_arm.h"
 | 
			
		||||
#include "hw/smbios/smbios.h"
 | 
			
		||||
#include "qapi/visitor.h"
 | 
			
		||||
#include "standard-headers/linux/input.h"
 | 
			
		||||
 | 
			
		||||
/* Number of external interrupt lines to configure the GIC with */
 | 
			
		||||
#define NUM_IRQS 256
 | 
			
		||||
@@ -120,6 +121,7 @@ static const MemMapEntry a15memmap[] = {
 | 
			
		||||
    [VIRT_UART] =               { 0x09000000, 0x00001000 },
 | 
			
		||||
    [VIRT_RTC] =                { 0x09010000, 0x00001000 },
 | 
			
		||||
    [VIRT_FW_CFG] =             { 0x09020000, 0x00000018 },
 | 
			
		||||
    [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
 | 
			
		||||
    [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
 | 
			
		||||
    /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
 | 
			
		||||
    [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
 | 
			
		||||
@@ -135,6 +137,7 @@ static const int a15irqmap[] = {
 | 
			
		||||
    [VIRT_UART] = 1,
 | 
			
		||||
    [VIRT_RTC] = 2,
 | 
			
		||||
    [VIRT_PCIE] = 3, /* ... to 6 */
 | 
			
		||||
    [VIRT_GPIO] = 7,
 | 
			
		||||
    [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
 | 
			
		||||
    [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
 | 
			
		||||
    [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
 | 
			
		||||
@@ -538,6 +541,61 @@ static void create_rtc(const VirtBoardInfo *vbi, qemu_irq *pic)
 | 
			
		||||
    g_free(nodename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DeviceState *pl061_dev;
 | 
			
		||||
static void virt_powerdown_req(Notifier *n, void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    /* use gpio Pin 3 for power button event */
 | 
			
		||||
    qemu_set_irq(qdev_get_gpio_in(pl061_dev, 3), 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Notifier virt_system_powerdown_notifier = {
 | 
			
		||||
    .notify = virt_powerdown_req
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void create_gpio(const VirtBoardInfo *vbi, qemu_irq *pic)
 | 
			
		||||
{
 | 
			
		||||
    char *nodename;
 | 
			
		||||
    hwaddr base = vbi->memmap[VIRT_GPIO].base;
 | 
			
		||||
    hwaddr size = vbi->memmap[VIRT_GPIO].size;
 | 
			
		||||
    int irq = vbi->irqmap[VIRT_GPIO];
 | 
			
		||||
    const char compat[] = "arm,pl061\0arm,primecell";
 | 
			
		||||
 | 
			
		||||
    pl061_dev = sysbus_create_simple("pl061", base, pic[irq]);
 | 
			
		||||
 | 
			
		||||
    uint32_t phandle = qemu_fdt_alloc_phandle(vbi->fdt);
 | 
			
		||||
    nodename = g_strdup_printf("/pl061@%" PRIx64, base);
 | 
			
		||||
    qemu_fdt_add_subnode(vbi->fdt, nodename);
 | 
			
		||||
    qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
 | 
			
		||||
                                 2, base, 2, size);
 | 
			
		||||
    qemu_fdt_setprop(vbi->fdt, nodename, "compatible", compat, sizeof(compat));
 | 
			
		||||
    qemu_fdt_setprop_cell(vbi->fdt, nodename, "#gpio-cells", 2);
 | 
			
		||||
    qemu_fdt_setprop(vbi->fdt, nodename, "gpio-controller", NULL, 0);
 | 
			
		||||
    qemu_fdt_setprop_cells(vbi->fdt, nodename, "interrupts",
 | 
			
		||||
                           GIC_FDT_IRQ_TYPE_SPI, irq,
 | 
			
		||||
                           GIC_FDT_IRQ_FLAGS_LEVEL_HI);
 | 
			
		||||
    qemu_fdt_setprop_cell(vbi->fdt, nodename, "clocks", vbi->clock_phandle);
 | 
			
		||||
    qemu_fdt_setprop_string(vbi->fdt, nodename, "clock-names", "apb_pclk");
 | 
			
		||||
    qemu_fdt_setprop_cell(vbi->fdt, nodename, "phandle", phandle);
 | 
			
		||||
 | 
			
		||||
    qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys");
 | 
			
		||||
    qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys", "compatible", "gpio-keys");
 | 
			
		||||
    qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#size-cells", 0);
 | 
			
		||||
    qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys", "#address-cells", 1);
 | 
			
		||||
 | 
			
		||||
    qemu_fdt_add_subnode(vbi->fdt, "/gpio-keys/poweroff");
 | 
			
		||||
    qemu_fdt_setprop_string(vbi->fdt, "/gpio-keys/poweroff",
 | 
			
		||||
                            "label", "GPIO Key Poweroff");
 | 
			
		||||
    qemu_fdt_setprop_cell(vbi->fdt, "/gpio-keys/poweroff", "linux,code",
 | 
			
		||||
                          KEY_POWER);
 | 
			
		||||
    qemu_fdt_setprop_cells(vbi->fdt, "/gpio-keys/poweroff",
 | 
			
		||||
                           "gpios", phandle, 3, 0);
 | 
			
		||||
 | 
			
		||||
    /* connect powerdown request */
 | 
			
		||||
    qemu_register_powerdown_notifier(&virt_system_powerdown_notifier);
 | 
			
		||||
 | 
			
		||||
    g_free(nodename);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void create_virtio_devices(const VirtBoardInfo *vbi, qemu_irq *pic)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
@@ -1041,6 +1099,8 @@ static void machvirt_init(MachineState *machine)
 | 
			
		||||
 | 
			
		||||
    create_pcie(vbi, pic, vms->highmem);
 | 
			
		||||
 | 
			
		||||
    create_gpio(vbi, pic);
 | 
			
		||||
 | 
			
		||||
    /* Create mmio transports, so the user can create virtio backends
 | 
			
		||||
     * (which will be automatically plugged in to the transports). If
 | 
			
		||||
     * no backend is created the transport will just sit harmlessly idle.
 | 
			
		||||
 
 | 
			
		||||
@@ -407,24 +407,16 @@ void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb)
 | 
			
		||||
    for (i = 0; i < mrb->num_reqs; i++) {
 | 
			
		||||
        VirtIOBlockReq *req = mrb->reqs[i];
 | 
			
		||||
        if (num_reqs > 0) {
 | 
			
		||||
            bool merge = true;
 | 
			
		||||
 | 
			
		||||
            /* merge would exceed maximum number of IOVs */
 | 
			
		||||
            if (niov + req->qiov.niov > IOV_MAX) {
 | 
			
		||||
                merge = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* merge would exceed maximum transfer length of backend device */
 | 
			
		||||
            if (req->qiov.size / BDRV_SECTOR_SIZE + nb_sectors > max_xfer_len) {
 | 
			
		||||
                merge = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* requests are not sequential */
 | 
			
		||||
            if (sector_num + nb_sectors != req->sector_num) {
 | 
			
		||||
                merge = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!merge) {
 | 
			
		||||
            /*
 | 
			
		||||
             * NOTE: We cannot merge the requests in below situations:
 | 
			
		||||
             * 1. requests are not sequential
 | 
			
		||||
             * 2. merge would exceed maximum number of IOVs
 | 
			
		||||
             * 3. merge would exceed maximum transfer length of backend device
 | 
			
		||||
             */
 | 
			
		||||
            if (sector_num + nb_sectors != req->sector_num ||
 | 
			
		||||
                niov > blk_get_max_iov(blk) - req->qiov.niov ||
 | 
			
		||||
                req->qiov.size / BDRV_SECTOR_SIZE > max_xfer_len ||
 | 
			
		||||
                nb_sectors > max_xfer_len - req->qiov.size / BDRV_SECTOR_SIZE) {
 | 
			
		||||
                submit_requests(blk, mrb, start, num_reqs, niov);
 | 
			
		||||
                num_reqs = 0;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -85,8 +85,10 @@ static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_reque
 | 
			
		||||
		d->nr_sectors = s->nr_sectors;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (n > src->nr_segments)
 | 
			
		||||
		n = src->nr_segments;
 | 
			
		||||
	/* prevent the compiler from optimizing the code and using src->nr_segments instead */
 | 
			
		||||
	barrier();
 | 
			
		||||
	if (n > dst->nr_segments)
 | 
			
		||||
		n = dst->nr_segments;
 | 
			
		||||
	for (i = 0; i < n; i++)
 | 
			
		||||
		dst->seg[i] = src->seg[i];
 | 
			
		||||
}
 | 
			
		||||
@@ -106,8 +108,10 @@ static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_reque
 | 
			
		||||
		d->nr_sectors = s->nr_sectors;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (n > src->nr_segments)
 | 
			
		||||
		n = src->nr_segments;
 | 
			
		||||
	/* prevent the compiler from optimizing the code and using src->nr_segments instead */
 | 
			
		||||
	barrier();
 | 
			
		||||
	if (n > dst->nr_segments)
 | 
			
		||||
		n = dst->nr_segments;
 | 
			
		||||
	for (i = 0; i < n; i++)
 | 
			
		||||
		dst->seg[i] = src->seg[i];
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -825,6 +825,9 @@ static int blk_init(struct XenDevice *xendev)
 | 
			
		||||
    if (!strcmp("aio", blkdev->fileproto)) {
 | 
			
		||||
        blkdev->fileproto = "raw";
 | 
			
		||||
    }
 | 
			
		||||
    if (!strcmp("vhd", blkdev->fileproto)) {
 | 
			
		||||
        blkdev->fileproto = "vpc";
 | 
			
		||||
    }
 | 
			
		||||
    if (blkdev->mode == NULL) {
 | 
			
		||||
        blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -714,7 +714,7 @@ MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
 | 
			
		||||
    return &d->mmio;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const uint8_t qcode_to_keycode[Q_KEY_CODE_MAX] = {
 | 
			
		||||
static const uint8_t qcode_to_keycode[Q_KEY_CODE__MAX] = {
 | 
			
		||||
    [Q_KEY_CODE_SHIFT]         = 99,
 | 
			
		||||
    [Q_KEY_CODE_SHIFT_R]       = 110,
 | 
			
		||||
    [Q_KEY_CODE_ALT]           = 19,
 | 
			
		||||
 
 | 
			
		||||
@@ -165,7 +165,7 @@ static void serial_receive(void *opaque, const uint8_t *buf, int size)
 | 
			
		||||
 | 
			
		||||
    /* Got a byte.  */
 | 
			
		||||
    if (s->rx_fifo_len >= 16) {
 | 
			
		||||
        qemu_log("WARNING: UART dropped char.\n");
 | 
			
		||||
        D(qemu_log("WARNING: UART dropped char.\n"));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -51,12 +51,10 @@
 | 
			
		||||
#include "hw/nvram/fw_cfg.h"
 | 
			
		||||
#include "exec/memory.h"
 | 
			
		||||
#include "exec/address-spaces.h"
 | 
			
		||||
#include "hw/boards.h"
 | 
			
		||||
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
 | 
			
		||||
bool option_rom_has_mr = false;
 | 
			
		||||
bool rom_file_has_mr = true;
 | 
			
		||||
 | 
			
		||||
static int roms_loaded;
 | 
			
		||||
 | 
			
		||||
/* return the size or -1 if error */
 | 
			
		||||
@@ -754,6 +752,7 @@ int rom_add_file(const char *file, const char *fw_dir,
 | 
			
		||||
                 hwaddr addr, int32_t bootindex,
 | 
			
		||||
                 bool option_rom)
 | 
			
		||||
{
 | 
			
		||||
    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
 | 
			
		||||
    Rom *rom;
 | 
			
		||||
    int rc, fd = -1;
 | 
			
		||||
    char devpath[100];
 | 
			
		||||
@@ -810,7 +809,7 @@ int rom_add_file(const char *file, const char *fw_dir,
 | 
			
		||||
                 basename);
 | 
			
		||||
        snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
 | 
			
		||||
 | 
			
		||||
        if ((!option_rom || option_rom_has_mr) && rom_file_has_mr) {
 | 
			
		||||
        if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) {
 | 
			
		||||
            data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
 | 
			
		||||
        } else {
 | 
			
		||||
            data = rom->data;
 | 
			
		||||
@@ -838,6 +837,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
 | 
			
		||||
                   size_t max_len, hwaddr addr, const char *fw_file_name,
 | 
			
		||||
                   FWCfgReadCallback fw_callback, void *callback_opaque)
 | 
			
		||||
{
 | 
			
		||||
    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
 | 
			
		||||
    Rom *rom;
 | 
			
		||||
    MemoryRegion *mr = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -855,7 +855,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len,
 | 
			
		||||
 | 
			
		||||
        snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
 | 
			
		||||
 | 
			
		||||
        if (rom_file_has_mr) {
 | 
			
		||||
        if (mc->rom_file_has_mr) {
 | 
			
		||||
            data = rom_set_mr(rom, OBJECT(fw_cfg), devpath);
 | 
			
		||||
            mr = rom->mr;
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/boards.h"
 | 
			
		||||
#include "qapi-visit.h"
 | 
			
		||||
#include "qapi/visitor.h"
 | 
			
		||||
#include "hw/sysbus.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
@@ -31,12 +32,39 @@ static void machine_set_accel(Object *obj, const char *value, Error **errp)
 | 
			
		||||
    ms->accel = g_strdup(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void machine_set_kernel_irqchip(Object *obj, bool value, Error **errp)
 | 
			
		||||
static void machine_set_kernel_irqchip(Object *obj, Visitor *v,
 | 
			
		||||
                                       void *opaque, const char *name,
 | 
			
		||||
                                       Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    Error *err = NULL;
 | 
			
		||||
    MachineState *ms = MACHINE(obj);
 | 
			
		||||
    OnOffSplit mode;
 | 
			
		||||
 | 
			
		||||
    ms->kernel_irqchip_allowed = value;
 | 
			
		||||
    ms->kernel_irqchip_required = value;
 | 
			
		||||
    visit_type_OnOffSplit(v, &mode, name, &err);
 | 
			
		||||
    if (err) {
 | 
			
		||||
        error_propagate(errp, err);
 | 
			
		||||
        return;
 | 
			
		||||
    } else {
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        case ON_OFF_SPLIT_ON:
 | 
			
		||||
            ms->kernel_irqchip_allowed = true;
 | 
			
		||||
            ms->kernel_irqchip_required = true;
 | 
			
		||||
            ms->kernel_irqchip_split = false;
 | 
			
		||||
            break;
 | 
			
		||||
        case ON_OFF_SPLIT_OFF:
 | 
			
		||||
            ms->kernel_irqchip_allowed = false;
 | 
			
		||||
            ms->kernel_irqchip_required = false;
 | 
			
		||||
            ms->kernel_irqchip_split = false;
 | 
			
		||||
            break;
 | 
			
		||||
        case ON_OFF_SPLIT_SPLIT:
 | 
			
		||||
            ms->kernel_irqchip_allowed = true;
 | 
			
		||||
            ms->kernel_irqchip_required = true;
 | 
			
		||||
            ms->kernel_irqchip_split = true;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            abort();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void machine_get_kvm_shadow_mem(Object *obj, Visitor *v,
 | 
			
		||||
@@ -314,6 +342,7 @@ static void machine_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
 | 
			
		||||
    /* Default 128 MB as guest ram size */
 | 
			
		||||
    mc->default_ram_size = 128 * M_BYTE;
 | 
			
		||||
    mc->rom_file_has_mr = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void machine_class_base_init(ObjectClass *oc, void *data)
 | 
			
		||||
@@ -341,12 +370,12 @@ static void machine_initfn(Object *obj)
 | 
			
		||||
    object_property_set_description(obj, "accel",
 | 
			
		||||
                                    "Accelerator list",
 | 
			
		||||
                                    NULL);
 | 
			
		||||
    object_property_add_bool(obj, "kernel-irqchip",
 | 
			
		||||
                             NULL,
 | 
			
		||||
                             machine_set_kernel_irqchip,
 | 
			
		||||
                             NULL);
 | 
			
		||||
    object_property_add(obj, "kernel-irqchip", "OnOffSplit",
 | 
			
		||||
                        NULL,
 | 
			
		||||
                        machine_set_kernel_irqchip,
 | 
			
		||||
                        NULL, NULL, NULL);
 | 
			
		||||
    object_property_set_description(obj, "kernel-irqchip",
 | 
			
		||||
                                    "Use KVM in-kernel irqchip",
 | 
			
		||||
                                    "Configure KVM in-kernel irqchip",
 | 
			
		||||
                                    NULL);
 | 
			
		||||
    object_property_add(obj, "kvm-shadow-mem", "int",
 | 
			
		||||
                        machine_get_kvm_shadow_mem,
 | 
			
		||||
@@ -472,6 +501,11 @@ bool machine_kernel_irqchip_required(MachineState *machine)
 | 
			
		||||
    return machine->kernel_irqchip_required;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool machine_kernel_irqchip_split(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    return machine->kernel_irqchip_split;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int machine_kvm_shadow_mem(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    return machine->kvm_shadow_mem;
 | 
			
		||||
 
 | 
			
		||||
@@ -233,8 +233,10 @@ static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
 | 
			
		||||
 | 
			
		||||
    /* TODO: draw the segments */
 | 
			
		||||
    snprintf(buf, 2, "%02hhx\n", s->segments);
 | 
			
		||||
    console_write_ch(chardata++, 0x00200100 | buf[0]);
 | 
			
		||||
    console_write_ch(chardata++, 0x00200100 | buf[1]);
 | 
			
		||||
    console_write_ch(chardata++, ATTR2CHTYPE(buf[0], QEMU_COLOR_BLUE,
 | 
			
		||||
                                             QEMU_COLOR_BLACK, 1));
 | 
			
		||||
    console_write_ch(chardata++, ATTR2CHTYPE(buf[1], QEMU_COLOR_BLUE,
 | 
			
		||||
                                             QEMU_COLOR_BLACK, 1));
 | 
			
		||||
 | 
			
		||||
    dpy_text_update(s->con, 0, 0, 2, 1);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@ static void glue(draw_line12_, DEPTH)(void *opaque,
 | 
			
		||||
    uint8_t r, g, b;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        v = lduw_p((void *) s);
 | 
			
		||||
        v = lduw_le_p((void *) s);
 | 
			
		||||
        r = (v >> 4) & 0xf0;
 | 
			
		||||
        g = v & 0xf0;
 | 
			
		||||
        b = (v << 4) & 0xf0;
 | 
			
		||||
@@ -159,7 +159,7 @@ static void glue(draw_line16_, DEPTH)(void *opaque,
 | 
			
		||||
    uint8_t r, g, b;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        v = lduw_p((void *) s);
 | 
			
		||||
        v = lduw_le_p((void *) s);
 | 
			
		||||
        r = (v >> 8) & 0xf8;
 | 
			
		||||
        g = (v >> 3) & 0xfc;
 | 
			
		||||
        b = (v << 3) & 0xf8;
 | 
			
		||||
 
 | 
			
		||||
@@ -309,10 +309,10 @@ static void pxa2xx_descriptor_load(PXA2xxLCDState *s)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cpu_physical_memory_read(descptr, &desc, sizeof(desc));
 | 
			
		||||
        s->dma_ch[i].descriptor = tswap32(desc.fdaddr);
 | 
			
		||||
        s->dma_ch[i].source = tswap32(desc.fsaddr);
 | 
			
		||||
        s->dma_ch[i].id = tswap32(desc.fidr);
 | 
			
		||||
        s->dma_ch[i].command = tswap32(desc.ldcmd);
 | 
			
		||||
        s->dma_ch[i].descriptor = le32_to_cpu(desc.fdaddr);
 | 
			
		||||
        s->dma_ch[i].source = le32_to_cpu(desc.fsaddr);
 | 
			
		||||
        s->dma_ch[i].id = le32_to_cpu(desc.fidr);
 | 
			
		||||
        s->dma_ch[i].command = le32_to_cpu(desc.ldcmd);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1979,7 +1979,8 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
 | 
			
		||||
    width = (s->last_width - size) / 2;
 | 
			
		||||
    dst = chardata + s->last_width + width;
 | 
			
		||||
    for (i = 0; i < size; i ++)
 | 
			
		||||
        console_write_ch(dst ++, 0x00200100 | msg_buffer[i]);
 | 
			
		||||
        console_write_ch(dst ++, ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE,
 | 
			
		||||
                                             QEMU_COLOR_BLACK, 1));
 | 
			
		||||
 | 
			
		||||
    dpy_text_update(s->con, 0, 0, s->last_width, height);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -146,14 +146,14 @@ static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features,
 | 
			
		||||
    VirtIOGPU *g = VIRTIO_GPU(vdev);
 | 
			
		||||
 | 
			
		||||
    if (virtio_gpu_virgl_enabled(g->conf)) {
 | 
			
		||||
        features |= (1 << VIRTIO_GPU_FEATURE_VIRGL);
 | 
			
		||||
        features |= (1 << VIRTIO_GPU_F_VIRGL);
 | 
			
		||||
    }
 | 
			
		||||
    return features;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features)
 | 
			
		||||
{
 | 
			
		||||
    static const uint32_t virgl = (1 << VIRTIO_GPU_FEATURE_VIRGL);
 | 
			
		||||
    static const uint32_t virgl = (1 << VIRTIO_GPU_F_VIRGL);
 | 
			
		||||
    VirtIOGPU *g = VIRTIO_GPU(vdev);
 | 
			
		||||
 | 
			
		||||
    g->use_virgl_renderer = ((features & virgl) == virgl);
 | 
			
		||||
 
 | 
			
		||||
@@ -784,18 +784,20 @@ static void xenfb_invalidate(void *opaque)
 | 
			
		||||
 | 
			
		||||
static void xenfb_handle_events(struct XenFB *xenfb)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t prod, cons;
 | 
			
		||||
    uint32_t prod, cons, out_cons;
 | 
			
		||||
    struct xenfb_page *page = xenfb->c.page;
 | 
			
		||||
 | 
			
		||||
    prod = page->out_prod;
 | 
			
		||||
    if (prod == page->out_cons)
 | 
			
		||||
    out_cons = page->out_cons;
 | 
			
		||||
    if (prod == out_cons)
 | 
			
		||||
	return;
 | 
			
		||||
    xen_rmb();		/* ensure we see ring contents up to prod */
 | 
			
		||||
    for (cons = page->out_cons; cons != prod; cons++) {
 | 
			
		||||
    for (cons = out_cons; cons != prod; cons++) {
 | 
			
		||||
	union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
 | 
			
		||||
        uint8_t type = event->type;
 | 
			
		||||
	int x, y, w, h;
 | 
			
		||||
 | 
			
		||||
	switch (event->type) {
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case XENFB_TYPE_UPDATE:
 | 
			
		||||
	    if (xenfb->up_count == UP_QUEUE)
 | 
			
		||||
		xenfb->up_fullscreen = 1;
 | 
			
		||||
 
 | 
			
		||||
@@ -269,11 +269,10 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
 | 
			
		||||
        if (entry->type == soc_dma_port_mem) {
 | 
			
		||||
            if (entry->addr <= virt_base &&
 | 
			
		||||
                            entry->addr + entry->u.mem.size > virt_base) {
 | 
			
		||||
                fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
 | 
			
		||||
                                " collides with RAM region at " TARGET_FMT_lx
 | 
			
		||||
                                "-" TARGET_FMT_lx "\n", __FUNCTION__,
 | 
			
		||||
                                (target_ulong) virt_base,
 | 
			
		||||
                                (target_ulong) entry->addr, (target_ulong)
 | 
			
		||||
                fprintf(stderr, "%s: FIFO at %"PRIx64
 | 
			
		||||
                                " collides with RAM region at %"PRIx64
 | 
			
		||||
                                "-%"PRIx64 "\n", __func__,
 | 
			
		||||
                                virt_base, entry->addr,
 | 
			
		||||
                                (entry->addr + entry->u.mem.size));
 | 
			
		||||
                exit(-1);
 | 
			
		||||
            }
 | 
			
		||||
@@ -284,10 +283,9 @@ void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
 | 
			
		||||
            while (entry < dma->memmap + dma->memmap_size &&
 | 
			
		||||
                            entry->addr <= virt_base) {
 | 
			
		||||
                if (entry->addr == virt_base && entry->u.fifo.out == out) {
 | 
			
		||||
                    fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
 | 
			
		||||
                                    " collides FIFO at " TARGET_FMT_lx "\n",
 | 
			
		||||
                                    __FUNCTION__, (target_ulong) virt_base,
 | 
			
		||||
                                    (target_ulong) entry->addr);
 | 
			
		||||
                    fprintf(stderr, "%s: FIFO at %"PRIx64
 | 
			
		||||
                                    " collides FIFO at %"PRIx64 "\n",
 | 
			
		||||
                                    __func__, virt_base, entry->addr);
 | 
			
		||||
                    exit(-1);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -322,13 +320,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
 | 
			
		||||
            if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
 | 
			
		||||
                            (entry->addr <= virt_base &&
 | 
			
		||||
                             entry->addr + entry->u.mem.size > virt_base)) {
 | 
			
		||||
                fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
 | 
			
		||||
                                " collides with RAM region at " TARGET_FMT_lx
 | 
			
		||||
                                "-" TARGET_FMT_lx "\n", __FUNCTION__,
 | 
			
		||||
                                (target_ulong) virt_base,
 | 
			
		||||
                                (target_ulong) (virt_base + size),
 | 
			
		||||
                                (target_ulong) entry->addr, (target_ulong)
 | 
			
		||||
                                (entry->addr + entry->u.mem.size));
 | 
			
		||||
                fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
 | 
			
		||||
                                " collides with RAM region at %"PRIx64
 | 
			
		||||
                                "-%"PRIx64 "\n", __func__,
 | 
			
		||||
                                virt_base, virt_base + size,
 | 
			
		||||
                                entry->addr, entry->addr + entry->u.mem.size);
 | 
			
		||||
                exit(-1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -337,12 +333,11 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
 | 
			
		||||
        } else {
 | 
			
		||||
            if (entry->addr >= virt_base &&
 | 
			
		||||
                            entry->addr < virt_base + size) {
 | 
			
		||||
                fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
 | 
			
		||||
                                " collides with FIFO at " TARGET_FMT_lx
 | 
			
		||||
                                "\n", __FUNCTION__,
 | 
			
		||||
                                (target_ulong) virt_base,
 | 
			
		||||
                                (target_ulong) (virt_base + size),
 | 
			
		||||
                                (target_ulong) entry->addr);
 | 
			
		||||
                fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
 | 
			
		||||
                                " collides with FIFO at %"PRIx64
 | 
			
		||||
                                "\n", __func__,
 | 
			
		||||
                                virt_base, virt_base + size,
 | 
			
		||||
                                entry->addr);
 | 
			
		||||
                exit(-1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -62,7 +62,12 @@ static const char *imx_gpio_reg_name(uint32_t reg)
 | 
			
		||||
 | 
			
		||||
static void imx_gpio_update_int(IMXGPIOState *s)
 | 
			
		||||
{
 | 
			
		||||
    qemu_set_irq(s->irq, (s->isr & s->imr) ? 1 : 0);
 | 
			
		||||
    if (s->has_upper_pin_irq) {
 | 
			
		||||
        qemu_set_irq(s->irq[0], (s->isr & s->imr & 0x0000FFFF) ? 1 : 0);
 | 
			
		||||
        qemu_set_irq(s->irq[1], (s->isr & s->imr & 0xFFFF0000) ? 1 : 0);
 | 
			
		||||
    } else {
 | 
			
		||||
        qemu_set_irq(s->irq[0], (s->isr & s->imr) ? 1 : 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx_gpio_set_int_line(IMXGPIOState *s, int line, IMXGPIOLevel level)
 | 
			
		||||
@@ -282,6 +287,8 @@ static const VMStateDescription vmstate_imx_gpio = {
 | 
			
		||||
 | 
			
		||||
static Property imx_gpio_properties[] = {
 | 
			
		||||
    DEFINE_PROP_BOOL("has-edge-sel", IMXGPIOState, has_edge_sel, true),
 | 
			
		||||
    DEFINE_PROP_BOOL("has-upper-pin-irq", IMXGPIOState, has_upper_pin_irq,
 | 
			
		||||
                     false),
 | 
			
		||||
    DEFINE_PROP_END_OF_LIST(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -311,7 +318,8 @@ static void imx_gpio_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
    qdev_init_gpio_in(DEVICE(s), imx_gpio_set, IMX_GPIO_PIN_COUNT);
 | 
			
		||||
    qdev_init_gpio_out(DEVICE(s), s->output, IMX_GPIO_PIN_COUNT);
 | 
			
		||||
 | 
			
		||||
    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
 | 
			
		||||
    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[0]);
 | 
			
		||||
    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[1]);
 | 
			
		||||
    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,7 @@
 | 
			
		||||
#include "hw/loader.h"
 | 
			
		||||
#include "hw/isa/isa.h"
 | 
			
		||||
#include "hw/acpi/memory_hotplug.h"
 | 
			
		||||
#include "hw/mem/nvdimm.h"
 | 
			
		||||
#include "sysemu/tpm.h"
 | 
			
		||||
#include "hw/acpi/tpm.h"
 | 
			
		||||
#include "sysemu/tpm_backend.h"
 | 
			
		||||
@@ -361,7 +362,7 @@ build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm,
 | 
			
		||||
    fadt_setup(fadt, pm);
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)fadt, "FACP", sizeof(*fadt), 1);
 | 
			
		||||
                 (void *)fadt, "FACP", sizeof(*fadt), 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -431,7 +432,7 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu,
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)(table_data->data + madt_start), "APIC",
 | 
			
		||||
                 table_data->len - madt_start, 1);
 | 
			
		||||
                 table_data->len - madt_start, 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Assign BSEL property to all buses.  In the future, this can be changed
 | 
			
		||||
@@ -469,7 +470,7 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot)
 | 
			
		||||
    Aml *if_ctx;
 | 
			
		||||
    int32_t devfn = PCI_DEVFN(slot, 0);
 | 
			
		||||
 | 
			
		||||
    if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot)));
 | 
			
		||||
    if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL));
 | 
			
		||||
    aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1)));
 | 
			
		||||
    aml_append(method, if_ctx);
 | 
			
		||||
}
 | 
			
		||||
@@ -487,7 +488,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
 | 
			
		||||
        int64_t bsel_val = qint_get_int(qobject_to_qint(bsel));
 | 
			
		||||
 | 
			
		||||
        aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val)));
 | 
			
		||||
        notify_method = aml_method("DVNT", 2);
 | 
			
		||||
        notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) {
 | 
			
		||||
@@ -503,7 +504,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
 | 
			
		||||
                dev = aml_device("S%.02X", PCI_DEVFN(slot, 0));
 | 
			
		||||
                aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
 | 
			
		||||
                aml_append(dev, aml_name_decl("_ADR", aml_int(slot << 16)));
 | 
			
		||||
                method = aml_method("_EJ0", 1);
 | 
			
		||||
                method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
 | 
			
		||||
                aml_append(method,
 | 
			
		||||
                    aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
 | 
			
		||||
                );
 | 
			
		||||
@@ -546,22 +547,22 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
 | 
			
		||||
                s3d = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_S1D", 0);
 | 
			
		||||
            method = aml_method("_S1D", 0, AML_NOTSERIALIZED);
 | 
			
		||||
            aml_append(method, aml_return(aml_int(0)));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_S2D", 0);
 | 
			
		||||
            method = aml_method("_S2D", 0, AML_NOTSERIALIZED);
 | 
			
		||||
            aml_append(method, aml_return(aml_int(0)));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_S3D", 0);
 | 
			
		||||
            method = aml_method("_S3D", 0, AML_NOTSERIALIZED);
 | 
			
		||||
            aml_append(method, aml_return(aml_int(s3d)));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
        } else if (hotplug_enabled_dev) {
 | 
			
		||||
            /* add _SUN/_EJ0 to make slot hotpluggable  */
 | 
			
		||||
            aml_append(dev, aml_name_decl("_SUN", aml_int(slot)));
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_EJ0", 1);
 | 
			
		||||
            method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
 | 
			
		||||
            aml_append(method,
 | 
			
		||||
                aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN"))
 | 
			
		||||
            );
 | 
			
		||||
@@ -590,7 +591,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus,
 | 
			
		||||
    /* Append PCNT method to notify about events on local and child buses.
 | 
			
		||||
     * Add unconditionally for root since DSDT expects it.
 | 
			
		||||
     */
 | 
			
		||||
    method = aml_method("PCNT", 0);
 | 
			
		||||
    method = aml_method("PCNT", 0, AML_NOTSERIALIZED);
 | 
			
		||||
 | 
			
		||||
    /* If bus supports hotplug select it and notify about local events */
 | 
			
		||||
    if (bsel) {
 | 
			
		||||
@@ -651,7 +652,7 @@ static Aml *build_prt(void)
 | 
			
		||||
{
 | 
			
		||||
    Aml *method, *while_ctx, *pin, *res;
 | 
			
		||||
 | 
			
		||||
    method = aml_method("_PRT", 0);
 | 
			
		||||
    method = aml_method("_PRT", 0, AML_NOTSERIALIZED);
 | 
			
		||||
    res = aml_local(0);
 | 
			
		||||
    pin = aml_local(1);
 | 
			
		||||
    aml_append(method, aml_store(aml_package(128), res));
 | 
			
		||||
@@ -666,10 +667,11 @@ static Aml *build_prt(void)
 | 
			
		||||
 | 
			
		||||
        /* slot = pin >> 2 */
 | 
			
		||||
        aml_append(while_ctx,
 | 
			
		||||
                   aml_store(aml_shiftright(pin, aml_int(2)), slot));
 | 
			
		||||
                   aml_store(aml_shiftright(pin, aml_int(2), NULL), slot));
 | 
			
		||||
        /* lnk_idx = (slot + pin) & 3 */
 | 
			
		||||
        aml_append(while_ctx,
 | 
			
		||||
                   aml_store(aml_and(aml_add(pin, slot), aml_int(3)), lnk_idx));
 | 
			
		||||
            aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL),
 | 
			
		||||
                      lnk_idx));
 | 
			
		||||
 | 
			
		||||
        /* route[2] = "LNK[D|A|B|C]", selection based on pin % 3  */
 | 
			
		||||
        aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0));
 | 
			
		||||
@@ -679,11 +681,13 @@ static Aml *build_prt(void)
 | 
			
		||||
 | 
			
		||||
        /* route[0] = 0x[slot]FFFF */
 | 
			
		||||
        aml_append(while_ctx,
 | 
			
		||||
            aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF)),
 | 
			
		||||
            aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF),
 | 
			
		||||
                             NULL),
 | 
			
		||||
                      aml_index(route, aml_int(0))));
 | 
			
		||||
        /* route[1] = pin & 3 */
 | 
			
		||||
        aml_append(while_ctx,
 | 
			
		||||
            aml_store(aml_and(pin, aml_int(3)), aml_index(route, aml_int(1))));
 | 
			
		||||
            aml_store(aml_and(pin, aml_int(3), NULL),
 | 
			
		||||
                      aml_index(route, aml_int(1))));
 | 
			
		||||
        /* res[pin] = route */
 | 
			
		||||
        aml_append(while_ctx, aml_store(route, aml_index(res, pin)));
 | 
			
		||||
        /* pin++ */
 | 
			
		||||
@@ -762,16 +766,59 @@ static void crs_replace_with_free_ranges(GPtrArray *ranges,
 | 
			
		||||
    g_ptr_array_free(free_ranges, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * crs_range_merge - merges adjacent ranges in the given array.
 | 
			
		||||
 * Array elements are deleted and replaced with the merged ranges.
 | 
			
		||||
 */
 | 
			
		||||
static void crs_range_merge(GPtrArray *range)
 | 
			
		||||
{
 | 
			
		||||
    GPtrArray *tmp =  g_ptr_array_new_with_free_func(crs_range_free);
 | 
			
		||||
    CrsRangeEntry *entry;
 | 
			
		||||
    uint64_t range_base, range_limit;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    if (!range->len) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    g_ptr_array_sort(range, crs_range_compare);
 | 
			
		||||
 | 
			
		||||
    entry = g_ptr_array_index(range, 0);
 | 
			
		||||
    range_base = entry->base;
 | 
			
		||||
    range_limit = entry->limit;
 | 
			
		||||
    for (i = 1; i < range->len; i++) {
 | 
			
		||||
        entry = g_ptr_array_index(range, i);
 | 
			
		||||
        if (entry->base - 1 == range_limit) {
 | 
			
		||||
            range_limit = entry->limit;
 | 
			
		||||
        } else {
 | 
			
		||||
            crs_range_insert(tmp, range_base, range_limit);
 | 
			
		||||
            range_base = entry->base;
 | 
			
		||||
            range_limit = entry->limit;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    crs_range_insert(tmp, range_base, range_limit);
 | 
			
		||||
 | 
			
		||||
    g_ptr_array_set_size(range, 0);
 | 
			
		||||
    for (i = 0; i < tmp->len; i++) {
 | 
			
		||||
        entry = g_ptr_array_index(tmp, i);
 | 
			
		||||
        crs_range_insert(range, entry->base, entry->limit);
 | 
			
		||||
    }
 | 
			
		||||
    g_ptr_array_free(tmp, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Aml *build_crs(PCIHostState *host,
 | 
			
		||||
                      GPtrArray *io_ranges, GPtrArray *mem_ranges)
 | 
			
		||||
{
 | 
			
		||||
    Aml *crs = aml_resource_template();
 | 
			
		||||
    GPtrArray *host_io_ranges = g_ptr_array_new_with_free_func(crs_range_free);
 | 
			
		||||
    GPtrArray *host_mem_ranges = g_ptr_array_new_with_free_func(crs_range_free);
 | 
			
		||||
    CrsRangeEntry *entry;
 | 
			
		||||
    uint8_t max_bus = pci_bus_num(host->bus);
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    int devfn;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) {
 | 
			
		||||
        int i;
 | 
			
		||||
        uint64_t range_base, range_limit;
 | 
			
		||||
        PCIDevice *dev = host->bus->devices[devfn];
 | 
			
		||||
 | 
			
		||||
@@ -794,26 +841,9 @@ static Aml *build_crs(PCIHostState *host,
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
 | 
			
		||||
                aml_append(crs,
 | 
			
		||||
                    aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
 | 
			
		||||
                                AML_POS_DECODE, AML_ENTIRE_RANGE,
 | 
			
		||||
                                0,
 | 
			
		||||
                                range_base,
 | 
			
		||||
                                range_limit,
 | 
			
		||||
                                0,
 | 
			
		||||
                                range_limit - range_base + 1));
 | 
			
		||||
                crs_range_insert(io_ranges, range_base, range_limit);
 | 
			
		||||
                crs_range_insert(host_io_ranges, range_base, range_limit);
 | 
			
		||||
            } else { /* "memory" */
 | 
			
		||||
                aml_append(crs,
 | 
			
		||||
                    aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
 | 
			
		||||
                                     AML_MAX_FIXED, AML_NON_CACHEABLE,
 | 
			
		||||
                                     AML_READ_WRITE,
 | 
			
		||||
                                     0,
 | 
			
		||||
                                     range_base,
 | 
			
		||||
                                     range_limit,
 | 
			
		||||
                                     0,
 | 
			
		||||
                                     range_limit - range_base + 1));
 | 
			
		||||
                crs_range_insert(mem_ranges, range_base, range_limit);
 | 
			
		||||
                crs_range_insert(host_mem_ranges, range_base, range_limit);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -832,15 +862,7 @@ static Aml *build_crs(PCIHostState *host,
 | 
			
		||||
             * that do not support multiple root buses
 | 
			
		||||
             */
 | 
			
		||||
            if (range_base && range_base <= range_limit) {
 | 
			
		||||
                aml_append(crs,
 | 
			
		||||
                           aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
 | 
			
		||||
                                       AML_POS_DECODE, AML_ENTIRE_RANGE,
 | 
			
		||||
                                       0,
 | 
			
		||||
                                       range_base,
 | 
			
		||||
                                       range_limit,
 | 
			
		||||
                                       0,
 | 
			
		||||
                                       range_limit - range_base + 1));
 | 
			
		||||
                crs_range_insert(io_ranges, range_base, range_limit);
 | 
			
		||||
                crs_range_insert(host_io_ranges, range_base, range_limit);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            range_base =
 | 
			
		||||
@@ -853,16 +875,7 @@ static Aml *build_crs(PCIHostState *host,
 | 
			
		||||
             * that do not support multiple root buses
 | 
			
		||||
             */
 | 
			
		||||
            if (range_base && range_base <= range_limit) {
 | 
			
		||||
                aml_append(crs,
 | 
			
		||||
                           aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
 | 
			
		||||
                                            AML_MAX_FIXED, AML_NON_CACHEABLE,
 | 
			
		||||
                                            AML_READ_WRITE,
 | 
			
		||||
                                            0,
 | 
			
		||||
                                            range_base,
 | 
			
		||||
                                            range_limit,
 | 
			
		||||
                                            0,
 | 
			
		||||
                                            range_limit - range_base + 1));
 | 
			
		||||
                crs_range_insert(mem_ranges, range_base, range_limit);
 | 
			
		||||
                crs_range_insert(host_mem_ranges, range_base, range_limit);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            range_base =
 | 
			
		||||
@@ -875,20 +888,36 @@ static Aml *build_crs(PCIHostState *host,
 | 
			
		||||
             * that do not support multiple root buses
 | 
			
		||||
             */
 | 
			
		||||
            if (range_base && range_base <= range_limit) {
 | 
			
		||||
                aml_append(crs,
 | 
			
		||||
                           aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
 | 
			
		||||
                                            AML_MAX_FIXED, AML_NON_CACHEABLE,
 | 
			
		||||
                                            AML_READ_WRITE,
 | 
			
		||||
                                            0,
 | 
			
		||||
                                            range_base,
 | 
			
		||||
                                            range_limit,
 | 
			
		||||
                                            0,
 | 
			
		||||
                                            range_limit - range_base + 1));
 | 
			
		||||
                crs_range_insert(mem_ranges, range_base, range_limit);
 | 
			
		||||
                crs_range_insert(host_mem_ranges, range_base, range_limit);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    crs_range_merge(host_io_ranges);
 | 
			
		||||
    for (i = 0; i < host_io_ranges->len; i++) {
 | 
			
		||||
        entry = g_ptr_array_index(host_io_ranges, i);
 | 
			
		||||
        aml_append(crs,
 | 
			
		||||
                   aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
 | 
			
		||||
                               AML_POS_DECODE, AML_ENTIRE_RANGE,
 | 
			
		||||
                               0, entry->base, entry->limit, 0,
 | 
			
		||||
                               entry->limit - entry->base + 1));
 | 
			
		||||
        crs_range_insert(io_ranges, entry->base, entry->limit);
 | 
			
		||||
    }
 | 
			
		||||
    g_ptr_array_free(host_io_ranges, true);
 | 
			
		||||
 | 
			
		||||
    crs_range_merge(host_mem_ranges);
 | 
			
		||||
    for (i = 0; i < host_mem_ranges->len; i++) {
 | 
			
		||||
        entry = g_ptr_array_index(host_mem_ranges, i);
 | 
			
		||||
        aml_append(crs,
 | 
			
		||||
                   aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED,
 | 
			
		||||
                                    AML_MAX_FIXED, AML_NON_CACHEABLE,
 | 
			
		||||
                                    AML_READ_WRITE,
 | 
			
		||||
                                    0, entry->base, entry->limit, 0,
 | 
			
		||||
                                    entry->limit - entry->base + 1));
 | 
			
		||||
        crs_range_insert(mem_ranges, entry->base, entry->limit);
 | 
			
		||||
    }
 | 
			
		||||
    g_ptr_array_free(host_mem_ranges, true);
 | 
			
		||||
 | 
			
		||||
    aml_append(crs,
 | 
			
		||||
        aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
 | 
			
		||||
                            0,
 | 
			
		||||
@@ -925,8 +954,7 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
    /* Reserve space for header */
 | 
			
		||||
    acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader));
 | 
			
		||||
 | 
			
		||||
    /* Extra PCI root buses are implemented  only for i440fx */
 | 
			
		||||
    bus = find_i440fx();
 | 
			
		||||
    bus = PC_MACHINE(machine)->bus;
 | 
			
		||||
    if (bus) {
 | 
			
		||||
        QLIST_FOREACH(bus, &bus->child, sibling) {
 | 
			
		||||
            uint8_t bus_num = pci_bus_num(bus);
 | 
			
		||||
@@ -1105,19 +1133,19 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
 | 
			
		||||
        aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO,
 | 
			
		||||
                                              misc->pvpanic_port, 1));
 | 
			
		||||
        field = aml_field("PEOR", AML_BYTE_ACC, AML_PRESERVE);
 | 
			
		||||
        field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
 | 
			
		||||
        aml_append(field, aml_named_field("PEPT", 8));
 | 
			
		||||
        aml_append(dev, field);
 | 
			
		||||
 | 
			
		||||
        /* device present, functioning, decoding, shown in UI */
 | 
			
		||||
        aml_append(dev, aml_name_decl("_STA", aml_int(0xF)));
 | 
			
		||||
 | 
			
		||||
        method = aml_method("RDPT", 0);
 | 
			
		||||
        method = aml_method("RDPT", 0, AML_NOTSERIALIZED);
 | 
			
		||||
        aml_append(method, aml_store(aml_name("PEPT"), aml_local(0)));
 | 
			
		||||
        aml_append(method, aml_return(aml_local(0)));
 | 
			
		||||
        aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
        method = aml_method("WRPT", 1);
 | 
			
		||||
        method = aml_method("WRPT", 1, AML_NOTSERIALIZED);
 | 
			
		||||
        aml_append(method, aml_store(aml_arg(0), aml_name("PEPT")));
 | 
			
		||||
        aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
@@ -1145,7 +1173,7 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
        /* declare CPU hotplug MMIO region and PRS field to access it */
 | 
			
		||||
        aml_append(sb_scope, aml_operation_region(
 | 
			
		||||
            "PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len));
 | 
			
		||||
        field = aml_field("PRST", AML_BYTE_ACC, AML_PRESERVE);
 | 
			
		||||
        field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
 | 
			
		||||
        aml_append(field, aml_named_field("PRS", 256));
 | 
			
		||||
        aml_append(sb_scope, field);
 | 
			
		||||
 | 
			
		||||
@@ -1153,15 +1181,15 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
        for (i = 0; i < acpi_cpus; i++) {
 | 
			
		||||
            dev = aml_processor(i, 0, 0, "CP%.02X", i);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_MAT", 0);
 | 
			
		||||
            method = aml_method("_MAT", 0, AML_NOTSERIALIZED);
 | 
			
		||||
            aml_append(method, aml_return(aml_call1("CPMA", aml_int(i))));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_STA", 0);
 | 
			
		||||
            method = aml_method("_STA", 0, AML_NOTSERIALIZED);
 | 
			
		||||
            aml_append(method, aml_return(aml_call1("CPST", aml_int(i))));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_EJ0", 1);
 | 
			
		||||
            method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
 | 
			
		||||
            aml_append(method,
 | 
			
		||||
                aml_return(aml_call2("CPEJ", aml_int(i), aml_arg(0)))
 | 
			
		||||
            );
 | 
			
		||||
@@ -1174,7 +1202,7 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
         *   Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...}
 | 
			
		||||
         */
 | 
			
		||||
        /* Arg0 = Processor ID = APIC ID */
 | 
			
		||||
        method = aml_method("NTFY", 2);
 | 
			
		||||
        method = aml_method("NTFY", 2, AML_NOTSERIALIZED);
 | 
			
		||||
        for (i = 0; i < acpi_cpus; i++) {
 | 
			
		||||
            ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
 | 
			
		||||
            aml_append(ifctx,
 | 
			
		||||
@@ -1220,7 +1248,7 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
 | 
			
		||||
                          AML_PRESERVE);
 | 
			
		||||
                          AML_NOLOCK, AML_PRESERVE);
 | 
			
		||||
        aml_append(field, /* read only */
 | 
			
		||||
            aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32));
 | 
			
		||||
        aml_append(field, /* read only */
 | 
			
		||||
@@ -1234,7 +1262,7 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
        aml_append(scope, field);
 | 
			
		||||
 | 
			
		||||
        field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_BYTE_ACC,
 | 
			
		||||
                          AML_WRITE_AS_ZEROS);
 | 
			
		||||
                          AML_NOLOCK, AML_WRITE_AS_ZEROS);
 | 
			
		||||
        aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */));
 | 
			
		||||
        aml_append(field, /* 1 if enabled, read only */
 | 
			
		||||
            aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1));
 | 
			
		||||
@@ -1250,7 +1278,7 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
        aml_append(scope, field);
 | 
			
		||||
 | 
			
		||||
        field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC,
 | 
			
		||||
                          AML_PRESERVE);
 | 
			
		||||
                          AML_NOLOCK, AML_PRESERVE);
 | 
			
		||||
        aml_append(field, /* DIMM selector, write only */
 | 
			
		||||
            aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32));
 | 
			
		||||
        aml_append(field, /* _OST event code, write only */
 | 
			
		||||
@@ -1269,29 +1297,29 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
            aml_append(dev, aml_name_decl("_UID", aml_string("0x%02X", i)));
 | 
			
		||||
            aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C80")));
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_CRS", 0);
 | 
			
		||||
            method = aml_method("_CRS", 0, AML_NOTSERIALIZED);
 | 
			
		||||
            s = BASEPATH stringify(MEMORY_SLOT_CRS_METHOD);
 | 
			
		||||
            aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_STA", 0);
 | 
			
		||||
            method = aml_method("_STA", 0, AML_NOTSERIALIZED);
 | 
			
		||||
            s = BASEPATH stringify(MEMORY_SLOT_STATUS_METHOD);
 | 
			
		||||
            aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_PXM", 0);
 | 
			
		||||
            method = aml_method("_PXM", 0, AML_NOTSERIALIZED);
 | 
			
		||||
            s = BASEPATH stringify(MEMORY_SLOT_PROXIMITY_METHOD);
 | 
			
		||||
            aml_append(method, aml_return(aml_call1(s, aml_name("_UID"))));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_OST", 3);
 | 
			
		||||
            method = aml_method("_OST", 3, AML_NOTSERIALIZED);
 | 
			
		||||
            s = BASEPATH stringify(MEMORY_SLOT_OST_METHOD);
 | 
			
		||||
            aml_append(method, aml_return(aml_call4(
 | 
			
		||||
                s, aml_name("_UID"), aml_arg(0), aml_arg(1), aml_arg(2)
 | 
			
		||||
            )));
 | 
			
		||||
            aml_append(dev, method);
 | 
			
		||||
 | 
			
		||||
            method = aml_method("_EJ0", 1);
 | 
			
		||||
            method = aml_method("_EJ0", 1, AML_NOTSERIALIZED);
 | 
			
		||||
            s = BASEPATH stringify(MEMORY_SLOT_EJECT_METHOD);
 | 
			
		||||
            aml_append(method, aml_return(aml_call2(
 | 
			
		||||
                       s, aml_name("_UID"), aml_arg(0))));
 | 
			
		||||
@@ -1303,7 +1331,8 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
        /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) {
 | 
			
		||||
         *     If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... }
 | 
			
		||||
         */
 | 
			
		||||
        method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2);
 | 
			
		||||
        method = aml_method(stringify(MEMORY_SLOT_NOTIFY_METHOD), 2,
 | 
			
		||||
                            AML_NOTSERIALIZED);
 | 
			
		||||
        for (i = 0; i < nr_mem; i++) {
 | 
			
		||||
            ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i)));
 | 
			
		||||
            aml_append(ifctx,
 | 
			
		||||
@@ -1349,7 +1378,7 @@ build_ssdt(GArray *table_data, GArray *linker,
 | 
			
		||||
    g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
        (void *)(table_data->data + table_data->len - ssdt->buf->len),
 | 
			
		||||
        "SSDT", ssdt->buf->len, 1);
 | 
			
		||||
        "SSDT", ssdt->buf->len, 1, NULL);
 | 
			
		||||
    free_aml_allocator();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1365,7 +1394,7 @@ build_hpet(GArray *table_data, GArray *linker)
 | 
			
		||||
    hpet->timer_block_id = cpu_to_le32(0x8086a201);
 | 
			
		||||
    hpet->addr.address = cpu_to_le64(HPET_BASE);
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)hpet, "HPET", sizeof(*hpet), 1);
 | 
			
		||||
                 (void *)hpet, "HPET", sizeof(*hpet), 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -1388,7 +1417,7 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog)
 | 
			
		||||
                                   sizeof(tcpa->log_area_start_address));
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)tcpa, "TCPA", sizeof(*tcpa), 2);
 | 
			
		||||
                 (void *)tcpa, "TCPA", sizeof(*tcpa), 2, NULL);
 | 
			
		||||
 | 
			
		||||
    acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE);
 | 
			
		||||
}
 | 
			
		||||
@@ -1405,7 +1434,7 @@ build_tpm2(GArray *table_data, GArray *linker)
 | 
			
		||||
    tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO);
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4);
 | 
			
		||||
                 (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
@@ -1519,7 +1548,7 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info)
 | 
			
		||||
    build_header(linker, table_data,
 | 
			
		||||
                 (void *)(table_data->data + srat_start),
 | 
			
		||||
                 "SRAT",
 | 
			
		||||
                 table_data->len - srat_start, 1);
 | 
			
		||||
                 table_data->len - srat_start, 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -1548,7 +1577,7 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info)
 | 
			
		||||
    } else {
 | 
			
		||||
        sig = "MCFG";
 | 
			
		||||
    }
 | 
			
		||||
    build_header(linker, table_data, (void *)mcfg, sig, len, 1);
 | 
			
		||||
    build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -1572,7 +1601,7 @@ build_dmar_q35(GArray *table_data, GArray *linker)
 | 
			
		||||
    drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR);
 | 
			
		||||
 | 
			
		||||
    build_header(linker, table_data, (void *)(table_data->data + dmar_start),
 | 
			
		||||
                 "DMAR", table_data->len - dmar_start, 1);
 | 
			
		||||
                 "DMAR", table_data->len - dmar_start, 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -1587,7 +1616,7 @@ build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc)
 | 
			
		||||
 | 
			
		||||
    memset(dsdt, 0, sizeof *dsdt);
 | 
			
		||||
    build_header(linker, table_data, dsdt, "DSDT",
 | 
			
		||||
                 misc->dsdt_size, 1);
 | 
			
		||||
                 misc->dsdt_size, 1, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GArray *
 | 
			
		||||
@@ -1658,6 +1687,13 @@ static bool acpi_has_iommu(void)
 | 
			
		||||
    return intel_iommu && !ambiguous;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool acpi_has_nvdimm(void)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
 | 
			
		||||
 | 
			
		||||
    return pcms->nvdimm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static
 | 
			
		||||
void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
 | 
			
		||||
{
 | 
			
		||||
@@ -1742,6 +1778,10 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables)
 | 
			
		||||
        build_dmar_q35(tables_blob, tables->linker);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (acpi_has_nvdimm()) {
 | 
			
		||||
        nvdimm_build_acpi(table_offsets, tables_blob, tables->linker);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Add tables supplied by user (if any) */
 | 
			
		||||
    for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
 | 
			
		||||
        unsigned len = acpi_table_len(u);
 | 
			
		||||
@@ -1818,7 +1858,7 @@ static void acpi_ram_update(MemoryRegion *mr, GArray *data)
 | 
			
		||||
    memory_region_set_dirty(mr, 0, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void acpi_build_update(void *build_opaque, uint32_t offset)
 | 
			
		||||
static void acpi_build_update(void *build_opaque)
 | 
			
		||||
{
 | 
			
		||||
    AcpiBuildState *build_state = build_opaque;
 | 
			
		||||
    AcpiBuildTables tables;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								hw/i386/pc.c
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								hw/i386/pc.c
									
									
									
									
									
								
							@@ -65,6 +65,7 @@
 | 
			
		||||
#include "hw/mem/pc-dimm.h"
 | 
			
		||||
#include "qapi/visitor.h"
 | 
			
		||||
#include "qapi-visit.h"
 | 
			
		||||
#include "qom/cpu.h"
 | 
			
		||||
 | 
			
		||||
/* debug PC/ISA interrupts */
 | 
			
		||||
//#define DEBUG_IRQ
 | 
			
		||||
@@ -76,15 +77,6 @@
 | 
			
		||||
#define DPRINTF(fmt, ...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables
 | 
			
		||||
 * (128K) and other BIOS datastructures (less than 4K reported to be used at
 | 
			
		||||
 * the moment, 32K should be enough for a while).  */
 | 
			
		||||
static unsigned acpi_data_size = 0x20000 + 0x8000;
 | 
			
		||||
void pc_set_legacy_acpi_data_size(void)
 | 
			
		||||
{
 | 
			
		||||
    acpi_data_size = 0x10000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define BIOS_CFG_IOPORT 0x510
 | 
			
		||||
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
 | 
			
		||||
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
 | 
			
		||||
@@ -840,6 +832,7 @@ static void load_linux(PCMachineState *pcms,
 | 
			
		||||
    FILE *f;
 | 
			
		||||
    char *vmode;
 | 
			
		||||
    MachineState *machine = MACHINE(pcms);
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
 | 
			
		||||
    const char *kernel_filename = machine->kernel_filename;
 | 
			
		||||
    const char *initrd_filename = machine->initrd_filename;
 | 
			
		||||
    const char *kernel_cmdline = machine->kernel_cmdline;
 | 
			
		||||
@@ -907,8 +900,8 @@ static void load_linux(PCMachineState *pcms,
 | 
			
		||||
        initrd_max = 0x37ffffff;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (initrd_max >= pcms->below_4g_mem_size - acpi_data_size) {
 | 
			
		||||
        initrd_max = pcms->below_4g_mem_size - acpi_data_size - 1;
 | 
			
		||||
    if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) {
 | 
			
		||||
        initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
 | 
			
		||||
@@ -1174,7 +1167,7 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data)
 | 
			
		||||
    PcGuestInfoState *guest_info_state = container_of(notifier,
 | 
			
		||||
                                                      PcGuestInfoState,
 | 
			
		||||
                                                      machine_done);
 | 
			
		||||
    PCIBus *bus = find_i440fx();
 | 
			
		||||
    PCIBus *bus = PC_MACHINE(qdev_get_machine())->bus;
 | 
			
		||||
 | 
			
		||||
    if (bus) {
 | 
			
		||||
        int extra_hosts = 0;
 | 
			
		||||
@@ -1302,6 +1295,7 @@ FWCfgState *pc_memory_init(PCMachineState *pcms,
 | 
			
		||||
    MemoryRegion *ram_below_4g, *ram_above_4g;
 | 
			
		||||
    FWCfgState *fw_cfg;
 | 
			
		||||
    MachineState *machine = MACHINE(pcms);
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
 | 
			
		||||
 | 
			
		||||
    assert(machine->ram_size == pcms->below_4g_mem_size +
 | 
			
		||||
                                pcms->above_4g_mem_size);
 | 
			
		||||
@@ -1363,7 +1357,7 @@ FWCfgState *pc_memory_init(PCMachineState *pcms,
 | 
			
		||||
        pcms->hotplug_memory.base =
 | 
			
		||||
            ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30);
 | 
			
		||||
 | 
			
		||||
        if (pcms->enforce_aligned_dimm) {
 | 
			
		||||
        if (pcmc->enforce_aligned_dimm) {
 | 
			
		||||
            /* size hotplug region assuming 1G page max alignment per slot */
 | 
			
		||||
            hotplug_mem_size += (1ULL << 30) * machine->ram_slots;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1517,7 +1511,7 @@ void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
 | 
			
		||||
    qemu_register_boot_set(pc_boot_set, *rtc_state);
 | 
			
		||||
 | 
			
		||||
    if (!xen_enabled()) {
 | 
			
		||||
        if (kvm_irqchip_in_kernel()) {
 | 
			
		||||
        if (kvm_pit_in_kernel()) {
 | 
			
		||||
            pit = kvm_pit_init(isa_bus, 0x40);
 | 
			
		||||
        } else {
 | 
			
		||||
            pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
 | 
			
		||||
@@ -1592,7 +1586,7 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
 | 
			
		||||
    SysBusDevice *d;
 | 
			
		||||
    unsigned int i;
 | 
			
		||||
 | 
			
		||||
    if (kvm_irqchip_in_kernel()) {
 | 
			
		||||
    if (kvm_ioapic_in_kernel()) {
 | 
			
		||||
        dev = qdev_create(NULL, "kvm-ioapic");
 | 
			
		||||
    } else {
 | 
			
		||||
        dev = qdev_create(NULL, "ioapic");
 | 
			
		||||
@@ -1616,12 +1610,13 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev,
 | 
			
		||||
    HotplugHandlerClass *hhc;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    PCMachineState *pcms = PC_MACHINE(hotplug_dev);
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
 | 
			
		||||
    PCDIMMDevice *dimm = PC_DIMM(dev);
 | 
			
		||||
    PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
 | 
			
		||||
    MemoryRegion *mr = ddc->get_memory_region(dimm);
 | 
			
		||||
    uint64_t align = TARGET_PAGE_SIZE;
 | 
			
		||||
 | 
			
		||||
    if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) {
 | 
			
		||||
    if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
 | 
			
		||||
        align = memory_region_get_alignment(mr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1870,11 +1865,18 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
 | 
			
		||||
    visit_type_OnOffAuto(v, &pcms->smm, name, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp)
 | 
			
		||||
static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineState *pcms = PC_MACHINE(obj);
 | 
			
		||||
 | 
			
		||||
    return pcms->enforce_aligned_dimm;
 | 
			
		||||
    return pcms->nvdimm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineState *pcms = PC_MACHINE(obj);
 | 
			
		||||
 | 
			
		||||
    pcms->nvdimm = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_machine_initfn(Object *obj)
 | 
			
		||||
@@ -1912,10 +1914,10 @@ static void pc_machine_initfn(Object *obj)
 | 
			
		||||
                                    "Enable vmport (pc & q35)",
 | 
			
		||||
                                    &error_abort);
 | 
			
		||||
 | 
			
		||||
    pcms->enforce_aligned_dimm = true;
 | 
			
		||||
    object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM,
 | 
			
		||||
                             pc_machine_get_aligned_dimm,
 | 
			
		||||
                             NULL, &error_abort);
 | 
			
		||||
    /* nvdimm is disabled on default. */
 | 
			
		||||
    pcms->nvdimm = false;
 | 
			
		||||
    object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
 | 
			
		||||
                             pc_machine_set_nvdimm, &error_abort);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_machine_reset(void)
 | 
			
		||||
@@ -1952,6 +1954,18 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
 | 
			
		||||
 | 
			
		||||
    pcmc->get_hotplug_handler = mc->get_hotplug_handler;
 | 
			
		||||
    pcmc->pci_enabled = true;
 | 
			
		||||
    pcmc->has_acpi_build = true;
 | 
			
		||||
    pcmc->rsdp_in_ram = true;
 | 
			
		||||
    pcmc->smbios_defaults = true;
 | 
			
		||||
    pcmc->smbios_uuid_encoded = true;
 | 
			
		||||
    pcmc->gigabyte_align = true;
 | 
			
		||||
    pcmc->has_reserved_memory = true;
 | 
			
		||||
    pcmc->kvmclock_enabled = true;
 | 
			
		||||
    pcmc->enforce_aligned_dimm = true;
 | 
			
		||||
    /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported
 | 
			
		||||
     * to be used at the moment, 32K should be enough for a while.  */
 | 
			
		||||
    pcmc->acpi_data_size = 0x20000 + 0x8000;
 | 
			
		||||
    mc->get_hotplug_handler = pc_get_hotpug_handler;
 | 
			
		||||
    mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
 | 
			
		||||
    mc->default_boot_order = "cad";
 | 
			
		||||
 
 | 
			
		||||
@@ -53,6 +53,7 @@
 | 
			
		||||
#include "hw/xen/xen_pt.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "migration/migration.h"
 | 
			
		||||
#include "kvm_i386.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_IDE_BUS 2
 | 
			
		||||
 | 
			
		||||
@@ -60,26 +61,12 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 };
 | 
			
		||||
static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 };
 | 
			
		||||
static const int ide_irq[MAX_IDE_BUS] = { 14, 15 };
 | 
			
		||||
 | 
			
		||||
static bool pci_enabled = true;
 | 
			
		||||
static bool has_acpi_build = true;
 | 
			
		||||
static bool rsdp_in_ram = true;
 | 
			
		||||
static int legacy_acpi_table_size;
 | 
			
		||||
static bool smbios_defaults = true;
 | 
			
		||||
static bool smbios_legacy_mode;
 | 
			
		||||
static bool smbios_uuid_encoded = true;
 | 
			
		||||
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
 | 
			
		||||
 * host addresses aligned at 1Gbyte boundaries.  This way we can use 1GByte
 | 
			
		||||
 * pages in the host.
 | 
			
		||||
 */
 | 
			
		||||
static bool gigabyte_align = true;
 | 
			
		||||
static bool has_reserved_memory = true;
 | 
			
		||||
static bool kvmclock_enabled = true;
 | 
			
		||||
 | 
			
		||||
/* PC hardware initialisation */
 | 
			
		||||
static void pc_init1(MachineState *machine,
 | 
			
		||||
                     const char *host_type, const char *pci_type)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineState *pcms = PC_MACHINE(machine);
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
 | 
			
		||||
    MemoryRegion *system_memory = get_system_memory();
 | 
			
		||||
    MemoryRegion *system_io = get_system_io();
 | 
			
		||||
    int i;
 | 
			
		||||
@@ -108,7 +95,7 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
     * breaking migration.
 | 
			
		||||
     */
 | 
			
		||||
    if (machine->ram_size >= 0xe0000000) {
 | 
			
		||||
        lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000;
 | 
			
		||||
        lowmem = pcmc->gigabyte_align ? 0xc0000000 : 0xe0000000;
 | 
			
		||||
    } else {
 | 
			
		||||
        lowmem = 0xe0000000;
 | 
			
		||||
    }
 | 
			
		||||
@@ -141,11 +128,11 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
 | 
			
		||||
    pc_cpus_init(pcms);
 | 
			
		||||
 | 
			
		||||
    if (kvm_enabled() && kvmclock_enabled) {
 | 
			
		||||
    if (kvm_enabled() && pcmc->kvmclock_enabled) {
 | 
			
		||||
        kvmclock_create();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pci_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled) {
 | 
			
		||||
        pci_memory = g_new(MemoryRegion, 1);
 | 
			
		||||
        memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
 | 
			
		||||
        rom_memory = pci_memory;
 | 
			
		||||
@@ -156,18 +143,19 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
 | 
			
		||||
    guest_info = pc_guest_info_init(pcms);
 | 
			
		||||
 | 
			
		||||
    guest_info->has_acpi_build = has_acpi_build;
 | 
			
		||||
    guest_info->legacy_acpi_table_size = legacy_acpi_table_size;
 | 
			
		||||
    guest_info->has_acpi_build = pcmc->has_acpi_build;
 | 
			
		||||
    guest_info->legacy_acpi_table_size = pcmc->legacy_acpi_table_size;
 | 
			
		||||
 | 
			
		||||
    guest_info->isapc_ram_fw = !pci_enabled;
 | 
			
		||||
    guest_info->has_reserved_memory = has_reserved_memory;
 | 
			
		||||
    guest_info->rsdp_in_ram = rsdp_in_ram;
 | 
			
		||||
    guest_info->isapc_ram_fw = !pcmc->pci_enabled;
 | 
			
		||||
    guest_info->has_reserved_memory = pcmc->has_reserved_memory;
 | 
			
		||||
    guest_info->rsdp_in_ram = pcmc->rsdp_in_ram;
 | 
			
		||||
 | 
			
		||||
    if (smbios_defaults) {
 | 
			
		||||
    if (pcmc->smbios_defaults) {
 | 
			
		||||
        MachineClass *mc = MACHINE_GET_CLASS(machine);
 | 
			
		||||
        /* These values are guest ABI, do not change */
 | 
			
		||||
        smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)",
 | 
			
		||||
                            mc->name, smbios_legacy_mode, smbios_uuid_encoded,
 | 
			
		||||
                            mc->name, pcmc->smbios_legacy_mode,
 | 
			
		||||
                            pcmc->smbios_uuid_encoded,
 | 
			
		||||
                            SMBIOS_ENTRY_POINT_21);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -181,15 +169,15 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gsi_state = g_malloc0(sizeof(*gsi_state));
 | 
			
		||||
    if (kvm_irqchip_in_kernel()) {
 | 
			
		||||
        kvm_pc_setup_irq_routing(pci_enabled);
 | 
			
		||||
    if (kvm_ioapic_in_kernel()) {
 | 
			
		||||
        kvm_pc_setup_irq_routing(pcmc->pci_enabled);
 | 
			
		||||
        gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
 | 
			
		||||
                                 GSI_NUM_PINS);
 | 
			
		||||
    } else {
 | 
			
		||||
        gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pci_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled) {
 | 
			
		||||
        pci_bus = i440fx_init(host_type,
 | 
			
		||||
                              pci_type,
 | 
			
		||||
                              &i440fx_state, &piix3_devfn, &isa_bus, gsi,
 | 
			
		||||
@@ -197,6 +185,7 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
                              pcms->below_4g_mem_size,
 | 
			
		||||
                              pcms->above_4g_mem_size,
 | 
			
		||||
                              pci_memory, ram_memory);
 | 
			
		||||
        pcms->bus = pci_bus;
 | 
			
		||||
    } else {
 | 
			
		||||
        pci_bus = NULL;
 | 
			
		||||
        i440fx_state = NULL;
 | 
			
		||||
@@ -205,7 +194,7 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
    }
 | 
			
		||||
    isa_bus_irqs(isa_bus, gsi);
 | 
			
		||||
 | 
			
		||||
    if (kvm_irqchip_in_kernel()) {
 | 
			
		||||
    if (kvm_pic_in_kernel()) {
 | 
			
		||||
        i8259 = kvm_i8259_init(isa_bus);
 | 
			
		||||
    } else if (xen_enabled()) {
 | 
			
		||||
        i8259 = xen_interrupt_controller_init();
 | 
			
		||||
@@ -217,15 +206,15 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
        gsi_state->i8259_irq[i] = i8259[i];
 | 
			
		||||
    }
 | 
			
		||||
    g_free(i8259);
 | 
			
		||||
    if (pci_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled) {
 | 
			
		||||
        ioapic_init_gsi(gsi_state, "i440fx");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pc_register_ferr_irq(gsi[13]);
 | 
			
		||||
 | 
			
		||||
    pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL);
 | 
			
		||||
    pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL);
 | 
			
		||||
 | 
			
		||||
    assert(pcms->vmport != ON_OFF_AUTO_MAX);
 | 
			
		||||
    assert(pcms->vmport != ON_OFF_AUTO__MAX);
 | 
			
		||||
    if (pcms->vmport == ON_OFF_AUTO_AUTO) {
 | 
			
		||||
        pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
 | 
			
		||||
    }
 | 
			
		||||
@@ -237,7 +226,7 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
    pc_nic_init(isa_bus, pci_bus);
 | 
			
		||||
 | 
			
		||||
    ide_drive_get(hd, ARRAY_SIZE(hd));
 | 
			
		||||
    if (pci_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled) {
 | 
			
		||||
        PCIDevice *dev;
 | 
			
		||||
        if (xen_enabled()) {
 | 
			
		||||
            dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1);
 | 
			
		||||
@@ -264,11 +253,11 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
 | 
			
		||||
    pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state);
 | 
			
		||||
 | 
			
		||||
    if (pci_enabled && usb_enabled()) {
 | 
			
		||||
    if (pcmc->pci_enabled && usb_enabled()) {
 | 
			
		||||
        pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pci_enabled && acpi_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled && acpi_enabled) {
 | 
			
		||||
        DeviceState *piix4_pm;
 | 
			
		||||
        I2CBus *smbus;
 | 
			
		||||
 | 
			
		||||
@@ -289,7 +278,7 @@ static void pc_init1(MachineState *machine,
 | 
			
		||||
                                 PC_MACHINE_ACPI_DEVICE_PROP, &error_abort);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (pci_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled) {
 | 
			
		||||
        pc_pci_device_init(pci_bus);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -315,60 +304,29 @@ static void pc_compat_2_3(MachineState *machine)
 | 
			
		||||
static void pc_compat_2_2(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_2_3(machine);
 | 
			
		||||
    rsdp_in_ram = false;
 | 
			
		||||
    machine->suppress_vmdesc = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_2_1(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineState *pcms = PC_MACHINE(machine);
 | 
			
		||||
 | 
			
		||||
    pc_compat_2_2(machine);
 | 
			
		||||
    smbios_uuid_encoded = false;
 | 
			
		||||
    x86_cpu_change_kvm_default("svm", NULL);
 | 
			
		||||
    pcms->enforce_aligned_dimm = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_2_0(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_2_1(machine);
 | 
			
		||||
    /* This value depends on the actual DSDT and SSDT compiled into
 | 
			
		||||
     * the source QEMU; unfortunately it depends on the binary and
 | 
			
		||||
     * not on the machine type, so we cannot make pc-i440fx-1.7 work on
 | 
			
		||||
     * both QEMU 1.7 and QEMU 2.0.
 | 
			
		||||
     *
 | 
			
		||||
     * Large variations cause migration to fail for more than one
 | 
			
		||||
     * consecutive value of the "-smp" maxcpus option.
 | 
			
		||||
     *
 | 
			
		||||
     * For small variations of the kind caused by different iasl versions,
 | 
			
		||||
     * the 4k rounding usually leaves slack.  However, there could be still
 | 
			
		||||
     * one or two values that break.  For QEMU 1.7 and QEMU 2.0 the
 | 
			
		||||
     * slack is only ~10 bytes before one "-smp maxcpus" value breaks!
 | 
			
		||||
     *
 | 
			
		||||
     * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on
 | 
			
		||||
     * QEMU 1.7 it is 6414.  For RHEL/CentOS 7.0 it is 6418.
 | 
			
		||||
     */
 | 
			
		||||
    legacy_acpi_table_size = 6652;
 | 
			
		||||
    smbios_legacy_mode = true;
 | 
			
		||||
    has_reserved_memory = false;
 | 
			
		||||
    pc_set_legacy_acpi_data_size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_1_7(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_2_0(machine);
 | 
			
		||||
    smbios_defaults = false;
 | 
			
		||||
    gigabyte_align = false;
 | 
			
		||||
    option_rom_has_mr = true;
 | 
			
		||||
    legacy_acpi_table_size = 6414;
 | 
			
		||||
    x86_cpu_change_kvm_default("x2apic", NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_1_6(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_1_7(machine);
 | 
			
		||||
    rom_file_has_mr = false;
 | 
			
		||||
    has_acpi_build = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_1_5(MachineState *machine)
 | 
			
		||||
@@ -398,19 +356,10 @@ static void pc_compat_1_2(MachineState *machine)
 | 
			
		||||
static void pc_compat_0_13(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_1_2(machine);
 | 
			
		||||
    kvmclock_enabled = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_init_isa(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pci_enabled = false;
 | 
			
		||||
    has_acpi_build = false;
 | 
			
		||||
    smbios_defaults = false;
 | 
			
		||||
    gigabyte_align = false;
 | 
			
		||||
    smbios_legacy_mode = true;
 | 
			
		||||
    has_reserved_memory = false;
 | 
			
		||||
    option_rom_has_mr = true;
 | 
			
		||||
    rom_file_has_mr = false;
 | 
			
		||||
    if (!machine->cpu_model) {
 | 
			
		||||
        machine->cpu_model = "486";
 | 
			
		||||
    }
 | 
			
		||||
@@ -469,13 +418,25 @@ static void pc_i440fx_machine_options(MachineClass *m)
 | 
			
		||||
    m->default_display = "std";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_i440fx_2_5_machine_options(MachineClass *m)
 | 
			
		||||
static void pc_i440fx_2_6_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    pc_i440fx_machine_options(m);
 | 
			
		||||
    m->alias = "pc";
 | 
			
		||||
    m->is_default = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL,
 | 
			
		||||
                      pc_i440fx_2_6_machine_options);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void pc_i440fx_2_5_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    pc_i440fx_2_6_machine_options(m);
 | 
			
		||||
    m->alias = NULL;
 | 
			
		||||
    m->is_default = 0;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL,
 | 
			
		||||
                      pc_i440fx_2_5_machine_options);
 | 
			
		||||
 | 
			
		||||
@@ -485,8 +446,6 @@ static void pc_i440fx_2_4_machine_options(MachineClass *m)
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_i440fx_2_5_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.4.0";
 | 
			
		||||
    m->alias = NULL;
 | 
			
		||||
    m->is_default = 0;
 | 
			
		||||
    pcmc->broken_reserved_end = true;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
 | 
			
		||||
}
 | 
			
		||||
@@ -499,8 +458,6 @@ static void pc_i440fx_2_3_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    pc_i440fx_2_4_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.3.0";
 | 
			
		||||
    m->alias = NULL;
 | 
			
		||||
    m->is_default = 0;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -510,9 +467,11 @@ DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3,
 | 
			
		||||
 | 
			
		||||
static void pc_i440fx_2_2_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_i440fx_2_3_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.2.0";
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
 | 
			
		||||
    pcmc->rsdp_in_ram = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
 | 
			
		||||
@@ -521,10 +480,13 @@ DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2,
 | 
			
		||||
 | 
			
		||||
static void pc_i440fx_2_1_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_i440fx_2_2_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.1.0";
 | 
			
		||||
    m->default_display = NULL;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
 | 
			
		||||
    pcmc->smbios_uuid_encoded = false;
 | 
			
		||||
    pcmc->enforce_aligned_dimm = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
 | 
			
		||||
@@ -534,9 +496,30 @@ DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1,
 | 
			
		||||
 | 
			
		||||
static void pc_i440fx_2_0_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_i440fx_2_1_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.0.0";
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
 | 
			
		||||
    pcmc->smbios_legacy_mode = true;
 | 
			
		||||
    pcmc->has_reserved_memory = false;
 | 
			
		||||
    /* This value depends on the actual DSDT and SSDT compiled into
 | 
			
		||||
     * the source QEMU; unfortunately it depends on the binary and
 | 
			
		||||
     * not on the machine type, so we cannot make pc-i440fx-1.7 work on
 | 
			
		||||
     * both QEMU 1.7 and QEMU 2.0.
 | 
			
		||||
     *
 | 
			
		||||
     * Large variations cause migration to fail for more than one
 | 
			
		||||
     * consecutive value of the "-smp" maxcpus option.
 | 
			
		||||
     *
 | 
			
		||||
     * For small variations of the kind caused by different iasl versions,
 | 
			
		||||
     * the 4k rounding usually leaves slack.  However, there could be still
 | 
			
		||||
     * one or two values that break.  For QEMU 1.7 and QEMU 2.0 the
 | 
			
		||||
     * slack is only ~10 bytes before one "-smp maxcpus" value breaks!
 | 
			
		||||
     *
 | 
			
		||||
     * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on
 | 
			
		||||
     * QEMU 1.7 it is 6414.  For RHEL/CentOS 7.0 it is 6418.
 | 
			
		||||
     */
 | 
			
		||||
    pcmc->legacy_acpi_table_size = 6652;
 | 
			
		||||
    pcmc->acpi_data_size = 0x10000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
 | 
			
		||||
@@ -545,10 +528,15 @@ DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0,
 | 
			
		||||
 | 
			
		||||
static void pc_i440fx_1_7_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_i440fx_2_0_machine_options(m);
 | 
			
		||||
    m->hw_version = "1.7.0";
 | 
			
		||||
    m->default_machine_opts = NULL;
 | 
			
		||||
    m->option_rom_has_mr = true;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
 | 
			
		||||
    pcmc->smbios_defaults = false;
 | 
			
		||||
    pcmc->gigabyte_align = false;
 | 
			
		||||
    pcmc->legacy_acpi_table_size = 6414;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
 | 
			
		||||
@@ -557,9 +545,12 @@ DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7,
 | 
			
		||||
 | 
			
		||||
static void pc_i440fx_1_6_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_i440fx_1_7_machine_options(m);
 | 
			
		||||
    m->hw_version = "1.6.0";
 | 
			
		||||
    m->rom_file_has_mr = false;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
 | 
			
		||||
    pcmc->has_acpi_build = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6,
 | 
			
		||||
@@ -813,9 +804,11 @@ DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2,
 | 
			
		||||
 | 
			
		||||
static void pc_i440fx_0_13_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_i440fx_0_14_machine_options(m);
 | 
			
		||||
    m->hw_version = "0.13";
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_0_13);
 | 
			
		||||
    pcmc->kvmclock_enabled = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13,
 | 
			
		||||
@@ -1037,8 +1030,17 @@ void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id)
 | 
			
		||||
 | 
			
		||||
static void isapc_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    m->desc = "ISA-only PC";
 | 
			
		||||
    m->max_cpus = 1;
 | 
			
		||||
    m->option_rom_has_mr = true;
 | 
			
		||||
    m->rom_file_has_mr = false;
 | 
			
		||||
    pcmc->pci_enabled = false;
 | 
			
		||||
    pcmc->has_acpi_build = false;
 | 
			
		||||
    pcmc->smbios_defaults = false;
 | 
			
		||||
    pcmc->gigabyte_align = false;
 | 
			
		||||
    pcmc->smbios_legacy_mode = true;
 | 
			
		||||
    pcmc->has_reserved_memory = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa,
 | 
			
		||||
 
 | 
			
		||||
@@ -49,22 +49,11 @@
 | 
			
		||||
/* ICH9 AHCI has 6 ports */
 | 
			
		||||
#define MAX_SATA_PORTS     6
 | 
			
		||||
 | 
			
		||||
static bool has_acpi_build = true;
 | 
			
		||||
static bool rsdp_in_ram = true;
 | 
			
		||||
static bool smbios_defaults = true;
 | 
			
		||||
static bool smbios_legacy_mode;
 | 
			
		||||
static bool smbios_uuid_encoded = true;
 | 
			
		||||
/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to
 | 
			
		||||
 * host addresses aligned at 1Gbyte boundaries.  This way we can use 1GByte
 | 
			
		||||
 * pages in the host.
 | 
			
		||||
 */
 | 
			
		||||
static bool gigabyte_align = true;
 | 
			
		||||
static bool has_reserved_memory = true;
 | 
			
		||||
 | 
			
		||||
/* PC hardware initialisation */
 | 
			
		||||
static void pc_q35_init(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineState *pcms = PC_MACHINE(machine);
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
 | 
			
		||||
    Q35PCIHost *q35_host;
 | 
			
		||||
    PCIHostState *phb;
 | 
			
		||||
    PCIBus *host_bus;
 | 
			
		||||
@@ -76,7 +65,6 @@ static void pc_q35_init(MachineState *machine)
 | 
			
		||||
    MemoryRegion *ram_memory;
 | 
			
		||||
    GSIState *gsi_state;
 | 
			
		||||
    ISABus *isa_bus;
 | 
			
		||||
    int pci_enabled = 1;
 | 
			
		||||
    qemu_irq *gsi;
 | 
			
		||||
    qemu_irq *i8259;
 | 
			
		||||
    int i;
 | 
			
		||||
@@ -97,7 +85,7 @@ static void pc_q35_init(MachineState *machine)
 | 
			
		||||
     * breaking migration.
 | 
			
		||||
     */
 | 
			
		||||
    if (machine->ram_size >= 0xb0000000) {
 | 
			
		||||
        lowmem = gigabyte_align ? 0x80000000 : 0xb0000000;
 | 
			
		||||
        lowmem = pcmc->gigabyte_align ? 0x80000000 : 0xb0000000;
 | 
			
		||||
    } else {
 | 
			
		||||
        lowmem = 0xb0000000;
 | 
			
		||||
    }
 | 
			
		||||
@@ -129,12 +117,15 @@ static void pc_q35_init(MachineState *machine)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pc_cpus_init(pcms);
 | 
			
		||||
    pc_acpi_init("q35-acpi-dsdt.aml");
 | 
			
		||||
    if (!pcmc->has_acpi_build) {
 | 
			
		||||
        /* only machine types 1.7 & older need this */
 | 
			
		||||
        pc_acpi_init("q35-acpi-dsdt.aml");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    kvmclock_create();
 | 
			
		||||
 | 
			
		||||
    /* pci enabled */
 | 
			
		||||
    if (pci_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled) {
 | 
			
		||||
        pci_memory = g_new(MemoryRegion, 1);
 | 
			
		||||
        memory_region_init(pci_memory, NULL, "pci", UINT64_MAX);
 | 
			
		||||
        rom_memory = pci_memory;
 | 
			
		||||
@@ -145,19 +136,20 @@ static void pc_q35_init(MachineState *machine)
 | 
			
		||||
 | 
			
		||||
    guest_info = pc_guest_info_init(pcms);
 | 
			
		||||
    guest_info->isapc_ram_fw = false;
 | 
			
		||||
    guest_info->has_acpi_build = has_acpi_build;
 | 
			
		||||
    guest_info->has_reserved_memory = has_reserved_memory;
 | 
			
		||||
    guest_info->rsdp_in_ram = rsdp_in_ram;
 | 
			
		||||
    guest_info->has_acpi_build = pcmc->has_acpi_build;
 | 
			
		||||
    guest_info->has_reserved_memory = pcmc->has_reserved_memory;
 | 
			
		||||
    guest_info->rsdp_in_ram = pcmc->rsdp_in_ram;
 | 
			
		||||
 | 
			
		||||
    /* Migration was not supported in 2.0 for Q35, so do not bother
 | 
			
		||||
     * with this hack (see hw/i386/acpi-build.c).
 | 
			
		||||
     */
 | 
			
		||||
    guest_info->legacy_acpi_table_size = 0;
 | 
			
		||||
 | 
			
		||||
    if (smbios_defaults) {
 | 
			
		||||
    if (pcmc->smbios_defaults) {
 | 
			
		||||
        /* These values are guest ABI, do not change */
 | 
			
		||||
        smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)",
 | 
			
		||||
                            mc->name, smbios_legacy_mode, smbios_uuid_encoded,
 | 
			
		||||
                            mc->name, pcmc->smbios_legacy_mode,
 | 
			
		||||
                            pcmc->smbios_uuid_encoded,
 | 
			
		||||
                            SMBIOS_ENTRY_POINT_21);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -170,7 +162,7 @@ static void pc_q35_init(MachineState *machine)
 | 
			
		||||
    /* irq lines */
 | 
			
		||||
    gsi_state = g_malloc0(sizeof(*gsi_state));
 | 
			
		||||
    if (kvm_irqchip_in_kernel()) {
 | 
			
		||||
        kvm_pc_setup_irq_routing(pci_enabled);
 | 
			
		||||
        kvm_pc_setup_irq_routing(pcmc->pci_enabled);
 | 
			
		||||
        gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
 | 
			
		||||
                                 GSI_NUM_PINS);
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -187,11 +179,11 @@ static void pc_q35_init(MachineState *machine)
 | 
			
		||||
    q35_host->mch.address_space_io = get_system_io();
 | 
			
		||||
    q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size;
 | 
			
		||||
    q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size;
 | 
			
		||||
    q35_host->mch.guest_info = guest_info;
 | 
			
		||||
    /* pci */
 | 
			
		||||
    qdev_init_nofail(DEVICE(q35_host));
 | 
			
		||||
    phb = PCI_HOST_BRIDGE(q35_host);
 | 
			
		||||
    host_bus = phb->bus;
 | 
			
		||||
    pcms->bus = phb->bus;
 | 
			
		||||
    /* create ISA bus */
 | 
			
		||||
    lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV,
 | 
			
		||||
                                          ICH9_LPC_FUNC), true,
 | 
			
		||||
@@ -227,13 +219,13 @@ static void pc_q35_init(MachineState *machine)
 | 
			
		||||
    for (i = 0; i < ISA_NUM_IRQS; i++) {
 | 
			
		||||
        gsi_state->i8259_irq[i] = i8259[i];
 | 
			
		||||
    }
 | 
			
		||||
    if (pci_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled) {
 | 
			
		||||
        ioapic_init_gsi(gsi_state, "q35");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pc_register_ferr_irq(gsi[13]);
 | 
			
		||||
 | 
			
		||||
    assert(pcms->vmport != ON_OFF_AUTO_MAX);
 | 
			
		||||
    assert(pcms->vmport != ON_OFF_AUTO__MAX);
 | 
			
		||||
    if (pcms->vmport == ON_OFF_AUTO_AUTO) {
 | 
			
		||||
        pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON;
 | 
			
		||||
    }
 | 
			
		||||
@@ -272,7 +264,7 @@ static void pc_q35_init(MachineState *machine)
 | 
			
		||||
    /* the rest devices to which pci devfn is automatically assigned */
 | 
			
		||||
    pc_vga_init(isa_bus, host_bus);
 | 
			
		||||
    pc_nic_init(isa_bus, host_bus);
 | 
			
		||||
    if (pci_enabled) {
 | 
			
		||||
    if (pcmc->pci_enabled) {
 | 
			
		||||
        pc_pci_device_init(host_bus);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -298,42 +290,29 @@ static void pc_compat_2_3(MachineState *machine)
 | 
			
		||||
static void pc_compat_2_2(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_2_3(machine);
 | 
			
		||||
    rsdp_in_ram = false;
 | 
			
		||||
    machine->suppress_vmdesc = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_2_1(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineState *pcms = PC_MACHINE(machine);
 | 
			
		||||
 | 
			
		||||
    pc_compat_2_2(machine);
 | 
			
		||||
    pcms->enforce_aligned_dimm = false;
 | 
			
		||||
    smbios_uuid_encoded = false;
 | 
			
		||||
    x86_cpu_change_kvm_default("svm", NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_2_0(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_2_1(machine);
 | 
			
		||||
    smbios_legacy_mode = true;
 | 
			
		||||
    has_reserved_memory = false;
 | 
			
		||||
    pc_set_legacy_acpi_data_size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_1_7(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_2_0(machine);
 | 
			
		||||
    smbios_defaults = false;
 | 
			
		||||
    gigabyte_align = false;
 | 
			
		||||
    option_rom_has_mr = true;
 | 
			
		||||
    x86_cpu_change_kvm_default("x2apic", NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_1_6(MachineState *machine)
 | 
			
		||||
{
 | 
			
		||||
    pc_compat_1_7(machine);
 | 
			
		||||
    rom_file_has_mr = false;
 | 
			
		||||
    has_acpi_build = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_compat_1_5(MachineState *machine)
 | 
			
		||||
@@ -370,12 +349,22 @@ static void pc_q35_machine_options(MachineClass *m)
 | 
			
		||||
    m->no_tco = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pc_q35_2_5_machine_options(MachineClass *m)
 | 
			
		||||
static void pc_q35_2_6_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    pc_q35_machine_options(m);
 | 
			
		||||
    m->alias = "q35";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL,
 | 
			
		||||
                   pc_q35_2_6_machine_options);
 | 
			
		||||
 | 
			
		||||
static void pc_q35_2_5_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    pc_q35_2_6_machine_options(m);
 | 
			
		||||
    m->alias = NULL;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL,
 | 
			
		||||
                   pc_q35_2_5_machine_options);
 | 
			
		||||
 | 
			
		||||
@@ -384,7 +373,6 @@ static void pc_q35_2_4_machine_options(MachineClass *m)
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_q35_2_5_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.4.0";
 | 
			
		||||
    m->alias = NULL;
 | 
			
		||||
    pcmc->broken_reserved_end = true;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_4);
 | 
			
		||||
}
 | 
			
		||||
@@ -399,7 +387,6 @@ static void pc_q35_2_3_machine_options(MachineClass *m)
 | 
			
		||||
    m->hw_version = "2.3.0";
 | 
			
		||||
    m->no_floppy = 0;
 | 
			
		||||
    m->no_tco = 1;
 | 
			
		||||
    m->alias = NULL;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -409,9 +396,11 @@ DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3,
 | 
			
		||||
 | 
			
		||||
static void pc_q35_2_2_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_q35_2_3_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.2.0";
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_2);
 | 
			
		||||
    pcmc->rsdp_in_ram = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
 | 
			
		||||
@@ -420,10 +409,13 @@ DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2,
 | 
			
		||||
 | 
			
		||||
static void pc_q35_2_1_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_q35_2_2_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.1.0";
 | 
			
		||||
    m->default_display = NULL;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_1);
 | 
			
		||||
    pcmc->smbios_uuid_encoded = false;
 | 
			
		||||
    pcmc->enforce_aligned_dimm = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
 | 
			
		||||
@@ -432,9 +424,13 @@ DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1,
 | 
			
		||||
 | 
			
		||||
static void pc_q35_2_0_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_q35_2_1_machine_options(m);
 | 
			
		||||
    m->hw_version = "2.0.0";
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_2_0);
 | 
			
		||||
    pcmc->has_reserved_memory = false;
 | 
			
		||||
    pcmc->smbios_legacy_mode = true;
 | 
			
		||||
    pcmc->acpi_data_size = 0x10000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
 | 
			
		||||
@@ -443,10 +439,14 @@ DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0,
 | 
			
		||||
 | 
			
		||||
static void pc_q35_1_7_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_q35_2_0_machine_options(m);
 | 
			
		||||
    m->hw_version = "1.7.0";
 | 
			
		||||
    m->default_machine_opts = NULL;
 | 
			
		||||
    m->option_rom_has_mr = true;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_1_7);
 | 
			
		||||
    pcmc->smbios_defaults = false;
 | 
			
		||||
    pcmc->gigabyte_align = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
 | 
			
		||||
@@ -455,9 +455,12 @@ DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7,
 | 
			
		||||
 | 
			
		||||
static void pc_q35_1_6_machine_options(MachineClass *m)
 | 
			
		||||
{
 | 
			
		||||
    PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
 | 
			
		||||
    pc_q35_machine_options(m);
 | 
			
		||||
    m->hw_version = "1.6.0";
 | 
			
		||||
    m->rom_file_has_mr = false;
 | 
			
		||||
    SET_MACHINE_COMPAT(m, PC_COMPAT_1_6);
 | 
			
		||||
    pcmc->has_acpi_build = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_Q35_MACHINE(v1_6, "pc-q35-1.6", pc_compat_1_6,
 | 
			
		||||
 
 | 
			
		||||
@@ -108,7 +108,7 @@ void hid_set_next_idle(HIDState *hs)
 | 
			
		||||
static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
 | 
			
		||||
                              InputEvent *evt)
 | 
			
		||||
{
 | 
			
		||||
    static const int bmap[INPUT_BUTTON_MAX] = {
 | 
			
		||||
    static const int bmap[INPUT_BUTTON__MAX] = {
 | 
			
		||||
        [INPUT_BUTTON_LEFT]   = 0x01,
 | 
			
		||||
        [INPUT_BUTTON_RIGHT]  = 0x02,
 | 
			
		||||
        [INPUT_BUTTON_MIDDLE] = 0x04,
 | 
			
		||||
@@ -139,9 +139,9 @@ static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
 | 
			
		||||
    case INPUT_EVENT_KIND_BTN:
 | 
			
		||||
        if (evt->u.btn->down) {
 | 
			
		||||
            e->buttons_state |= bmap[evt->u.btn->button];
 | 
			
		||||
            if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
 | 
			
		||||
            if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
 | 
			
		||||
                e->dz--;
 | 
			
		||||
            } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
 | 
			
		||||
            } else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
 | 
			
		||||
                e->dz++;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -382,7 +382,7 @@ static void ps2_mouse_send_packet(PS2MouseState *s)
 | 
			
		||||
static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
 | 
			
		||||
                            InputEvent *evt)
 | 
			
		||||
{
 | 
			
		||||
    static const int bmap[INPUT_BUTTON_MAX] = {
 | 
			
		||||
    static const int bmap[INPUT_BUTTON__MAX] = {
 | 
			
		||||
        [INPUT_BUTTON_LEFT]   = MOUSE_EVENT_LBUTTON,
 | 
			
		||||
        [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
 | 
			
		||||
        [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
 | 
			
		||||
@@ -405,9 +405,9 @@ static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
 | 
			
		||||
    case INPUT_EVENT_KIND_BTN:
 | 
			
		||||
        if (evt->u.btn->down) {
 | 
			
		||||
            s->mouse_buttons |= bmap[evt->u.btn->button];
 | 
			
		||||
            if (evt->u.btn->button == INPUT_BUTTON_WHEEL_UP) {
 | 
			
		||||
            if (evt->u.btn->button == INPUT_BUTTON_WHEELUP) {
 | 
			
		||||
                s->mouse_dz--;
 | 
			
		||||
            } else if (evt->u.btn->button == INPUT_BUTTON_WHEEL_DOWN) {
 | 
			
		||||
            } else if (evt->u.btn->button == INPUT_BUTTON_WHEELDOWN) {
 | 
			
		||||
                s->mouse_dz++;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
 | 
			
		||||
/* ----------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
 | 
			
		||||
static const unsigned int keymap_qcode[Q_KEY_CODE__MAX] = {
 | 
			
		||||
    [Q_KEY_CODE_ESC]                 = KEY_ESC,
 | 
			
		||||
    [Q_KEY_CODE_1]                   = KEY_1,
 | 
			
		||||
    [Q_KEY_CODE_2]                   = KEY_2,
 | 
			
		||||
@@ -138,20 +138,20 @@ static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
 | 
			
		||||
    [Q_KEY_CODE_MENU]                = KEY_MENU,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const unsigned int keymap_button[INPUT_BUTTON_MAX] = {
 | 
			
		||||
static const unsigned int keymap_button[INPUT_BUTTON__MAX] = {
 | 
			
		||||
    [INPUT_BUTTON_LEFT]              = BTN_LEFT,
 | 
			
		||||
    [INPUT_BUTTON_RIGHT]             = BTN_RIGHT,
 | 
			
		||||
    [INPUT_BUTTON_MIDDLE]            = BTN_MIDDLE,
 | 
			
		||||
    [INPUT_BUTTON_WHEEL_UP]          = BTN_GEAR_UP,
 | 
			
		||||
    [INPUT_BUTTON_WHEEL_DOWN]        = BTN_GEAR_DOWN,
 | 
			
		||||
    [INPUT_BUTTON_WHEELUP]           = BTN_GEAR_UP,
 | 
			
		||||
    [INPUT_BUTTON_WHEELDOWN]         = BTN_GEAR_DOWN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const unsigned int axismap_rel[INPUT_AXIS_MAX] = {
 | 
			
		||||
static const unsigned int axismap_rel[INPUT_AXIS__MAX] = {
 | 
			
		||||
    [INPUT_AXIS_X]                   = REL_X,
 | 
			
		||||
    [INPUT_AXIS_Y]                   = REL_Y,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const unsigned int axismap_abs[INPUT_AXIS_MAX] = {
 | 
			
		||||
static const unsigned int axismap_abs[INPUT_AXIS__MAX] = {
 | 
			
		||||
    [INPUT_AXIS_X]                   = ABS_X,
 | 
			
		||||
    [INPUT_AXIS_Y]                   = ABS_Y,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,8 @@
 | 
			
		||||
#include "hw/i386/pc.h"
 | 
			
		||||
#include "hw/i386/ioapic.h"
 | 
			
		||||
#include "hw/i386/ioapic_internal.h"
 | 
			
		||||
#include "include/hw/pci/msi.h"
 | 
			
		||||
#include "sysemu/kvm.h"
 | 
			
		||||
 | 
			
		||||
//#define DEBUG_IOAPIC
 | 
			
		||||
 | 
			
		||||
@@ -35,6 +37,10 @@
 | 
			
		||||
#define DPRINTF(fmt, ...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define APIC_DELIVERY_MODE_SHIFT 8
 | 
			
		||||
#define APIC_POLARITY_SHIFT 14
 | 
			
		||||
#define APIC_TRIG_MODE_SHIFT 15
 | 
			
		||||
 | 
			
		||||
static IOAPICCommonState *ioapics[MAX_IOAPICS];
 | 
			
		||||
 | 
			
		||||
/* global variable from ioapic_common.c */
 | 
			
		||||
@@ -54,6 +60,8 @@ static void ioapic_service(IOAPICCommonState *s)
 | 
			
		||||
    for (i = 0; i < IOAPIC_NUM_PINS; i++) {
 | 
			
		||||
        mask = 1 << i;
 | 
			
		||||
        if (s->irr & mask) {
 | 
			
		||||
            int coalesce = 0;
 | 
			
		||||
 | 
			
		||||
            entry = s->ioredtbl[i];
 | 
			
		||||
            if (!(entry & IOAPIC_LVT_MASKED)) {
 | 
			
		||||
                trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
 | 
			
		||||
@@ -64,6 +72,7 @@ static void ioapic_service(IOAPICCommonState *s)
 | 
			
		||||
                if (trig_mode == IOAPIC_TRIGGER_EDGE) {
 | 
			
		||||
                    s->irr &= ~mask;
 | 
			
		||||
                } else {
 | 
			
		||||
                    coalesce = s->ioredtbl[i] & IOAPIC_LVT_REMOTE_IRR;
 | 
			
		||||
                    s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR;
 | 
			
		||||
                }
 | 
			
		||||
                if (delivery_mode == IOAPIC_DM_EXTINT) {
 | 
			
		||||
@@ -71,8 +80,23 @@ static void ioapic_service(IOAPICCommonState *s)
 | 
			
		||||
                } else {
 | 
			
		||||
                    vector = entry & IOAPIC_VECTOR_MASK;
 | 
			
		||||
                }
 | 
			
		||||
                apic_deliver_irq(dest, dest_mode, delivery_mode,
 | 
			
		||||
                                 vector, trig_mode);
 | 
			
		||||
#ifdef CONFIG_KVM
 | 
			
		||||
                if (kvm_irqchip_is_split()) {
 | 
			
		||||
                    if (trig_mode == IOAPIC_TRIGGER_EDGE) {
 | 
			
		||||
                        kvm_set_irq(kvm_state, i, 1);
 | 
			
		||||
                        kvm_set_irq(kvm_state, i, 0);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if (!coalesce) {
 | 
			
		||||
                            kvm_set_irq(kvm_state, i, 1);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
#else
 | 
			
		||||
                (void)coalesce;
 | 
			
		||||
#endif
 | 
			
		||||
                apic_deliver_irq(dest, dest_mode, delivery_mode, vector,
 | 
			
		||||
                                 trig_mode);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -116,6 +140,44 @@ static void ioapic_set_irq(void *opaque, int vector, int level)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ioapic_update_kvm_routes(IOAPICCommonState *s)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_KVM
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    if (kvm_irqchip_is_split()) {
 | 
			
		||||
        for (i = 0; i < IOAPIC_NUM_PINS; i++) {
 | 
			
		||||
            uint64_t entry = s->ioredtbl[i];
 | 
			
		||||
            uint8_t trig_mode;
 | 
			
		||||
            uint8_t delivery_mode;
 | 
			
		||||
            uint8_t dest;
 | 
			
		||||
            uint8_t dest_mode;
 | 
			
		||||
            uint64_t pin_polarity;
 | 
			
		||||
            MSIMessage msg;
 | 
			
		||||
 | 
			
		||||
            trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1);
 | 
			
		||||
            dest = entry >> IOAPIC_LVT_DEST_SHIFT;
 | 
			
		||||
            dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1;
 | 
			
		||||
            pin_polarity = (entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1;
 | 
			
		||||
            delivery_mode =
 | 
			
		||||
                (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK;
 | 
			
		||||
 | 
			
		||||
            msg.address = APIC_DEFAULT_ADDRESS;
 | 
			
		||||
            msg.address |= dest_mode << 2;
 | 
			
		||||
            msg.address |= dest << 12;
 | 
			
		||||
 | 
			
		||||
            msg.data = entry & IOAPIC_VECTOR_MASK;
 | 
			
		||||
            msg.data |= delivery_mode << APIC_DELIVERY_MODE_SHIFT;
 | 
			
		||||
            msg.data |= pin_polarity << APIC_POLARITY_SHIFT;
 | 
			
		||||
            msg.data |= trig_mode << APIC_TRIG_MODE_SHIFT;
 | 
			
		||||
 | 
			
		||||
            kvm_irqchip_update_msi_route(kvm_state, i, msg, NULL);
 | 
			
		||||
        }
 | 
			
		||||
        kvm_irqchip_commit_routes(kvm_state);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ioapic_eoi_broadcast(int vector)
 | 
			
		||||
{
 | 
			
		||||
    IOAPICCommonState *s;
 | 
			
		||||
@@ -229,6 +291,8 @@ ioapic_mem_write(void *opaque, hwaddr addr, uint64_t val,
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ioapic_update_kvm_routes(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const MemoryRegionOps ioapic_io_ops = {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								hw/ipmi/Makefile.objs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								hw/ipmi/Makefile.objs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
common-obj-$(CONFIG_IPMI) += ipmi.o
 | 
			
		||||
common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o
 | 
			
		||||
common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o
 | 
			
		||||
common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o
 | 
			
		||||
common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o
 | 
			
		||||
							
								
								
									
										152
									
								
								hw/ipmi/ipmi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								hw/ipmi/ipmi.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU IPMI emulation
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/hw.h"
 | 
			
		||||
#include "hw/ipmi/ipmi.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
#include "qmp-commands.h"
 | 
			
		||||
#include "qom/object_interfaces.h"
 | 
			
		||||
#include "qapi/visitor.h"
 | 
			
		||||
 | 
			
		||||
static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly)
 | 
			
		||||
{
 | 
			
		||||
    switch (op) {
 | 
			
		||||
    case IPMI_RESET_CHASSIS:
 | 
			
		||||
        if (checkonly) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        qemu_system_reset_request();
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case IPMI_POWEROFF_CHASSIS:
 | 
			
		||||
        if (checkonly) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        qemu_system_powerdown_request();
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case IPMI_SEND_NMI:
 | 
			
		||||
        if (checkonly) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        qemu_mutex_lock_iothread();
 | 
			
		||||
        qmp_inject_nmi(NULL);
 | 
			
		||||
        qemu_mutex_unlock_iothread();
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    case IPMI_POWERCYCLE_CHASSIS:
 | 
			
		||||
    case IPMI_PULSE_DIAG_IRQ:
 | 
			
		||||
    case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP:
 | 
			
		||||
    case IPMI_POWERON_CHASSIS:
 | 
			
		||||
    default:
 | 
			
		||||
        return IPMI_CC_COMMAND_NOT_SUPPORTED;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_interface_class_init(ObjectClass *class, void *data)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class);
 | 
			
		||||
 | 
			
		||||
    ik->do_hw_op = ipmi_do_hw_op;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TypeInfo ipmi_interface_type_info = {
 | 
			
		||||
    .name = TYPE_IPMI_INTERFACE,
 | 
			
		||||
    .parent = TYPE_INTERFACE,
 | 
			
		||||
    .class_size = sizeof(IPMIInterfaceClass),
 | 
			
		||||
    .class_init = ipmi_interface_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void isa_ipmi_bmc_check(Object *obj, const char *name,
 | 
			
		||||
                               Object *val, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmc *bmc = IPMI_BMC(val);
 | 
			
		||||
 | 
			
		||||
    if (bmc->intf)
 | 
			
		||||
        error_setg(errp, "BMC object is already in use");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ipmi_bmc_find_and_link(Object *obj, Object **bmc)
 | 
			
		||||
{
 | 
			
		||||
    object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc,
 | 
			
		||||
                             isa_ipmi_bmc_check,
 | 
			
		||||
                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
 | 
			
		||||
                             &error_abort);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Property ipmi_bmc_properties[] = {
 | 
			
		||||
    DEFINE_PROP_UINT8("slave_addr",  IPMIBmc, slave_addr, 0x20),
 | 
			
		||||
    DEFINE_PROP_END_OF_LIST(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void bmc_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(oc);
 | 
			
		||||
 | 
			
		||||
    dc->props = ipmi_bmc_properties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TypeInfo ipmi_bmc_type_info = {
 | 
			
		||||
    .name = TYPE_IPMI_BMC,
 | 
			
		||||
    .parent = TYPE_DEVICE,
 | 
			
		||||
    .instance_size = sizeof(IPMIBmc),
 | 
			
		||||
    .abstract = true,
 | 
			
		||||
    .class_size = sizeof(IPMIBmcClass),
 | 
			
		||||
    .class_init = bmc_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ipmi_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&ipmi_interface_type_info);
 | 
			
		||||
    type_register_static(&ipmi_bmc_type_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(ipmi_register_types)
 | 
			
		||||
 | 
			
		||||
static IPMIFwInfo *ipmi_fw_info;
 | 
			
		||||
static unsigned int ipmi_fw_info_len;
 | 
			
		||||
 | 
			
		||||
static uint32_t current_uuid = 1;
 | 
			
		||||
 | 
			
		||||
void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    info->uuid = current_uuid++;
 | 
			
		||||
    ipmi_fw_info = g_realloc(ipmi_fw_info,
 | 
			
		||||
                             sizeof(*ipmi_fw_info) * (ipmi_fw_info_len + 1));
 | 
			
		||||
    ipmi_fw_info[ipmi_fw_info_len] = *info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IPMIFwInfo *ipmi_first_fwinfo(void)
 | 
			
		||||
{
 | 
			
		||||
    return ipmi_fw_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current)
 | 
			
		||||
{
 | 
			
		||||
    current++;
 | 
			
		||||
    if (current >= &ipmi_fw_info[ipmi_fw_info_len]) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return current;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										518
									
								
								hw/ipmi/ipmi_bmc_extern.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										518
									
								
								hw/ipmi/ipmi_bmc_extern.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,518 @@
 | 
			
		||||
/*
 | 
			
		||||
 * IPMI BMC external connection
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This is designed to connect with OpenIPMI's lanserv serial interface
 | 
			
		||||
 * using the "VM" connection type.  See that for details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "qemu/timer.h"
 | 
			
		||||
#include "sysemu/char.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
#include "hw/ipmi/ipmi.h"
 | 
			
		||||
 | 
			
		||||
#define VM_MSG_CHAR        0xA0 /* Marks end of message */
 | 
			
		||||
#define VM_CMD_CHAR        0xA1 /* Marks end of a command */
 | 
			
		||||
#define VM_ESCAPE_CHAR     0xAA /* Set bit 4 from the next byte to 0 */
 | 
			
		||||
 | 
			
		||||
#define VM_PROTOCOL_VERSION        1
 | 
			
		||||
#define VM_CMD_VERSION             0xff /* A version number byte follows */
 | 
			
		||||
#define VM_CMD_NOATTN              0x00
 | 
			
		||||
#define VM_CMD_ATTN                0x01
 | 
			
		||||
#define VM_CMD_ATTN_IRQ            0x02
 | 
			
		||||
#define VM_CMD_POWEROFF            0x03
 | 
			
		||||
#define VM_CMD_RESET               0x04
 | 
			
		||||
#define VM_CMD_ENABLE_IRQ          0x05 /* Enable/disable the messaging irq */
 | 
			
		||||
#define VM_CMD_DISABLE_IRQ         0x06
 | 
			
		||||
#define VM_CMD_SEND_NMI            0x07
 | 
			
		||||
#define VM_CMD_CAPABILITIES        0x08
 | 
			
		||||
#define   VM_CAPABILITIES_POWER    0x01
 | 
			
		||||
#define   VM_CAPABILITIES_RESET    0x02
 | 
			
		||||
#define   VM_CAPABILITIES_IRQ      0x04
 | 
			
		||||
#define   VM_CAPABILITIES_NMI      0x08
 | 
			
		||||
#define   VM_CAPABILITIES_ATTN     0x10
 | 
			
		||||
#define VM_CMD_FORCEOFF            0x09
 | 
			
		||||
 | 
			
		||||
#define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern"
 | 
			
		||||
#define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \
 | 
			
		||||
                                        TYPE_IPMI_BMC_EXTERN)
 | 
			
		||||
typedef struct IPMIBmcExtern {
 | 
			
		||||
    IPMIBmc parent;
 | 
			
		||||
 | 
			
		||||
    CharDriverState *chr;
 | 
			
		||||
 | 
			
		||||
    bool connected;
 | 
			
		||||
 | 
			
		||||
    unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2];
 | 
			
		||||
    unsigned int inpos;
 | 
			
		||||
    bool in_escape;
 | 
			
		||||
    bool in_too_many;
 | 
			
		||||
    bool waiting_rsp;
 | 
			
		||||
    bool sending_cmd;
 | 
			
		||||
 | 
			
		||||
    unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1];
 | 
			
		||||
    unsigned int outpos;
 | 
			
		||||
    unsigned int outlen;
 | 
			
		||||
 | 
			
		||||
    struct QEMUTimer *extern_timer;
 | 
			
		||||
 | 
			
		||||
    /* A reset event is pending to be sent upstream. */
 | 
			
		||||
    bool send_reset;
 | 
			
		||||
} IPMIBmcExtern;
 | 
			
		||||
 | 
			
		||||
static int can_receive(void *opaque);
 | 
			
		||||
static void receive(void *opaque, const uint8_t *buf, int size);
 | 
			
		||||
static void chr_event(void *opaque, int event);
 | 
			
		||||
 | 
			
		||||
static unsigned char
 | 
			
		||||
ipmb_checksum(const unsigned char *data, int size, unsigned char start)
 | 
			
		||||
{
 | 
			
		||||
        unsigned char csum = start;
 | 
			
		||||
 | 
			
		||||
        for (; size > 0; size--, data++) {
 | 
			
		||||
                csum += *data;
 | 
			
		||||
        }
 | 
			
		||||
        return csum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void continue_send(IPMIBmcExtern *ibe)
 | 
			
		||||
{
 | 
			
		||||
    if (ibe->outlen == 0) {
 | 
			
		||||
        goto check_reset;
 | 
			
		||||
    }
 | 
			
		||||
 send:
 | 
			
		||||
    ibe->outpos += qemu_chr_fe_write(ibe->chr, ibe->outbuf + ibe->outpos,
 | 
			
		||||
                                     ibe->outlen - ibe->outpos);
 | 
			
		||||
    if (ibe->outpos < ibe->outlen) {
 | 
			
		||||
        /* Not fully transmitted, try again in a 10ms */
 | 
			
		||||
        timer_mod_ns(ibe->extern_timer,
 | 
			
		||||
                     qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Sent */
 | 
			
		||||
        ibe->outlen = 0;
 | 
			
		||||
        ibe->outpos = 0;
 | 
			
		||||
        if (!ibe->sending_cmd) {
 | 
			
		||||
            ibe->waiting_rsp = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            ibe->sending_cmd = false;
 | 
			
		||||
        }
 | 
			
		||||
    check_reset:
 | 
			
		||||
        if (ibe->connected && ibe->send_reset) {
 | 
			
		||||
            /* Send the reset */
 | 
			
		||||
            ibe->outbuf[0] = VM_CMD_RESET;
 | 
			
		||||
            ibe->outbuf[1] = VM_CMD_CHAR;
 | 
			
		||||
            ibe->outlen = 2;
 | 
			
		||||
            ibe->outpos = 0;
 | 
			
		||||
            ibe->send_reset = false;
 | 
			
		||||
            ibe->sending_cmd = true;
 | 
			
		||||
            goto send;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ibe->waiting_rsp) {
 | 
			
		||||
            /* Make sure we get a response within 4 seconds. */
 | 
			
		||||
            timer_mod_ns(ibe->extern_timer,
 | 
			
		||||
                         qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void extern_timeout(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmcExtern *ibe = opaque;
 | 
			
		||||
    IPMIInterface *s = ibe->parent.intf;
 | 
			
		||||
 | 
			
		||||
    if (ibe->connected) {
 | 
			
		||||
        if (ibe->waiting_rsp && (ibe->outlen == 0)) {
 | 
			
		||||
            IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
 | 
			
		||||
            /* The message response timed out, return an error. */
 | 
			
		||||
            ibe->waiting_rsp = false;
 | 
			
		||||
            ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
 | 
			
		||||
            ibe->inbuf[2] = ibe->outbuf[2];
 | 
			
		||||
            ibe->inbuf[3] = IPMI_CC_TIMEOUT;
 | 
			
		||||
            k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
 | 
			
		||||
        } else {
 | 
			
		||||
            continue_send(ibe);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void addchar(IPMIBmcExtern *ibe, unsigned char ch)
 | 
			
		||||
{
 | 
			
		||||
    switch (ch) {
 | 
			
		||||
    case VM_MSG_CHAR:
 | 
			
		||||
    case VM_CMD_CHAR:
 | 
			
		||||
    case VM_ESCAPE_CHAR:
 | 
			
		||||
        ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR;
 | 
			
		||||
        ibe->outlen++;
 | 
			
		||||
        ch |= 0x10;
 | 
			
		||||
        /* No break */
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        ibe->outbuf[ibe->outlen] = ch;
 | 
			
		||||
        ibe->outlen++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bmc_extern_handle_command(IPMIBmc *b,
 | 
			
		||||
                                       uint8_t *cmd, unsigned int cmd_len,
 | 
			
		||||
                                       unsigned int max_cmd_len,
 | 
			
		||||
                                       uint8_t msg_id)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
 | 
			
		||||
    IPMIInterface *s = ibe->parent.intf;
 | 
			
		||||
    uint8_t err = 0, csum;
 | 
			
		||||
    unsigned int i;
 | 
			
		||||
 | 
			
		||||
    if (ibe->outlen) {
 | 
			
		||||
        /* We already have a command queued.  Shouldn't ever happen. */
 | 
			
		||||
        fprintf(stderr, "IPMI KCS: Got command when not finished with the"
 | 
			
		||||
                " previous commmand\n");
 | 
			
		||||
        abort();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If it's too short or it was truncated, return an error. */
 | 
			
		||||
    if (cmd_len < 2) {
 | 
			
		||||
        err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
 | 
			
		||||
    } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) {
 | 
			
		||||
        err = IPMI_CC_REQUEST_DATA_TRUNCATED;
 | 
			
		||||
    } else if (!ibe->connected) {
 | 
			
		||||
        err = IPMI_CC_BMC_INIT_IN_PROGRESS;
 | 
			
		||||
    }
 | 
			
		||||
    if (err) {
 | 
			
		||||
        IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
 | 
			
		||||
        unsigned char rsp[3];
 | 
			
		||||
        rsp[0] = cmd[0] | 0x04;
 | 
			
		||||
        rsp[1] = cmd[1];
 | 
			
		||||
        rsp[2] = err;
 | 
			
		||||
        ibe->waiting_rsp = false;
 | 
			
		||||
        k->handle_rsp(s, msg_id, rsp, 3);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    addchar(ibe, msg_id);
 | 
			
		||||
    for (i = 0; i < cmd_len; i++) {
 | 
			
		||||
        addchar(ibe, cmd[i]);
 | 
			
		||||
    }
 | 
			
		||||
    csum = ipmb_checksum(&msg_id, 1, 0);
 | 
			
		||||
    addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum));
 | 
			
		||||
 | 
			
		||||
    ibe->outbuf[ibe->outlen] = VM_MSG_CHAR;
 | 
			
		||||
    ibe->outlen++;
 | 
			
		||||
 | 
			
		||||
    /* Start the transmit */
 | 
			
		||||
    continue_send(ibe);
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterface *s = ibe->parent.intf;
 | 
			
		||||
    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
 | 
			
		||||
 | 
			
		||||
    switch (hw_op) {
 | 
			
		||||
    case VM_CMD_VERSION:
 | 
			
		||||
        /* We only support one version at this time. */
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_NOATTN:
 | 
			
		||||
        k->set_atn(s, 0, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_ATTN:
 | 
			
		||||
        k->set_atn(s, 1, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_ATTN_IRQ:
 | 
			
		||||
        k->set_atn(s, 1, 1);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_POWEROFF:
 | 
			
		||||
        k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_RESET:
 | 
			
		||||
        k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_ENABLE_IRQ:
 | 
			
		||||
        k->set_irq_enable(s, 1);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_DISABLE_IRQ:
 | 
			
		||||
        k->set_irq_enable(s, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_SEND_NMI:
 | 
			
		||||
        k->do_hw_op(s, IPMI_SEND_NMI, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case VM_CMD_FORCEOFF:
 | 
			
		||||
        qemu_system_shutdown_request();
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_msg(IPMIBmcExtern *ibe)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->parent.intf);
 | 
			
		||||
 | 
			
		||||
    if (ibe->in_escape) {
 | 
			
		||||
        ipmi_debug("msg escape not ended\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (ibe->inpos < 5) {
 | 
			
		||||
        ipmi_debug("msg too short\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (ibe->in_too_many) {
 | 
			
		||||
        ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED;
 | 
			
		||||
        ibe->inpos = 4;
 | 
			
		||||
    } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) {
 | 
			
		||||
        ipmi_debug("msg checksum failure\n");
 | 
			
		||||
        return;
 | 
			
		||||
    } else {
 | 
			
		||||
        ibe->inpos--; /* Remove checkum */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    timer_del(ibe->extern_timer);
 | 
			
		||||
    ibe->waiting_rsp = false;
 | 
			
		||||
    k->handle_rsp(ibe->parent.intf, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int can_receive(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void receive(void *opaque, const uint8_t *buf, int size)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmcExtern *ibe = opaque;
 | 
			
		||||
    int i;
 | 
			
		||||
    unsigned char hw_op;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < size; i++) {
 | 
			
		||||
        unsigned char ch = buf[i];
 | 
			
		||||
 | 
			
		||||
        switch (ch) {
 | 
			
		||||
        case VM_MSG_CHAR:
 | 
			
		||||
            handle_msg(ibe);
 | 
			
		||||
            ibe->in_too_many = false;
 | 
			
		||||
            ibe->inpos = 0;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case VM_CMD_CHAR:
 | 
			
		||||
            if (ibe->in_too_many) {
 | 
			
		||||
                ipmi_debug("cmd in too many\n");
 | 
			
		||||
                ibe->in_too_many = false;
 | 
			
		||||
                ibe->inpos = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (ibe->in_escape) {
 | 
			
		||||
                ipmi_debug("cmd in escape\n");
 | 
			
		||||
                ibe->in_too_many = false;
 | 
			
		||||
                ibe->inpos = 0;
 | 
			
		||||
                ibe->in_escape = false;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            ibe->in_too_many = false;
 | 
			
		||||
            if (ibe->inpos < 1) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            hw_op = ibe->inbuf[0];
 | 
			
		||||
            ibe->inpos = 0;
 | 
			
		||||
            goto out_hw_op;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case VM_ESCAPE_CHAR:
 | 
			
		||||
            ibe->in_escape = true;
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            if (ibe->in_escape) {
 | 
			
		||||
                ch &= ~0x10;
 | 
			
		||||
                ibe->in_escape = false;
 | 
			
		||||
            }
 | 
			
		||||
            if (ibe->in_too_many) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (ibe->inpos >= sizeof(ibe->inbuf)) {
 | 
			
		||||
                ibe->in_too_many = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            ibe->inbuf[ibe->inpos] = ch;
 | 
			
		||||
            ibe->inpos++;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
 out_hw_op:
 | 
			
		||||
    handle_hw_op(ibe, hw_op);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void chr_event(void *opaque, int event)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmcExtern *ibe = opaque;
 | 
			
		||||
    IPMIInterface *s = ibe->parent.intf;
 | 
			
		||||
    IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
 | 
			
		||||
    unsigned char v;
 | 
			
		||||
 | 
			
		||||
    switch (event) {
 | 
			
		||||
    case CHR_EVENT_OPENED:
 | 
			
		||||
        ibe->connected = true;
 | 
			
		||||
        ibe->outpos = 0;
 | 
			
		||||
        ibe->outlen = 0;
 | 
			
		||||
        addchar(ibe, VM_CMD_VERSION);
 | 
			
		||||
        addchar(ibe, VM_PROTOCOL_VERSION);
 | 
			
		||||
        ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
 | 
			
		||||
        ibe->outlen++;
 | 
			
		||||
        addchar(ibe, VM_CMD_CAPABILITIES);
 | 
			
		||||
        v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN;
 | 
			
		||||
        if (k->do_hw_op(ibe->parent.intf, IPMI_POWEROFF_CHASSIS, 1) == 0) {
 | 
			
		||||
            v |= VM_CAPABILITIES_POWER;
 | 
			
		||||
        }
 | 
			
		||||
        if (k->do_hw_op(ibe->parent.intf, IPMI_RESET_CHASSIS, 1) == 0) {
 | 
			
		||||
            v |= VM_CAPABILITIES_RESET;
 | 
			
		||||
        }
 | 
			
		||||
        if (k->do_hw_op(ibe->parent.intf, IPMI_SEND_NMI, 1) == 0) {
 | 
			
		||||
            v |= VM_CAPABILITIES_NMI;
 | 
			
		||||
        }
 | 
			
		||||
        addchar(ibe, v);
 | 
			
		||||
        ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
 | 
			
		||||
        ibe->outlen++;
 | 
			
		||||
        ibe->sending_cmd = false;
 | 
			
		||||
        continue_send(ibe);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case CHR_EVENT_CLOSED:
 | 
			
		||||
        if (!ibe->connected) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        ibe->connected = false;
 | 
			
		||||
        if (ibe->waiting_rsp) {
 | 
			
		||||
            ibe->waiting_rsp = false;
 | 
			
		||||
            ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
 | 
			
		||||
            ibe->inbuf[2] = ibe->outbuf[2];
 | 
			
		||||
            ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
 | 
			
		||||
            k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bmc_extern_handle_reset(IPMIBmc *b)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
 | 
			
		||||
 | 
			
		||||
    ibe->send_reset = true;
 | 
			
		||||
    continue_send(ibe);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
 | 
			
		||||
 | 
			
		||||
    if (!ibe->chr) {
 | 
			
		||||
        error_setg(errp, "IPMI external bmc requires chardev attribute");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmcExtern *ibe = opaque;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * We don't directly restore waiting_rsp, Instead, we return an
 | 
			
		||||
     * error on the interface if a response was being waited for.
 | 
			
		||||
     */
 | 
			
		||||
    if (ibe->waiting_rsp) {
 | 
			
		||||
        IPMIInterface *ii = ibe->parent.intf;
 | 
			
		||||
        IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
 | 
			
		||||
        ibe->waiting_rsp = false;
 | 
			
		||||
        ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
 | 
			
		||||
        ibe->inbuf[2] = ibe->outbuf[2];
 | 
			
		||||
        ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
 | 
			
		||||
        iic->handle_rsp(ii, ibe->outbuf[0], ibe->inbuf + 1, 3);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_ipmi_bmc_extern = {
 | 
			
		||||
    .name = TYPE_IPMI_BMC_EXTERN,
 | 
			
		||||
    .version_id = 1,
 | 
			
		||||
    .minimum_version_id = 1,
 | 
			
		||||
    .post_load = ipmi_bmc_extern_post_migrate,
 | 
			
		||||
    .fields      = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_BOOL(send_reset, IPMIBmcExtern),
 | 
			
		||||
        VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ipmi_bmc_extern_init(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
 | 
			
		||||
 | 
			
		||||
    ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
 | 
			
		||||
    vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Property ipmi_bmc_extern_properties[] = {
 | 
			
		||||
    DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr),
 | 
			
		||||
    DEFINE_PROP_END_OF_LIST(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(oc);
 | 
			
		||||
    IPMIBmcClass *bk = IPMI_BMC_CLASS(oc);
 | 
			
		||||
 | 
			
		||||
    bk->handle_command = ipmi_bmc_extern_handle_command;
 | 
			
		||||
    bk->handle_reset = ipmi_bmc_extern_handle_reset;
 | 
			
		||||
    dc->realize = ipmi_bmc_extern_realize;
 | 
			
		||||
    dc->props = ipmi_bmc_extern_properties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo ipmi_bmc_extern_type = {
 | 
			
		||||
    .name          = TYPE_IPMI_BMC_EXTERN,
 | 
			
		||||
    .parent        = TYPE_IPMI_BMC,
 | 
			
		||||
    .instance_size = sizeof(IPMIBmcExtern),
 | 
			
		||||
    .instance_init = ipmi_bmc_extern_init,
 | 
			
		||||
    .class_init    = ipmi_bmc_extern_class_init,
 | 
			
		||||
 };
 | 
			
		||||
 | 
			
		||||
static void ipmi_bmc_extern_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&ipmi_bmc_extern_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(ipmi_bmc_extern_register_types)
 | 
			
		||||
							
								
								
									
										1756
									
								
								hw/ipmi/ipmi_bmc_sim.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1756
									
								
								hw/ipmi/ipmi_bmc_sim.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										528
									
								
								hw/ipmi/isa_ipmi_bt.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										528
									
								
								hw/ipmi/isa_ipmi_bt.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,528 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU ISA IPMI BT emulation
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "hw/hw.h"
 | 
			
		||||
#include "hw/ipmi/ipmi.h"
 | 
			
		||||
#include "hw/isa/isa.h"
 | 
			
		||||
#include "hw/i386/pc.h"
 | 
			
		||||
 | 
			
		||||
/* Control register */
 | 
			
		||||
#define IPMI_BT_CLR_WR_BIT         0
 | 
			
		||||
#define IPMI_BT_CLR_RD_BIT         1
 | 
			
		||||
#define IPMI_BT_H2B_ATN_BIT        2
 | 
			
		||||
#define IPMI_BT_B2H_ATN_BIT        3
 | 
			
		||||
#define IPMI_BT_SMS_ATN_BIT        4
 | 
			
		||||
#define IPMI_BT_HBUSY_BIT          6
 | 
			
		||||
#define IPMI_BT_BBUSY_BIT          7
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_CLR_WR_MASK        (1 << IPMI_BT_CLR_WR_BIT)
 | 
			
		||||
#define IPMI_BT_GET_CLR_WR(d)      (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_CLR_WR(d, v)   (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \
 | 
			
		||||
                                       (((v & 1) << IPMI_BT_CLR_WR_BIT)))
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_CLR_RD_MASK        (1 << IPMI_BT_CLR_RD_BIT)
 | 
			
		||||
#define IPMI_BT_GET_CLR_RD(d)      (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_CLR_RD(d, v)   (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \
 | 
			
		||||
                                       (((v & 1) << IPMI_BT_CLR_RD_BIT)))
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_H2B_ATN_MASK       (1 << IPMI_BT_H2B_ATN_BIT)
 | 
			
		||||
#define IPMI_BT_GET_H2B_ATN(d)     (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_H2B_ATN(d, v)  (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \
 | 
			
		||||
                                        (((v & 1) << IPMI_BT_H2B_ATN_BIT)))
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_B2H_ATN_MASK       (1 << IPMI_BT_B2H_ATN_BIT)
 | 
			
		||||
#define IPMI_BT_GET_B2H_ATN(d)     (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_B2H_ATN(d, v)  (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \
 | 
			
		||||
                                        (((v & 1) << IPMI_BT_B2H_ATN_BIT)))
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_SMS_ATN_MASK       (1 << IPMI_BT_SMS_ATN_BIT)
 | 
			
		||||
#define IPMI_BT_GET_SMS_ATN(d)     (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_SMS_ATN(d, v)  (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \
 | 
			
		||||
                                        (((v & 1) << IPMI_BT_SMS_ATN_BIT)))
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_HBUSY_MASK         (1 << IPMI_BT_HBUSY_BIT)
 | 
			
		||||
#define IPMI_BT_GET_HBUSY(d)       (((d) >> IPMI_BT_HBUSY_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_HBUSY(d, v)    (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \
 | 
			
		||||
                                       (((v & 1) << IPMI_BT_HBUSY_BIT)))
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_BBUSY_MASK         (1 << IPMI_BT_BBUSY_BIT)
 | 
			
		||||
#define IPMI_BT_GET_BBUSY(d)       (((d) >> IPMI_BT_BBUSY_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_BBUSY(d, v)    (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \
 | 
			
		||||
                                       (((v & 1) << IPMI_BT_BBUSY_BIT)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Mask register */
 | 
			
		||||
#define IPMI_BT_B2H_IRQ_EN_BIT     0
 | 
			
		||||
#define IPMI_BT_B2H_IRQ_BIT        1
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_B2H_IRQ_EN_MASK      (1 << IPMI_BT_B2H_IRQ_EN_BIT)
 | 
			
		||||
#define IPMI_BT_GET_B2H_IRQ_EN(d)    (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_B2H_IRQ_EN(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \
 | 
			
		||||
                                        (((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT)))
 | 
			
		||||
 | 
			
		||||
#define IPMI_BT_B2H_IRQ_MASK         (1 << IPMI_BT_B2H_IRQ_BIT)
 | 
			
		||||
#define IPMI_BT_GET_B2H_IRQ(d)       (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1)
 | 
			
		||||
#define IPMI_BT_SET_B2H_IRQ(d, v)    (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \
 | 
			
		||||
                                        (((v & 1) << IPMI_BT_B2H_IRQ_BIT)))
 | 
			
		||||
 | 
			
		||||
typedef struct IPMIBT {
 | 
			
		||||
    IPMIBmc *bmc;
 | 
			
		||||
 | 
			
		||||
    bool do_wake;
 | 
			
		||||
 | 
			
		||||
    qemu_irq irq;
 | 
			
		||||
 | 
			
		||||
    uint32_t io_base;
 | 
			
		||||
    unsigned long io_length;
 | 
			
		||||
    MemoryRegion io;
 | 
			
		||||
 | 
			
		||||
    bool obf_irq_set;
 | 
			
		||||
    bool atn_irq_set;
 | 
			
		||||
    bool use_irq;
 | 
			
		||||
    bool irqs_enabled;
 | 
			
		||||
 | 
			
		||||
    uint8_t outmsg[MAX_IPMI_MSG_SIZE];
 | 
			
		||||
    uint32_t outpos;
 | 
			
		||||
    uint32_t outlen;
 | 
			
		||||
 | 
			
		||||
    uint8_t inmsg[MAX_IPMI_MSG_SIZE];
 | 
			
		||||
    uint32_t inlen;
 | 
			
		||||
 | 
			
		||||
    uint8_t control_reg;
 | 
			
		||||
    uint8_t mask_reg;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * This is a response number that we send with the command to make
 | 
			
		||||
     * sure that the response matches the command.
 | 
			
		||||
     */
 | 
			
		||||
    uint8_t waiting_rsp;
 | 
			
		||||
    uint8_t waiting_seq;
 | 
			
		||||
} IPMIBT;
 | 
			
		||||
 | 
			
		||||
#define IPMI_CMD_GET_BT_INTF_CAP        0x36
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_handle_event(IPMIInterface *ii)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIBT *ib = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    if (ib->inlen < 4) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    /* Note that overruns are handled by handle_command */
 | 
			
		||||
    if (ib->inmsg[0] != (ib->inlen - 1)) {
 | 
			
		||||
        /* Length mismatch, just ignore. */
 | 
			
		||||
        IPMI_BT_SET_BBUSY(ib->control_reg, 1);
 | 
			
		||||
        ib->inlen = 0;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) &&
 | 
			
		||||
                        (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) {
 | 
			
		||||
        /* We handle this one ourselves. */
 | 
			
		||||
        ib->outmsg[0] = 9;
 | 
			
		||||
        ib->outmsg[1] = ib->inmsg[1] | 0x04;
 | 
			
		||||
        ib->outmsg[2] = ib->inmsg[2];
 | 
			
		||||
        ib->outmsg[3] = ib->inmsg[3];
 | 
			
		||||
        ib->outmsg[4] = 0;
 | 
			
		||||
        ib->outmsg[5] = 1; /* Only support 1 outstanding request. */
 | 
			
		||||
        if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */
 | 
			
		||||
            ib->outmsg[6] = 0xff;
 | 
			
		||||
        } else {
 | 
			
		||||
            ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg);
 | 
			
		||||
        }
 | 
			
		||||
        if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */
 | 
			
		||||
            ib->outmsg[7] = 0xff;
 | 
			
		||||
        } else {
 | 
			
		||||
            ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg);
 | 
			
		||||
        }
 | 
			
		||||
        ib->outmsg[8] = 10; /* Max request to response time */
 | 
			
		||||
        ib->outmsg[9] = 0; /* Don't recommend retries */
 | 
			
		||||
        ib->outlen = 10;
 | 
			
		||||
        IPMI_BT_SET_BBUSY(ib->control_reg, 0);
 | 
			
		||||
        IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
 | 
			
		||||
        if (ib->use_irq && ib->irqs_enabled &&
 | 
			
		||||
                !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
 | 
			
		||||
                IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
 | 
			
		||||
            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
 | 
			
		||||
            qemu_irq_raise(ib->irq);
 | 
			
		||||
        }
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
    ib->waiting_seq = ib->inmsg[2];
 | 
			
		||||
    ib->inmsg[2] = ib->inmsg[1];
 | 
			
		||||
    {
 | 
			
		||||
        IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc);
 | 
			
		||||
        bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2,
 | 
			
		||||
                           sizeof(ib->inmsg), ib->waiting_rsp);
 | 
			
		||||
    }
 | 
			
		||||
 out:
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
 | 
			
		||||
                                unsigned char *rsp, unsigned int rsp_len)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIBT *ib = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    if (ib->waiting_rsp == msg_id) {
 | 
			
		||||
        ib->waiting_rsp++;
 | 
			
		||||
        if (rsp_len > (sizeof(ib->outmsg) - 2)) {
 | 
			
		||||
            ib->outmsg[0] = 4;
 | 
			
		||||
            ib->outmsg[1] = rsp[0];
 | 
			
		||||
            ib->outmsg[2] = ib->waiting_seq;
 | 
			
		||||
            ib->outmsg[3] = rsp[1];
 | 
			
		||||
            ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
 | 
			
		||||
            ib->outlen = 5;
 | 
			
		||||
        } else {
 | 
			
		||||
            ib->outmsg[0] = rsp_len + 1;
 | 
			
		||||
            ib->outmsg[1] = rsp[0];
 | 
			
		||||
            ib->outmsg[2] = ib->waiting_seq;
 | 
			
		||||
            memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1);
 | 
			
		||||
            ib->outlen = rsp_len + 2;
 | 
			
		||||
        }
 | 
			
		||||
        IPMI_BT_SET_BBUSY(ib->control_reg, 0);
 | 
			
		||||
        IPMI_BT_SET_B2H_ATN(ib->control_reg, 1);
 | 
			
		||||
        if (ib->use_irq && ib->irqs_enabled &&
 | 
			
		||||
                !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) &&
 | 
			
		||||
                IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
 | 
			
		||||
            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
 | 
			
		||||
            qemu_irq_raise(ib->irq);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterface *ii = opaque;
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIBT *ib = iic->get_backend_data(ii);
 | 
			
		||||
    uint32_t ret = 0xff;
 | 
			
		||||
 | 
			
		||||
    switch (addr & 3) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        ret = ib->control_reg;
 | 
			
		||||
        break;
 | 
			
		||||
    case 1:
 | 
			
		||||
        if (ib->outpos < ib->outlen) {
 | 
			
		||||
            ret = ib->outmsg[ib->outpos];
 | 
			
		||||
            ib->outpos++;
 | 
			
		||||
            if (ib->outpos == ib->outlen) {
 | 
			
		||||
                ib->outpos = 0;
 | 
			
		||||
                ib->outlen = 0;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = 0xff;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 2:
 | 
			
		||||
        ret = ib->mask_reg;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
 | 
			
		||||
    ib->do_wake = 1;
 | 
			
		||||
    while (ib->do_wake) {
 | 
			
		||||
        ib->do_wake = 0;
 | 
			
		||||
        iic->handle_if_event(ii);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val,
 | 
			
		||||
                                 unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterface *ii = opaque;
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIBT *ib = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    switch (addr & 3) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        if (IPMI_BT_GET_CLR_WR(val)) {
 | 
			
		||||
            ib->inlen = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (IPMI_BT_GET_CLR_RD(val)) {
 | 
			
		||||
            ib->outpos = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (IPMI_BT_GET_B2H_ATN(val)) {
 | 
			
		||||
            IPMI_BT_SET_B2H_ATN(ib->control_reg, 0);
 | 
			
		||||
        }
 | 
			
		||||
        if (IPMI_BT_GET_SMS_ATN(val)) {
 | 
			
		||||
            IPMI_BT_SET_SMS_ATN(ib->control_reg, 0);
 | 
			
		||||
        }
 | 
			
		||||
        if (IPMI_BT_GET_HBUSY(val)) {
 | 
			
		||||
            /* Toggle */
 | 
			
		||||
            IPMI_BT_SET_HBUSY(ib->control_reg,
 | 
			
		||||
                              !IPMI_BT_GET_HBUSY(ib->control_reg));
 | 
			
		||||
        }
 | 
			
		||||
        if (IPMI_BT_GET_H2B_ATN(val)) {
 | 
			
		||||
            IPMI_BT_SET_BBUSY(ib->control_reg, 1);
 | 
			
		||||
            ipmi_bt_signal(ib, ii);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 1:
 | 
			
		||||
        if (ib->inlen < sizeof(ib->inmsg)) {
 | 
			
		||||
            ib->inmsg[ib->inlen] = val;
 | 
			
		||||
        }
 | 
			
		||||
        ib->inlen++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 2:
 | 
			
		||||
        if (IPMI_BT_GET_B2H_IRQ_EN(val) !=
 | 
			
		||||
                        IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
 | 
			
		||||
            if (IPMI_BT_GET_B2H_IRQ_EN(val)) {
 | 
			
		||||
                if (IPMI_BT_GET_B2H_ATN(ib->control_reg) ||
 | 
			
		||||
                        IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
 | 
			
		||||
                    IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
 | 
			
		||||
                    qemu_irq_raise(ib->irq);
 | 
			
		||||
                }
 | 
			
		||||
                IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
 | 
			
		||||
                    IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
 | 
			
		||||
                    qemu_irq_lower(ib->irq);
 | 
			
		||||
                }
 | 
			
		||||
                IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
 | 
			
		||||
            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
 | 
			
		||||
            qemu_irq_lower(ib->irq);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const MemoryRegionOps ipmi_bt_io_ops = {
 | 
			
		||||
    .read = ipmi_bt_ioport_read,
 | 
			
		||||
    .write = ipmi_bt_ioport_write,
 | 
			
		||||
    .impl = {
 | 
			
		||||
        .min_access_size = 1,
 | 
			
		||||
        .max_access_size = 1,
 | 
			
		||||
    },
 | 
			
		||||
    .endianness = DEVICE_LITTLE_ENDIAN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIBT *ib = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IPMI_BT_SET_SMS_ATN(ib->control_reg, val);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        if (irq && ib->use_irq && ib->irqs_enabled &&
 | 
			
		||||
                !IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
 | 
			
		||||
                IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) {
 | 
			
		||||
            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1);
 | 
			
		||||
            qemu_irq_raise(ib->irq);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) &&
 | 
			
		||||
                IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
 | 
			
		||||
            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
 | 
			
		||||
            qemu_irq_lower(ib->irq);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIBT *ib = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    if (is_cold) {
 | 
			
		||||
        /* Disable the BT interrupt on reset */
 | 
			
		||||
        if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) {
 | 
			
		||||
            IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0);
 | 
			
		||||
            qemu_irq_lower(ib->irq);
 | 
			
		||||
        }
 | 
			
		||||
        IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIBT *ib = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    ib->irqs_enabled = val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_init(IPMIInterface *ii, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIBT *ib = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    ib->io_length = 3;
 | 
			
		||||
 | 
			
		||||
    memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_bt_class_init(IPMIInterfaceClass *iic)
 | 
			
		||||
{
 | 
			
		||||
    iic->init = ipmi_bt_init;
 | 
			
		||||
    iic->set_atn = ipmi_bt_set_atn;
 | 
			
		||||
    iic->handle_rsp = ipmi_bt_handle_rsp;
 | 
			
		||||
    iic->handle_if_event = ipmi_bt_handle_event;
 | 
			
		||||
    iic->set_irq_enable = ipmi_bt_set_irq_enable;
 | 
			
		||||
    iic->reset = ipmi_bt_handle_reset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define TYPE_ISA_IPMI_BT "isa-ipmi-bt"
 | 
			
		||||
#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \
 | 
			
		||||
                                       TYPE_ISA_IPMI_BT)
 | 
			
		||||
 | 
			
		||||
typedef struct ISAIPMIBTDevice {
 | 
			
		||||
    ISADevice dev;
 | 
			
		||||
    int32 isairq;
 | 
			
		||||
    IPMIBT bt;
 | 
			
		||||
    IPMIFwInfo fwinfo;
 | 
			
		||||
} ISAIPMIBTDevice;
 | 
			
		||||
 | 
			
		||||
static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    ISADevice *isadev = ISA_DEVICE(dev);
 | 
			
		||||
    ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev);
 | 
			
		||||
    IPMIInterface *ii = IPMI_INTERFACE(dev);
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
 | 
			
		||||
    if (!iib->bt.bmc) {
 | 
			
		||||
        error_setg(errp, "IPMI device requires a bmc attribute to be set");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iib->bt.bmc->intf = ii;
 | 
			
		||||
 | 
			
		||||
    iic->init(ii, errp);
 | 
			
		||||
    if (*errp)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (iib->isairq > 0) {
 | 
			
		||||
        isa_init_irq(isadev, &iib->bt.irq, iib->isairq);
 | 
			
		||||
        iib->bt.use_irq = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length);
 | 
			
		||||
 | 
			
		||||
    isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base);
 | 
			
		||||
 | 
			
		||||
    iib->fwinfo.interface_name = "bt";
 | 
			
		||||
    iib->fwinfo.interface_type = IPMI_SMBIOS_BT;
 | 
			
		||||
    iib->fwinfo.ipmi_spec_major_revision = 2;
 | 
			
		||||
    iib->fwinfo.ipmi_spec_minor_revision = 0;
 | 
			
		||||
    iib->fwinfo.base_address = iib->bt.io_base;
 | 
			
		||||
    iib->fwinfo.register_length = iib->bt.io_length;
 | 
			
		||||
    iib->fwinfo.register_spacing = 1;
 | 
			
		||||
    iib->fwinfo.memspace = IPMI_MEMSPACE_IO;
 | 
			
		||||
    iib->fwinfo.irq_type = IPMI_LEVEL_IRQ;
 | 
			
		||||
    iib->fwinfo.interrupt_number = iib->isairq;
 | 
			
		||||
    iib->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
 | 
			
		||||
    iib->fwinfo.i2c_slave_address = iib->bt.bmc->slave_addr;
 | 
			
		||||
    ipmi_add_fwinfo(&iib->fwinfo, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_ISAIPMIBTDevice = {
 | 
			
		||||
    .name = TYPE_IPMI_INTERFACE,
 | 
			
		||||
    .version_id = 1,
 | 
			
		||||
    .minimum_version_id = 1,
 | 
			
		||||
    .fields      = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, 0,
 | 
			
		||||
                               bt.outlen),
 | 
			
		||||
        VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 0,
 | 
			
		||||
                               bt.inlen),
 | 
			
		||||
        VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void isa_ipmi_bt_init(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj);
 | 
			
		||||
 | 
			
		||||
    ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc);
 | 
			
		||||
 | 
			
		||||
    vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii)
 | 
			
		||||
{
 | 
			
		||||
    ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii);
 | 
			
		||||
 | 
			
		||||
    return &iib->bt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Property ipmi_isa_properties[] = {
 | 
			
		||||
    DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base,  0xe4),
 | 
			
		||||
    DEFINE_PROP_INT32("irq",   ISAIPMIBTDevice, isairq,  5),
 | 
			
		||||
    DEFINE_PROP_END_OF_LIST(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(oc);
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
 | 
			
		||||
 | 
			
		||||
    dc->realize = isa_ipmi_bt_realize;
 | 
			
		||||
    dc->props = ipmi_isa_properties;
 | 
			
		||||
 | 
			
		||||
    iic->get_backend_data = isa_ipmi_bt_get_backend_data;
 | 
			
		||||
    ipmi_bt_class_init(iic);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo isa_ipmi_bt_info = {
 | 
			
		||||
    .name          = TYPE_ISA_IPMI_BT,
 | 
			
		||||
    .parent        = TYPE_ISA_DEVICE,
 | 
			
		||||
    .instance_size = sizeof(ISAIPMIBTDevice),
 | 
			
		||||
    .instance_init = isa_ipmi_bt_init,
 | 
			
		||||
    .class_init    = isa_ipmi_bt_class_init,
 | 
			
		||||
    .interfaces = (InterfaceInfo[]) {
 | 
			
		||||
        { TYPE_IPMI_INTERFACE },
 | 
			
		||||
        { }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ipmi_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&isa_ipmi_bt_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(ipmi_register_types)
 | 
			
		||||
							
								
								
									
										493
									
								
								hw/ipmi/isa_ipmi_kcs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										493
									
								
								hw/ipmi/isa_ipmi_kcs.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,493 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU ISA IPMI KCS emulation
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | 
			
		||||
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
#include "hw/hw.h"
 | 
			
		||||
#include "hw/ipmi/ipmi.h"
 | 
			
		||||
#include "hw/isa/isa.h"
 | 
			
		||||
#include "hw/i386/pc.h"
 | 
			
		||||
 | 
			
		||||
#define IPMI_KCS_OBF_BIT        0
 | 
			
		||||
#define IPMI_KCS_IBF_BIT        1
 | 
			
		||||
#define IPMI_KCS_SMS_ATN_BIT    2
 | 
			
		||||
#define IPMI_KCS_CD_BIT         3
 | 
			
		||||
 | 
			
		||||
#define IPMI_KCS_OBF_MASK          (1 << IPMI_KCS_OBF_BIT)
 | 
			
		||||
#define IPMI_KCS_GET_OBF(d)        (((d) >> IPMI_KCS_OBF_BIT) & 0x1)
 | 
			
		||||
#define IPMI_KCS_SET_OBF(d, v)     (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \
 | 
			
		||||
                                       (((v) & 1) << IPMI_KCS_OBF_BIT))
 | 
			
		||||
#define IPMI_KCS_IBF_MASK          (1 << IPMI_KCS_IBF_BIT)
 | 
			
		||||
#define IPMI_KCS_GET_IBF(d)        (((d) >> IPMI_KCS_IBF_BIT) & 0x1)
 | 
			
		||||
#define IPMI_KCS_SET_IBF(d, v)     (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \
 | 
			
		||||
                                       (((v) & 1) << IPMI_KCS_IBF_BIT))
 | 
			
		||||
#define IPMI_KCS_SMS_ATN_MASK      (1 << IPMI_KCS_SMS_ATN_BIT)
 | 
			
		||||
#define IPMI_KCS_GET_SMS_ATN(d)    (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1)
 | 
			
		||||
#define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \
 | 
			
		||||
                                       (((v) & 1) << IPMI_KCS_SMS_ATN_BIT))
 | 
			
		||||
#define IPMI_KCS_CD_MASK           (1 << IPMI_KCS_CD_BIT)
 | 
			
		||||
#define IPMI_KCS_GET_CD(d)         (((d) >> IPMI_KCS_CD_BIT) & 0x1)
 | 
			
		||||
#define IPMI_KCS_SET_CD(d, v)      (d) = (((d) & ~IPMI_KCS_CD_MASK) | \
 | 
			
		||||
                                       (((v) & 1) << IPMI_KCS_CD_BIT))
 | 
			
		||||
 | 
			
		||||
#define IPMI_KCS_IDLE_STATE        0
 | 
			
		||||
#define IPMI_KCS_READ_STATE        1
 | 
			
		||||
#define IPMI_KCS_WRITE_STATE       2
 | 
			
		||||
#define IPMI_KCS_ERROR_STATE       3
 | 
			
		||||
 | 
			
		||||
#define IPMI_KCS_GET_STATE(d)    (((d) >> 6) & 0x3)
 | 
			
		||||
#define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6))
 | 
			
		||||
 | 
			
		||||
#define IPMI_KCS_ABORT_STATUS_CMD       0x60
 | 
			
		||||
#define IPMI_KCS_WRITE_START_CMD        0x61
 | 
			
		||||
#define IPMI_KCS_WRITE_END_CMD          0x62
 | 
			
		||||
#define IPMI_KCS_READ_CMD               0x68
 | 
			
		||||
 | 
			
		||||
#define IPMI_KCS_STATUS_NO_ERR          0x00
 | 
			
		||||
#define IPMI_KCS_STATUS_ABORTED_ERR     0x01
 | 
			
		||||
#define IPMI_KCS_STATUS_BAD_CC_ERR      0x02
 | 
			
		||||
#define IPMI_KCS_STATUS_LENGTH_ERR      0x06
 | 
			
		||||
 | 
			
		||||
typedef struct IPMIKCS {
 | 
			
		||||
    IPMIBmc *bmc;
 | 
			
		||||
 | 
			
		||||
    bool do_wake;
 | 
			
		||||
 | 
			
		||||
    qemu_irq irq;
 | 
			
		||||
 | 
			
		||||
    uint32_t io_base;
 | 
			
		||||
    unsigned long io_length;
 | 
			
		||||
    MemoryRegion io;
 | 
			
		||||
 | 
			
		||||
    bool obf_irq_set;
 | 
			
		||||
    bool atn_irq_set;
 | 
			
		||||
    bool use_irq;
 | 
			
		||||
    bool irqs_enabled;
 | 
			
		||||
 | 
			
		||||
    uint8_t outmsg[MAX_IPMI_MSG_SIZE];
 | 
			
		||||
    uint32_t outpos;
 | 
			
		||||
    uint32_t outlen;
 | 
			
		||||
 | 
			
		||||
    uint8_t inmsg[MAX_IPMI_MSG_SIZE];
 | 
			
		||||
    uint32_t inlen;
 | 
			
		||||
    bool write_end;
 | 
			
		||||
 | 
			
		||||
    uint8_t status_reg;
 | 
			
		||||
    uint8_t data_out_reg;
 | 
			
		||||
 | 
			
		||||
    int16_t data_in_reg; /* -1 means not written */
 | 
			
		||||
    int16_t cmd_reg;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * This is a response number that we send with the command to make
 | 
			
		||||
     * sure that the response matches the command.
 | 
			
		||||
     */
 | 
			
		||||
    uint8_t waiting_rsp;
 | 
			
		||||
} IPMIKCS;
 | 
			
		||||
 | 
			
		||||
#define SET_OBF() \
 | 
			
		||||
    do {                                                                      \
 | 
			
		||||
        IPMI_KCS_SET_OBF(ik->status_reg, 1);                                  \
 | 
			
		||||
        if (ik->use_irq && ik->irqs_enabled && !ik->obf_irq_set) {            \
 | 
			
		||||
            ik->obf_irq_set = 1;                                              \
 | 
			
		||||
            if (!ik->atn_irq_set) {                                           \
 | 
			
		||||
                qemu_irq_raise(ik->irq);                                      \
 | 
			
		||||
            }                                                                 \
 | 
			
		||||
        }                                                                     \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
 | 
			
		||||
    ik->do_wake = 1;
 | 
			
		||||
    while (ik->do_wake) {
 | 
			
		||||
        ik->do_wake = 0;
 | 
			
		||||
        iic->handle_if_event(ii);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_kcs_handle_event(IPMIInterface *ii)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIKCS *ik = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) {
 | 
			
		||||
        if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) {
 | 
			
		||||
            ik->waiting_rsp++; /* Invalidate the message */
 | 
			
		||||
            ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR;
 | 
			
		||||
            ik->outlen = 1;
 | 
			
		||||
            ik->outpos = 0;
 | 
			
		||||
            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
 | 
			
		||||
            SET_OBF();
 | 
			
		||||
        }
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (IPMI_KCS_GET_STATE(ik->status_reg)) {
 | 
			
		||||
    case IPMI_KCS_IDLE_STATE:
 | 
			
		||||
        if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) {
 | 
			
		||||
            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE);
 | 
			
		||||
            ik->cmd_reg = -1;
 | 
			
		||||
            ik->write_end = 0;
 | 
			
		||||
            ik->inlen = 0;
 | 
			
		||||
            SET_OBF();
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case IPMI_KCS_READ_STATE:
 | 
			
		||||
    handle_read:
 | 
			
		||||
        if (ik->outpos >= ik->outlen) {
 | 
			
		||||
            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE);
 | 
			
		||||
            SET_OBF();
 | 
			
		||||
        } else if (ik->data_in_reg == IPMI_KCS_READ_CMD) {
 | 
			
		||||
            ik->data_out_reg = ik->outmsg[ik->outpos];
 | 
			
		||||
            ik->outpos++;
 | 
			
		||||
            SET_OBF();
 | 
			
		||||
        } else {
 | 
			
		||||
            ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
 | 
			
		||||
            ik->outlen = 1;
 | 
			
		||||
            ik->outpos = 0;
 | 
			
		||||
            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
 | 
			
		||||
            SET_OBF();
 | 
			
		||||
            goto out;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case IPMI_KCS_WRITE_STATE:
 | 
			
		||||
        if (ik->data_in_reg != -1) {
 | 
			
		||||
            /*
 | 
			
		||||
             * Don't worry about input overrun here, that will be
 | 
			
		||||
             * handled in the BMC.
 | 
			
		||||
             */
 | 
			
		||||
            if (ik->inlen < sizeof(ik->inmsg)) {
 | 
			
		||||
                ik->inmsg[ik->inlen] = ik->data_in_reg;
 | 
			
		||||
            }
 | 
			
		||||
            ik->inlen++;
 | 
			
		||||
        }
 | 
			
		||||
        if (ik->write_end) {
 | 
			
		||||
            IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc);
 | 
			
		||||
            ik->outlen = 0;
 | 
			
		||||
            ik->write_end = 0;
 | 
			
		||||
            ik->outpos = 0;
 | 
			
		||||
            bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg),
 | 
			
		||||
                               ik->waiting_rsp);
 | 
			
		||||
            goto out_noibf;
 | 
			
		||||
        } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) {
 | 
			
		||||
            ik->cmd_reg = -1;
 | 
			
		||||
            ik->write_end = 1;
 | 
			
		||||
        }
 | 
			
		||||
        SET_OBF();
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case IPMI_KCS_ERROR_STATE:
 | 
			
		||||
        if (ik->data_in_reg != -1) {
 | 
			
		||||
            IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
 | 
			
		||||
            ik->data_in_reg = IPMI_KCS_READ_CMD;
 | 
			
		||||
            goto handle_read;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ik->cmd_reg != -1) {
 | 
			
		||||
        /* Got an invalid command */
 | 
			
		||||
        ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR;
 | 
			
		||||
        ik->outlen = 1;
 | 
			
		||||
        ik->outpos = 0;
 | 
			
		||||
        IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    ik->cmd_reg = -1;
 | 
			
		||||
    ik->data_in_reg = -1;
 | 
			
		||||
    IPMI_KCS_SET_IBF(ik->status_reg, 0);
 | 
			
		||||
 out_noibf:
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id,
 | 
			
		||||
                                unsigned char *rsp, unsigned int rsp_len)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIKCS *ik = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    if (ik->waiting_rsp == msg_id) {
 | 
			
		||||
        ik->waiting_rsp++;
 | 
			
		||||
        if (rsp_len > sizeof(ik->outmsg)) {
 | 
			
		||||
            ik->outmsg[0] = rsp[0];
 | 
			
		||||
            ik->outmsg[1] = rsp[1];
 | 
			
		||||
            ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES;
 | 
			
		||||
            ik->outlen = 3;
 | 
			
		||||
        } else {
 | 
			
		||||
            memcpy(ik->outmsg, rsp, rsp_len);
 | 
			
		||||
            ik->outlen = rsp_len;
 | 
			
		||||
        }
 | 
			
		||||
        IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE);
 | 
			
		||||
        ik->data_in_reg = IPMI_KCS_READ_CMD;
 | 
			
		||||
        ipmi_kcs_signal(ik, ii);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterface *ii = opaque;
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIKCS *ik = iic->get_backend_data(ii);
 | 
			
		||||
    uint32_t ret;
 | 
			
		||||
 | 
			
		||||
    switch (addr & 1) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        ret = ik->data_out_reg;
 | 
			
		||||
        IPMI_KCS_SET_OBF(ik->status_reg, 0);
 | 
			
		||||
        if (ik->obf_irq_set) {
 | 
			
		||||
            ik->obf_irq_set = 0;
 | 
			
		||||
            if (!ik->atn_irq_set) {
 | 
			
		||||
                qemu_irq_lower(ik->irq);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 1:
 | 
			
		||||
        ret = ik->status_reg;
 | 
			
		||||
        if (ik->atn_irq_set) {
 | 
			
		||||
            ik->atn_irq_set = 0;
 | 
			
		||||
            if (!ik->obf_irq_set) {
 | 
			
		||||
                qemu_irq_lower(ik->irq);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val,
 | 
			
		||||
                                  unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterface *ii = opaque;
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIKCS *ik = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    if (IPMI_KCS_GET_IBF(ik->status_reg)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (addr & 1) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        ik->data_in_reg = val;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case 1:
 | 
			
		||||
        ik->cmd_reg = val;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    IPMI_KCS_SET_IBF(ik->status_reg, 1);
 | 
			
		||||
    ipmi_kcs_signal(ik, ii);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const MemoryRegionOps ipmi_kcs_io_ops = {
 | 
			
		||||
    .read = ipmi_kcs_ioport_read,
 | 
			
		||||
    .write = ipmi_kcs_ioport_write,
 | 
			
		||||
    .impl = {
 | 
			
		||||
        .min_access_size = 1,
 | 
			
		||||
        .max_access_size = 1,
 | 
			
		||||
    },
 | 
			
		||||
    .endianness = DEVICE_LITTLE_ENDIAN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIKCS *ik = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    IPMI_KCS_SET_SMS_ATN(ik->status_reg, val);
 | 
			
		||||
    if (val) {
 | 
			
		||||
        if (irq && !ik->atn_irq_set && ik->use_irq && ik->irqs_enabled) {
 | 
			
		||||
            ik->atn_irq_set = 1;
 | 
			
		||||
            if (!ik->obf_irq_set) {
 | 
			
		||||
                qemu_irq_raise(ik->irq);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (ik->atn_irq_set) {
 | 
			
		||||
            ik->atn_irq_set = 0;
 | 
			
		||||
            if (!ik->obf_irq_set) {
 | 
			
		||||
                qemu_irq_lower(ik->irq);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIKCS *ik = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    ik->irqs_enabled = val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_kcs_init(IPMIInterface *ii, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
    IPMIKCS *ik = iic->get_backend_data(ii);
 | 
			
		||||
 | 
			
		||||
    ik->io_length = 2;
 | 
			
		||||
    memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ipmi_kcs_class_init(IPMIInterfaceClass *iic)
 | 
			
		||||
{
 | 
			
		||||
    iic->init = ipmi_kcs_init;
 | 
			
		||||
    iic->set_atn = ipmi_kcs_set_atn;
 | 
			
		||||
    iic->handle_rsp = ipmi_kcs_handle_rsp;
 | 
			
		||||
    iic->handle_if_event = ipmi_kcs_handle_event;
 | 
			
		||||
    iic->set_irq_enable = ipmi_kcs_set_irq_enable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs"
 | 
			
		||||
#define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \
 | 
			
		||||
                                       TYPE_ISA_IPMI_KCS)
 | 
			
		||||
 | 
			
		||||
typedef struct ISAIPMIKCSDevice {
 | 
			
		||||
    ISADevice dev;
 | 
			
		||||
    int32 isairq;
 | 
			
		||||
    IPMIKCS kcs;
 | 
			
		||||
    IPMIFwInfo fwinfo;
 | 
			
		||||
} ISAIPMIKCSDevice;
 | 
			
		||||
 | 
			
		||||
static void ipmi_isa_realize(DeviceState *dev, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    ISADevice *isadev = ISA_DEVICE(dev);
 | 
			
		||||
    ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(dev);
 | 
			
		||||
    IPMIInterface *ii = IPMI_INTERFACE(dev);
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
 | 
			
		||||
 | 
			
		||||
    if (!iik->kcs.bmc) {
 | 
			
		||||
        error_setg(errp, "IPMI device requires a bmc attribute to be set");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    iik->kcs.bmc->intf = ii;
 | 
			
		||||
 | 
			
		||||
    iic->init(ii, errp);
 | 
			
		||||
    if (*errp)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (iik->isairq > 0) {
 | 
			
		||||
        isa_init_irq(isadev, &iik->kcs.irq, iik->isairq);
 | 
			
		||||
        iik->kcs.use_irq = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length);
 | 
			
		||||
 | 
			
		||||
    isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base);
 | 
			
		||||
 | 
			
		||||
    iik->fwinfo.interface_name = "kcs";
 | 
			
		||||
    iik->fwinfo.interface_type = IPMI_SMBIOS_KCS;
 | 
			
		||||
    iik->fwinfo.ipmi_spec_major_revision = 2;
 | 
			
		||||
    iik->fwinfo.ipmi_spec_minor_revision = 0;
 | 
			
		||||
    iik->fwinfo.base_address = iik->kcs.io_base;
 | 
			
		||||
    iik->fwinfo.i2c_slave_address = iik->kcs.bmc->slave_addr;
 | 
			
		||||
    iik->fwinfo.register_length = iik->kcs.io_length;
 | 
			
		||||
    iik->fwinfo.register_spacing = 1;
 | 
			
		||||
    iik->fwinfo.memspace = IPMI_MEMSPACE_IO;
 | 
			
		||||
    iik->fwinfo.irq_type = IPMI_LEVEL_IRQ;
 | 
			
		||||
    iik->fwinfo.interrupt_number = iik->isairq;
 | 
			
		||||
    iik->fwinfo.acpi_parent = "\\_SB.PCI0.ISA";
 | 
			
		||||
    ipmi_add_fwinfo(&iik->fwinfo, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const VMStateDescription vmstate_ISAIPMIKCSDevice = {
 | 
			
		||||
    .name = TYPE_IPMI_INTERFACE,
 | 
			
		||||
    .version_id = 1,
 | 
			
		||||
    .minimum_version_id = 1,
 | 
			
		||||
    .fields      = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_VBUFFER_UINT32(kcs.outmsg, ISAIPMIKCSDevice, 1, NULL, 0,
 | 
			
		||||
                               kcs.outlen),
 | 
			
		||||
        VMSTATE_VBUFFER_UINT32(kcs.inmsg, ISAIPMIKCSDevice, 1, NULL, 0,
 | 
			
		||||
                               kcs.inlen),
 | 
			
		||||
        VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void isa_ipmi_kcs_init(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj);
 | 
			
		||||
 | 
			
		||||
    ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc);
 | 
			
		||||
 | 
			
		||||
    vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii)
 | 
			
		||||
{
 | 
			
		||||
    ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii);
 | 
			
		||||
 | 
			
		||||
    return &iik->kcs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Property ipmi_isa_properties[] = {
 | 
			
		||||
    DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base,  0xca2),
 | 
			
		||||
    DEFINE_PROP_INT32("irq",   ISAIPMIKCSDevice, isairq,  5),
 | 
			
		||||
    DEFINE_PROP_END_OF_LIST(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(oc);
 | 
			
		||||
    IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc);
 | 
			
		||||
 | 
			
		||||
    dc->realize = ipmi_isa_realize;
 | 
			
		||||
    dc->props = ipmi_isa_properties;
 | 
			
		||||
 | 
			
		||||
    iic->get_backend_data = isa_ipmi_kcs_get_backend_data;
 | 
			
		||||
    ipmi_kcs_class_init(iic);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo isa_ipmi_kcs_info = {
 | 
			
		||||
    .name          = TYPE_ISA_IPMI_KCS,
 | 
			
		||||
    .parent        = TYPE_ISA_DEVICE,
 | 
			
		||||
    .instance_size = sizeof(ISAIPMIKCSDevice),
 | 
			
		||||
    .instance_init = isa_ipmi_kcs_init,
 | 
			
		||||
    .class_init    = isa_ipmi_kcs_class_init,
 | 
			
		||||
    .interfaces = (InterfaceInfo[]) {
 | 
			
		||||
        { TYPE_IPMI_INTERFACE },
 | 
			
		||||
        { }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ipmi_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&isa_ipmi_kcs_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(ipmi_register_types)
 | 
			
		||||
@@ -1 +1,2 @@
 | 
			
		||||
common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o
 | 
			
		||||
common-obj-$(CONFIG_NVDIMM) += nvdimm.o
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								hw/mem/nvdimm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								hw/mem/nvdimm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Non-Volatile Dual In-line Memory Module Virtualization Implementation
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright(C) 2015 Intel Corporation.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:
 | 
			
		||||
 *  Xiao Guangrong <guangrong.xiao@linux.intel.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Currently, it only supports PMEM Virtualization.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library; if not, see <http://www.gnu.org/licenses/>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/mem/nvdimm.h"
 | 
			
		||||
 | 
			
		||||
static void nvdimm_class_init(ObjectClass *oc, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(oc);
 | 
			
		||||
 | 
			
		||||
    /* nvdimm hotplug has not been supported yet. */
 | 
			
		||||
    dc->hotpluggable = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static TypeInfo nvdimm_info = {
 | 
			
		||||
    .name          = TYPE_NVDIMM,
 | 
			
		||||
    .parent        = TYPE_PC_DIMM,
 | 
			
		||||
    .class_init    = nvdimm_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void nvdimm_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&nvdimm_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(nvdimm_register_types)
 | 
			
		||||
@@ -81,6 +81,7 @@ petalogix_ml605_init(MachineState *machine)
 | 
			
		||||
 | 
			
		||||
    /* init CPUs */
 | 
			
		||||
    cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
 | 
			
		||||
    object_property_set_str(OBJECT(cpu), "8.10.a", "version", &error_abort);
 | 
			
		||||
    /* Use FPU but don't use floating point conversion and square
 | 
			
		||||
     * root instructions
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,7 @@ petalogix_s3adsp1800_init(MachineState *machine)
 | 
			
		||||
    MemoryRegion *sysmem = get_system_memory();
 | 
			
		||||
 | 
			
		||||
    cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU));
 | 
			
		||||
    object_property_set_str(OBJECT(cpu), "7.10.d", "version", &error_abort);
 | 
			
		||||
    object_property_set_bool(OBJECT(cpu), true, "realized", &error_abort);
 | 
			
		||||
 | 
			
		||||
    /* Attach emulated BRAM through the LMB.  */
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,8 @@ obj-$(CONFIG_NSERIES) += cbus.o
 | 
			
		||||
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
 | 
			
		||||
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
 | 
			
		||||
obj-$(CONFIG_IMX) += imx_ccm.o
 | 
			
		||||
obj-$(CONFIG_IMX) += imx31_ccm.o
 | 
			
		||||
obj-$(CONFIG_IMX) += imx25_ccm.o
 | 
			
		||||
obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
 | 
			
		||||
obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
 | 
			
		||||
obj-$(CONFIG_MAINSTONE) += mst_fpga.o
 | 
			
		||||
@@ -41,3 +43,4 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
 | 
			
		||||
obj-$(CONFIG_EDU) += edu.o
 | 
			
		||||
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										167
									
								
								hw/misc/hyperv_testdev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								hw/misc/hyperv_testdev.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,167 @@
 | 
			
		||||
/*
 | 
			
		||||
 * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Authors:
 | 
			
		||||
 *  Andrey Smetanin <asmetanin@virtuozzo.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
 * See the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/hw.h"
 | 
			
		||||
#include "hw/qdev.h"
 | 
			
		||||
#include "hw/isa/isa.h"
 | 
			
		||||
#include "sysemu/kvm.h"
 | 
			
		||||
#include "linux/kvm.h"
 | 
			
		||||
#include "target-i386/hyperv.h"
 | 
			
		||||
#include "kvm_i386.h"
 | 
			
		||||
 | 
			
		||||
#define HV_TEST_DEV_MAX_SINT_ROUTES 64
 | 
			
		||||
 | 
			
		||||
struct HypervTestDev {
 | 
			
		||||
    ISADevice parent_obj;
 | 
			
		||||
    MemoryRegion sint_control;
 | 
			
		||||
    HvSintRoute *sint_route[HV_TEST_DEV_MAX_SINT_ROUTES];
 | 
			
		||||
};
 | 
			
		||||
typedef struct HypervTestDev HypervTestDev;
 | 
			
		||||
 | 
			
		||||
#define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
 | 
			
		||||
#define HYPERV_TEST_DEV(obj) \
 | 
			
		||||
        OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
    HV_TEST_DEV_SINT_ROUTE_CREATE = 1,
 | 
			
		||||
    HV_TEST_DEV_SINT_ROUTE_DESTROY,
 | 
			
		||||
    HV_TEST_DEV_SINT_ROUTE_SET_SINT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int alloc_sint_route_index(HypervTestDev *dev)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
 | 
			
		||||
        if (dev->sint_route[i] == NULL) {
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void free_sint_route_index(HypervTestDev *dev, int i)
 | 
			
		||||
{
 | 
			
		||||
    assert(i >= 0 && i < ARRAY_SIZE(dev->sint_route));
 | 
			
		||||
    dev->sint_route[i] = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id,
 | 
			
		||||
                                 uint32_t sint)
 | 
			
		||||
{
 | 
			
		||||
    HvSintRoute *sint_route;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) {
 | 
			
		||||
        sint_route = dev->sint_route[i];
 | 
			
		||||
        if (sint_route && sint_route->vcpu_id == vcpu_id &&
 | 
			
		||||
            sint_route->sint == sint) {
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl,
 | 
			
		||||
                                      uint32_t vcpu_id, uint32_t sint)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    HvSintRoute *sint_route;
 | 
			
		||||
 | 
			
		||||
    switch (ctl) {
 | 
			
		||||
    case HV_TEST_DEV_SINT_ROUTE_CREATE:
 | 
			
		||||
        i = alloc_sint_route_index(dev);
 | 
			
		||||
        assert(i >= 0);
 | 
			
		||||
        sint_route = kvm_hv_sint_route_create(vcpu_id, sint, NULL);
 | 
			
		||||
        assert(sint_route);
 | 
			
		||||
        dev->sint_route[i] = sint_route;
 | 
			
		||||
        break;
 | 
			
		||||
    case HV_TEST_DEV_SINT_ROUTE_DESTROY:
 | 
			
		||||
        i = find_sint_route_index(dev, vcpu_id, sint);
 | 
			
		||||
        assert(i >= 0);
 | 
			
		||||
        sint_route = dev->sint_route[i];
 | 
			
		||||
        kvm_hv_sint_route_destroy(sint_route);
 | 
			
		||||
        free_sint_route_index(dev, i);
 | 
			
		||||
        break;
 | 
			
		||||
    case HV_TEST_DEV_SINT_ROUTE_SET_SINT:
 | 
			
		||||
        i = find_sint_route_index(dev, vcpu_id, sint);
 | 
			
		||||
        assert(i >= 0);
 | 
			
		||||
        sint_route = dev->sint_route[i];
 | 
			
		||||
        kvm_hv_sint_route_set_sint(sint_route);
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data,
 | 
			
		||||
                                uint32_t len)
 | 
			
		||||
{
 | 
			
		||||
    HypervTestDev *dev = HYPERV_TEST_DEV(opaque);
 | 
			
		||||
    uint8_t ctl;
 | 
			
		||||
 | 
			
		||||
    ctl = (data >> 16ULL) & 0xFF;
 | 
			
		||||
    switch (ctl) {
 | 
			
		||||
    case HV_TEST_DEV_SINT_ROUTE_CREATE:
 | 
			
		||||
    case HV_TEST_DEV_SINT_ROUTE_DESTROY:
 | 
			
		||||
    case HV_TEST_DEV_SINT_ROUTE_SET_SINT: {
 | 
			
		||||
        uint8_t sint = data & 0xFF;
 | 
			
		||||
        uint8_t vcpu_id = (data >> 8ULL) & 0xFF;
 | 
			
		||||
        hv_synic_test_dev_control(dev, ctl, vcpu_id, sint);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    default:
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const MemoryRegionOps synic_test_sint_ops = {
 | 
			
		||||
    .write = hv_test_dev_control,
 | 
			
		||||
    .valid.min_access_size = 4,
 | 
			
		||||
    .valid.max_access_size = 4,
 | 
			
		||||
    .endianness = DEVICE_LITTLE_ENDIAN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void hv_test_dev_realizefn(DeviceState *d, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    ISADevice *isa = ISA_DEVICE(d);
 | 
			
		||||
    HypervTestDev *dev = HYPERV_TEST_DEV(d);
 | 
			
		||||
    MemoryRegion *io = isa_address_space_io(isa);
 | 
			
		||||
 | 
			
		||||
    memset(dev->sint_route, 0, sizeof(dev->sint_route));
 | 
			
		||||
    memory_region_init_io(&dev->sint_control, OBJECT(dev),
 | 
			
		||||
                          &synic_test_sint_ops, dev,
 | 
			
		||||
                          "hyperv-testdev-ctl", 4);
 | 
			
		||||
    memory_region_add_subregion(io, 0x3000, &dev->sint_control);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hv_test_dev_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 | 
			
		||||
    dc->realize = hv_test_dev_realizefn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo hv_test_dev_info = {
 | 
			
		||||
    .name           = TYPE_HYPERV_TEST_DEV,
 | 
			
		||||
    .parent         = TYPE_ISA_DEVICE,
 | 
			
		||||
    .instance_size  = sizeof(HypervTestDev),
 | 
			
		||||
    .class_init     = hv_test_dev_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void hv_test_dev_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&hv_test_dev_info);
 | 
			
		||||
}
 | 
			
		||||
type_init(hv_test_dev_register_types);
 | 
			
		||||
							
								
								
									
										341
									
								
								hw/misc/imx25_ccm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								hw/misc/imx25_ccm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,341 @@
 | 
			
		||||
/*
 | 
			
		||||
 * IMX25 Clock Control Module
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 NICTA
 | 
			
		||||
 * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
 * See the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 * To get the timer frequencies right, we need to emulate at least part of
 | 
			
		||||
 * the CCM.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/misc/imx25_ccm.h"
 | 
			
		||||
 | 
			
		||||
#ifndef DEBUG_IMX25_CCM
 | 
			
		||||
#define DEBUG_IMX25_CCM 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define DPRINTF(fmt, args...) \
 | 
			
		||||
    do { \
 | 
			
		||||
        if (DEBUG_IMX25_CCM) { \
 | 
			
		||||
            fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX25_CCM, \
 | 
			
		||||
                                             __func__, ##args); \
 | 
			
		||||
        } \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
static char const *imx25_ccm_reg_name(uint32_t reg)
 | 
			
		||||
{
 | 
			
		||||
    static char unknown[20];
 | 
			
		||||
 | 
			
		||||
    switch (reg) {
 | 
			
		||||
    case IMX25_CCM_MPCTL_REG:
 | 
			
		||||
        return "mpctl";
 | 
			
		||||
    case IMX25_CCM_UPCTL_REG:
 | 
			
		||||
        return "upctl";
 | 
			
		||||
    case IMX25_CCM_CCTL_REG:
 | 
			
		||||
        return "cctl";
 | 
			
		||||
    case IMX25_CCM_CGCR0_REG:
 | 
			
		||||
        return "cgcr0";
 | 
			
		||||
    case IMX25_CCM_CGCR1_REG:
 | 
			
		||||
        return "cgcr1";
 | 
			
		||||
    case IMX25_CCM_CGCR2_REG:
 | 
			
		||||
        return "cgcr2";
 | 
			
		||||
    case IMX25_CCM_PCDR0_REG:
 | 
			
		||||
        return "pcdr0";
 | 
			
		||||
    case IMX25_CCM_PCDR1_REG:
 | 
			
		||||
        return "pcdr1";
 | 
			
		||||
    case IMX25_CCM_PCDR2_REG:
 | 
			
		||||
        return "pcdr2";
 | 
			
		||||
    case IMX25_CCM_PCDR3_REG:
 | 
			
		||||
        return "pcdr3";
 | 
			
		||||
    case IMX25_CCM_RCSR_REG:
 | 
			
		||||
        return "rcsr";
 | 
			
		||||
    case IMX25_CCM_CRDR_REG:
 | 
			
		||||
        return "crdr";
 | 
			
		||||
    case IMX25_CCM_DCVR0_REG:
 | 
			
		||||
        return "dcvr0";
 | 
			
		||||
    case IMX25_CCM_DCVR1_REG:
 | 
			
		||||
        return "dcvr1";
 | 
			
		||||
    case IMX25_CCM_DCVR2_REG:
 | 
			
		||||
        return "dcvr2";
 | 
			
		||||
    case IMX25_CCM_DCVR3_REG:
 | 
			
		||||
        return "dcvr3";
 | 
			
		||||
    case IMX25_CCM_LTR0_REG:
 | 
			
		||||
        return "ltr0";
 | 
			
		||||
    case IMX25_CCM_LTR1_REG:
 | 
			
		||||
        return "ltr1";
 | 
			
		||||
    case IMX25_CCM_LTR2_REG:
 | 
			
		||||
        return "ltr2";
 | 
			
		||||
    case IMX25_CCM_LTR3_REG:
 | 
			
		||||
        return "ltr3";
 | 
			
		||||
    case IMX25_CCM_LTBR0_REG:
 | 
			
		||||
        return "ltbr0";
 | 
			
		||||
    case IMX25_CCM_LTBR1_REG:
 | 
			
		||||
        return "ltbr1";
 | 
			
		||||
    case IMX25_CCM_PMCR0_REG:
 | 
			
		||||
        return "pmcr0";
 | 
			
		||||
    case IMX25_CCM_PMCR1_REG:
 | 
			
		||||
        return "pmcr1";
 | 
			
		||||
    case IMX25_CCM_PMCR2_REG:
 | 
			
		||||
        return "pmcr2";
 | 
			
		||||
    case IMX25_CCM_MCR_REG:
 | 
			
		||||
        return "mcr";
 | 
			
		||||
    case IMX25_CCM_LPIMR0_REG:
 | 
			
		||||
        return "lpimr0";
 | 
			
		||||
    case IMX25_CCM_LPIMR1_REG:
 | 
			
		||||
        return "lpimr1";
 | 
			
		||||
    default:
 | 
			
		||||
        sprintf(unknown, "[%d ?]", reg);
 | 
			
		||||
        return unknown;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#define CKIH_FREQ 24000000 /* 24MHz crystal input */
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_imx25_ccm = {
 | 
			
		||||
    .name = TYPE_IMX25_CCM,
 | 
			
		||||
    .version_id = 1,
 | 
			
		||||
    .minimum_version_id = 1,
 | 
			
		||||
    .fields = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_UINT32_ARRAY(reg, IMX25CCMState, IMX25_CCM_MAX_REG),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint32_t imx25_ccm_get_mpll_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX25CCMState *s = IMX25_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], MPLL_BYPASS)) {
 | 
			
		||||
        freq = CKIH_FREQ;
 | 
			
		||||
    } else {
 | 
			
		||||
        freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_MPCTL_REG], CKIH_FREQ);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx25_ccm_get_upll_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq = 0;
 | 
			
		||||
    IMX25CCMState *s = IMX25_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    if (!EXTRACT(s->reg[IMX25_CCM_CCTL_REG], UPLL_DIS)) {
 | 
			
		||||
        freq = imx_ccm_calc_pll(s->reg[IMX25_CCM_UPCTL_REG], CKIH_FREQ);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx25_ccm_get_mcu_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX25CCMState *s = IMX25_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    freq = imx25_ccm_get_mpll_clk(dev);
 | 
			
		||||
 | 
			
		||||
    if (EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_SRC)) {
 | 
			
		||||
        freq = (freq * 3 / 4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    freq = freq / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], ARM_CLK_DIV));
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx25_ccm_get_ahb_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX25CCMState *s = IMX25_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    freq = imx25_ccm_get_mcu_clk(dev)
 | 
			
		||||
           / (1 + EXTRACT(s->reg[IMX25_CCM_CCTL_REG], AHB_CLK_DIV));
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx25_ccm_get_ipg_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
 | 
			
		||||
    freq = imx25_ccm_get_ahb_clk(dev) / 2;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx25_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq = 0;
 | 
			
		||||
    DPRINTF("Clock = %d)\n", clock);
 | 
			
		||||
 | 
			
		||||
    switch (clock) {
 | 
			
		||||
    case NOCLK:
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_MPLL:
 | 
			
		||||
        freq = imx25_ccm_get_mpll_clk(dev);
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_UPLL:
 | 
			
		||||
        freq = imx25_ccm_get_upll_clk(dev);
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_MCU:
 | 
			
		||||
        freq = imx25_ccm_get_mcu_clk(dev);
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_AHB:
 | 
			
		||||
        freq = imx25_ccm_get_ahb_clk(dev);
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_IPG:
 | 
			
		||||
        freq = imx25_ccm_get_ipg_clk(dev);
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_32k:
 | 
			
		||||
        freq = CKIL_FREQ;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
 | 
			
		||||
                      TYPE_IMX25_CCM, __func__, clock);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("Clock = %d) = %d\n", clock, freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx25_ccm_reset(DeviceState *dev)
 | 
			
		||||
{
 | 
			
		||||
    IMX25CCMState *s = IMX25_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    DPRINTF("\n");
 | 
			
		||||
 | 
			
		||||
    memset(s->reg, 0, IMX25_CCM_MAX_REG * sizeof(uint32_t));
 | 
			
		||||
    s->reg[IMX25_CCM_MPCTL_REG] = 0x800b2c01;
 | 
			
		||||
    s->reg[IMX25_CCM_UPCTL_REG] = 0x84042800;
 | 
			
		||||
    /* 
 | 
			
		||||
     * The value below gives:
 | 
			
		||||
     * CPU = 133 MHz, AHB = 66,5 MHz, IPG = 33 MHz. 
 | 
			
		||||
     */
 | 
			
		||||
    s->reg[IMX25_CCM_CCTL_REG]  = 0xd0030000;
 | 
			
		||||
    s->reg[IMX25_CCM_CGCR0_REG] = 0x028A0100;
 | 
			
		||||
    s->reg[IMX25_CCM_CGCR1_REG] = 0x04008100;
 | 
			
		||||
    s->reg[IMX25_CCM_CGCR2_REG] = 0x00000438;
 | 
			
		||||
    s->reg[IMX25_CCM_PCDR0_REG] = 0x01010101;
 | 
			
		||||
    s->reg[IMX25_CCM_PCDR1_REG] = 0x01010101;
 | 
			
		||||
    s->reg[IMX25_CCM_PCDR2_REG] = 0x01010101;
 | 
			
		||||
    s->reg[IMX25_CCM_PCDR3_REG] = 0x01010101;
 | 
			
		||||
    s->reg[IMX25_CCM_PMCR0_REG] = 0x00A00000;
 | 
			
		||||
    s->reg[IMX25_CCM_PMCR1_REG] = 0x0000A030;
 | 
			
		||||
    s->reg[IMX25_CCM_PMCR2_REG] = 0x0000A030;
 | 
			
		||||
    s->reg[IMX25_CCM_MCR_REG]   = 0x43000000;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * default boot will change the reset values to allow:
 | 
			
		||||
     * CPU = 399 MHz, AHB = 133 MHz, IPG = 66,5 MHz. 
 | 
			
		||||
     * For some reason, this doesn't work. With the value below, linux
 | 
			
		||||
     * detects a 88 MHz IPG CLK instead of 66,5 MHz.
 | 
			
		||||
    s->reg[IMX25_CCM_CCTL_REG]  = 0x20032000;
 | 
			
		||||
     */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t imx25_ccm_read(void *opaque, hwaddr offset, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    uint32 value = 0;
 | 
			
		||||
    IMX25CCMState *s = (IMX25CCMState *)opaque;
 | 
			
		||||
 | 
			
		||||
    if (offset < 0x70) {
 | 
			
		||||
        value = s->reg[offset >> 2];
 | 
			
		||||
    } else {
 | 
			
		||||
        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
 | 
			
		||||
                      HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
 | 
			
		||||
            value);
 | 
			
		||||
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx25_ccm_write(void *opaque, hwaddr offset, uint64_t value,
 | 
			
		||||
                            unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    IMX25CCMState *s = (IMX25CCMState *)opaque;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx25_ccm_reg_name(offset >> 2),
 | 
			
		||||
            (uint32_t)value);
 | 
			
		||||
 | 
			
		||||
    if (offset < 0x70) {
 | 
			
		||||
        /*
 | 
			
		||||
         * We will do a better implementation later. In particular some bits
 | 
			
		||||
         * cannot be written to.
 | 
			
		||||
         */
 | 
			
		||||
        s->reg[offset >> 2] = value;
 | 
			
		||||
    } else {
 | 
			
		||||
        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
 | 
			
		||||
                      HWADDR_PRIx "\n", TYPE_IMX25_CCM, __func__, offset);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct MemoryRegionOps imx25_ccm_ops = {
 | 
			
		||||
    .read = imx25_ccm_read,
 | 
			
		||||
    .write = imx25_ccm_write,
 | 
			
		||||
    .endianness = DEVICE_NATIVE_ENDIAN,
 | 
			
		||||
    .valid = {
 | 
			
		||||
        /*
 | 
			
		||||
         * Our device would not work correctly if the guest was doing
 | 
			
		||||
         * unaligned access. This might not be a limitation on the real
 | 
			
		||||
         * device but in practice there is no reason for a guest to access
 | 
			
		||||
         * this device unaligned.
 | 
			
		||||
         */
 | 
			
		||||
        .min_access_size = 4,
 | 
			
		||||
        .max_access_size = 4,
 | 
			
		||||
        .unaligned = false,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void imx25_ccm_init(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    DeviceState *dev = DEVICE(obj);
 | 
			
		||||
    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
 | 
			
		||||
    IMX25CCMState *s = IMX25_CCM(obj);
 | 
			
		||||
 | 
			
		||||
    memory_region_init_io(&s->iomem, OBJECT(dev), &imx25_ccm_ops, s,
 | 
			
		||||
                          TYPE_IMX25_CCM, 0x1000);
 | 
			
		||||
    sysbus_init_mmio(sd, &s->iomem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx25_ccm_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
			
		||||
    IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    dc->reset = imx25_ccm_reset;
 | 
			
		||||
    dc->vmsd = &vmstate_imx25_ccm;
 | 
			
		||||
    dc->desc = "i.MX25 Clock Control Module";
 | 
			
		||||
 | 
			
		||||
    ccm->get_clock_frequency = imx25_ccm_get_clock_frequency;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo imx25_ccm_info = {
 | 
			
		||||
    .name          = TYPE_IMX25_CCM,
 | 
			
		||||
    .parent        = TYPE_IMX_CCM,
 | 
			
		||||
    .instance_size = sizeof(IMX25CCMState),
 | 
			
		||||
    .instance_init = imx25_ccm_init,
 | 
			
		||||
    .class_init    = imx25_ccm_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void imx25_ccm_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&imx25_ccm_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(imx25_ccm_register_types)
 | 
			
		||||
							
								
								
									
										392
									
								
								hw/misc/imx31_ccm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								hw/misc/imx31_ccm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,392 @@
 | 
			
		||||
/*
 | 
			
		||||
 * IMX31 Clock Control Module
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 NICTA
 | 
			
		||||
 * Updated by Jean-Christophe Dubois <jcd@tribudubois.net>
 | 
			
		||||
 *
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
 * See the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 * To get the timer frequencies right, we need to emulate at least part of
 | 
			
		||||
 * the i.MX31 CCM.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/misc/imx31_ccm.h"
 | 
			
		||||
 | 
			
		||||
#define CKIH_FREQ 26000000 /* 26MHz crystal input */
 | 
			
		||||
 | 
			
		||||
#ifndef DEBUG_IMX31_CCM
 | 
			
		||||
#define DEBUG_IMX31_CCM 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define DPRINTF(fmt, args...) \
 | 
			
		||||
    do { \
 | 
			
		||||
        if (DEBUG_IMX31_CCM) { \
 | 
			
		||||
            fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX31_CCM, \
 | 
			
		||||
                                             __func__, ##args); \
 | 
			
		||||
        } \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
static char const *imx31_ccm_reg_name(uint32_t reg)
 | 
			
		||||
{
 | 
			
		||||
    switch (reg) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        return "CCMR";
 | 
			
		||||
    case 1:
 | 
			
		||||
        return "PDR0";
 | 
			
		||||
    case 2:
 | 
			
		||||
        return "PDR1";
 | 
			
		||||
    case 3:
 | 
			
		||||
        return "RCSR";
 | 
			
		||||
    case 4:
 | 
			
		||||
        return "MPCTL";
 | 
			
		||||
    case 5:
 | 
			
		||||
        return "UPCTL";
 | 
			
		||||
    case 6:
 | 
			
		||||
        return "SPCTL";
 | 
			
		||||
    case 7:
 | 
			
		||||
        return "COSR";
 | 
			
		||||
    case 8:
 | 
			
		||||
        return "CGR0";
 | 
			
		||||
    case 9:
 | 
			
		||||
        return "CGR1";
 | 
			
		||||
    case 10:
 | 
			
		||||
        return "CGR2";
 | 
			
		||||
    case 11:
 | 
			
		||||
        return "WIMR";
 | 
			
		||||
    case 12:
 | 
			
		||||
        return "LDC";
 | 
			
		||||
    case 13:
 | 
			
		||||
        return "DCVR0";
 | 
			
		||||
    case 14:
 | 
			
		||||
        return "DCVR1";
 | 
			
		||||
    case 15:
 | 
			
		||||
        return "DCVR2";
 | 
			
		||||
    case 16:
 | 
			
		||||
        return "DCVR3";
 | 
			
		||||
    case 17:
 | 
			
		||||
        return "LTR0";
 | 
			
		||||
    case 18:
 | 
			
		||||
        return "LTR1";
 | 
			
		||||
    case 19:
 | 
			
		||||
        return "LTR2";
 | 
			
		||||
    case 20:
 | 
			
		||||
        return "LTR3";
 | 
			
		||||
    case 21:
 | 
			
		||||
        return "LTBR0";
 | 
			
		||||
    case 22:
 | 
			
		||||
        return "LTBR1";
 | 
			
		||||
    case 23:
 | 
			
		||||
        return "PMCR0";
 | 
			
		||||
    case 24:
 | 
			
		||||
        return "PMCR1";
 | 
			
		||||
    case 25:
 | 
			
		||||
        return "PDR2";
 | 
			
		||||
    default:
 | 
			
		||||
        return "???";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_imx31_ccm = {
 | 
			
		||||
    .name = TYPE_IMX31_CCM,
 | 
			
		||||
    .version_id = 1,
 | 
			
		||||
    .minimum_version_id = 1,
 | 
			
		||||
    .fields = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_UINT32(ccmr, IMX31CCMState),
 | 
			
		||||
        VMSTATE_UINT32(pdr0, IMX31CCMState),
 | 
			
		||||
        VMSTATE_UINT32(pdr1, IMX31CCMState),
 | 
			
		||||
        VMSTATE_UINT32(mpctl, IMX31CCMState),
 | 
			
		||||
        VMSTATE_UINT32(spctl, IMX31CCMState),
 | 
			
		||||
        VMSTATE_UINT32_ARRAY(cgr, IMX31CCMState, 3),
 | 
			
		||||
        VMSTATE_UINT32(pmcr0, IMX31CCMState),
 | 
			
		||||
        VMSTATE_UINT32(pmcr1, IMX31CCMState),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint32_t imx31_ccm_get_pll_ref_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq = 0;
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    if ((s->ccmr & CCMR_PRCS) == 2) {
 | 
			
		||||
        if (s->ccmr & CCMR_FPME) {
 | 
			
		||||
            freq = CKIL_FREQ;
 | 
			
		||||
            if (s->ccmr & CCMR_FPMF) {
 | 
			
		||||
                freq *= 1024;
 | 
			
		||||
            }
 | 
			
		||||
        } 
 | 
			
		||||
    } else {
 | 
			
		||||
        freq = CKIH_FREQ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx31_ccm_get_mpll_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    freq = imx_ccm_calc_pll(s->mpctl, imx31_ccm_get_pll_ref_clk(dev));
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx31_ccm_get_mcu_main_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
 | 
			
		||||
        freq = imx31_ccm_get_pll_ref_clk(dev);
 | 
			
		||||
    } else {
 | 
			
		||||
        freq = imx31_ccm_get_mpll_clk(dev);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx31_ccm_get_mcu_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MCU));
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx31_ccm_get_hsp_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, HSP));
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx31_ccm_get_hclk_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    freq = imx31_ccm_get_mcu_main_clk(dev) / (1 + EXTRACT(s->pdr0, MAX));
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx31_ccm_get_ipg_clk(IMXCCMState *dev)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    freq = imx31_ccm_get_hclk_clk(dev) / (1 + EXTRACT(s->pdr0, IPG));
 | 
			
		||||
 | 
			
		||||
    DPRINTF("freq = %d\n", freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t imx31_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t freq = 0;
 | 
			
		||||
 | 
			
		||||
    switch (clock) {
 | 
			
		||||
    case NOCLK:
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_MCU:
 | 
			
		||||
        freq = imx31_ccm_get_mcu_clk(dev);
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_HSP:
 | 
			
		||||
        freq = imx31_ccm_get_hsp_clk(dev);
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_IPG:
 | 
			
		||||
        freq = imx31_ccm_get_ipg_clk(dev);
 | 
			
		||||
        break;
 | 
			
		||||
    case CLK_32k:
 | 
			
		||||
        freq = CKIL_FREQ;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
 | 
			
		||||
                      TYPE_IMX31_CCM, __func__, clock);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("Clock = %d) = %d\n", clock, freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx31_ccm_reset(DeviceState *dev)
 | 
			
		||||
{
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    DPRINTF("()\n");
 | 
			
		||||
 | 
			
		||||
    s->ccmr   = 0x074b0b7d;
 | 
			
		||||
    s->pdr0   = 0xff870b48;
 | 
			
		||||
    s->pdr1   = 0x49fcfe7f;
 | 
			
		||||
    s->mpctl  = 0x04001800;
 | 
			
		||||
    s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
 | 
			
		||||
    s->spctl  = 0x04043001;
 | 
			
		||||
    s->pmcr0  = 0x80209828;
 | 
			
		||||
    s->pmcr1  = 0x00aa0000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t imx31_ccm_read(void *opaque, hwaddr offset, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    uint32 value = 0;
 | 
			
		||||
    IMX31CCMState *s = (IMX31CCMState *)opaque;
 | 
			
		||||
 | 
			
		||||
    switch (offset >> 2) {
 | 
			
		||||
    case 0: /* CCMR */
 | 
			
		||||
        value = s->ccmr;
 | 
			
		||||
        break;
 | 
			
		||||
    case 1:
 | 
			
		||||
        value = s->pdr0;
 | 
			
		||||
        break;
 | 
			
		||||
    case 2:
 | 
			
		||||
        value = s->pdr1;
 | 
			
		||||
        break;
 | 
			
		||||
    case 4:
 | 
			
		||||
        value = s->mpctl;
 | 
			
		||||
        break;
 | 
			
		||||
    case 6:
 | 
			
		||||
        value = s->spctl;
 | 
			
		||||
        break;
 | 
			
		||||
    case 8:
 | 
			
		||||
        value = s->cgr[0];
 | 
			
		||||
        break;
 | 
			
		||||
    case 9:
 | 
			
		||||
        value = s->cgr[1];
 | 
			
		||||
        break;
 | 
			
		||||
    case 10:
 | 
			
		||||
        value = s->cgr[2];
 | 
			
		||||
        break;
 | 
			
		||||
    case 18: /* LTR1 */
 | 
			
		||||
        value = 0x00004040;
 | 
			
		||||
        break;
 | 
			
		||||
    case 23:
 | 
			
		||||
        value = s->pmcr0;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
 | 
			
		||||
                      HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
 | 
			
		||||
            value);
 | 
			
		||||
 | 
			
		||||
    return (uint64_t)value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx31_ccm_write(void *opaque, hwaddr offset, uint64_t value,
 | 
			
		||||
                            unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    IMX31CCMState *s = (IMX31CCMState *)opaque;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx31_ccm_reg_name(offset >> 2),
 | 
			
		||||
            (uint32_t)value);
 | 
			
		||||
 | 
			
		||||
    switch (offset >> 2) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
 | 
			
		||||
        break;
 | 
			
		||||
    case 1:
 | 
			
		||||
        s->pdr0 = value & 0xff9f3fff;
 | 
			
		||||
        break;
 | 
			
		||||
    case 2:
 | 
			
		||||
        s->pdr1 = value;
 | 
			
		||||
        break;
 | 
			
		||||
    case 4:
 | 
			
		||||
        s->mpctl = value & 0xbfff3fff;
 | 
			
		||||
        break;
 | 
			
		||||
    case 6:
 | 
			
		||||
        s->spctl = value & 0xbfff3fff;
 | 
			
		||||
        break;
 | 
			
		||||
    case 8:
 | 
			
		||||
        s->cgr[0] = value;
 | 
			
		||||
        break;
 | 
			
		||||
    case 9:
 | 
			
		||||
        s->cgr[1] = value;
 | 
			
		||||
        break;
 | 
			
		||||
    case 10:
 | 
			
		||||
        s->cgr[2] = value;
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
 | 
			
		||||
                      HWADDR_PRIx "\n", TYPE_IMX31_CCM, __func__, offset);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct MemoryRegionOps imx31_ccm_ops = {
 | 
			
		||||
    .read = imx31_ccm_read,
 | 
			
		||||
    .write = imx31_ccm_write,
 | 
			
		||||
    .endianness = DEVICE_NATIVE_ENDIAN,
 | 
			
		||||
    .valid = {
 | 
			
		||||
        /*
 | 
			
		||||
         * Our device would not work correctly if the guest was doing
 | 
			
		||||
         * unaligned access. This might not be a limitation on the real
 | 
			
		||||
         * device but in practice there is no reason for a guest to access
 | 
			
		||||
         * this device unaligned.
 | 
			
		||||
         */
 | 
			
		||||
        .min_access_size = 4,
 | 
			
		||||
        .max_access_size = 4,
 | 
			
		||||
        .unaligned = false,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void imx31_ccm_init(Object *obj)
 | 
			
		||||
{
 | 
			
		||||
    DeviceState *dev = DEVICE(obj);
 | 
			
		||||
    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
 | 
			
		||||
    IMX31CCMState *s = IMX31_CCM(obj);
 | 
			
		||||
 | 
			
		||||
    memory_region_init_io(&s->iomem, OBJECT(dev), &imx31_ccm_ops, s,
 | 
			
		||||
                          TYPE_IMX31_CCM, 0x1000);
 | 
			
		||||
    sysbus_init_mmio(sd, &s->iomem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx31_ccm_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc  = DEVICE_CLASS(klass);
 | 
			
		||||
    IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    dc->reset = imx31_ccm_reset;
 | 
			
		||||
    dc->vmsd  = &vmstate_imx31_ccm;
 | 
			
		||||
    dc->desc  = "i.MX31 Clock Control Module";
 | 
			
		||||
 | 
			
		||||
    ccm->get_clock_frequency = imx31_ccm_get_clock_frequency;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo imx31_ccm_info = {
 | 
			
		||||
    .name          = TYPE_IMX31_CCM,
 | 
			
		||||
    .parent        = TYPE_IMX_CCM,
 | 
			
		||||
    .instance_size = sizeof(IMX31CCMState),
 | 
			
		||||
    .instance_init = imx31_ccm_init,
 | 
			
		||||
    .class_init    = imx31_ccm_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void imx31_ccm_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&imx31_ccm_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(imx31_ccm_register_types)
 | 
			
		||||
@@ -7,15 +7,12 @@
 | 
			
		||||
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | 
			
		||||
 * See the COPYING file in the top-level directory.
 | 
			
		||||
 *
 | 
			
		||||
 * To get the timer frequencies right, we need to emulate at least part of
 | 
			
		||||
 * the CCM.
 | 
			
		||||
 * This is an abstract base class used to get a common interface to
 | 
			
		||||
 * retrieve the CCM frequencies from the various i.MX SOC.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/misc/imx_ccm.h"
 | 
			
		||||
 | 
			
		||||
#define CKIH_FREQ 26000000 /* 26MHz crystal input */
 | 
			
		||||
#define CKIL_FREQ    32768 /* nominal 32khz clock */
 | 
			
		||||
 | 
			
		||||
#ifndef DEBUG_IMX_CCM
 | 
			
		||||
#define DEBUG_IMX_CCM 0
 | 
			
		||||
#endif
 | 
			
		||||
@@ -28,51 +25,27 @@
 | 
			
		||||
        } \
 | 
			
		||||
    } while (0)
 | 
			
		||||
 | 
			
		||||
static int imx_ccm_post_load(void *opaque, int version_id);
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_imx_ccm = {
 | 
			
		||||
    .name = TYPE_IMX_CCM,
 | 
			
		||||
    .version_id = 1,
 | 
			
		||||
    .minimum_version_id = 1,
 | 
			
		||||
    .fields = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_UINT32(ccmr, IMXCCMState),
 | 
			
		||||
        VMSTATE_UINT32(pdr0, IMXCCMState),
 | 
			
		||||
        VMSTATE_UINT32(pdr1, IMXCCMState),
 | 
			
		||||
        VMSTATE_UINT32(mpctl, IMXCCMState),
 | 
			
		||||
        VMSTATE_UINT32(spctl, IMXCCMState),
 | 
			
		||||
        VMSTATE_UINT32_ARRAY(cgr, IMXCCMState, 3),
 | 
			
		||||
        VMSTATE_UINT32(pmcr0, IMXCCMState),
 | 
			
		||||
        VMSTATE_UINT32(pmcr1, IMXCCMState),
 | 
			
		||||
        VMSTATE_UINT32(pll_refclk_freq, IMXCCMState),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    },
 | 
			
		||||
    .post_load = imx_ccm_post_load,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint32_t imx_clock_frequency(DeviceState *dev, IMXClk clock)
 | 
			
		||||
uint32_t imx_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
 | 
			
		||||
{
 | 
			
		||||
    IMXCCMState *s = IMX_CCM(dev);
 | 
			
		||||
    uint32_t freq = 0;
 | 
			
		||||
    IMXCCMClass *klass = IMX_GET_CLASS(dev);
 | 
			
		||||
 | 
			
		||||
    switch (clock) {
 | 
			
		||||
    case NOCLK:
 | 
			
		||||
        return 0;
 | 
			
		||||
    case MCU:
 | 
			
		||||
        return s->mcu_clk_freq;
 | 
			
		||||
    case HSP:
 | 
			
		||||
        return s->hsp_clk_freq;
 | 
			
		||||
    case IPG:
 | 
			
		||||
        return s->ipg_clk_freq;
 | 
			
		||||
    case CLK_32k:
 | 
			
		||||
        return CKIL_FREQ;
 | 
			
		||||
    if (klass->get_clock_frequency) {
 | 
			
		||||
        freq = klass->get_clock_frequency(dev, clock);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("(clock = %d) = %d\n", clock, freq);
 | 
			
		||||
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Calculate PLL output frequency
 | 
			
		||||
 */
 | 
			
		||||
static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
 | 
			
		||||
uint32_t imx_ccm_calc_pll(uint32_t pllreg, uint32_t base_freq)
 | 
			
		||||
{
 | 
			
		||||
    int32_t freq;
 | 
			
		||||
    int32_t mfn = MFN(pllreg);  /* Numerator */
 | 
			
		||||
    uint32_t mfi = MFI(pllreg); /* Integer part */
 | 
			
		||||
    uint32_t mfd = 1 + MFD(pllreg); /* Denominator */
 | 
			
		||||
@@ -81,186 +54,26 @@ static uint32_t calc_pll(uint32_t pllreg, uint32_t base_freq)
 | 
			
		||||
    if (mfi < 5) {
 | 
			
		||||
        mfi = 5;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* mfn is 10-bit signed twos-complement */
 | 
			
		||||
    mfn <<= 32 - 10;
 | 
			
		||||
    mfn >>= 32 - 10;
 | 
			
		||||
 | 
			
		||||
    return ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
 | 
			
		||||
    freq = ((2 * (base_freq >> 10) * (mfi * mfd + mfn)) /
 | 
			
		||||
            (mfd * pd)) << 10;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void update_clocks(IMXCCMState *s)
 | 
			
		||||
{
 | 
			
		||||
    /*
 | 
			
		||||
     * If we ever emulate more clocks, this should switch to a data-driven
 | 
			
		||||
     * approach
 | 
			
		||||
     */
 | 
			
		||||
    DPRINTF("(pllreg = 0x%08x, base_freq = %d) = %d\n", pllreg, base_freq,
 | 
			
		||||
            freq);
 | 
			
		||||
 | 
			
		||||
    if ((s->ccmr & CCMR_PRCS) == 2) {
 | 
			
		||||
        s->pll_refclk_freq = CKIL_FREQ * 1024;
 | 
			
		||||
    } else {
 | 
			
		||||
        s->pll_refclk_freq = CKIH_FREQ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* ipg_clk_arm aka MCU clock */
 | 
			
		||||
    if ((s->ccmr & CCMR_MDS) || !(s->ccmr & CCMR_MPE)) {
 | 
			
		||||
        s->mcu_clk_freq = s->pll_refclk_freq;
 | 
			
		||||
    } else {
 | 
			
		||||
        s->mcu_clk_freq = calc_pll(s->mpctl, s->pll_refclk_freq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* High-speed clock */
 | 
			
		||||
    s->hsp_clk_freq = s->mcu_clk_freq / (1 + EXTRACT(s->pdr0, HSP));
 | 
			
		||||
    s->ipg_clk_freq = s->hsp_clk_freq / (1 + EXTRACT(s->pdr0, IPG));
 | 
			
		||||
 | 
			
		||||
    DPRINTF("mcu %uMHz, HSP %uMHz, IPG %uHz\n",
 | 
			
		||||
            s->mcu_clk_freq / 1000000,
 | 
			
		||||
            s->hsp_clk_freq / 1000000,
 | 
			
		||||
            s->ipg_clk_freq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx_ccm_reset(DeviceState *dev)
 | 
			
		||||
{
 | 
			
		||||
    IMXCCMState *s = IMX_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    s->ccmr = 0x074b0b7b;
 | 
			
		||||
    s->pdr0 = 0xff870b48;
 | 
			
		||||
    s->pdr1 = 0x49fcfe7f;
 | 
			
		||||
    s->mpctl = PLL_PD(1) | PLL_MFD(0) | PLL_MFI(6) | PLL_MFN(0);
 | 
			
		||||
    s->cgr[0] = s->cgr[1] = s->cgr[2] = 0xffffffff;
 | 
			
		||||
    s->spctl = PLL_PD(1) | PLL_MFD(4) | PLL_MFI(0xc) | PLL_MFN(1);
 | 
			
		||||
    s->pmcr0 = 0x80209828;
 | 
			
		||||
 | 
			
		||||
    update_clocks(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t imx_ccm_read(void *opaque, hwaddr offset,
 | 
			
		||||
                                unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    IMXCCMState *s = (IMXCCMState *)opaque;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("(offset=0x%" HWADDR_PRIx ")\n", offset);
 | 
			
		||||
 | 
			
		||||
    switch (offset >> 2) {
 | 
			
		||||
    case 0: /* CCMR */
 | 
			
		||||
        DPRINTF(" ccmr = 0x%x\n", s->ccmr);
 | 
			
		||||
        return s->ccmr;
 | 
			
		||||
    case 1:
 | 
			
		||||
        DPRINTF(" pdr0 = 0x%x\n", s->pdr0);
 | 
			
		||||
        return s->pdr0;
 | 
			
		||||
    case 2:
 | 
			
		||||
        DPRINTF(" pdr1 = 0x%x\n", s->pdr1);
 | 
			
		||||
        return s->pdr1;
 | 
			
		||||
    case 4:
 | 
			
		||||
        DPRINTF(" mpctl = 0x%x\n", s->mpctl);
 | 
			
		||||
        return s->mpctl;
 | 
			
		||||
    case 6:
 | 
			
		||||
        DPRINTF(" spctl = 0x%x\n", s->spctl);
 | 
			
		||||
        return s->spctl;
 | 
			
		||||
    case 8:
 | 
			
		||||
        DPRINTF(" cgr0 = 0x%x\n", s->cgr[0]);
 | 
			
		||||
        return s->cgr[0];
 | 
			
		||||
    case 9:
 | 
			
		||||
        DPRINTF(" cgr1 = 0x%x\n", s->cgr[1]);
 | 
			
		||||
        return s->cgr[1];
 | 
			
		||||
    case 10:
 | 
			
		||||
        DPRINTF(" cgr2 = 0x%x\n", s->cgr[2]);
 | 
			
		||||
        return s->cgr[2];
 | 
			
		||||
    case 18: /* LTR1 */
 | 
			
		||||
        return 0x00004040;
 | 
			
		||||
    case 23:
 | 
			
		||||
        DPRINTF(" pcmr0 = 0x%x\n", s->pmcr0);
 | 
			
		||||
        return s->pmcr0;
 | 
			
		||||
    default:
 | 
			
		||||
        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
 | 
			
		||||
                      HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx_ccm_write(void *opaque, hwaddr offset,
 | 
			
		||||
                          uint64_t value, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    IMXCCMState *s = (IMXCCMState *)opaque;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("(offset=0x%" HWADDR_PRIx ", value = 0x%x)\n",
 | 
			
		||||
            offset, (unsigned int)value);
 | 
			
		||||
 | 
			
		||||
    switch (offset >> 2) {
 | 
			
		||||
    case 0:
 | 
			
		||||
        s->ccmr = CCMR_FPMF | (value & 0x3b6fdfff);
 | 
			
		||||
        break;
 | 
			
		||||
    case 1:
 | 
			
		||||
        s->pdr0 = value & 0xff9f3fff;
 | 
			
		||||
        break;
 | 
			
		||||
    case 2:
 | 
			
		||||
        s->pdr1 = value;
 | 
			
		||||
        break;
 | 
			
		||||
    case 4:
 | 
			
		||||
        s->mpctl = value & 0xbfff3fff;
 | 
			
		||||
        break;
 | 
			
		||||
    case 6:
 | 
			
		||||
        s->spctl = value & 0xbfff3fff;
 | 
			
		||||
        break;
 | 
			
		||||
    case 8:
 | 
			
		||||
        s->cgr[0] = value;
 | 
			
		||||
        return;
 | 
			
		||||
    case 9:
 | 
			
		||||
        s->cgr[1] = value;
 | 
			
		||||
        return;
 | 
			
		||||
    case 10:
 | 
			
		||||
        s->cgr[2] = value;
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
 | 
			
		||||
                      HWADDR_PRIx "\n", TYPE_IMX_CCM, __func__, offset);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    update_clocks(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct MemoryRegionOps imx_ccm_ops = {
 | 
			
		||||
    .read = imx_ccm_read,
 | 
			
		||||
    .write = imx_ccm_write,
 | 
			
		||||
    .endianness = DEVICE_NATIVE_ENDIAN,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int imx_ccm_init(SysBusDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
    IMXCCMState *s = IMX_CCM(dev);
 | 
			
		||||
 | 
			
		||||
    memory_region_init_io(&s->iomem, OBJECT(dev), &imx_ccm_ops, s,
 | 
			
		||||
                          TYPE_IMX_CCM, 0x1000);
 | 
			
		||||
    sysbus_init_mmio(dev, &s->iomem);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int imx_ccm_post_load(void *opaque, int version_id)
 | 
			
		||||
{
 | 
			
		||||
    IMXCCMState *s = (IMXCCMState *)opaque;
 | 
			
		||||
 | 
			
		||||
    update_clocks(s);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void imx_ccm_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
			
		||||
    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    sbc->init = imx_ccm_init;
 | 
			
		||||
    dc->reset = imx_ccm_reset;
 | 
			
		||||
    dc->vmsd = &vmstate_imx_ccm;
 | 
			
		||||
    dc->desc = "i.MX Clock Control Module";
 | 
			
		||||
    return freq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo imx_ccm_info = {
 | 
			
		||||
    .name = TYPE_IMX_CCM,
 | 
			
		||||
    .parent = TYPE_SYS_BUS_DEVICE,
 | 
			
		||||
    .name          = TYPE_IMX_CCM,
 | 
			
		||||
    .parent        = TYPE_SYS_BUS_DEVICE,
 | 
			
		||||
    .instance_size = sizeof(IMXCCMState),
 | 
			
		||||
    .class_init = imx_ccm_class_init,
 | 
			
		||||
    .class_size    = sizeof(IMXCCMClass),
 | 
			
		||||
    .abstract      = true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void imx_ccm_register_types(void)
 | 
			
		||||
 
 | 
			
		||||
@@ -252,7 +252,8 @@ static void fw_cfg_write(FWCfgState *s, uint8_t value)
 | 
			
		||||
 | 
			
		||||
static int fw_cfg_select(FWCfgState *s, uint16_t key)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
    int arch, ret;
 | 
			
		||||
    FWCfgEntry *e;
 | 
			
		||||
 | 
			
		||||
    s->cur_offset = 0;
 | 
			
		||||
    if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
 | 
			
		||||
@@ -261,41 +262,45 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key)
 | 
			
		||||
    } else {
 | 
			
		||||
        s->cur_entry = key;
 | 
			
		||||
        ret = 1;
 | 
			
		||||
        /* entry successfully selected, now run callback if present */
 | 
			
		||||
        arch = !!(key & FW_CFG_ARCH_LOCAL);
 | 
			
		||||
        e = &s->entries[arch][key & FW_CFG_ENTRY_MASK];
 | 
			
		||||
        if (e->read_callback) {
 | 
			
		||||
            e->read_callback(e->callback_opaque);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_fw_cfg_select(s, key, ret);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t fw_cfg_read(FWCfgState *s)
 | 
			
		||||
{
 | 
			
		||||
    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
 | 
			
		||||
    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
 | 
			
		||||
    uint8_t ret;
 | 
			
		||||
 | 
			
		||||
    if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
 | 
			
		||||
        ret = 0;
 | 
			
		||||
    else {
 | 
			
		||||
        if (e->read_callback) {
 | 
			
		||||
            e->read_callback(e->callback_opaque, s->cur_offset);
 | 
			
		||||
        }
 | 
			
		||||
        ret = e->data[s->cur_offset++];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_fw_cfg_read(s, ret);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr,
 | 
			
		||||
                                     unsigned size)
 | 
			
		||||
static uint64_t fw_cfg_data_read(void *opaque, hwaddr addr, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    FWCfgState *s = opaque;
 | 
			
		||||
    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
 | 
			
		||||
    FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
 | 
			
		||||
                    &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
 | 
			
		||||
    uint64_t value = 0;
 | 
			
		||||
    unsigned i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < size; ++i) {
 | 
			
		||||
        value = (value << 8) | fw_cfg_read(s);
 | 
			
		||||
    assert(size > 0 && size <= sizeof(value));
 | 
			
		||||
    if (s->cur_entry != FW_CFG_INVALID && e->data && s->cur_offset < e->len) {
 | 
			
		||||
        /* The least significant 'size' bytes of the return value are
 | 
			
		||||
         * expected to contain a string preserving portion of the item
 | 
			
		||||
         * data, padded with zeros on the right in case we run out early.
 | 
			
		||||
         * In technical terms, we're composing the host-endian representation
 | 
			
		||||
         * of the big endian interpretation of the fw_cfg string.
 | 
			
		||||
         */
 | 
			
		||||
        do {
 | 
			
		||||
            value = (value << 8) | e->data[s->cur_offset++];
 | 
			
		||||
        } while (--size && s->cur_offset < e->len);
 | 
			
		||||
        /* If size is still not zero, we *did* run out early, so continue
 | 
			
		||||
         * left-shifting, to add the appropriate number of padding zeros
 | 
			
		||||
         * on the right.
 | 
			
		||||
         */
 | 
			
		||||
        value <<= 8 * size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_fw_cfg_read(s, value);
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -338,7 +343,8 @@ static void fw_cfg_dma_transfer(FWCfgState *s)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
 | 
			
		||||
    e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
 | 
			
		||||
    e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
 | 
			
		||||
        &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
 | 
			
		||||
 | 
			
		||||
    if (dma.control & FW_CFG_DMA_CTL_READ) {
 | 
			
		||||
        read = 1;
 | 
			
		||||
@@ -371,10 +377,6 @@ static void fw_cfg_dma_transfer(FWCfgState *s)
 | 
			
		||||
                len = (e->len - s->cur_offset);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (e->read_callback) {
 | 
			
		||||
                e->read_callback(e->callback_opaque, s->cur_offset);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* If the access is not a read access, it will be a skip access,
 | 
			
		||||
             * tested before.
 | 
			
		||||
             */
 | 
			
		||||
@@ -451,12 +453,6 @@ static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr,
 | 
			
		||||
    return is_write && size == 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr,
 | 
			
		||||
                                 unsigned size)
 | 
			
		||||
{
 | 
			
		||||
    return fw_cfg_read(opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fw_cfg_comb_write(void *opaque, hwaddr addr,
 | 
			
		||||
                              uint64_t value, unsigned size)
 | 
			
		||||
{
 | 
			
		||||
@@ -483,7 +479,7 @@ static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const MemoryRegionOps fw_cfg_data_mem_ops = {
 | 
			
		||||
    .read = fw_cfg_data_mem_read,
 | 
			
		||||
    .read = fw_cfg_data_read,
 | 
			
		||||
    .write = fw_cfg_data_mem_write,
 | 
			
		||||
    .endianness = DEVICE_BIG_ENDIAN,
 | 
			
		||||
    .valid = {
 | 
			
		||||
@@ -494,7 +490,7 @@ static const MemoryRegionOps fw_cfg_data_mem_ops = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const MemoryRegionOps fw_cfg_comb_mem_ops = {
 | 
			
		||||
    .read = fw_cfg_comb_read,
 | 
			
		||||
    .read = fw_cfg_data_read,
 | 
			
		||||
    .write = fw_cfg_comb_write,
 | 
			
		||||
    .endianness = DEVICE_LITTLE_ENDIAN,
 | 
			
		||||
    .valid.accepts = fw_cfg_comb_valid,
 | 
			
		||||
@@ -513,7 +509,8 @@ static void fw_cfg_reset(DeviceState *d)
 | 
			
		||||
{
 | 
			
		||||
    FWCfgState *s = FW_CFG(d);
 | 
			
		||||
 | 
			
		||||
    fw_cfg_select(s, 0);
 | 
			
		||||
    /* we never register a read callback for FW_CFG_SIGNATURE */
 | 
			
		||||
    fw_cfg_select(s, FW_CFG_SIGNATURE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Save restore 32 bit int as uint16_t
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,9 @@
 | 
			
		||||
#define TYPE_PXB_BUS "pxb-bus"
 | 
			
		||||
#define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS)
 | 
			
		||||
 | 
			
		||||
#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus"
 | 
			
		||||
#define PXB_PCIE_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_PCIE_BUS)
 | 
			
		||||
 | 
			
		||||
typedef struct PXBBus {
 | 
			
		||||
    /*< private >*/
 | 
			
		||||
    PCIBus parent_obj;
 | 
			
		||||
@@ -34,6 +37,9 @@ typedef struct PXBBus {
 | 
			
		||||
#define TYPE_PXB_DEVICE "pxb"
 | 
			
		||||
#define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE)
 | 
			
		||||
 | 
			
		||||
#define TYPE_PXB_PCIE_DEVICE "pxb-pcie"
 | 
			
		||||
#define PXB_PCIE_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_PCIE_DEVICE)
 | 
			
		||||
 | 
			
		||||
typedef struct PXBDev {
 | 
			
		||||
    /*< private >*/
 | 
			
		||||
    PCIDevice parent_obj;
 | 
			
		||||
@@ -43,13 +49,18 @@ typedef struct PXBDev {
 | 
			
		||||
    uint16_t numa_node;
 | 
			
		||||
} PXBDev;
 | 
			
		||||
 | 
			
		||||
static PXBDev *convert_to_pxb(PCIDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
    return pci_bus_is_express(dev->bus) ? PXB_PCIE_DEV(dev) : PXB_DEV(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GList *pxb_dev_list;
 | 
			
		||||
 | 
			
		||||
#define TYPE_PXB_HOST "pxb-host"
 | 
			
		||||
 | 
			
		||||
static int pxb_bus_num(PCIBus *bus)
 | 
			
		||||
{
 | 
			
		||||
    PXBDev *pxb = PXB_DEV(bus->parent_dev);
 | 
			
		||||
    PXBDev *pxb = convert_to_pxb(bus->parent_dev);
 | 
			
		||||
 | 
			
		||||
    return pxb->bus_nr;
 | 
			
		||||
}
 | 
			
		||||
@@ -61,7 +72,7 @@ static bool pxb_is_root(PCIBus *bus)
 | 
			
		||||
 | 
			
		||||
static uint16_t pxb_bus_numa_node(PCIBus *bus)
 | 
			
		||||
{
 | 
			
		||||
    PXBDev *pxb = PXB_DEV(bus->parent_dev);
 | 
			
		||||
    PXBDev *pxb = convert_to_pxb(bus->parent_dev);
 | 
			
		||||
 | 
			
		||||
    return pxb->numa_node;
 | 
			
		||||
}
 | 
			
		||||
@@ -82,10 +93,18 @@ static const TypeInfo pxb_bus_info = {
 | 
			
		||||
    .class_init    = pxb_bus_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const TypeInfo pxb_pcie_bus_info = {
 | 
			
		||||
    .name          = TYPE_PXB_PCIE_BUS,
 | 
			
		||||
    .parent        = TYPE_PCIE_BUS,
 | 
			
		||||
    .instance_size = sizeof(PXBBus),
 | 
			
		||||
    .class_init    = pxb_bus_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *pxb_host_root_bus_path(PCIHostState *host_bridge,
 | 
			
		||||
                                          PCIBus *rootbus)
 | 
			
		||||
{
 | 
			
		||||
    PXBBus *bus = PXB_BUS(rootbus);
 | 
			
		||||
    PXBBus *bus = pci_bus_is_express(rootbus) ?
 | 
			
		||||
                  PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus);
 | 
			
		||||
 | 
			
		||||
    snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus));
 | 
			
		||||
    return bus->bus_path;
 | 
			
		||||
@@ -103,7 +122,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev)
 | 
			
		||||
 | 
			
		||||
    pxb_host = PCI_HOST_BRIDGE(dev);
 | 
			
		||||
    pxb_bus = pxb_host->bus;
 | 
			
		||||
    pxb_dev = PXB_DEV(pxb_bus->parent_dev);
 | 
			
		||||
    pxb_dev = convert_to_pxb(pxb_bus->parent_dev);
 | 
			
		||||
    position = g_list_index(pxb_dev_list, pxb_dev);
 | 
			
		||||
    assert(position >= 0);
 | 
			
		||||
 | 
			
		||||
@@ -193,10 +212,10 @@ static gint pxb_compare(gconstpointer a, gconstpointer b)
 | 
			
		||||
           0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pxb_dev_initfn(PCIDevice *dev)
 | 
			
		||||
static int pxb_dev_init_common(PCIDevice *dev, bool pcie)
 | 
			
		||||
{
 | 
			
		||||
    PXBDev *pxb = PXB_DEV(dev);
 | 
			
		||||
    DeviceState *ds, *bds;
 | 
			
		||||
    PXBDev *pxb = convert_to_pxb(dev);
 | 
			
		||||
    DeviceState *ds, *bds = NULL;
 | 
			
		||||
    PCIBus *bus;
 | 
			
		||||
    const char *dev_name = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -211,18 +230,21 @@ static int pxb_dev_initfn(PCIDevice *dev)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ds = qdev_create(NULL, TYPE_PXB_HOST);
 | 
			
		||||
    bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
 | 
			
		||||
    if (pcie) {
 | 
			
		||||
        bus = pci_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS);
 | 
			
		||||
    } else {
 | 
			
		||||
        bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS);
 | 
			
		||||
        bds = qdev_create(BUS(bus), "pci-bridge");
 | 
			
		||||
        bds->id = dev_name;
 | 
			
		||||
        qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
 | 
			
		||||
        qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bus->parent_dev = dev;
 | 
			
		||||
    bus->address_space_mem = dev->bus->address_space_mem;
 | 
			
		||||
    bus->address_space_io = dev->bus->address_space_io;
 | 
			
		||||
    bus->map_irq = pxb_map_irq_fn;
 | 
			
		||||
 | 
			
		||||
    bds = qdev_create(BUS(bus), "pci-bridge");
 | 
			
		||||
    bds->id = dev_name;
 | 
			
		||||
    qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr);
 | 
			
		||||
    qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false);
 | 
			
		||||
 | 
			
		||||
    PCI_HOST_BRIDGE(ds)->bus = bus;
 | 
			
		||||
 | 
			
		||||
    if (pxb_register_bus(dev, bus)) {
 | 
			
		||||
@@ -230,7 +252,9 @@ static int pxb_dev_initfn(PCIDevice *dev)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qdev_init_nofail(ds);
 | 
			
		||||
    qdev_init_nofail(bds);
 | 
			
		||||
    if (bds) {
 | 
			
		||||
        qdev_init_nofail(bds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pci_word_test_and_set_mask(dev->config + PCI_STATUS,
 | 
			
		||||
                               PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
 | 
			
		||||
@@ -240,9 +264,19 @@ static int pxb_dev_initfn(PCIDevice *dev)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pxb_dev_initfn(PCIDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
    if (pci_bus_is_express(dev->bus)) {
 | 
			
		||||
        error_report("pxb devices cannot reside on a PCIe bus!");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return pxb_dev_init_common(dev, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pxb_dev_exitfn(PCIDevice *pci_dev)
 | 
			
		||||
{
 | 
			
		||||
    PXBDev *pxb = PXB_DEV(pci_dev);
 | 
			
		||||
    PXBDev *pxb = convert_to_pxb(pci_dev);
 | 
			
		||||
 | 
			
		||||
    pxb_dev_list = g_list_remove(pxb_dev_list, pxb);
 | 
			
		||||
}
 | 
			
		||||
@@ -276,11 +310,45 @@ static const TypeInfo pxb_dev_info = {
 | 
			
		||||
    .class_init    = pxb_dev_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int pxb_pcie_dev_initfn(PCIDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
    if (!pci_bus_is_express(dev->bus)) {
 | 
			
		||||
        error_report("pxb-pcie devices cannot reside on a PCI bus!");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return pxb_dev_init_common(dev, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
			
		||||
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    k->init = pxb_pcie_dev_initfn;
 | 
			
		||||
    k->exit = pxb_dev_exitfn;
 | 
			
		||||
    k->vendor_id = PCI_VENDOR_ID_REDHAT;
 | 
			
		||||
    k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE;
 | 
			
		||||
    k->class_id = PCI_CLASS_BRIDGE_HOST;
 | 
			
		||||
 | 
			
		||||
    dc->desc = "PCI Express Expander Bridge";
 | 
			
		||||
    dc->props = pxb_dev_properties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const TypeInfo pxb_pcie_dev_info = {
 | 
			
		||||
    .name          = TYPE_PXB_PCIE_DEVICE,
 | 
			
		||||
    .parent        = TYPE_PCI_DEVICE,
 | 
			
		||||
    .instance_size = sizeof(PXBDev),
 | 
			
		||||
    .class_init    = pxb_pcie_dev_class_init,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void pxb_register_types(void)
 | 
			
		||||
{
 | 
			
		||||
    type_register_static(&pxb_bus_info);
 | 
			
		||||
    type_register_static(&pxb_pcie_bus_info);
 | 
			
		||||
    type_register_static(&pxb_host_info);
 | 
			
		||||
    type_register_static(&pxb_dev_info);
 | 
			
		||||
    type_register_static(&pxb_pcie_dev_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type_init(pxb_register_types)
 | 
			
		||||
 
 | 
			
		||||
@@ -1759,9 +1759,6 @@ void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier)
 | 
			
		||||
    if (notifier) {
 | 
			
		||||
        notifier_list_add(&req->cancel_notifiers, notifier);
 | 
			
		||||
    }
 | 
			
		||||
    if (req->io_canceled) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    scsi_req_ref(req);
 | 
			
		||||
    scsi_req_dequeue(req);
 | 
			
		||||
    req->io_canceled = true;
 | 
			
		||||
@@ -1841,11 +1838,13 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
 | 
			
		||||
{
 | 
			
		||||
    SCSIRequest *req;
 | 
			
		||||
 | 
			
		||||
    aio_context_acquire(blk_get_aio_context(sdev->conf.blk));
 | 
			
		||||
    while (!QTAILQ_EMPTY(&sdev->requests)) {
 | 
			
		||||
        req = QTAILQ_FIRST(&sdev->requests);
 | 
			
		||||
        scsi_req_cancel(req);
 | 
			
		||||
        scsi_req_cancel_async(req, NULL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    blk_drain(sdev->conf.blk);
 | 
			
		||||
    aio_context_release(blk_get_aio_context(sdev->conf.blk));
 | 
			
		||||
    scsi_device_set_ua(sdev, sense);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define PVSCSI_MSI_OFFSET        (0x50)
 | 
			
		||||
#define PVSCSI_USE_64BIT         (true)
 | 
			
		||||
#define PVSCSI_PER_VECTOR_MASK   (false)
 | 
			
		||||
 | 
			
		||||
@@ -49,9 +48,33 @@
 | 
			
		||||
    (stl_le_pci_dma(&container_of(m, PVSCSIState, rings)->parent_obj, \
 | 
			
		||||
                 (m)->rs_pa + offsetof(struct PVSCSIRingsState, field), val))
 | 
			
		||||
 | 
			
		||||
typedef struct PVSCSIClass {
 | 
			
		||||
    PCIDeviceClass parent_class;
 | 
			
		||||
    DeviceRealize parent_dc_realize;
 | 
			
		||||
} PVSCSIClass;
 | 
			
		||||
 | 
			
		||||
#define TYPE_PVSCSI "pvscsi"
 | 
			
		||||
#define PVSCSI(obj) OBJECT_CHECK(PVSCSIState, (obj), TYPE_PVSCSI)
 | 
			
		||||
 | 
			
		||||
#define PVSCSI_DEVICE_CLASS(klass) \
 | 
			
		||||
    OBJECT_CLASS_CHECK(PVSCSIClass, (klass), TYPE_PVSCSI)
 | 
			
		||||
#define PVSCSI_DEVICE_GET_CLASS(obj) \
 | 
			
		||||
    OBJECT_GET_CLASS(PVSCSIClass, (obj), TYPE_PVSCSI)
 | 
			
		||||
 | 
			
		||||
/* Compatability flags for migration */
 | 
			
		||||
#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0
 | 
			
		||||
#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \
 | 
			
		||||
    (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT)
 | 
			
		||||
#define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1
 | 
			
		||||
#define PVSCSI_COMPAT_DISABLE_PCIE \
 | 
			
		||||
    (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT)
 | 
			
		||||
 | 
			
		||||
#define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \
 | 
			
		||||
    ((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION)
 | 
			
		||||
#define PVSCSI_MSI_OFFSET(s) \
 | 
			
		||||
    (PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c)
 | 
			
		||||
#define PVSCSI_EXP_EP_OFFSET (0x40)
 | 
			
		||||
 | 
			
		||||
typedef struct PVSCSIRingInfo {
 | 
			
		||||
    uint64_t            rs_pa;
 | 
			
		||||
    uint32_t            txr_len_mask;
 | 
			
		||||
@@ -100,6 +123,8 @@ typedef struct {
 | 
			
		||||
 | 
			
		||||
    PVSCSIRingInfo rings;                /* Data transfer rings manager      */
 | 
			
		||||
    uint32_t resetting;                  /* Reset in progress                */
 | 
			
		||||
 | 
			
		||||
    uint32_t compat_flags;
 | 
			
		||||
} PVSCSIState;
 | 
			
		||||
 | 
			
		||||
typedef struct PVSCSIRequest {
 | 
			
		||||
@@ -1019,7 +1044,7 @@ pvscsi_init_msi(PVSCSIState *s)
 | 
			
		||||
    int res;
 | 
			
		||||
    PCIDevice *d = PCI_DEVICE(s);
 | 
			
		||||
 | 
			
		||||
    res = msi_init(d, PVSCSI_MSI_OFFSET, PVSCSI_MSIX_NUM_VECTORS,
 | 
			
		||||
    res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS,
 | 
			
		||||
                   PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK);
 | 
			
		||||
    if (res < 0) {
 | 
			
		||||
        trace_pvscsi_init_msi_fail(res);
 | 
			
		||||
@@ -1069,9 +1094,16 @@ pvscsi_init(PCIDevice *pci_dev)
 | 
			
		||||
 | 
			
		||||
    trace_pvscsi_state("init");
 | 
			
		||||
 | 
			
		||||
    /* PCI subsystem ID */
 | 
			
		||||
    pci_dev->config[PCI_SUBSYSTEM_ID] = 0x00;
 | 
			
		||||
    pci_dev->config[PCI_SUBSYSTEM_ID + 1] = 0x10;
 | 
			
		||||
    /* PCI subsystem ID, subsystem vendor ID, revision */
 | 
			
		||||
    if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) {
 | 
			
		||||
        pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000);
 | 
			
		||||
    } else {
 | 
			
		||||
        pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
 | 
			
		||||
                     PCI_VENDOR_ID_VMWARE);
 | 
			
		||||
        pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
 | 
			
		||||
                     PCI_DEVICE_ID_VMWARE_PVSCSI);
 | 
			
		||||
        pci_config_set_revision(pci_dev->config, 0x2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* PCI latency timer = 255 */
 | 
			
		||||
    pci_dev->config[PCI_LATENCY_TIMER] = 0xff;
 | 
			
		||||
@@ -1085,6 +1117,10 @@ pvscsi_init(PCIDevice *pci_dev)
 | 
			
		||||
 | 
			
		||||
    pvscsi_init_msi(s);
 | 
			
		||||
 | 
			
		||||
    if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus)) {
 | 
			
		||||
        pcie_endpoint_cap_init(pci_dev, PVSCSI_EXP_EP_OFFSET);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s->completion_worker = qemu_bh_new(pvscsi_process_completion_queue, s);
 | 
			
		||||
    if (!s->completion_worker) {
 | 
			
		||||
        pvscsi_cleanup_msi(s);
 | 
			
		||||
@@ -1139,6 +1175,27 @@ pvscsi_post_load(void *opaque, int version_id)
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool pvscsi_vmstate_need_pcie_device(void *opaque)
 | 
			
		||||
{
 | 
			
		||||
    PVSCSIState *s = PVSCSI(opaque);
 | 
			
		||||
 | 
			
		||||
    return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id)
 | 
			
		||||
{
 | 
			
		||||
    return !pvscsi_vmstate_need_pcie_device(opaque);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_pvscsi_pcie_device = {
 | 
			
		||||
    .name = "pvscsi/pcie",
 | 
			
		||||
    .needed = pvscsi_vmstate_need_pcie_device,
 | 
			
		||||
    .fields = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_PCIE_DEVICE(parent_obj, PVSCSIState),
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const VMStateDescription vmstate_pvscsi = {
 | 
			
		||||
    .name = "pvscsi",
 | 
			
		||||
    .version_id = 0,
 | 
			
		||||
@@ -1146,7 +1203,9 @@ static const VMStateDescription vmstate_pvscsi = {
 | 
			
		||||
    .pre_save = pvscsi_pre_save,
 | 
			
		||||
    .post_load = pvscsi_post_load,
 | 
			
		||||
    .fields = (VMStateField[]) {
 | 
			
		||||
        VMSTATE_PCI_DEVICE(parent_obj, PVSCSIState),
 | 
			
		||||
        VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState,
 | 
			
		||||
                            pvscsi_vmstate_test_pci_device, 0,
 | 
			
		||||
                            vmstate_pci_device, PCIDevice),
 | 
			
		||||
        VMSTATE_UINT8(msi_used, PVSCSIState),
 | 
			
		||||
        VMSTATE_UINT32(resetting, PVSCSIState),
 | 
			
		||||
        VMSTATE_UINT64(reg_interrupt_status, PVSCSIState),
 | 
			
		||||
@@ -1171,18 +1230,40 @@ static const VMStateDescription vmstate_pvscsi = {
 | 
			
		||||
        VMSTATE_UINT64(rings.filled_cmp_ptr, PVSCSIState),
 | 
			
		||||
 | 
			
		||||
        VMSTATE_END_OF_LIST()
 | 
			
		||||
    },
 | 
			
		||||
    .subsections = (const VMStateDescription*[]) {
 | 
			
		||||
        &vmstate_pvscsi_pcie_device,
 | 
			
		||||
        NULL
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static Property pvscsi_properties[] = {
 | 
			
		||||
    DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1),
 | 
			
		||||
    DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags,
 | 
			
		||||
                    PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false),
 | 
			
		||||
    DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags,
 | 
			
		||||
                    PVSCSI_COMPAT_DISABLE_PCIE_BIT, false),
 | 
			
		||||
    DEFINE_PROP_END_OF_LIST(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void pvscsi_realize(DeviceState *qdev, Error **errp)
 | 
			
		||||
{
 | 
			
		||||
    PVSCSIClass *pvs_c = PVSCSI_DEVICE_GET_CLASS(qdev);
 | 
			
		||||
    PCIDevice *pci_dev = PCI_DEVICE(qdev);
 | 
			
		||||
    PVSCSIState *s = PVSCSI(qdev);
 | 
			
		||||
 | 
			
		||||
    if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) {
 | 
			
		||||
        pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pvs_c->parent_dc_realize(qdev, errp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pvscsi_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
{
 | 
			
		||||
    DeviceClass *dc = DEVICE_CLASS(klass);
 | 
			
		||||
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
 | 
			
		||||
    PVSCSIClass *pvs_k = PVSCSI_DEVICE_CLASS(klass);
 | 
			
		||||
    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
 | 
			
		||||
 | 
			
		||||
    k->init = pvscsi_init;
 | 
			
		||||
@@ -1191,6 +1272,8 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
    k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI;
 | 
			
		||||
    k->class_id = PCI_CLASS_STORAGE_SCSI;
 | 
			
		||||
    k->subsystem_id = 0x1000;
 | 
			
		||||
    pvs_k->parent_dc_realize = dc->realize;
 | 
			
		||||
    dc->realize = pvscsi_realize;
 | 
			
		||||
    dc->reset = pvscsi_reset;
 | 
			
		||||
    dc->vmsd = &vmstate_pvscsi;
 | 
			
		||||
    dc->props = pvscsi_properties;
 | 
			
		||||
@@ -1202,6 +1285,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data)
 | 
			
		||||
static const TypeInfo pvscsi_info = {
 | 
			
		||||
    .name          = TYPE_PVSCSI,
 | 
			
		||||
    .parent        = TYPE_PCI_DEVICE,
 | 
			
		||||
    .class_size    = sizeof(PVSCSIClass),
 | 
			
		||||
    .instance_size = sizeof(PVSCSIState),
 | 
			
		||||
    .class_init    = pvscsi_class_init,
 | 
			
		||||
    .interfaces = (InterfaceInfo[]) {
 | 
			
		||||
 
 | 
			
		||||
@@ -193,7 +193,9 @@ static void sdhci_reset(SDHCIState *s)
 | 
			
		||||
     * initialization */
 | 
			
		||||
    memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
 | 
			
		||||
 | 
			
		||||
    sd_set_cb(s->card, s->ro_cb, s->eject_cb);
 | 
			
		||||
    if (!s->noeject_quirk) {
 | 
			
		||||
        sd_set_cb(s->card, s->ro_cb, s->eject_cb);
 | 
			
		||||
    }
 | 
			
		||||
    s->data_count = 0;
 | 
			
		||||
    s->stopped_state = sdhc_not_stopped;
 | 
			
		||||
}
 | 
			
		||||
@@ -243,9 +245,6 @@ static void sdhci_send_command(SDHCIState *s)
 | 
			
		||||
            (s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) {
 | 
			
		||||
            s->norintsts |= SDHC_NIS_TRSCMP;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (rlen != 0 && (s->errintstsen & SDHC_EISEN_CMDIDX)) {
 | 
			
		||||
        s->errintsts |= SDHC_EIS_CMDIDX;
 | 
			
		||||
        s->norintsts |= SDHC_NIS_ERR;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (s->norintstsen & SDHC_NISEN_CMDCMP) {
 | 
			
		||||
@@ -831,7 +830,7 @@ static void sdhci_data_transfer(void *opaque)
 | 
			
		||||
 | 
			
		||||
static bool sdhci_can_issue_command(SDHCIState *s)
 | 
			
		||||
{
 | 
			
		||||
    if (!SDHC_CLOCK_IS_ON(s->clkcon) || !(s->pwrcon & SDHC_POWER_ON) ||
 | 
			
		||||
    if (!SDHC_CLOCK_IS_ON(s->clkcon) ||
 | 
			
		||||
        (((s->prnsts & SDHC_DATA_INHIBIT) || s->stopped_state) &&
 | 
			
		||||
        ((s->cmdreg & SDHC_CMD_DATA_PRESENT) ||
 | 
			
		||||
        ((s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY &&
 | 
			
		||||
@@ -1279,6 +1278,7 @@ static Property sdhci_sysbus_properties[] = {
 | 
			
		||||
    DEFINE_PROP_UINT32("capareg", SDHCIState, capareg,
 | 
			
		||||
            SDHC_CAPAB_REG_DEFAULT),
 | 
			
		||||
    DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0),
 | 
			
		||||
    DEFINE_PROP_BOOL("noeject-quirk", SDHCIState, noeject_quirk, false),
 | 
			
		||||
    DEFINE_PROP_END_OF_LIST(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -363,6 +363,8 @@ void cpu_put_timer(QEMUFile *f, CPUTimer *s)
 | 
			
		||||
    qemu_put_be32s(f, &s->frequency);
 | 
			
		||||
    qemu_put_be32s(f, &s->disabled);
 | 
			
		||||
    qemu_put_be64s(f, &s->disabled_mask);
 | 
			
		||||
    qemu_put_be32s(f, &s->npt);
 | 
			
		||||
    qemu_put_be64s(f, &s->npt_mask);
 | 
			
		||||
    qemu_put_sbe64s(f, &s->clock_offset);
 | 
			
		||||
 | 
			
		||||
    timer_put(f, s->qtimer);
 | 
			
		||||
@@ -373,6 +375,8 @@ void cpu_get_timer(QEMUFile *f, CPUTimer *s)
 | 
			
		||||
    qemu_get_be32s(f, &s->frequency);
 | 
			
		||||
    qemu_get_be32s(f, &s->disabled);
 | 
			
		||||
    qemu_get_be64s(f, &s->disabled_mask);
 | 
			
		||||
    qemu_get_be32s(f, &s->npt);
 | 
			
		||||
    qemu_get_be64s(f, &s->npt_mask);
 | 
			
		||||
    qemu_get_sbe64s(f, &s->clock_offset);
 | 
			
		||||
 | 
			
		||||
    timer_get(f, s->qtimer);
 | 
			
		||||
@@ -380,15 +384,17 @@ void cpu_get_timer(QEMUFile *f, CPUTimer *s)
 | 
			
		||||
 | 
			
		||||
static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu,
 | 
			
		||||
                                  QEMUBHFunc *cb, uint32_t frequency,
 | 
			
		||||
                                  uint64_t disabled_mask)
 | 
			
		||||
                                  uint64_t disabled_mask, uint64_t npt_mask)
 | 
			
		||||
{
 | 
			
		||||
    CPUTimer *timer = g_malloc0(sizeof (CPUTimer));
 | 
			
		||||
 | 
			
		||||
    timer->name = name;
 | 
			
		||||
    timer->frequency = frequency;
 | 
			
		||||
    timer->disabled_mask = disabled_mask;
 | 
			
		||||
    timer->npt_mask = npt_mask;
 | 
			
		||||
 | 
			
		||||
    timer->disabled = 1;
 | 
			
		||||
    timer->npt = 1;
 | 
			
		||||
    timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
 | 
			
		||||
 | 
			
		||||
    timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu);
 | 
			
		||||
@@ -494,17 +500,17 @@ static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency)
 | 
			
		||||
 | 
			
		||||
void cpu_tick_set_count(CPUTimer *timer, uint64_t count)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t real_count = count & ~timer->disabled_mask;
 | 
			
		||||
    uint64_t disabled_bit = count & timer->disabled_mask;
 | 
			
		||||
    uint64_t real_count = count & ~timer->npt_mask;
 | 
			
		||||
    uint64_t npt_bit = count & timer->npt_mask;
 | 
			
		||||
 | 
			
		||||
    int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
 | 
			
		||||
                    cpu_to_timer_ticks(real_count, timer->frequency);
 | 
			
		||||
 | 
			
		||||
    TIMER_DPRINTF("%s set_count count=0x%016lx (%s) p=%p\n",
 | 
			
		||||
    TIMER_DPRINTF("%s set_count count=0x%016lx (npt %s) p=%p\n",
 | 
			
		||||
                  timer->name, real_count,
 | 
			
		||||
                  timer->disabled?"disabled":"enabled", timer);
 | 
			
		||||
                  timer->npt ? "disabled" : "enabled", timer);
 | 
			
		||||
 | 
			
		||||
    timer->disabled = disabled_bit ? 1 : 0;
 | 
			
		||||
    timer->npt = npt_bit ? 1 : 0;
 | 
			
		||||
    timer->clock_offset = vm_clock_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -514,12 +520,13 @@ uint64_t cpu_tick_get_count(CPUTimer *timer)
 | 
			
		||||
                    qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset,
 | 
			
		||||
                    timer->frequency);
 | 
			
		||||
 | 
			
		||||
    TIMER_DPRINTF("%s get_count count=0x%016lx (%s) p=%p\n",
 | 
			
		||||
    TIMER_DPRINTF("%s get_count count=0x%016lx (npt %s) p=%p\n",
 | 
			
		||||
           timer->name, real_count,
 | 
			
		||||
           timer->disabled?"disabled":"enabled", timer);
 | 
			
		||||
           timer->npt ? "disabled" : "enabled", timer);
 | 
			
		||||
 | 
			
		||||
    if (timer->disabled)
 | 
			
		||||
        real_count |= timer->disabled_mask;
 | 
			
		||||
    if (timer->npt) {
 | 
			
		||||
        real_count |= timer->npt_mask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return real_count;
 | 
			
		||||
}
 | 
			
		||||
@@ -799,13 +806,16 @@ static SPARCCPU *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef)
 | 
			
		||||
    env = &cpu->env;
 | 
			
		||||
 | 
			
		||||
    env->tick = cpu_timer_create("tick", cpu, tick_irq,
 | 
			
		||||
                                  tick_frequency, TICK_NPT_MASK);
 | 
			
		||||
                                  tick_frequency, TICK_INT_DIS,
 | 
			
		||||
                                  TICK_NPT_MASK);
 | 
			
		||||
 | 
			
		||||
    env->stick = cpu_timer_create("stick", cpu, stick_irq,
 | 
			
		||||
                                   stick_frequency, TICK_INT_DIS);
 | 
			
		||||
                                   stick_frequency, TICK_INT_DIS,
 | 
			
		||||
                                   TICK_NPT_MASK);
 | 
			
		||||
 | 
			
		||||
    env->hstick = cpu_timer_create("hstick", cpu, hstick_irq,
 | 
			
		||||
                                    hstick_frequency, TICK_INT_DIS);
 | 
			
		||||
                                    hstick_frequency, TICK_INT_DIS,
 | 
			
		||||
                                    TICK_NPT_MASK);
 | 
			
		||||
 | 
			
		||||
    reset_info = g_malloc0(sizeof(ResetData));
 | 
			
		||||
    reset_info->cpu = cpu;
 | 
			
		||||
 
 | 
			
		||||
@@ -51,9 +51,9 @@ static char const *imx_epit_reg_name(uint32_t reg)
 | 
			
		||||
 * These are typical.
 | 
			
		||||
 */
 | 
			
		||||
static const IMXClk imx_epit_clocks[] =  {
 | 
			
		||||
    0,        /* 00 disabled */
 | 
			
		||||
    IPG,      /* 01 ipg_clk, ~532MHz */
 | 
			
		||||
    IPG,      /* 10 ipg_clk_highfreq */
 | 
			
		||||
    NOCLK,    /* 00 disabled */
 | 
			
		||||
    CLK_IPG,  /* 01 ipg_clk, ~532MHz */
 | 
			
		||||
    CLK_IPG,  /* 10 ipg_clk_highfreq */
 | 
			
		||||
    CLK_32k,  /* 11 ipg_clk_32k -- ~32kHz */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -73,20 +73,18 @@ static void imx_epit_set_freq(IMXEPITState *s)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t clksrc;
 | 
			
		||||
    uint32_t prescaler;
 | 
			
		||||
    uint32_t freq;
 | 
			
		||||
 | 
			
		||||
    clksrc = extract32(s->cr, CR_CLKSRC_SHIFT, 2);
 | 
			
		||||
    prescaler = 1 + extract32(s->cr, CR_PRESCALE_SHIFT, 12);
 | 
			
		||||
 | 
			
		||||
    freq = imx_clock_frequency(s->ccm, imx_epit_clocks[clksrc]) / prescaler;
 | 
			
		||||
    s->freq = imx_ccm_get_clock_frequency(s->ccm,
 | 
			
		||||
                                imx_epit_clocks[clksrc]) / prescaler;
 | 
			
		||||
 | 
			
		||||
    s->freq = freq;
 | 
			
		||||
    DPRINTF("Setting ptimer frequency to %u\n", s->freq);
 | 
			
		||||
 | 
			
		||||
    DPRINTF("Setting ptimer frequency to %u\n", freq);
 | 
			
		||||
 | 
			
		||||
    if (freq) {
 | 
			
		||||
        ptimer_set_freq(s->timer_reload, freq);
 | 
			
		||||
        ptimer_set_freq(s->timer_cmp, freq);
 | 
			
		||||
    if (s->freq) {
 | 
			
		||||
        ptimer_set_freq(s->timer_reload, s->freq);
 | 
			
		||||
        ptimer_set_freq(s->timer_cmp, s->freq);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -81,8 +81,8 @@ static const VMStateDescription vmstate_imx_timer_gpt = {
 | 
			
		||||
 | 
			
		||||
static const IMXClk imx_gpt_clocks[] = {
 | 
			
		||||
    NOCLK,    /* 000 No clock source */
 | 
			
		||||
    IPG,      /* 001 ipg_clk, 532MHz*/
 | 
			
		||||
    IPG,      /* 010 ipg_clk_highfreq */
 | 
			
		||||
    CLK_IPG,  /* 001 ipg_clk, 532MHz*/
 | 
			
		||||
    CLK_IPG,  /* 010 ipg_clk_highfreq */
 | 
			
		||||
    NOCLK,    /* 011 not defined */
 | 
			
		||||
    CLK_32k,  /* 100 ipg_clk_32k */
 | 
			
		||||
    NOCLK,    /* 101 not defined */
 | 
			
		||||
@@ -93,14 +93,14 @@ static const IMXClk imx_gpt_clocks[] = {
 | 
			
		||||
static void imx_gpt_set_freq(IMXGPTState *s)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t clksrc = extract32(s->cr, GPT_CR_CLKSRC_SHIFT, 3);
 | 
			
		||||
    uint32_t freq = imx_clock_frequency(s->ccm, imx_gpt_clocks[clksrc])
 | 
			
		||||
                    / (1 + s->pr);
 | 
			
		||||
    s->freq = freq;
 | 
			
		||||
 | 
			
		||||
    DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, freq);
 | 
			
		||||
    s->freq = imx_ccm_get_clock_frequency(s->ccm,
 | 
			
		||||
                                imx_gpt_clocks[clksrc]) / (1 + s->pr);
 | 
			
		||||
 | 
			
		||||
    if (freq) {
 | 
			
		||||
        ptimer_set_freq(s->timer, freq);
 | 
			
		||||
    DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq);
 | 
			
		||||
 | 
			
		||||
    if (s->freq) {
 | 
			
		||||
        ptimer_set_freq(s->timer, s->freq);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										315
									
								
								hw/usb/dev-mtp.c
									
									
									
									
									
								
							
							
						
						
									
										315
									
								
								hw/usb/dev-mtp.c
									
									
									
									
									
								
							@@ -15,6 +15,10 @@
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/statvfs.h>
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
#include <sys/inotify.h>
 | 
			
		||||
#include "qemu/main-loop.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "qemu-common.h"
 | 
			
		||||
#include "qemu/iov.h"
 | 
			
		||||
@@ -62,6 +66,11 @@ enum mtp_code {
 | 
			
		||||
    /* format codes */
 | 
			
		||||
    FMT_UNDEFINED_OBJECT           = 0x3000,
 | 
			
		||||
    FMT_ASSOCIATION                = 0x3001,
 | 
			
		||||
 | 
			
		||||
    /* event codes */
 | 
			
		||||
    EVT_OBJ_ADDED                  = 0x4002,
 | 
			
		||||
    EVT_OBJ_REMOVED                = 0x4003,
 | 
			
		||||
    EVT_OBJ_INFO_CHANGED           = 0x4007,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -84,6 +93,17 @@ enum {
 | 
			
		||||
    EP_EVENT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
typedef struct MTPMonEntry MTPMonEntry;
 | 
			
		||||
 | 
			
		||||
struct MTPMonEntry {
 | 
			
		||||
    uint32_t event;
 | 
			
		||||
    uint32_t handle;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_ENTRY(MTPMonEntry) next;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct MTPControl {
 | 
			
		||||
    uint16_t     code;
 | 
			
		||||
    uint32_t     trans;
 | 
			
		||||
@@ -108,9 +128,14 @@ struct MTPObject {
 | 
			
		||||
    char         *name;
 | 
			
		||||
    char         *path;
 | 
			
		||||
    struct stat  stat;
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    /* inotify watch cookie */
 | 
			
		||||
    int          watchfd;
 | 
			
		||||
#endif
 | 
			
		||||
    MTPObject    *parent;
 | 
			
		||||
    MTPObject    **children;
 | 
			
		||||
    uint32_t     nchildren;
 | 
			
		||||
    QLIST_HEAD(, MTPObject) children;
 | 
			
		||||
    QLIST_ENTRY(MTPObject) list;
 | 
			
		||||
    bool         have_children;
 | 
			
		||||
    QTAILQ_ENTRY(MTPObject) next;
 | 
			
		||||
};
 | 
			
		||||
@@ -128,6 +153,11 @@ struct MTPState {
 | 
			
		||||
    uint32_t     next_handle;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_HEAD(, MTPObject) objects;
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    /* inotify descriptor */
 | 
			
		||||
    int          inotifyfd;
 | 
			
		||||
    QTAILQ_HEAD(events, MTPMonEntry) events;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define TYPE_USB_MTP "usb-mtp"
 | 
			
		||||
@@ -183,7 +213,7 @@ static const USBDescIface desc_iface_full = {
 | 
			
		||||
        },{
 | 
			
		||||
            .bEndpointAddress      = USB_DIR_IN | EP_EVENT,
 | 
			
		||||
            .bmAttributes          = USB_ENDPOINT_XFER_INT,
 | 
			
		||||
            .wMaxPacketSize        = 8,
 | 
			
		||||
            .wMaxPacketSize        = 64,
 | 
			
		||||
            .bInterval             = 0x0a,
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
@@ -225,7 +255,7 @@ static const USBDescIface desc_iface_high = {
 | 
			
		||||
        },{
 | 
			
		||||
            .bEndpointAddress      = USB_DIR_IN | EP_EVENT,
 | 
			
		||||
            .bmAttributes          = USB_ENDPOINT_XFER_INT,
 | 
			
		||||
            .wMaxPacketSize        = 8,
 | 
			
		||||
            .wMaxPacketSize        = 64,
 | 
			
		||||
            .bInterval             = 0x0a,
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
@@ -317,15 +347,24 @@ ignore:
 | 
			
		||||
 | 
			
		||||
static void usb_mtp_object_free(MTPState *s, MTPObject *o)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
    MTPObject *iter;
 | 
			
		||||
 | 
			
		||||
    if (!o) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path);
 | 
			
		||||
 | 
			
		||||
    QTAILQ_REMOVE(&s->objects, o, next);
 | 
			
		||||
    for (i = 0; i < o->nchildren; i++) {
 | 
			
		||||
        usb_mtp_object_free(s, o->children[i]);
 | 
			
		||||
    if (o->parent) {
 | 
			
		||||
        QLIST_REMOVE(o, list);
 | 
			
		||||
        o->parent->nchildren--;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (!QLIST_EMPTY(&o->children)) {
 | 
			
		||||
        iter = QLIST_FIRST(&o->children);
 | 
			
		||||
        usb_mtp_object_free(s, iter);
 | 
			
		||||
    }
 | 
			
		||||
    g_free(o->children);
 | 
			
		||||
    g_free(o->name);
 | 
			
		||||
    g_free(o->path);
 | 
			
		||||
    g_free(o);
 | 
			
		||||
@@ -343,6 +382,204 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle)
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o,
 | 
			
		||||
                                    char *name)
 | 
			
		||||
{
 | 
			
		||||
    MTPObject *child =
 | 
			
		||||
        usb_mtp_object_alloc(s, s->next_handle++, o, name);
 | 
			
		||||
 | 
			
		||||
    if (child) {
 | 
			
		||||
        trace_usb_mtp_add_child(s->dev.addr, child->handle, child->path);
 | 
			
		||||
        QLIST_INSERT_HEAD(&o->children, child, list);
 | 
			
		||||
        o->nchildren++;
 | 
			
		||||
 | 
			
		||||
        if (child->format == FMT_ASSOCIATION) {
 | 
			
		||||
            QLIST_INIT(&child->children);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return child;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent,
 | 
			
		||||
                                             char *name, int len)
 | 
			
		||||
{
 | 
			
		||||
    MTPObject *iter;
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(iter, &parent->children, list) {
 | 
			
		||||
        if (strncmp(iter->name, name, len) == 0) {
 | 
			
		||||
            return iter;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd)
 | 
			
		||||
{
 | 
			
		||||
    MTPObject *iter;
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(iter, &s->objects, next) {
 | 
			
		||||
        if (iter->watchfd == wd) {
 | 
			
		||||
            return iter;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void inotify_watchfn(void *arg)
 | 
			
		||||
{
 | 
			
		||||
    MTPState *s = arg;
 | 
			
		||||
    ssize_t bytes;
 | 
			
		||||
    /* From the man page: atleast one event can be read */
 | 
			
		||||
    int len = sizeof(struct inotify_event) + NAME_MAX + 1;
 | 
			
		||||
    int pos;
 | 
			
		||||
    char buf[len];
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        bytes = read(s->inotifyfd, buf, len);
 | 
			
		||||
        pos = 0;
 | 
			
		||||
 | 
			
		||||
        if (bytes <= 0) {
 | 
			
		||||
            /* Better luck next time */
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
         * TODO: Ignore initiator initiated events.
 | 
			
		||||
         * For now we are good because the store is RO
 | 
			
		||||
         */
 | 
			
		||||
        while (bytes > 0) {
 | 
			
		||||
            char *p = buf + pos;
 | 
			
		||||
            struct inotify_event *event = (struct inotify_event *)p;
 | 
			
		||||
            int watchfd = 0;
 | 
			
		||||
            uint32_t mask = event->mask & (IN_CREATE | IN_DELETE |
 | 
			
		||||
                                           IN_MODIFY | IN_IGNORED);
 | 
			
		||||
            MTPObject *parent = usb_mtp_object_lookup_wd(s, event->wd);
 | 
			
		||||
            MTPMonEntry *entry = NULL;
 | 
			
		||||
            MTPObject *o;
 | 
			
		||||
 | 
			
		||||
            pos = pos + sizeof(struct inotify_event) + event->len;
 | 
			
		||||
            bytes = bytes - pos;
 | 
			
		||||
 | 
			
		||||
            if (!parent) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            switch (mask) {
 | 
			
		||||
            case IN_CREATE:
 | 
			
		||||
                if (usb_mtp_object_lookup_name
 | 
			
		||||
                    (parent, event->name, event->len)) {
 | 
			
		||||
                    /* Duplicate create event */
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                entry = g_new0(MTPMonEntry, 1);
 | 
			
		||||
                entry->handle = s->next_handle;
 | 
			
		||||
                entry->event = EVT_OBJ_ADDED;
 | 
			
		||||
                o = usb_mtp_add_child(s, parent, event->name);
 | 
			
		||||
                if (!o) {
 | 
			
		||||
                    g_free(entry);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                o->watchfd = watchfd;
 | 
			
		||||
                trace_usb_mtp_inotify_event(s->dev.addr, event->name,
 | 
			
		||||
                                            event->mask, "Obj Added");
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IN_DELETE:
 | 
			
		||||
                /*
 | 
			
		||||
                 * The kernel issues a IN_IGNORED event
 | 
			
		||||
                 * when a dir containing a watchpoint is
 | 
			
		||||
                 * deleted, so we don't have to delete the
 | 
			
		||||
                 * watchpoint
 | 
			
		||||
                 */
 | 
			
		||||
                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
 | 
			
		||||
                if (!o) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                entry = g_new0(MTPMonEntry, 1);
 | 
			
		||||
                entry->handle = o->handle;
 | 
			
		||||
                entry->event = EVT_OBJ_REMOVED;
 | 
			
		||||
                usb_mtp_object_free(s, o);
 | 
			
		||||
                trace_usb_mtp_inotify_event(s->dev.addr, o->path,
 | 
			
		||||
                                      event->mask, "Obj Deleted");
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IN_MODIFY:
 | 
			
		||||
                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
 | 
			
		||||
                if (!o) {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                entry = g_new0(MTPMonEntry, 1);
 | 
			
		||||
                entry->handle = o->handle;
 | 
			
		||||
                entry->event = EVT_OBJ_INFO_CHANGED;
 | 
			
		||||
                trace_usb_mtp_inotify_event(s->dev.addr, o->path,
 | 
			
		||||
                                      event->mask, "Obj Modified");
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IN_IGNORED:
 | 
			
		||||
                o = usb_mtp_object_lookup_name(parent, event->name, event->len);
 | 
			
		||||
                trace_usb_mtp_inotify_event(s->dev.addr, o->path,
 | 
			
		||||
                                      event->mask, "Obj ignored");
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                fprintf(stderr, "usb-mtp: failed to parse inotify event\n");
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (entry) {
 | 
			
		||||
                QTAILQ_INSERT_HEAD(&s->events, entry, next);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int usb_mtp_inotify_init(MTPState *s)
 | 
			
		||||
{
 | 
			
		||||
    int fd;
 | 
			
		||||
 | 
			
		||||
    fd = inotify_init1(IN_NONBLOCK);
 | 
			
		||||
    if (fd == -1) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QTAILQ_INIT(&s->events);
 | 
			
		||||
    s->inotifyfd = fd;
 | 
			
		||||
 | 
			
		||||
    qemu_set_fd_handler(fd, inotify_watchfn, NULL, s);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void usb_mtp_inotify_cleanup(MTPState *s)
 | 
			
		||||
{
 | 
			
		||||
    MTPMonEntry *e;
 | 
			
		||||
 | 
			
		||||
    if (!s->inotifyfd) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s);
 | 
			
		||||
    close(s->inotifyfd);
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(e, &s->events, next) {
 | 
			
		||||
        QTAILQ_REMOVE(&s->events, e, next);
 | 
			
		||||
        g_free(e);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int usb_mtp_add_watch(int inotifyfd, char *path)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY |
 | 
			
		||||
        IN_ISDIR;
 | 
			
		||||
 | 
			
		||||
    return inotify_add_watch(inotifyfd, path, mask);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
 | 
			
		||||
{
 | 
			
		||||
    struct dirent *entry;
 | 
			
		||||
@@ -357,15 +594,18 @@ static void usb_mtp_object_readdir(MTPState *s, MTPObject *o)
 | 
			
		||||
    if (!dir) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    int watchfd = usb_mtp_add_watch(s->inotifyfd, o->path);
 | 
			
		||||
    if (watchfd == -1) {
 | 
			
		||||
        fprintf(stderr, "usb-mtp: failed to add watch for %s\n", o->path);
 | 
			
		||||
    } else {
 | 
			
		||||
        trace_usb_mtp_inotify_event(s->dev.addr, o->path,
 | 
			
		||||
                                    0, "Watch Added");
 | 
			
		||||
        o->watchfd = watchfd;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    while ((entry = readdir(dir)) != NULL) {
 | 
			
		||||
        if ((o->nchildren % 32) == 0) {
 | 
			
		||||
            o->children = g_renew(MTPObject *, o->children, o->nchildren + 32);
 | 
			
		||||
        }
 | 
			
		||||
        o->children[o->nchildren] =
 | 
			
		||||
            usb_mtp_object_alloc(s, s->next_handle++, o, entry->d_name);
 | 
			
		||||
        if (o->children[o->nchildren] != NULL) {
 | 
			
		||||
            o->nchildren++;
 | 
			
		||||
        }
 | 
			
		||||
        usb_mtp_add_child(s, o, entry->d_name);
 | 
			
		||||
    }
 | 
			
		||||
    closedir(dir);
 | 
			
		||||
}
 | 
			
		||||
@@ -617,13 +857,15 @@ static MTPData *usb_mtp_get_object_handles(MTPState *s, MTPControl *c,
 | 
			
		||||
                                           MTPObject *o)
 | 
			
		||||
{
 | 
			
		||||
    MTPData *d = usb_mtp_data_alloc(c);
 | 
			
		||||
    uint32_t i, handles[o->nchildren];
 | 
			
		||||
    uint32_t i = 0, handles[o->nchildren];
 | 
			
		||||
    MTPObject *iter;
 | 
			
		||||
 | 
			
		||||
    trace_usb_mtp_op_get_object_handles(s->dev.addr, o->handle, o->path);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < o->nchildren; i++) {
 | 
			
		||||
        handles[i] = o->children[i]->handle;
 | 
			
		||||
    QLIST_FOREACH(iter, &o->children, list) {
 | 
			
		||||
        handles[i++] = iter->handle;
 | 
			
		||||
    }
 | 
			
		||||
    assert(i == o->nchildren);
 | 
			
		||||
    usb_mtp_add_u32_array(d, o->nchildren, handles);
 | 
			
		||||
 | 
			
		||||
    return d;
 | 
			
		||||
@@ -754,11 +996,19 @@ static void usb_mtp_command(MTPState *s, MTPControl *c)
 | 
			
		||||
        trace_usb_mtp_op_open_session(s->dev.addr);
 | 
			
		||||
        s->session = c->argv[0];
 | 
			
		||||
        usb_mtp_object_alloc(s, s->next_handle++, NULL, s->root);
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
        if (usb_mtp_inotify_init(s)) {
 | 
			
		||||
            fprintf(stderr, "usb-mtp: file monitoring init failed\n");
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        break;
 | 
			
		||||
    case CMD_CLOSE_SESSION:
 | 
			
		||||
        trace_usb_mtp_op_close_session(s->dev.addr);
 | 
			
		||||
        s->session = 0;
 | 
			
		||||
        s->next_handle = 0;
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
        usb_mtp_inotify_cleanup(s);
 | 
			
		||||
#endif
 | 
			
		||||
        usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
 | 
			
		||||
        assert(QTAILQ_EMPTY(&s->objects));
 | 
			
		||||
        break;
 | 
			
		||||
@@ -884,6 +1134,10 @@ static void usb_mtp_handle_reset(USBDevice *dev)
 | 
			
		||||
 | 
			
		||||
    trace_usb_mtp_reset(s->dev.addr);
 | 
			
		||||
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
    usb_mtp_inotify_cleanup(s);
 | 
			
		||||
#endif
 | 
			
		||||
    usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects));
 | 
			
		||||
    s->session = 0;
 | 
			
		||||
    usb_mtp_data_free(s->data_in);
 | 
			
		||||
    s->data_in = NULL;
 | 
			
		||||
@@ -1043,6 +1297,31 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p)
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case EP_EVENT:
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
        if (!QTAILQ_EMPTY(&s->events)) {
 | 
			
		||||
            struct MTPMonEntry *e = QTAILQ_LAST(&s->events, events);
 | 
			
		||||
            uint32_t handle;
 | 
			
		||||
            int len = sizeof(container) + sizeof(uint32_t);
 | 
			
		||||
 | 
			
		||||
            if (p->iov.size < len) {
 | 
			
		||||
                trace_usb_mtp_stall(s->dev.addr,
 | 
			
		||||
                                    "packet too small to send event");
 | 
			
		||||
                p->status = USB_RET_STALL;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            QTAILQ_REMOVE(&s->events, e, next);
 | 
			
		||||
            container.length = cpu_to_le32(len);
 | 
			
		||||
            container.type = cpu_to_le32(TYPE_EVENT);
 | 
			
		||||
            container.code = cpu_to_le16(e->event);
 | 
			
		||||
            container.trans = 0; /* no trans specific events */
 | 
			
		||||
            handle = cpu_to_le32(e->handle);
 | 
			
		||||
            usb_packet_copy(p, &container, sizeof(container));
 | 
			
		||||
            usb_packet_copy(p, &handle, sizeof(uint32_t));
 | 
			
		||||
            g_free(e);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        p->status = USB_RET_NAK;
 | 
			
		||||
        return;
 | 
			
		||||
    default:
 | 
			
		||||
 
 | 
			
		||||
@@ -1389,7 +1389,7 @@ static int ehci_process_itd(EHCIState *ehci,
 | 
			
		||||
{
 | 
			
		||||
    USBDevice *dev;
 | 
			
		||||
    USBEndpoint *ep;
 | 
			
		||||
    uint32_t i, len, pid, dir, devaddr, endp;
 | 
			
		||||
    uint32_t i, len, pid, dir, devaddr, endp, xfers = 0;
 | 
			
		||||
    uint32_t pg, off, ptr1, ptr2, max, mult;
 | 
			
		||||
 | 
			
		||||
    ehci->periodic_sched_active = PERIODIC_ACTIVE;
 | 
			
		||||
@@ -1479,9 +1479,10 @@ static int ehci_process_itd(EHCIState *ehci,
 | 
			
		||||
                ehci_raise_irq(ehci, USBSTS_INT);
 | 
			
		||||
            }
 | 
			
		||||
            itd->transact[i] &= ~ITD_XACT_ACTIVE;
 | 
			
		||||
            xfers++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
    return xfers ? 0 : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user