Compare commits
	
		
			341 Commits
		
	
	
		
			v0.14.1
			...
			v0.12.5-qe
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 69903f803a | ||
|  | 174f225e9d | ||
|  | e916448940 | ||
|  | bb44e0bbce | ||
|  | 191d44fc43 | ||
|  | a2f0cbaa58 | ||
|  | a9d9a66f13 | ||
|  | 37060c28e5 | ||
|  | 7205c21e76 | ||
|  | ceef722d01 | ||
|  | dfe0bb55ee | ||
|  | 6fd82592ce | ||
|  | 39187b5192 | ||
|  | 729862401d | ||
|  | 34d0d68bdf | ||
|  | 82e9cbeb0d | ||
|  | 2020dd5535 | ||
|  | 0c0f53e25c | ||
|  | 3dbe0714dd | ||
|  | 9067bac11d | ||
|  | 74471f3742 | ||
|  | 370f80376a | ||
|  | ed3aac289a | ||
|  | 11b52a6536 | ||
|  | b6185fc79c | ||
|  | 8fd7d5438e | ||
|  | a513171f80 | ||
|  | ff9e177617 | ||
|  | db3519a9ec | ||
|  | 258e351d12 | ||
|  | cd14f4d346 | ||
|  | df631629b1 | ||
|  | af0269b036 | ||
|  | d37dbf988d | ||
|  | cc7ed88f28 | ||
|  | 07442ab4a1 | ||
|  | dbe6a18d82 | ||
|  | 7dd007c2ed | ||
|  | 9c6a8f503d | ||
|  | 0c459361a1 | ||
|  | 72d3457e8d | ||
|  | e1f0c1d05d | ||
|  | 74bcc51b99 | ||
|  | 7e4f956056 | ||
|  | 1fb9798b69 | ||
|  | 9f6a84bc43 | ||
|  | 8cef921d18 | ||
|  | b04c3db504 | ||
|  | d04d7cf158 | ||
|  | 2b8bdd5c7f | ||
|  | 2a44494726 | ||
|  | 8f30db54d9 | ||
|  | b09ac1abe7 | ||
|  | 012d4869c1 | ||
|  | 3597c9c1d5 | ||
|  | 3b4bef0696 | ||
|  | d899303743 | ||
|  | 5773685183 | ||
|  | d40ba77ebf | ||
|  | a8c46d182c | ||
|  | d80e20a1c3 | ||
|  | 1ce4fad939 | ||
|  | 9167a242db | ||
|  | 09e96924ec | ||
|  | 69ff4e9dbd | ||
|  | 0434349d6a | ||
|  | e007221223 | ||
|  | 4622317288 | ||
|  | ffac613ff9 | ||
|  | aba5288247 | ||
|  | 4f7cb96931 | ||
|  | fafc2e4b33 | ||
|  | 83ef70f24a | ||
|  | de17c16e1f | ||
|  | 9462695b64 | ||
|  | 5eb089588e | ||
|  | 2039f70c23 | ||
|  | 082a9fc256 | ||
|  | 36a013c956 | ||
|  | c4c4b32b81 | ||
|  | 804b6ab08d | ||
|  | 81b168a702 | ||
|  | 5c6892078a | ||
|  | 18a21890ff | ||
|  | 6629fa6473 | ||
|  | 2a7996ce0e | ||
|  | 8ec131fb59 | ||
|  | 30d061750d | ||
|  | c5f5dc5bad | ||
|  | d2df336c58 | ||
|  | b299b12b17 | ||
|  | c248df6161 | ||
|  | 7d5625d5f7 | ||
|  | cc21d131e3 | ||
|  | 41a5bda61f | ||
|  | 5163f6e864 | ||
|  | 6173d56bdc | ||
|  | f39942d217 | ||
|  | 5dde87088f | ||
|  | 3fa017e24b | ||
|  | 35924dbe8c | ||
|  | 88aa905668 | ||
|  | b93c5c84c8 | ||
|  | f203baee5b | ||
|  | 5e3be62385 | ||
|  | b391493bc6 | ||
|  | 57f9f4c9f5 | ||
|  | 7ebc79037c | ||
|  | ea299062eb | ||
|  | e03dd1a6c2 | ||
|  | 535d2eb34a | ||
|  | beb8eab90c | ||
|  | 8d67694fbf | ||
|  | 02510b2436 | ||
|  | b57a2297f2 | ||
|  | 43fab08210 | ||
|  | 915080e6b1 | ||
|  | 9f59ddcc4f | ||
|  | 999ceb2c1d | ||
|  | 307331a42a | ||
|  | 6728dd464b | ||
|  | bb45bcc8de | ||
|  | 096109c804 | ||
|  | 7ae1fcc88c | ||
|  | 299e0bc52a | ||
|  | 74f0529e24 | ||
|  | 614971158c | ||
|  | afa328b1b2 | ||
|  | 868dab5dc2 | ||
|  | 29bb3bf350 | ||
|  | dbf45b44b7 | ||
|  | d0d888bc6d | ||
|  | 19abbad0da | ||
|  | f48aba6de7 | ||
|  | cb2ae96bf6 | ||
|  | 848f874ca1 | ||
|  | a1a86bf902 | ||
|  | c727a05459 | ||
|  | eb05143e24 | ||
|  | 0c709e6195 | ||
|  | dc88aa49b4 | ||
|  | dc2ffbf6d8 | ||
|  | d3bf9367f2 | ||
|  | c502715a74 | ||
|  | b9a61d2154 | ||
|  | 9525204c5d | ||
|  | f79d556b4f | ||
|  | 41ae9ece21 | ||
|  | 40480d2bf4 | ||
|  | e389e937a7 | ||
|  | 73b48d914f | ||
|  | 3999bf3244 | ||
|  | a3441a43a6 | ||
|  | 49a3aaac4a | ||
|  | 027866ce23 | ||
|  | 04babf6c6f | ||
|  | d2b8117310 | ||
|  | 0c4b9aef7b | ||
|  | 431c829f33 | ||
|  | be7398ec06 | ||
|  | be59ce1f48 | ||
|  | eacad66dbe | ||
|  | 66dbb62824 | ||
|  | d47d251286 | ||
|  | 348af56fae | ||
|  | 09866b9baa | ||
|  | e1daf40e3e | ||
|  | de3ea06d59 | ||
|  | fe46a160ce | ||
|  | 8033c42abd | ||
|  | 4713c69fa2 | ||
|  | d68bf60838 | ||
|  | 57fa5ca551 | ||
|  | 8610774f79 | ||
|  | 76ba04832b | ||
|  | 644f5de21b | ||
|  | dcc0da8297 | ||
|  | 41193c50fa | ||
|  | da0266005a | ||
|  | eacdccbb3e | ||
|  | 65e8c51928 | ||
|  | e470436f19 | ||
|  | b60c2c74f3 | ||
|  | fe1b69708c | ||
|  | a1678e85db | ||
|  | 8212d18cf5 | ||
|  | 6c412ddf1c | ||
|  | 862ad4be53 | ||
|  | aac2ad563a | ||
|  | eb41f58a4e | ||
|  | 5543b41167 | ||
|  | 31d85f6a6b | ||
|  | 9c49a2533c | ||
|  | c6faf5fd73 | ||
|  | 069def25cb | ||
|  | 3733a1e804 | ||
|  | 5b06a3f785 | ||
|  | baaf73aaac | ||
|  | 345c22aa80 | ||
|  | 26bb2a0865 | ||
|  | e6ea832410 | ||
|  | 22d0cc8d38 | ||
|  | 898829d5c7 | ||
|  | 72bb3c7571 | ||
|  | 48c437f0ab | ||
|  | 07d00c2174 | ||
|  | 3243a06f51 | ||
|  | 1c3f96be38 | ||
|  | df9e7219db | ||
|  | e83421f511 | ||
|  | 2b311b3cce | ||
|  | 4b5db3749c | ||
|  | a1497a782c | ||
|  | 3c547d7bb7 | ||
|  | 3b43502e3a | ||
|  | 078517421f | ||
|  | afc7055619 | ||
|  | 53425683d4 | ||
|  | ef5a63186a | ||
|  | 4a0e0accd7 | ||
|  | 73e47683de | ||
|  | 115e94a31e | ||
|  | 5fd5f6999d | ||
|  | 602e97b725 | ||
|  | 97b766dfcd | ||
|  | fb8cf78db6 | ||
|  | c5238ac21b | ||
|  | 99917a99cd | ||
|  | 55ed56908f | ||
|  | 139e310025 | ||
|  | bed93b1dcb | ||
|  | 73b4ac5cd8 | ||
|  | 00e8277b83 | ||
|  | a8ea3a357b | ||
|  | f8051485c1 | ||
|  | 807c80b259 | ||
|  | 686a3c3dc2 | ||
|  | a381d8277c | ||
|  | 8647b09bfd | ||
|  | 9153014fa0 | ||
|  | f6d4446ea8 | ||
|  | f1e247ee6b | ||
|  | a49668769d | ||
|  | 97d949d9da | ||
|  | 040093b1a5 | ||
|  | 5d4e53dc81 | ||
|  | 3ebee80226 | ||
|  | c56651312b | ||
|  | 869ca150e7 | ||
|  | 910628f396 | ||
|  | 251241dc90 | ||
|  | 03a23e5c6e | ||
|  | a68fc29ceb | ||
|  | 0014803d23 | ||
|  | 5118f7b47c | ||
|  | 1c1d7bda2c | ||
|  | bdae662c94 | ||
|  | 0108d4e323 | ||
|  | 4305793bad | ||
|  | d2d51eeff0 | ||
|  | 3be42b28c1 | ||
|  | ee70ef8771 | ||
|  | 5f9fe0f8d0 | ||
|  | 7589acc9e8 | ||
|  | 94f539bdac | ||
|  | e637fd2386 | ||
|  | 6e785bee32 | ||
|  | f883e4f7b8 | ||
|  | 5daa7bb7a4 | ||
|  | b0a84d0525 | ||
|  | f1f84ba223 | ||
|  | db830f26cb | ||
|  | 61a606dade | ||
|  | 2d95575edb | ||
|  | d707483ce3 | ||
|  | e2deb622c2 | ||
|  | 6e792a557e | ||
|  | ea2138cf90 | ||
|  | 992f3cb78e | ||
|  | 828b2ff676 | ||
|  | a231a8272c | ||
|  | f2604b35dc | ||
|  | fc05630f1f | ||
|  | ad960ddbce | ||
|  | 239a69680c | ||
|  | f4f1df70f2 | ||
|  | 782e9e6554 | ||
|  | 64de0113f1 | ||
|  | 84db615abc | ||
|  | 7c6a56cc63 | ||
|  | a20600b917 | ||
|  | 4986fd4111 | ||
|  | 96639424e2 | ||
|  | 6ac733bf09 | ||
|  | 25d82d3311 | ||
|  | f9800fe5a0 | ||
|  | 542d991b4c | ||
|  | d1d6963eba | ||
|  | 7058b807cd | ||
|  | f49d2561cb | ||
|  | a63e5f1971 | ||
|  | ebbc8a3d8e | ||
|  | 08b2d3ba9a | ||
|  | 72fbd9f97c | ||
|  | 5b6d0419d9 | ||
|  | 9df9eeeb18 | ||
|  | 5b6321a237 | ||
|  | 5e0c455842 | ||
|  | 4d687b13cf | ||
|  | d7b8193716 | ||
|  | 2e51813417 | ||
|  | 90f445e1c9 | ||
|  | 143d288cba | ||
|  | 13a2ccc46f | ||
|  | ea2b7d7079 | ||
|  | 0b52786ce1 | ||
|  | e36469149a | ||
|  | e5fc266be5 | ||
|  | 3e4cd634cc | ||
|  | 06976f82e7 | ||
|  | fe7c6c90a8 | ||
|  | 960a4b537a | ||
|  | c756b1e762 | ||
|  | 06921ec84f | ||
|  | 8cb1cec656 | ||
|  | a46657d185 | ||
|  | 28acf422cb | ||
|  | a7d5da8857 | ||
|  | 931a548be3 | ||
|  | bcddbd0f6a | ||
|  | b3dfdb5a3b | ||
|  | 6ccc51fd20 | ||
|  | 0ea5709a32 | ||
|  | 67a2698dac | ||
|  | eea4acfa5c | ||
|  | c99d32efe6 | ||
|  | 9fa7591beb | ||
|  | 066263f377 | ||
|  | 20c1a35211 | ||
|  | ea6112b165 | ||
|  | e222100afe | 
							
								
								
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -2,16 +2,11 @@ config-devices.* | ||||
| config-all-devices.* | ||||
| config-host.* | ||||
| config-target.* | ||||
| trace.h | ||||
| trace.c | ||||
| trace-dtrace.h | ||||
| trace-dtrace.dtrace | ||||
| *-timestamp | ||||
| i386 | ||||
| *-softmmu | ||||
| *-darwin-user | ||||
| *-linux-user | ||||
| *-bsd-user | ||||
| libdis* | ||||
| libhw32 | ||||
| libhw64 | ||||
| libuser | ||||
| @@ -27,13 +22,11 @@ qemu-img | ||||
| qemu-nbd | ||||
| qemu-nbd.8 | ||||
| qemu-nbd.pod | ||||
| qemu-options.def | ||||
| qemu-options.texi | ||||
| qemu-img-cmds.texi | ||||
| qemu-img-cmds.h | ||||
| qemu-io | ||||
| qemu-monitor.texi | ||||
| QMP/qmp-commands.txt | ||||
| .gdbinit | ||||
| *.a | ||||
| *.aux | ||||
| @@ -43,9 +36,7 @@ QMP/qmp-commands.txt | ||||
| *.fn | ||||
| *.ky | ||||
| *.log | ||||
| *.pdf | ||||
| *.pg | ||||
| *.pyc | ||||
| *.toc | ||||
| *.tp | ||||
| *.vr | ||||
| @@ -55,8 +46,7 @@ QMP/qmp-commands.txt | ||||
| patches | ||||
| pc-bios/bios-pq/status | ||||
| pc-bios/vgabios-pq/status | ||||
| pc-bios/optionrom/linuxboot.bin | ||||
| pc-bios/optionrom/multiboot.bin | ||||
| pc-bios/optionrom/multiboot.raw | ||||
| pc-bios/optionrom/extboot.bin | ||||
| .stgit-* | ||||
| cscope.* | ||||
|   | ||||
							
								
								
									
										4
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| [submodule "roms/vgabios"] | ||||
| 	path = roms/vgabios | ||||
| 	url = git://git.qemu.org/vgabios.git/ | ||||
| 	url = ../vgabios.git | ||||
| [submodule "roms/seabios"] | ||||
| 	path = roms/seabios | ||||
| 	url = git://git.qemu.org/seabios.git/ | ||||
| 	url = ../seabios.git | ||||
|   | ||||
| @@ -1,9 +1,6 @@ | ||||
| Qemu Coding Style | ||||
| ================= | ||||
|  | ||||
| Please use the script checkpatch.pl in the scripts directory to check | ||||
| patches before submitting. | ||||
|  | ||||
| 1. Whitespace | ||||
|  | ||||
| Of course, the most important aspect in any coding style is whitespace. | ||||
| @@ -49,8 +46,8 @@ names are lower_case_with_underscores_ending_with_a_t, like the POSIX | ||||
| uint64_t and family.  Note that this last convention contradicts POSIX | ||||
| and is therefore likely to be changed. | ||||
|  | ||||
| When wrapping standard library functions, use the prefix qemu_ to alert | ||||
| readers that they are seeing a wrapped version; otherwise avoid this prefix. | ||||
| Typedefs are used to eliminate the redundant 'struct' keyword.  It is the | ||||
| QEMU coding style. | ||||
|  | ||||
| 4. Block structure | ||||
|  | ||||
|   | ||||
							
								
								
									
										187
									
								
								Changelog
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								Changelog
									
									
									
									
									
								
							| @@ -1,3 +1,190 @@ | ||||
| version 0.12.5 | ||||
|  - audio/alsa: Handle SND_PCM_STATE_SETUP in alsa_poll_handler | ||||
|  - block: Handle multiwrite errors only when all requests have completed | ||||
|  - block: Fix early failure in multiwrite | ||||
|  - vpc: Use bdrv_(p)write_sync for metadata writes | ||||
|  - vmdk: Use bdrv_(p)write_sync for metadata writes | ||||
|  - qcow2: Use bdrv_(p)write_sync for metadata writes | ||||
|  - qcow: Use bdrv_(p)write_sync for metadata writes | ||||
|  - block: Add bdrv_(p)write_sync | ||||
|  - qcow2: Restore L1 entry on l2_allocate failure | ||||
|  - block/vdi: Fix image opening and creation for odd disk sizes | ||||
|  - block/vpc: Fix conversion from size to disk geometry | ||||
|  - qcow2: Remove abort on free_clusters failure | ||||
|  - vmdk: Fix COW | ||||
|  - qcow2: Fix creation of large images | ||||
|  - vmdk: fix double free | ||||
|  - qemu-options: add documentation for stdio signal=on|off | ||||
|  - target-arm : fix parallel saturated subtraction implementation | ||||
|  - target-arm : fix thumb2 parallel add/sub opcode decoding | ||||
|  - target-arm: fix addsub/subadd implementation | ||||
|  - target-i386: fix xchg rax,r8 | ||||
|  - block/vvfat.c: fix warnings with _FORTIFY_SOURCE | ||||
|  - audio/alsa: Spelling typo (paramters) | ||||
|  - target-mips: fix DINSU instruction | ||||
|  - Correct definitions for FD_CMD_SAVE and FD_CMD_RESTORE | ||||
|  - qcow2: Fix corruption after error in update_refcount | ||||
|  - qcow2: Fix corruption after refblock allocation | ||||
|  - block: Fix multiwrite with overlapping requests | ||||
|  - qcow2: Fix error handling in l2_allocate | ||||
|  - qcow2: Clear L2 table cache after write error | ||||
|  - ide: Fix ide_dma_cancel | ||||
|  - usb-bus: fix no params | ||||
|  - Avoid crash on '-usbdevice <device>' without parameters | ||||
|  - Fix -usbdevice crash | ||||
|  - Fix multiboot compilation | ||||
|  - Fix missing symbols in .rel/.rela.plt sections | ||||
|  - target-ppc: fix RFI by clearing some bits of MSR | ||||
|  - Fix typo in balloon help | ||||
|  - arm_timer: fix oneshot mode | ||||
|  - arm_timer: reload timer when enabled | ||||
|  - qemu-sockets: avoid strlen of NULL pointer | ||||
|  - block: fix aio_flush segfaults for read-only protocols (e.g. curl) | ||||
|  - virtio-blk: fix barrier support | ||||
|  - block: fix sector comparism in multiwrite_req_compare | ||||
|  - pci: irq_state vmstate breakage | ||||
|  - qemu-img: use the heap instead of the huge stack array for win32 | ||||
|  | ||||
| version 0.12.4 | ||||
|  - Workaround for broken OSS_GETVERSION on FreeBSD, part two (Juergen Lock) | ||||
|  - oss: fix fragment setting (malc) | ||||
|  - oss: issue OSS_GETVERSION ioctl only when needed (malc) | ||||
|  - oss: refactor code around policy setting (malc) | ||||
|  - oss: workaround for cases when OSS_GETVERSION is not defined (malc) | ||||
|  - block: Free iovec arrays allocated by multiwrite_merge() (Stefan Hajnoczi) | ||||
|  - lsi: fix segfault in lsi_command_complete (Gerd Hoffmann) | ||||
|  - lsi: pass lsi_request to lsi_reselect (Gerd Hoffmann) | ||||
|  - lsi: move dma_len+dma_buf into lsi_request (Gerd Hoffmann) | ||||
|  - lsi: move current_dev into lsi_request (Gerd Hoffmann) | ||||
|  - lsi: have lsi_request for the whole life time of the request. (Gerd Hoffmann) | ||||
|  - lsi: use QTAILQ for lsi_queue (Gerd Hoffmann) | ||||
|  - tcp/mips: Change TCG_AREG0 (fp -> s0) (Stefan Weil) | ||||
|  - sh_pci: fix memory and I/O access (Aurelien Jarno) | ||||
|  - Fix incoming migration with iothread (Marcelo Tosatti) | ||||
|  - Fix SIGFPE for vnc display of width/height = 1 (Chris Webb) | ||||
|  - net: remove broken net_set_boot_mask() boot device validation (Eduardo Habkost) | ||||
|  - qcow2: Remove request from in-flight list after error (Kevin Wolf) | ||||
|  - qcow2: Don't ignore immediate read/write failures (Kevin Wolf) | ||||
|  - block: Fix multiwrite memory leak in error case (Kevin Wolf) | ||||
|  - block: Fix error code in multiwrite for immediate failures (Kevin Wolf) | ||||
|  - block: Fix multiwrite error handling (Kevin Wolf) | ||||
|  - scsi-disk: fix buffer overflow (Gerd Hoffmann) | ||||
|  - qcow2: Rewrite alloc_refcount_block/grow_refcount_table (Kevin Wolf) | ||||
|  - qcow2: Factor next_refcount_table_size out (Kevin Wolf) | ||||
|  - block: avoid creating too large iovecs in multiwrite_merge (Christoph Hellwig) | ||||
|  - json-parser: Fix segfault on malformed input (Kevin Wolf) | ||||
|  - linux-user: switch default ppc64 CPU to 970fx from 970 (Aurelien Jarno) | ||||
|  - target-sh4: MMU: fix store queue addresses (Aurelien Jarno) | ||||
|  - target-sh4: MMU: fix ITLB priviledge check (Aurelien Jarno) | ||||
|  - target-sh4: MMU: fix mem_idx computation (Aurelien Jarno) | ||||
|  - sh7750: handle MMUCR TI bit (Aurelien Jarno) | ||||
|  - UHCI spurious interrut fix (Paul Brook) | ||||
|  - tcg/mips: fix branch offset during retranslation (Aurelien Jarno) | ||||
|  - tcg/arm: correctly save/restore registers in prologue/epilogue (Aurelien Jarno) | ||||
|  - workaround for cmd646 bmdma register access while no dma is active (Igor V. Kovalenko) | ||||
|  - Fix corner case in chardev udp: parameter (Jan Kiszka) | ||||
|  - Don't set default monitor when there is a mux'ed one (Jan Kiszka) | ||||
|  - spelling typo (compatibilty) in hw/fw_cfg.c (Vagrant Cascadian) | ||||
|  - fdc: fix drive property handling. (Gerd Hoffmann) | ||||
|  - target-i386: fix commit c22549204a6edc431e8e4358e61bd56386ff6957 (TeLeMan) | ||||
|  - target-i386: fix SIB decoding with index = 4 (Aurelien Jarno) | ||||
|  - Fix segfault with ram_size > 4095M without kvm (Ryan Harper) | ||||
|  - target-i386: Fix long jumps/calls in long mode with REX.W set (malc) | ||||
|  - target-i386: fix lddqu SSE instruction (Aurelien Jarno) | ||||
|  - qemu-char.c: drop debug printfs from qemu_chr_parse_compat (Jan Kiszka) | ||||
|  - fix undefined shifts by >32 (Paolo Bonzini) | ||||
|  - Fix qemu -net user,hostfwd= example (Aurelien Jarno) | ||||
|  | ||||
| version 0.12.3 | ||||
|   - kvm: Fix eflags corruption in kvm mode (Jan Kiszka) | ||||
|   - qcow2: Fix access after end of array (Kevin Wolf) | ||||
|   - ide save/restore pio/atapi cmd transfer fields and io buffer (Marcelo Tosatti) | ||||
|   - net: Monitor command set_link finds only VLAN clients, fix (Markus Armbruster) | ||||
|   - net: info network shows only VLAN clients, fix (Markus Armbruster) | ||||
|   - net: net_check_clients() checks only VLAN clients, fix (Markus Armbruster) | ||||
|   - net: Fix bogus "Warning: vlan 0 with no nics" with -device (Markus Armbruster) | ||||
|   - net: net_check_clients() runs too early to see -device, fix (Markus Armbruster) | ||||
|   - net: Remove unused net_client_uninit() (Markus Armbruster) | ||||
|   - don't dereference NULL after failed strdup (Jim Meyering) | ||||
|   - virtio-net: fix network stall under load (Tom Lendacky) | ||||
|   - json: fix PRId64 on Win32 (Roy Tam) | ||||
|   - fix inet_parse typo (Marcelo Tosatti) | ||||
|   - iothread: fix vcpu stop with smp tcg (Marcelo Tosatti) | ||||
|   - segfault due to buffer overrun in usb-serial (David S. Ahern) | ||||
|   - qcow2: Fix signedness bugs (Kevin Wolf) | ||||
|   - Do not ignore error, if open file failed (-serial /dev/tty) (Evgeniy Dushistov) | ||||
|   - pc-bios: update to newer version of (stable) seabios (Anthony Liguori) | ||||
|   - target-mips: fix ROTR and DROTR by zero (Aurelien Jarno) | ||||
|   - target-mips: fix CpU exception for coprocessor 0 (Nathan Froyd) | ||||
|   - tcg/mips: fix crash in tcg_out_qemu_ld() (Aurelien Jarno) | ||||
|   - target-mips: don't call cpu_loop_exit() from helper.c (Aurelien Jarno) | ||||
|   - virtio-blk: Fix error cases which ignored rerror/werror (Kevin Wolf) | ||||
|   - virtio-blk: Fix restart after read error (Kevin Wolf) | ||||
|   - virtio_blk: Factor virtio_blk_handle_request out (Kevin Wolf) | ||||
|   - cirrus: Properly re-register cirrus_linear_io_addr on vram unmap (Jan Kiszka) | ||||
|   - qcow2: Don't ignore qcow2_alloc_clusters return value (Kevin Wolf) | ||||
|   - qcow2: Don't ignore update_refcount return value (Kevin Wolf) | ||||
|   - qcow2: Allow updating no refcounts (Kevin Wolf) | ||||
|   - qcow2: Improve error handling in update_refcount (Kevin Wolf) | ||||
|   - qcow2: Fix error handling in grow_refcount_table (Kevin Wolf) | ||||
|   - block: Return original error codes in bdrv_pread/write (Kevin Wolf) | ||||
|   - qcow2: Return 0/-errno in qcow2_alloc_cluster_offset (Kevin Wolf) | ||||
|   - qcow2: Return 0/-errno in get_cluster_table (Kevin Wolf) | ||||
|   - qcow2: Fix error handling in qcow_save_vmstate (Kevin Wolf) | ||||
|   - qcow2: Fix error handling in qcow2_grow_l1_table (Kevin Wolf) | ||||
|   - win32/sdl: Fix toggle full screen (Herve Poussineau) | ||||
|   - win32: pair qemu_memalign() with qemu_vfree() (Herve Poussineau) | ||||
|   - vnc_refresh: calling vnc_update_client might free vs (Stefano Stabellini) | ||||
|   - Musicpal: Fix descriptor walk in eth_send (Jan Kiszka) | ||||
|   - Musicpal: Fix wm8750 I2C address (Jan Kiszka) | ||||
|   - fix savevm command without id or tag (Marcelo Tosatti) | ||||
|   - reduce number of reinjects on ACK (Gleb Natapov) | ||||
|   - QMP: Fix asynchronous events delivery (Luiz Capitulino) | ||||
|   - Documentation: Add missing documentation for qdev related command line options (Stefan Weil) | ||||
|   - pc: add driver version compat properties (Gerd Hoffmann) | ||||
|   - scsi: device version property (Gerd Hoffmann) | ||||
|   - ide: device version property (Gerd Hoffmann) | ||||
|   - QMP: Emit asynchronous events on all QMP monitors (Adam Litke) | ||||
|   - Fix QEMU_WARN_UNUSED_RESULT (Kevin Wolf) | ||||
|  | ||||
| version 0.12.2: | ||||
|   - Qemu's internal TFTP server breaks lock-step-iness of TFTP (Milan Plzik) | ||||
|   - osdep.c: Fix accept4 fallback (Kevin Wolf) | ||||
|   - pc: add rombar to compat properties for pc-0.10 and pc-0.11 (Gerd Hoffmann) | ||||
|   - pci: allow loading roms via fw_cfg. (Gerd Hoffmann) | ||||
|   - roms: rework rom loading via fw (Gerd Hoffmann) | ||||
|   - fw_cfg: rom loader tweaks. (Gerd Hoffmann) | ||||
|   - roms: minor fixes and cleanups. (Gerd Hoffmann) | ||||
|   - pc: add machine type for 0.12 (Gerd Hoffmann) | ||||
|   - loader: more ignores for rom intended to be loaded by the bios (Aurelien Jarno) | ||||
|   - vnc_refresh: return if vd->timer is NULL (Stefano Stabellini) | ||||
|   - QMP: Don't free async event's 'data' (Luiz Capitulino) | ||||
|   - Handle TFTP ERROR from client (Thomas Horsten) | ||||
|   - dmg: fix ->open failure (Christoph Hellwig) | ||||
|   - virtio-pci: thinko fix (Michael S. Tsirkin) | ||||
|   - pc-bios: Update README (SeaBIOS) (Stefan Weil) | ||||
|   - vmware_vga: Check cursor dimensions passed from guest to avoid buffer overflow (Roland Dreier) | ||||
|   - remove pending exception on vcpu reset. (Gleb Natapov) | ||||
|   - Fix CPU topology initialization (Jiri Denemark) | ||||
|   - MCE: Fix bug of IA32_MCG_STATUS after system reset (Huang Ying) | ||||
|   - linuxboot: fix gdt address calculation (Avi Kivity) | ||||
|   - QMP: Drop wrong assert() (Luiz Capitulino) | ||||
|   - vnc: Fix artifacts in hextile decoding (Anthony Liguori) | ||||
|   - target-i386: Fix "call im" on x86_64 when executing 32-bit code (Aurelien Jarno) | ||||
|   - Add missing newline at the end of options list (Michael Tokarev) | ||||
|   - Don't load options roms intended to be loaded by the bios in qemu (Avi Kivity) | ||||
|   - USB: Improve usbdevice error messages (Scott Tsai) | ||||
|   - cpu-all.h: fix cpu_get_real_ticks() #ifdef (Aurelien Jarno) | ||||
|   - alpha: fix compile (Blue Swirl) | ||||
|   - user_only: compile everything with -fpie (Kirill A. Shutemov) | ||||
|   - fdc/sparc32: don't hang on detection under OBP (Artyom Tarasenko) | ||||
|   - scsi-disk: Inquiry with allocation length of CDB < 36 (v4) (Artyom Tarasenko) | ||||
|   - e1000: fix init values for command register (Michael S. Tsirkin) | ||||
|  | ||||
| version 0.12.1: | ||||
|   - loader: fix rom loading at address 0 (fixes target-arm) (Aurelien Jarno) | ||||
|   - loader: fix rom_copy (fixes multiboot) (Kevin Wolf) | ||||
|  | ||||
| version 0.12.0: | ||||
|  | ||||
|   - Update to SeaBIOS 0.5.0 | ||||
|   | ||||
							
								
								
									
										2
									
								
								EXTERNAL_DEPENDENCIES
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								EXTERNAL_DEPENDENCIES
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| seabios 9fb3f4d950744e97cc655b7d7b523d8bf101e4a0 | ||||
| vgabios 6e62666cfc19e7fd45dd0d7c3ad62fd8d0b5f67a | ||||
							
								
								
									
										125
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								HACKING
									
									
									
									
									
								
							| @@ -1,125 +0,0 @@ | ||||
| 1. Preprocessor | ||||
|  | ||||
| For variadic macros, stick with this C99-like syntax: | ||||
|  | ||||
| #define DPRINTF(fmt, ...)                                       \ | ||||
|     do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0) | ||||
|  | ||||
| 2. C types | ||||
|  | ||||
| It should be common sense to use the right type, but we have collected | ||||
| a few useful guidelines here. | ||||
|  | ||||
| 2.1. Scalars | ||||
|  | ||||
| If you're using "int" or "long", odds are good that there's a better type. | ||||
| If a variable is counting something, it should be declared with an | ||||
| unsigned type. | ||||
|  | ||||
| If it's host memory-size related, size_t should be a good choice (use | ||||
| ssize_t only if required). Guest RAM memory offsets must use ram_addr_t, | ||||
| but only for RAM, it may not cover whole guest address space. | ||||
|  | ||||
| If it's file-size related, use off_t. | ||||
| If it's file-offset related (i.e., signed), use off_t. | ||||
| If it's just counting small numbers use "unsigned int"; | ||||
| (on all but oddball embedded systems, you can assume that that | ||||
| type is at least four bytes wide). | ||||
|  | ||||
| In the event that you require a specific width, use a standard type | ||||
| like int32_t, uint32_t, uint64_t, etc.  The specific types are | ||||
| mandatory for VMState fields. | ||||
|  | ||||
| Don't use Linux kernel internal types like u32, __u32 or __le32. | ||||
|  | ||||
| Use target_phys_addr_t for guest physical addresses except pcibus_t | ||||
| for PCI addresses.  In addition, ram_addr_t is a QEMU internal address | ||||
| space that maps guest RAM physical addresses into an intermediate | ||||
| address space that can map to host virtual address spaces.  Generally | ||||
| speaking, the size of guest memory can always fit into ram_addr_t but | ||||
| it would not be correct to store an actual guest physical address in a | ||||
| ram_addr_t. | ||||
|  | ||||
| Use target_ulong (or abi_ulong) for CPU virtual addresses, however | ||||
| devices should not need to use target_ulong. | ||||
|  | ||||
| Of course, take all of the above with a grain of salt.  If you're about | ||||
| to use some system interface that requires a type like size_t, pid_t or | ||||
| off_t, use matching types for any corresponding variables. | ||||
|  | ||||
| Also, if you try to use e.g., "unsigned int" as a type, and that | ||||
| conflicts with the signedness of a related variable, sometimes | ||||
| it's best just to use the *wrong* type, if "pulling the thread" | ||||
| and fixing all related variables would be too invasive. | ||||
|  | ||||
| Finally, while using descriptive types is important, be careful not to | ||||
| go overboard.  If whatever you're doing causes warnings, or requires | ||||
| casts, then reconsider or ask for help. | ||||
|  | ||||
| 2.2. Pointers | ||||
|  | ||||
| Ensure that all of your pointers are "const-correct". | ||||
| Unless a pointer is used to modify the pointed-to storage, | ||||
| give it the "const" attribute.  That way, the reader knows | ||||
| up-front that this is a read-only pointer.  Perhaps more | ||||
| importantly, if we're diligent about this, when you see a non-const | ||||
| pointer, you're guaranteed that it is used to modify the storage | ||||
| it points to, or it is aliased to another pointer that is. | ||||
|  | ||||
| 2.3. Typedefs | ||||
| Typedefs are used to eliminate the redundant 'struct' keyword. | ||||
|  | ||||
| 2.4. Reserved namespaces in C and POSIX | ||||
| Underscore capital, double underscore, and underscore 't' suffixes should be | ||||
| avoided. | ||||
|  | ||||
| 3. Low level memory management | ||||
|  | ||||
| Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign | ||||
| APIs is not allowed in the QEMU codebase. Instead of these routines, | ||||
| use the replacement qemu_malloc/qemu_mallocz/qemu_realloc/qemu_free or | ||||
| qemu_vmalloc/qemu_memalign/qemu_vfree APIs. | ||||
|  | ||||
| Please note that NULL check for the qemu_malloc result is redundant and | ||||
| that qemu_malloc() call with zero size is not allowed. | ||||
|  | ||||
| Memory allocated by qemu_vmalloc or qemu_memalign must be freed with | ||||
| qemu_vfree, since breaking this will cause problems on Win32 and user | ||||
| emulators. | ||||
|  | ||||
| 4. String manipulation | ||||
|  | ||||
| Do not use the strncpy function.  According to the man page, it does | ||||
| *not* guarantee a NULL-terminated buffer, which makes it extremely dangerous | ||||
| to use.  Instead, use functionally equivalent function: | ||||
| void pstrcpy(char *buf, int buf_size, const char *str) | ||||
|  | ||||
| Don't use strcat because it can't check for buffer overflows, but: | ||||
| char *pstrcat(char *buf, int buf_size, const char *s) | ||||
|  | ||||
| The same limitation exists with sprintf and vsprintf, so use snprintf and | ||||
| vsnprintf. | ||||
|  | ||||
| QEMU provides other useful string functions: | ||||
| int strstart(const char *str, const char *val, const char **ptr) | ||||
| int stristart(const char *str, const char *val, const char **ptr) | ||||
| int qemu_strnlen(const char *s, int max_len) | ||||
|  | ||||
| There are also replacement character processing macros for isxyz and toxyz, | ||||
| so instead of e.g. isalnum you should use qemu_isalnum. | ||||
|  | ||||
| Because of the memory management rules, you must use qemu_strdup/qemu_strndup | ||||
| instead of plain strdup/strndup. | ||||
|  | ||||
| 5. Printf-style functions | ||||
|  | ||||
| Whenever you add a new printf-style function, i.e., one with a format | ||||
| string argument and following "..." in its prototype, be sure to use | ||||
| gcc's printf attribute directive in the prototype. | ||||
|  | ||||
| This makes it so gcc's -Wformat and -Wformat-security options can do | ||||
| their jobs and cross-check format strings with the number and types | ||||
| of arguments. | ||||
|  | ||||
| Currently many functions in QEMU are not following this rule but | ||||
| patches to add the attribute would be very much appreciated. | ||||
							
								
								
									
										1
									
								
								KVM_VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								KVM_VERSION
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| qemu-kvm-0.12.5 | ||||
							
								
								
									
										551
									
								
								MAINTAINERS
									
									
									
									
									
								
							
							
						
						
									
										551
									
								
								MAINTAINERS
									
									
									
									
									
								
							| @@ -1,489 +1,88 @@ | ||||
| QEMU Maintainers | ||||
| ================ | ||||
|  | ||||
| The intention of this file is not to establish who owns what portions of the | ||||
| code base, but to provide a set of names that developers can consult when they | ||||
| have a question about a particular subset and also to provide a set of names | ||||
| to be CC'd when submitting a patch to obtain appropriate review. | ||||
|  | ||||
| In general, if you have a question about inclusion of a patch, you should | ||||
| consult qemu-devel and not any specific individual privately. | ||||
|  | ||||
| Descriptions of section entries: | ||||
|  | ||||
| 	M: Mail patches to: FullName <address@domain> | ||||
| 	L: Mailing list that is relevant to this area | ||||
| 	W: Web-page with status/info | ||||
| 	Q: Patchwork web based patch tracking system site | ||||
| 	T: SCM tree type and location.  Type is one of: git, hg, quilt, stgit. | ||||
| 	S: Status, one of the following: | ||||
| 	   Supported:	Someone is actually paid to look after this. | ||||
| 	   Maintained:	Someone actually looks after it. | ||||
| 	   Odd Fixes:	It has a maintainer but they don't have time to do | ||||
| 			much other than throw the odd patch in. See below.. | ||||
| 	   Orphan:	No current maintainer [but maybe you could take the | ||||
| 			role as you write your new code]. | ||||
| 	   Obsolete:	Old code. Something tagged obsolete generally means | ||||
| 			it has been replaced by a better system and you | ||||
| 			should be using that. | ||||
| 	F: Files and directories with wildcard patterns. | ||||
| 	   A trailing slash includes all files and subdirectory files. | ||||
| 	   F:	drivers/net/	all files in and below drivers/net | ||||
| 	   F:	drivers/net/*	all files in drivers/net, but not below | ||||
| 	   F:	*/net/*		all files in "any top level directory"/net | ||||
| 	   One pattern per line.  Multiple F: lines acceptable. | ||||
| 	X: Files and directories that are NOT maintained, same rules as F: | ||||
| 	   Files exclusions are tested before file matches. | ||||
| 	   Can be useful for excluding a specific subdirectory, for instance: | ||||
| 	   F:	net/ | ||||
| 	   X:	net/ipv6/ | ||||
| 	   matches all files in and below net excluding net/ipv6/ | ||||
| 	K: Keyword perl extended regex pattern to match content in a | ||||
| 	   patch or file.  For instance: | ||||
| 	   K: of_get_profile | ||||
| 	      matches patches or files that contain "of_get_profile" | ||||
| 	   K: \b(printk|pr_(info|err))\b | ||||
| 	      matches patches or files that contain one or more of the words | ||||
| 	      printk, pr_info or pr_err | ||||
| 	   One regex pattern per line.  Multiple K: lines acceptable. | ||||
|  | ||||
|  | ||||
| General Project Administration | ||||
| ------------------------------ | ||||
| M: Anthony Liguori <aliguori@us.ibm.com> | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
|  | ||||
| Guest CPU cores (TCG): | ||||
| ---------------------- | ||||
| Alpha | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Orphan | ||||
| F: target-alpha/ | ||||
|  | ||||
| ARM | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: target-arm/ | ||||
|  | ||||
| CRIS | ||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> | ||||
| S: Maintained | ||||
| F: target-cris/ | ||||
|  | ||||
| M68K | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: target-m68k/ | ||||
|  | ||||
| MicroBlaze | ||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> | ||||
| S: Maintained | ||||
| F: target-microblaze/ | ||||
|  | ||||
| MIPS | ||||
| M: Aurelien Jarno <aurelien@aurel32.net> | ||||
| S: Maintained | ||||
| F: target-mips/ | ||||
|  | ||||
| PowerPC | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| S: Maintained | ||||
| F: target-ppc/ | ||||
|  | ||||
| S390 | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| S: Maintained | ||||
| F: target-s390x/ | ||||
|  | ||||
| SH4 | ||||
| M: Aurelien Jarno <aurelien@aurel32.net> | ||||
| S: Maintained | ||||
| F: target-sh4/ | ||||
|  | ||||
| SPARC | ||||
| M: Blue Swirl <blauwirbel@gmail.com> | ||||
| S: Maintained | ||||
| F: target-sparc/ | ||||
|  | ||||
| X86 | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Odd Fixes | ||||
| F: target-i386/ | ||||
|  | ||||
| Guest CPU Cores (KVM): | ||||
| ---------------------- | ||||
|  | ||||
| Overall | ||||
| M: Avi Kivity <avi@redhat.com> | ||||
| M: Marcelo Tosatti <mtosatti@redhat.com> | ||||
| L: kvm@vger.kernel.org | ||||
| S: Supported | ||||
| F: kvm-* | ||||
| F: */kvm.* | ||||
|  | ||||
| PPC | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| S: Maintained | ||||
| F: target-ppc/kvm.c | ||||
|  | ||||
| S390 | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| S: Maintained | ||||
| F: target-s390x/kvm.c | ||||
|  | ||||
| X86 | ||||
| M: Avi Kivity <avi@redhat.com> | ||||
| M: Marcelo Tosatti <mtosatti@redhat.com> | ||||
| L: kvm@vger.kernel.org | ||||
| S: Supported | ||||
| F: target-i386/kvm.c | ||||
|  | ||||
| ARM Machines | ||||
| ------------ | ||||
| Gumstix | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Orphan | ||||
| F: hw/gumstix.c | ||||
|  | ||||
| Integrator CP | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: hw/integratorcp.c | ||||
|  | ||||
| Mainstone | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Orphan | ||||
| F: hw/mainstone.c | ||||
|  | ||||
| Musicpal | ||||
| M: Jan Kiszka <jan.kiszka@web.de> | ||||
| S: Maintained | ||||
| F: hw/musicpal.c | ||||
|  | ||||
| nSeries | ||||
| M: Andrzej Zaborowski <balrogg@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/nseries.c | ||||
|  | ||||
| Palm | ||||
| M: Andrzej Zaborowski <balrogg@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/palm.c | ||||
|  | ||||
| Real View | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: hw/realview* | ||||
|  | ||||
| Spitz | ||||
| M: Andrzej Zaborowski <balrogg@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/spitz.c | ||||
|  | ||||
| Stellaris | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: hw/stellaris.c | ||||
|  | ||||
| Versatile PB | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: hw/versatilepb.c | ||||
|  | ||||
| CRIS Machines | ||||
| ------------- | ||||
| Axis Dev88 | ||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/axis_dev88.c | ||||
|  | ||||
| etraxfs | ||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/etraxfs.c | ||||
|  | ||||
| M68K Machines | ||||
| ------------- | ||||
| an5206 | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: hw/an5206.c | ||||
|  | ||||
| dummy_m68k | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: hw/dummy_m68k.c | ||||
|  | ||||
| mcf5208 | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| S: Maintained | ||||
| F: hw/mcf5208.c | ||||
|  | ||||
| MicroBlaze Machines | ||||
| ------------------- | ||||
| petalogix_s3adsp1800 | ||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/petalogix_s3adsp1800.c | ||||
|  | ||||
| MIPS Machines | ||||
| ------------- | ||||
| Jazz | ||||
| M: Hervé Poussineau <hpoussin@reactos.org> | ||||
| S: Maintained | ||||
| F: hw/mips_jazz.c | ||||
|  | ||||
| Malta | ||||
| M: Aurelien Jarno <aurelien@aurel32.net> | ||||
| S: Maintained | ||||
| F: hw/mips_malta.c | ||||
|  | ||||
| Mipssim | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Orphan | ||||
| F: hw/mips_mipssim.c | ||||
|  | ||||
| R4000 | ||||
| M: Aurelien Jarno <aurelien@aurel32.net> | ||||
| S: Maintained | ||||
| F: hw/mips_r4k.c | ||||
|  | ||||
| PowerPC Machines | ||||
| Project leaders: | ||||
| ---------------- | ||||
| 405 | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| S: Maintained | ||||
| F: hw/ppc405_boards.c | ||||
|  | ||||
| New World | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| S: Maintained | ||||
| F: hw/ppc_newworld.c | ||||
| Fabrice Bellard | ||||
| Paul Brook | ||||
|  | ||||
| Old World | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| S: Maintained | ||||
| F: hw/ppc_oldworld.c | ||||
|  | ||||
| Prep | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Orphan | ||||
| F: hw/ppc_prep.c | ||||
|  | ||||
| SH4 Machines | ||||
| ------------ | ||||
| R2D | ||||
| M: Magnus Damm <magnus.damm@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/r2d.c | ||||
|  | ||||
| Shix | ||||
| M: Magnus Damm <magnus.damm@gmail.com> | ||||
| S: Orphan | ||||
| F: hw/shix.c | ||||
|  | ||||
| SPARC Machines | ||||
| -------------- | ||||
| Sun4m | ||||
| M: Blue Swirl <blauwirbel@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/sun4m.c | ||||
|  | ||||
| Sun4u | ||||
| M: Blue Swirl <blauwirbel@gmail.com> | ||||
| S: Maintained | ||||
| F: hw/sun4u.c | ||||
|  | ||||
| S390 Machines | ||||
| ------------- | ||||
| S390 Virtio | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| S: Maintained | ||||
| F: hw/s390-*.c | ||||
|  | ||||
| X86 Machines | ||||
| ------------ | ||||
| PC | ||||
| M: Anthony Liguori <aliguori@us.ibm.com> | ||||
| S: Supported | ||||
| F: hw/pc.[ch] hw/pc_piix.c | ||||
|  | ||||
| Devices | ||||
| ------- | ||||
| IDE | ||||
| M: Kevin Wolf <kwolf@redhat.com> | ||||
| S: Odd Fixes | ||||
| F: hw/ide/ | ||||
|  | ||||
| PCI | ||||
| M: Michael S. Tsirkin <mst@redhat.com> | ||||
| S: Supported | ||||
| F: hw/pci* | ||||
| F: hw/piix* | ||||
|  | ||||
| SCSI | ||||
| M: Paul Brook <paul@codesourcery.com> | ||||
| M: Kevin Wolf <kwolf@redhat.com> | ||||
| S: Odd Fixes | ||||
| F: hw/lsi53c895a.c | ||||
| F: hw/scsi* | ||||
|  | ||||
| USB | ||||
| M: Gerd Hoffmann <kraxel@redhat.com> | ||||
| S: Maintained | ||||
| F: hw/usb* | ||||
|  | ||||
| vhost | ||||
| M: Michael S. Tsirkin <mst@redhat.com> | ||||
| S: Supported | ||||
| F: hw/vhost* | ||||
|  | ||||
| virtio | ||||
| M: Anthony Liguori <aliguori@us.ibm.com> | ||||
| S: Supported | ||||
| F: hw/virtio* | ||||
|  | ||||
| virtio-9p | ||||
| M: Venkateswararao Jujjuri (JV) <jvrao@linux.vnet.ibm.com> | ||||
| S: Supported | ||||
| F: hw/virtio-9p* | ||||
|  | ||||
| virtio-blk | ||||
| M: Kevin Wolf <kwolf@redhat.com> | ||||
| S: Supported | ||||
| F: hw/virtio-blk* | ||||
|  | ||||
| virtio-serial | ||||
| M: Amit Shah <amit.shah@redhat.com> | ||||
| S: Supported | ||||
| F: hw/virtio-serial* | ||||
| F: hw/virtio-console* | ||||
|  | ||||
| Subsystems | ||||
| CPU cores: | ||||
| ---------- | ||||
| Audio | ||||
| M: Vassili Karpov (malc) <av1474@comtv.ru> | ||||
| S: Maintained | ||||
| F: audio/ | ||||
|  | ||||
| Block | ||||
| M: Kevin Wolf <kwolf@redhat.com> | ||||
| S: Supported | ||||
| F: block* | ||||
| F: block/ | ||||
| x86                Fabrice Bellard | ||||
| ARM                Paul Brook | ||||
| SPARC              Blue Swirl | ||||
| MIPS               Thiemo Seufer | ||||
| PowerPC            ? | ||||
| M68K               Paul Brook | ||||
| SH4                ? | ||||
| CRIS               Edgar E. Iglesias | ||||
| Alpha              ? | ||||
| MicroBlaze         Edgar E. Iglesias | ||||
| S390               ? | ||||
|  | ||||
| Character Devices | ||||
| M: Anthony Liguori <aliguori@us.ibm.com> | ||||
| S: Maintained | ||||
| F: qemu-char.c | ||||
|  | ||||
| GDB stub | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Odd Fixes | ||||
| F: gdbstub* | ||||
| F: gdb-xml/ | ||||
|  | ||||
| SPICE | ||||
| M: Gerd Hoffmann <kraxel@redhat.com> | ||||
| S: Supported | ||||
| F: ui/qemu-spice.h | ||||
| F: ui/spice-*.c | ||||
| F: audio/spiceaudio.c | ||||
| F: hw/qxl* | ||||
|  | ||||
| Graphics | ||||
| M: Anthony Liguori <aliguori@us.ibm.com> | ||||
| S: Maintained | ||||
| F: ui/ | ||||
|  | ||||
| Main loop | ||||
| M: Anthony Liguori <aliguori@us.ibm.com> | ||||
| S: Supported | ||||
| F: vl.c | ||||
|  | ||||
| Monitor (QMP/HMP) | ||||
| M: Luiz Capitulino <lcapitulino@redhat.com> | ||||
| M: Markus Armbruster <armbru@redhat.com> | ||||
| S: Supported | ||||
| F: monitor.c | ||||
|  | ||||
| Network device layer | ||||
| M: Anthony Liguori <aliguori@us.ibm.com> | ||||
| M: Mark McLoughlin <markmc@redhat.com> | ||||
| S: Maintained | ||||
| F: net/ | ||||
|  | ||||
| SLIRP | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Orphan | ||||
| F: slirp/ | ||||
|  | ||||
| Usermode Emulation | ||||
| ------------------ | ||||
| BSD user | ||||
| M: Blue Swirl <blauwirbel@gmail.com> | ||||
| S: Maintained | ||||
| F: bsd-user/ | ||||
|  | ||||
| Darwin user | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Orphan | ||||
| F: darwin-user/ | ||||
|  | ||||
| Linux user | ||||
| M: Riku Voipio <riku.voipio@iki.fi> | ||||
| S: Maintained | ||||
| F: linux-user/ | ||||
|  | ||||
| Tiny Code Generator (TCG) | ||||
| Machines (sorted by CPU): | ||||
| ------------------------- | ||||
| Common code | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Maintained | ||||
| F: tcg/ | ||||
|  | ||||
| ARM target | ||||
| M: Andrzej Zaborowski <balrogg@gmail.com> | ||||
| S: Maintained | ||||
| F: tcg/arm/ | ||||
| x86 | ||||
|   pc.c                    Fabrice Bellard (new maintainer needed) | ||||
| ARM | ||||
|   integratorcp.c          Paul Brook | ||||
|   versatilepb.c           Paul Brook | ||||
|   Real View               Paul Brook | ||||
|   spitz.c                 Andrzej Zaborowski | ||||
|   palm.c                  Andrzej Zaborowski | ||||
|   nseries.c               Andrzej Zaborowski | ||||
|   stellaris.c             Paul Brook | ||||
|   gumstix.c               Thorsten Zitterell | ||||
|   mainstone.c             Armin Kuster | ||||
|   musicpal.c              Jan Kiszka | ||||
| SPARC | ||||
|   sun4u.c                 Blue Swirl | ||||
|   sun4m.c                 Blue Swirl | ||||
| MIPS | ||||
|   mips_r4k.c              Aurelien Jarno | ||||
|   mips_malta.c            Aurelien Jarno | ||||
|   mips_jazz.c             Hervé Poussineau | ||||
|   mips_mipssim.c          Thiemo Seufer | ||||
| PowerPC | ||||
|   ppc_prep.c              ? | ||||
|   ppc_oldworld.c          Fabrice Bellard | ||||
|   ppc_chrp.c              Fabrice Bellard | ||||
|   ppc405_boards.c         ? | ||||
| M86K | ||||
|   mcf5208.c               Paul Brook | ||||
|   an5206.c                Paul Brook | ||||
|   dummy_m68k.c            Paul Brook | ||||
| SH4 | ||||
|   shix.c                  ? | ||||
|   r2d.c                   Magnus Damm | ||||
| CRIS | ||||
|   etraxfs.c               Edgar E. Iglesias | ||||
|   axis_dev88.c            Edgar E. Iglesias | ||||
| Alpha | ||||
| MicroBlaze | ||||
|   petalogix_s3adsp1800.c  Edgar E. Iglesias | ||||
| S390 | ||||
|   s390-*.c                Alexander Graf | ||||
|  | ||||
| HPPA target | ||||
| M: Richard Henderson <rth@twiddle.net> | ||||
| S: Maintained | ||||
| F: tcg/hppa/ | ||||
| Generic Subsystems: | ||||
| -------------------   | ||||
|  | ||||
| i386 target | ||||
| M: qemu-devel@nongnu.org | ||||
| S: Maintained | ||||
| F: tcg/i386/ | ||||
|  | ||||
| IA64 target | ||||
| M: Aurelien Jarno <aurelien@aurel32.net> | ||||
| S: Maintained | ||||
| F: tcg/ia64/ | ||||
|  | ||||
| MIPS target | ||||
| M: Aurelien Jarno <aurelien@aurel32.ne> | ||||
| S: Maintained | ||||
| F: tcg/mips/ | ||||
|  | ||||
| PPC | ||||
| M: Vassili Karpov (malc) <av1474@comtv.ru> | ||||
| S: Maintained | ||||
| F: tcg/ppc/ | ||||
|  | ||||
| PPC64 target | ||||
| M: Vassili Karpov (malc) <av1474@comtv.ru> | ||||
| S: Maintained | ||||
| F: tcg/ppc64/ | ||||
|  | ||||
| S390 target | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| M: Richard Henderson <rth@twiddle.net> | ||||
| S: Maintained | ||||
| F: tcg/s390/ | ||||
|  | ||||
| SPARC target | ||||
| M: Blue Swirl <blauwirbel@gmail.com> | ||||
| S: Maintained | ||||
| F: tcg/sparc/ | ||||
| Dynamic translator        Fabrice Bellard | ||||
| Main loop                 Fabrice Bellard (new maintainer needed) | ||||
| TCG                       Fabrice Bellard | ||||
| IDE device                ? | ||||
| SCSI device               Paul Brook | ||||
| PCI layer                 ? | ||||
| USB layer                 ? | ||||
| Block layer               ? | ||||
| Graphic layer             ? | ||||
| Audio device layer        Vassili Karpov (malc) | ||||
| Character device layer    ? | ||||
| Network device layer      ? | ||||
| GDB stub                  ? | ||||
| Linux user                ? | ||||
| Darwin user               ? | ||||
| SLIRP                     ? | ||||
|   | ||||
							
								
								
									
										402
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										402
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,16 +1,14 @@ | ||||
| # Makefile for QEMU. | ||||
|  | ||||
| GENERATED_HEADERS = config-host.h trace.h qemu-options.def | ||||
| ifeq ($(TRACE_BACKEND),dtrace) | ||||
| GENERATED_HEADERS += trace-dtrace.h | ||||
| endif | ||||
| # This needs to be defined before rules.mak | ||||
| GENERATED_HEADERS = config-host.h | ||||
|  | ||||
| ifneq ($(wildcard config-host.mak),) | ||||
| # Put the all: rule here so that config-host.mak can contain dependencies. | ||||
| all: build-all | ||||
| include config-host.mak | ||||
| include $(SRC_PATH)/rules.mak | ||||
| config-host.mak: $(SRC_PATH)/configure | ||||
| config-host.mak: configure | ||||
| 	@echo $@ is out-of-date, running configure | ||||
| 	@sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh | ||||
| else | ||||
| @@ -25,45 +23,32 @@ Makefile: ; | ||||
| configure: ; | ||||
|  | ||||
| .PHONY: all clean cscope distclean dvi html info install install-doc \ | ||||
| 	pdf recurse-all speed tar tarbin test build-all | ||||
| 	recurse-all speed tar tarbin test build-all | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw) | ||||
| VPATH=$(SRC_PATH):$(SRC_PATH)/hw | ||||
|  | ||||
| LIBS+=-lz $(LIBS_TOOLS) | ||||
|  | ||||
| ifdef BUILD_DOCS | ||||
| DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 QMP/qmp-commands.txt | ||||
| DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 | ||||
| else | ||||
| DOCS= | ||||
| endif | ||||
|  | ||||
| SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) | ||||
| SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) | ||||
| SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS)) | ||||
|  | ||||
| config-all-devices.mak: $(SUBDIR_DEVICES_MAK) | ||||
| 	$(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@,"  GEN   $@") | ||||
|  | ||||
| -include $(SUBDIR_DEVICES_MAK_DEP) | ||||
|  | ||||
| %/config-devices.mak: default-configs/%.mak | ||||
| 	$(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $@ $<, "  GEN   $@") | ||||
| 	@if test -f $@; then \ | ||||
| 	  if cmp -s $@.old $@; then \ | ||||
| 	    mv $@.tmp $@; \ | ||||
| 	    cp -p $@ $@.old; \ | ||||
| 	  else \ | ||||
| 	    if test -f $@.old; then \ | ||||
| 	      echo "WARNING: $@ (user modified) out of date.";\ | ||||
| 	    else \ | ||||
| 	      echo "WARNING: $@ out of date.";\ | ||||
| 	    fi; \ | ||||
| 	    echo "Run \"make defconfig\" to regenerate."; \ | ||||
| 	    rm $@.tmp; \ | ||||
| 	  fi; \ | ||||
| 	$(call quiet-command,cat $< > $@.tmp, "  GEN   $@") | ||||
| 	@if test -f $@ ; then \ | ||||
| 	  echo "WARNING: $@ out of date." ;\ | ||||
| 	  echo "Run \"make defconfig\" to regenerate." ; \ | ||||
| 	  rm $@.tmp ; \ | ||||
| 	 else \ | ||||
| 	  mv $@.tmp $@; \ | ||||
| 	  cp -p $@ $@.old; \ | ||||
| 	  mv $@.tmp $@ ; \ | ||||
| 	 fi | ||||
|  | ||||
| defconfig: | ||||
| @@ -75,22 +60,30 @@ build-all: $(DOCS) $(TOOLS) recurse-all | ||||
|  | ||||
| config-host.h: config-host.h-timestamp | ||||
| config-host.h-timestamp: config-host.mak | ||||
| qemu-options.def: $(SRC_PATH)/qemu-options.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $@") | ||||
|  | ||||
| SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) | ||||
|  | ||||
| ifeq ($(KVM_KMOD),yes) | ||||
|  | ||||
| .PHONEY: kvm-kmod | ||||
|  | ||||
| all: kvm-kmod | ||||
|  | ||||
| kvm-kmod: | ||||
| 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C kvm/kernel V="$(V)" ) | ||||
|  | ||||
|  | ||||
| endif | ||||
|  | ||||
| subdir-%: $(GENERATED_HEADERS) | ||||
| 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,) | ||||
|  | ||||
| ifneq ($(wildcard config-host.mak),) | ||||
| include $(SRC_PATH)/Makefile.objs | ||||
| endif | ||||
| $(filter %-softmmu,$(SUBDIR_RULES)): libqemu_common.a | ||||
|  | ||||
| $(common-obj-y): $(GENERATED_HEADERS) | ||||
| $(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis | ||||
| $(filter %-user,$(SUBDIR_RULES)): libuser.a | ||||
|  | ||||
| $(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser | ||||
| libuser.a: $(GENERATED_HEADERS) | ||||
| 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libuser V="$(V)" TARGET_DIR="libuser/" all,) | ||||
|  | ||||
| ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) | ||||
| romsubdir-%: | ||||
| @@ -100,102 +93,194 @@ ALL_SUBDIRS=$(TARGET_DIRS) $(patsubst %,pc-bios/%, $(ROMS)) | ||||
|  | ||||
| recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES) | ||||
|  | ||||
| ####################################################################### | ||||
| # QObject | ||||
| qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o | ||||
| qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o | ||||
| qobject-obj-y += qerror.o | ||||
|  | ||||
| ####################################################################### | ||||
| # block-obj-y is code used by both qemu system emulation and qemu-img | ||||
|  | ||||
| block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o | ||||
| block-obj-y += nbd.o block.o aio.o aes.o osdep.o | ||||
| block-obj-$(CONFIG_POSIX) += posix-aio-compat.o | ||||
| block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o | ||||
| block-obj-$(CONFIG_POSIX) += compatfd.o | ||||
|  | ||||
| block-nested-y += cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o | ||||
| block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o | ||||
| block-nested-y += parallels.o nbd.o | ||||
| block-nested-$(CONFIG_WIN32) += raw-win32.o | ||||
| block-nested-$(CONFIG_POSIX) += raw-posix.o | ||||
| block-nested-$(CONFIG_CURL) += curl.o | ||||
|  | ||||
| block-obj-y +=  $(addprefix block/, $(block-nested-y)) | ||||
|  | ||||
| net-obj-y = net.o | ||||
| net-nested-y = queue.o checksum.o util.o | ||||
| net-nested-y += socket.o | ||||
| net-nested-y += dump.o | ||||
| net-nested-$(CONFIG_POSIX) += tap.o | ||||
| net-nested-$(CONFIG_LINUX) += tap-linux.o | ||||
| net-nested-$(CONFIG_WIN32) += tap-win32.o | ||||
| net-nested-$(CONFIG_BSD) += tap-bsd.o | ||||
| net-nested-$(CONFIG_SOLARIS) += tap-solaris.o | ||||
| net-nested-$(CONFIG_AIX) += tap-aix.o | ||||
| net-nested-$(CONFIG_SLIRP) += slirp.o | ||||
| net-nested-$(CONFIG_VDE) += vde.o | ||||
| net-obj-y += $(addprefix net/, $(net-nested-y)) | ||||
|  | ||||
| ###################################################################### | ||||
| # libqemu_common.a: 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 single QEMU executable should support all | ||||
| # CPUs and machines. | ||||
|  | ||||
| obj-y = $(block-obj-y) | ||||
| obj-y += $(net-obj-y) | ||||
| obj-y += $(qobject-obj-y) | ||||
| obj-y += readline.o console.o | ||||
|  | ||||
| obj-y += tcg-runtime.o host-utils.o | ||||
| obj-y += irq.o ioport.o | ||||
| obj-$(CONFIG_PTIMER) += ptimer.o | ||||
| obj-$(CONFIG_MAX7310) += max7310.o | ||||
| obj-$(CONFIG_WM8750) += wm8750.o | ||||
| obj-$(CONFIG_TWL92230) += twl92230.o | ||||
| obj-$(CONFIG_TSC2005) += tsc2005.o | ||||
| obj-$(CONFIG_LM832X) += lm832x.o | ||||
| obj-$(CONFIG_TMP105) += tmp105.o | ||||
| obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o | ||||
| obj-$(CONFIG_SSD0303) += ssd0303.o | ||||
| obj-$(CONFIG_SSD0323) += ssd0323.o | ||||
| obj-$(CONFIG_ADS7846) += ads7846.o | ||||
| obj-$(CONFIG_MAX111X) += max111x.o | ||||
| obj-$(CONFIG_DS1338) += ds1338.o | ||||
| obj-y += i2c.o smbus.o smbus_eeprom.o | ||||
| obj-y += eeprom93xx.o | ||||
| obj-y += scsi-disk.o cdrom.o | ||||
| obj-y += scsi-generic.o scsi-bus.o | ||||
| obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o | ||||
| obj-y += usb-serial.o usb-net.o usb-bus.o | ||||
| obj-$(CONFIG_SSI) += ssi.o | ||||
| obj-$(CONFIG_SSI_SD) += ssi-sd.o | ||||
| obj-$(CONFIG_SD) += sd.o | ||||
| obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o | ||||
| obj-y += bt-hci-csr.o | ||||
| obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o | ||||
| obj-y += qemu-char.o aio.o savevm.o | ||||
| obj-y += msmouse.o ps2.o | ||||
| obj-y += qdev.o qdev-properties.o | ||||
| obj-y += qemu-config.o block-migration.o | ||||
|  | ||||
| obj-$(CONFIG_BRLAPI) += baum.o | ||||
| obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o | ||||
|  | ||||
| audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) | ||||
|  | ||||
| audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o | ||||
| audio-obj-$(CONFIG_SDL) += sdlaudio.o | ||||
| audio-obj-$(CONFIG_OSS) += ossaudio.o | ||||
| audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o | ||||
| audio-obj-$(CONFIG_ALSA) += alsaaudio.o | ||||
| audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o | ||||
| audio-obj-$(CONFIG_FMOD) += fmodaudio.o | ||||
| audio-obj-$(CONFIG_ESD) += esdaudio.o | ||||
| audio-obj-$(CONFIG_PA) += paaudio.o | ||||
| audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o | ||||
| audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o | ||||
| audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o | ||||
| audio-obj-y += wavcapture.o | ||||
| obj-y += $(addprefix audio/, $(audio-obj-y)) | ||||
|  | ||||
| obj-y += keymaps.o | ||||
| obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o | ||||
| obj-$(CONFIG_CURSES) += curses.o | ||||
| obj-y += vnc.o acl.o d3des.o | ||||
| obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o | ||||
| obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o | ||||
| obj-$(CONFIG_COCOA) += cocoa.o | ||||
| obj-$(CONFIG_IOTHREAD) += qemu-thread.o | ||||
|  | ||||
| slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o | ||||
| slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o | ||||
| slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o | ||||
| obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y)) | ||||
|  | ||||
| # xen backend driver support | ||||
| obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o | ||||
| obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o | ||||
|  | ||||
| QEMU_CFLAGS+=$(CURL_CFLAGS) | ||||
|  | ||||
| ui/cocoa.o: ui/cocoa.m | ||||
| cocoa.o: cocoa.m | ||||
|  | ||||
| ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) | ||||
| keymaps.o: keymaps.c keymaps.h | ||||
|  | ||||
| ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS) | ||||
| sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h | ||||
|  | ||||
| sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h | ||||
|  | ||||
| sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) | ||||
|  | ||||
| acl.o: acl.h acl.c | ||||
|  | ||||
| vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h | ||||
|  | ||||
| vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h | ||||
|  | ||||
| vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS) | ||||
|  | ||||
| vnc-tls.o: vnc-tls.c vnc.h | ||||
|  | ||||
| vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h | ||||
|  | ||||
| vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h | ||||
|  | ||||
| curses.o: curses.c keymaps.h curses_keys.h | ||||
|  | ||||
| bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS) | ||||
|  | ||||
| ifeq ($(TRACE_BACKEND),dtrace) | ||||
| trace.h: trace.h-timestamp trace-dtrace.h | ||||
| else | ||||
| trace.h: trace.h-timestamp | ||||
| endif | ||||
| trace.h-timestamp: $(SRC_PATH)/trace-events config-host.mak | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -h < $< > $@,"  GEN   trace.h") | ||||
| 	@cmp -s $@ trace.h || cp $@ trace.h | ||||
| libqemu_common.a: $(obj-y) | ||||
|  | ||||
| trace.c: trace.c-timestamp | ||||
| trace.c-timestamp: $(SRC_PATH)/trace-events config-host.mak | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -c < $< > $@,"  GEN   trace.c") | ||||
| 	@cmp -s $@ trace.c || cp $@ trace.c | ||||
|  | ||||
| trace.o: trace.c $(GENERATED_HEADERS) | ||||
|  | ||||
| trace-dtrace.h: trace-dtrace.dtrace | ||||
| 	$(call quiet-command,dtrace -o $@ -h -s $<, "  GEN   trace-dtrace.h") | ||||
|  | ||||
| # Normal practice is to name DTrace probe file with a '.d' extension | ||||
| # but that gets picked up by QEMU's Makefile as an external dependancy | ||||
| # rule file. So we use '.dtrace' instead | ||||
| trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp | ||||
| trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events config-host.mak | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -d < $< > $@,"  GEN   trace-dtrace.dtrace") | ||||
| 	@cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace | ||||
|  | ||||
| trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) | ||||
| 	$(call quiet-command,dtrace -o $@ -G -s $<, "  GEN trace-dtrace.o") | ||||
|  | ||||
| simpletrace.o: simpletrace.c $(GENERATED_HEADERS) | ||||
|  | ||||
| version.o: $(SRC_PATH)/version.rc config-host.mak | ||||
| 	$(call quiet-command,$(WINDRES) -I. -o $@ $<,"  RC    $(TARGET_DIR)$@") | ||||
|  | ||||
| version-obj-$(CONFIG_WIN32) += version.o | ||||
| ###################################################################### | ||||
|  | ||||
| qemu-img.o: qemu-img-cmds.h | ||||
| qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS) | ||||
|  | ||||
| qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o | ||||
| qemu-img$(EXESUF): qemu-img.o qemu-tool.o $(block-obj-y) $(qobject-obj-y) | ||||
|  | ||||
| qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o | ||||
| qemu-nbd$(EXESUF):  qemu-nbd.o qemu-tool.o $(block-obj-y) $(qobject-obj-y) | ||||
|  | ||||
| qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o | ||||
| qemu-io$(EXESUF):  qemu-io.o qemu-tool.o cmd.o $(block-obj-y) $(qobject-obj-y) | ||||
|  | ||||
| qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $@") | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $@") | ||||
|  | ||||
| check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS) | ||||
|  | ||||
| CHECK_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y) | ||||
|  | ||||
| check-qint: check-qint.o qint.o $(CHECK_PROG_DEPS) | ||||
| check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS) | ||||
| check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(CHECK_PROG_DEPS) | ||||
| check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS) | ||||
| check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS) | ||||
| check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS) | ||||
| check-qint: check-qint.o qint.o qemu-malloc.o | ||||
| check-qstring: check-qstring.o qstring.o qemu-malloc.o | ||||
| check-qdict: check-qdict.o qdict.o qint.o qstring.o qbool.o qemu-malloc.o qlist.o | ||||
| check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o | ||||
| check-qfloat: check-qfloat.o qfloat.o qemu-malloc.o | ||||
| check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o qemu-malloc.o | ||||
|  | ||||
| clean: | ||||
| # avoid old build problems by removing potentially incorrect old files | ||||
| 	rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h | ||||
| 	rm -f qemu-options.def | ||||
| 	rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~ | ||||
| 	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d | ||||
| 	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d | ||||
| 	rm -f qemu-img-cmds.h | ||||
| 	rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp | ||||
| 	rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp | ||||
| 	rm -f trace-dtrace.h trace-dtrace.h-timestamp | ||||
| 	$(MAKE) -C tests clean | ||||
| 	for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \ | ||||
| 	for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser; do \ | ||||
| 	if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ | ||||
| 	rm -f $$d/qemu-options.def; \ | ||||
|         done | ||||
|  | ||||
| distclean: clean | ||||
| 	rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi | ||||
| 	rm -f config-all-devices.mak | ||||
| 	rm -f roms/seabios/config.mak roms/vgabios/config.mak | ||||
| 	rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr | ||||
| 	rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr | ||||
| 	for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \ | ||||
| 	rm -f qemu-{doc,tech}.{info,aux,cp,dvi,fn,info,ky,log,pg,toc,tp,vr} | ||||
| 	for d in $(TARGET_DIRS) libhw32 libhw64 libuser; do \ | ||||
| 	rm -rf $$d || exit 1 ; \ | ||||
|         done | ||||
|  | ||||
| @@ -204,16 +289,15 @@ ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \ | ||||
| common  de-ch  es     fo  fr-ca  hu     ja  mk  nl-be      pt  sl     tr | ||||
|  | ||||
| ifdef INSTALL_BLOBS | ||||
| BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin \ | ||||
| vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \ | ||||
| ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \ | ||||
| gpxe-eepro100-80861209.rom \ | ||||
| pxe-e1000.bin \ | ||||
| BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ | ||||
| video.x openbios-sparc32 openbios-sparc64 openbios-ppc \ | ||||
| pxe-e1000.bin pxe-i82559er.bin \ | ||||
| pxe-ne2k_pci.bin pxe-pcnet.bin \ | ||||
| pxe-rtl8139.bin pxe-virtio.bin \ | ||||
| bamboo.dtb petalogix-s3adsp1800.dtb \ | ||||
| multiboot.bin linuxboot.bin \ | ||||
| s390-zipl.rom | ||||
| multiboot.bin linuxboot.bin | ||||
| BLOBS += extboot.bin | ||||
| BLOBS += vapic.bin | ||||
| else | ||||
| BLOBS= | ||||
| endif | ||||
| @@ -228,11 +312,7 @@ ifdef CONFIG_POSIX | ||||
| 	$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" | ||||
| endif | ||||
|  | ||||
| install-sysconfig: | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)/qemu" | ||||
| 	$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(sysconfdir)/qemu" | ||||
|  | ||||
| install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig | ||||
| install: all $(if $(BUILD_DOCS),install-doc) | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(bindir)" | ||||
| ifneq ($(TOOLS),) | ||||
| 	$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)" | ||||
| @@ -240,7 +320,12 @@ endif | ||||
| ifneq ($(BLOBS),) | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(datadir)" | ||||
| 	set -e; for x in $(BLOBS); do \ | ||||
| 	    if [ -f $(SRC_PATH)/pc-bios/$$x ];then \ | ||||
| 		$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \ | ||||
| 	    fi \ | ||||
| 	    ; if [ -f pc-bios/optionrom/$$x ];then \ | ||||
| 		$(INSTALL_DATA) pc-bios/optionrom/$$x "$(DESTDIR)$(datadir)"; \ | ||||
| 	    fi \ | ||||
| 	done | ||||
| endif | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps" | ||||
| @@ -250,6 +335,9 @@ endif | ||||
| 	for d in $(TARGET_DIRS); do \ | ||||
| 	$(MAKE) -C $$d $@ || exit 1 ; \ | ||||
|         done | ||||
| ifeq ($(KVM_KMOD),yes) | ||||
| 	$(MAKE) -C kvm/kernel $@ | ||||
| endif | ||||
|  | ||||
| # various test targets | ||||
| test speed: all | ||||
| @@ -265,60 +353,49 @@ cscope: | ||||
| 	cscope -b | ||||
|  | ||||
| # documentation | ||||
| MAKEINFO=makeinfo | ||||
| MAKEINFOFLAGS=--no-headers --no-split --number-sections | ||||
| TEXIFLAG=$(if $(V),,--quiet) | ||||
| %.dvi: %.texi | ||||
| 	$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<,"  GEN   $@") | ||||
|  | ||||
| %.html: %.texi | ||||
| 	$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \ | ||||
| 	"  GEN   $@") | ||||
| 	$(call quiet-command,texi2html -I=. -monolithic -number $<,"  GEN   $@") | ||||
|  | ||||
| %.info: %.texi | ||||
| 	$(call quiet-command,$(MAKEINFO) $< -o $@,"  GEN   $@") | ||||
| 	$(call quiet-command,makeinfo -I . $< -o $@,"  GEN   $@") | ||||
|  | ||||
| %.pdf: %.texi | ||||
| 	$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<,"  GEN   $@") | ||||
| %.dvi: %.texi | ||||
| 	$(call quiet-command,texi2dvi -I . $<,"  GEN   $@") | ||||
|  | ||||
| qemu-options.texi: $(SRC_PATH)/qemu-options.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@") | ||||
|  | ||||
| qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | ||||
|  | ||||
| QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@,"  GEN   $@") | ||||
| qemu-monitor.texi: $(SRC_PATH)/qemu-monitor.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@") | ||||
|  | ||||
| qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"  GEN   $@") | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@,"  GEN   $@") | ||||
|  | ||||
| qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi | ||||
| 	$(call quiet-command, \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu.pod && \ | ||||
| 	  pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \ | ||||
| 	  "  GEN   $@") | ||||
|  | ||||
| qemu-img.1: qemu-img.texi qemu-img-cmds.texi | ||||
| 	$(call quiet-command, \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-img.pod && \ | ||||
| 	  pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \ | ||||
| 	  "  GEN   $@") | ||||
|  | ||||
| qemu-nbd.8: qemu-nbd.texi | ||||
| 	$(call quiet-command, \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \ | ||||
| 	  perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-nbd.pod && \ | ||||
| 	  pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ | ||||
| 	  "  GEN   $@") | ||||
|  | ||||
| dvi: qemu-doc.dvi qemu-tech.dvi | ||||
| html: qemu-doc.html qemu-tech.html | ||||
| info: qemu-doc.info qemu-tech.info | ||||
| pdf: qemu-doc.pdf qemu-tech.pdf | ||||
|  | ||||
| qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \ | ||||
| 	qemu-img.texi qemu-nbd.texi qemu-options.texi \ | ||||
| 	qemu-monitor.texi qemu-img-cmds.texi | ||||
| dvi: qemu-doc.dvi qemu-tech.dvi | ||||
|  | ||||
| html: qemu-doc.html qemu-tech.html | ||||
|  | ||||
| qemu-doc.dvi qemu-doc.html qemu-doc.info: qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-monitor.texi qemu-img-cmds.texi | ||||
|  | ||||
| VERSION ?= $(shell cat VERSION) | ||||
| FILE = qemu-$(VERSION) | ||||
| @@ -330,28 +407,50 @@ tar: | ||||
| 	cd /tmp && tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS --exclude .git --exclude .svn | ||||
| 	rm -rf /tmp/$(FILE) | ||||
|  | ||||
| SYSTEM_TARGETS=$(filter %-softmmu,$(TARGET_DIRS)) | ||||
| SYSTEM_PROGS=$(patsubst qemu-system-i386,qemu, \ | ||||
|              $(patsubst %-softmmu,qemu-system-%, \ | ||||
|              $(SYSTEM_TARGETS))) | ||||
|  | ||||
| USER_TARGETS=$(filter %-user,$(TARGET_DIRS)) | ||||
| USER_PROGS=$(patsubst %-bsd-user,qemu-%, \ | ||||
|            $(patsubst %-darwin-user,qemu-%, \ | ||||
|            $(patsubst %-linux-user,qemu-%, \ | ||||
|            $(USER_TARGETS)))) | ||||
|  | ||||
| # generate a binary distribution | ||||
| tarbin: | ||||
| 	cd / && tar zcvf ~/qemu-$(VERSION)-$(ARCH).tar.gz \ | ||||
| 	$(patsubst %,$(bindir)/%, $(SYSTEM_PROGS)) \ | ||||
| 	$(patsubst %,$(bindir)/%, $(USER_PROGS)) \ | ||||
| 	$(bindir)/qemu \ | ||||
| 	$(bindir)/qemu-system-x86_64 \ | ||||
| 	$(bindir)/qemu-system-arm \ | ||||
| 	$(bindir)/qemu-system-cris \ | ||||
| 	$(bindir)/qemu-system-m68k \ | ||||
| 	$(bindir)/qemu-system-microblaze \ | ||||
| 	$(bindir)/qemu-system-mips \ | ||||
| 	$(bindir)/qemu-system-mipsel \ | ||||
| 	$(bindir)/qemu-system-mips64 \ | ||||
| 	$(bindir)/qemu-system-mips64el \ | ||||
| 	$(bindir)/qemu-system-ppc \ | ||||
| 	$(bindir)/qemu-system-ppcemb \ | ||||
| 	$(bindir)/qemu-system-ppc64 \ | ||||
| 	$(bindir)/qemu-system-sh4 \ | ||||
| 	$(bindir)/qemu-system-sh4eb \ | ||||
| 	$(bindir)/qemu-system-sparc \ | ||||
| 	$(bindir)/qemu-i386 \ | ||||
| 	$(bindir)/qemu-x86_64 \ | ||||
| 	$(bindir)/qemu-alpha \ | ||||
| 	$(bindir)/qemu-arm \ | ||||
| 	$(bindir)/qemu-armeb \ | ||||
| 	$(bindir)/qemu-cris \ | ||||
| 	$(bindir)/qemu-m68k \ | ||||
| 	$(bindir)/qemu-microblaze \ | ||||
| 	$(bindir)/qemu-mips \ | ||||
| 	$(bindir)/qemu-mipsel \ | ||||
| 	$(bindir)/qemu-ppc \ | ||||
| 	$(bindir)/qemu-ppc64 \ | ||||
| 	$(bindir)/qemu-ppc64abi32 \ | ||||
| 	$(bindir)/qemu-sh4 \ | ||||
| 	$(bindir)/qemu-sh4eb \ | ||||
| 	$(bindir)/qemu-sparc \ | ||||
| 	$(bindir)/qemu-sparc64 \ | ||||
| 	$(bindir)/qemu-sparc32plus \ | ||||
| 	$(bindir)/qemu-img \ | ||||
| 	$(bindir)/qemu-nbd \ | ||||
| 	$(datadir)/bios.bin \ | ||||
| 	$(datadir)/vgabios.bin \ | ||||
| 	$(datadir)/vgabios-cirrus.bin \ | ||||
| 	$(datadir)/ppc_rom.bin \ | ||||
| 	$(datadir)/video.x \ | ||||
| 	$(datadir)/openbios-sparc32 \ | ||||
| 	$(datadir)/openbios-sparc64 \ | ||||
| 	$(datadir)/openbios-ppc \ | ||||
| @@ -359,6 +458,7 @@ tarbin: | ||||
| 	$(datadir)/pxe-rtl8139.bin \ | ||||
| 	$(datadir)/pxe-pcnet.bin \ | ||||
| 	$(datadir)/pxe-e1000.bin \ | ||||
| 	$(datadir)/extboot.bin \ | ||||
| 	$(docdir)/qemu-doc.html \ | ||||
| 	$(docdir)/qemu-tech.html \ | ||||
| 	$(mandir)/man1/qemu.1 \ | ||||
| @@ -366,4 +466,4 @@ tarbin: | ||||
| 	$(mandir)/man8/qemu-nbd.8 | ||||
|  | ||||
| # Include automatically generated dependency files | ||||
| -include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d) | ||||
| -include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d) | ||||
|   | ||||
							
								
								
									
										23
									
								
								Makefile.dis
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile.dis
									
									
									
									
									
								
							| @@ -1,23 +0,0 @@ | ||||
| # Makefile for disassemblers. | ||||
|  | ||||
| include ../config-host.mak | ||||
| include config.mak | ||||
| include $(SRC_PATH)/rules.mak | ||||
|  | ||||
| .PHONY: all | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH)) | ||||
|  | ||||
| QEMU_CFLAGS+=-I.. | ||||
|  | ||||
| include $(SRC_PATH)/Makefile.objs | ||||
|  | ||||
| all: $(libdis-y) | ||||
| # Dummy command so that make thinks it has done something | ||||
| 	@true | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o *.d *.a *~ | ||||
|  | ||||
| # Include automatically generated dependency files | ||||
| -include $(wildcard *.d */*.d) | ||||
							
								
								
									
										39
									
								
								Makefile.hw
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								Makefile.hw
									
									
									
									
									
								
							| @@ -7,18 +7,49 @@ include $(SRC_PATH)/rules.mak | ||||
|  | ||||
| .PHONY: all | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw) | ||||
| VPATH=$(SRC_PATH):$(SRC_PATH)/hw | ||||
|  | ||||
| QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu | ||||
|  | ||||
| include $(SRC_PATH)/Makefile.objs | ||||
| obj-y = | ||||
| obj-y += loader.o | ||||
| obj-y += virtio.o | ||||
| obj-y += fw_cfg.o | ||||
| obj-y += watchdog.o | ||||
| obj-$(CONFIG_ECC) += ecc.o | ||||
| obj-$(CONFIG_NAND) += nand.o | ||||
|  | ||||
| all: $(hw-obj-y) | ||||
| obj-$(CONFIG_M48T59) += m48t59.o | ||||
| obj-$(CONFIG_ESCC) += escc.o | ||||
|  | ||||
| # PCI watchdog devices | ||||
| obj-y += wdt_i6300esb.o | ||||
|  | ||||
| # MSI-X depends on kvm for interrupt injection, | ||||
| # so moved it from Makefile.hw to Makefile.target for now | ||||
| # obj-y += msix.o | ||||
|  | ||||
| # PCI network cards | ||||
| obj-y += ne2000.o | ||||
|  | ||||
| obj-$(CONFIG_SMC91C111) += smc91c111.o | ||||
| obj-$(CONFIG_LAN9118) += lan9118.o | ||||
|  | ||||
| # SCSI layer | ||||
| obj-y += lsi53c895a.o | ||||
| obj-$(CONFIG_ESP) += esp.o | ||||
|  | ||||
| obj-y += dma-helpers.o sysbus.o isa-bus.o | ||||
| obj-$(CONFIG_QDEV_ADDR) += qdev-addr.o | ||||
|  | ||||
| all: $(HWLIB) | ||||
| # Dummy command so that make thinks it has done something | ||||
| 	@true | ||||
|  | ||||
| $(HWLIB): $(obj-y) | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ | ||||
| 	rm -f *.o *.d *.a *~ | ||||
|  | ||||
| # Include automatically generated dependency files | ||||
| -include $(wildcard *.d */*.d) | ||||
|   | ||||
							
								
								
									
										319
									
								
								Makefile.objs
									
									
									
									
									
								
							
							
						
						
									
										319
									
								
								Makefile.objs
									
									
									
									
									
								
							| @@ -1,319 +0,0 @@ | ||||
| ####################################################################### | ||||
| # QObject | ||||
| qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o | ||||
| qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o | ||||
| qobject-obj-y += qerror.o | ||||
|  | ||||
| ####################################################################### | ||||
| # oslib-obj-y is code depending on the OS (win32 vs posix) | ||||
| oslib-obj-y = osdep.o | ||||
| oslib-obj-$(CONFIG_WIN32) += oslib-win32.o | ||||
| oslib-obj-$(CONFIG_POSIX) += oslib-posix.o | ||||
|  | ||||
| ####################################################################### | ||||
| # block-obj-y is code used by both qemu system emulation and qemu-img | ||||
|  | ||||
| block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o | ||||
| block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o | ||||
| block-obj-$(CONFIG_POSIX) += posix-aio-compat.o | ||||
| block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o | ||||
|  | ||||
| block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o | ||||
| block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o | ||||
| block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o | ||||
| block-nested-y += qed-check.o | ||||
| block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o | ||||
| block-nested-$(CONFIG_WIN32) += raw-win32.o | ||||
| block-nested-$(CONFIG_POSIX) += raw-posix.o | ||||
| block-nested-$(CONFIG_CURL) += curl.o | ||||
| block-nested-$(CONFIG_RBD) += rbd.o | ||||
|  | ||||
| block-obj-y +=  $(addprefix block/, $(block-nested-y)) | ||||
|  | ||||
| net-obj-y = net.o | ||||
| net-nested-y = queue.o checksum.o util.o | ||||
| net-nested-y += socket.o | ||||
| net-nested-y += dump.o | ||||
| net-nested-$(CONFIG_POSIX) += tap.o | ||||
| net-nested-$(CONFIG_LINUX) += tap-linux.o | ||||
| net-nested-$(CONFIG_WIN32) += tap-win32.o | ||||
| net-nested-$(CONFIG_BSD) += tap-bsd.o | ||||
| net-nested-$(CONFIG_SOLARIS) += tap-solaris.o | ||||
| net-nested-$(CONFIG_AIX) += tap-aix.o | ||||
| net-nested-$(CONFIG_HAIKU) += tap-haiku.o | ||||
| net-nested-$(CONFIG_SLIRP) += slirp.o | ||||
| net-nested-$(CONFIG_VDE) += vde.o | ||||
| net-obj-y += $(addprefix net/, $(net-nested-y)) | ||||
|  | ||||
| ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS),yy) | ||||
| # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. | ||||
| # only pull in the actual virtio-9p device if we also enabled virtio. | ||||
| CONFIG_REALLY_VIRTFS=y | ||||
| endif | ||||
| fsdev-nested-$(CONFIG_VIRTFS) = qemu-fsdev.o | ||||
| fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y)) | ||||
|  | ||||
| ###################################################################### | ||||
| # libqemu_common.a: 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 single QEMU executable should support all | ||||
| # CPUs and machines. | ||||
|  | ||||
| common-obj-y = $(block-obj-y) blockdev.o | ||||
| common-obj-y += $(net-obj-y) | ||||
| common-obj-y += $(qobject-obj-y) | ||||
| common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX)) | ||||
| common-obj-y += readline.o console.o cursor.o async.o qemu-error.o | ||||
| common-obj-y += $(oslib-obj-y) | ||||
| common-obj-$(CONFIG_WIN32) += os-win32.o | ||||
| common-obj-$(CONFIG_POSIX) += os-posix.o | ||||
|  | ||||
| common-obj-y += tcg-runtime.o host-utils.o | ||||
| common-obj-y += irq.o ioport.o input.o | ||||
| common-obj-$(CONFIG_PTIMER) += ptimer.o | ||||
| common-obj-$(CONFIG_MAX7310) += max7310.o | ||||
| common-obj-$(CONFIG_WM8750) += wm8750.o | ||||
| common-obj-$(CONFIG_TWL92230) += twl92230.o | ||||
| common-obj-$(CONFIG_TSC2005) += tsc2005.o | ||||
| common-obj-$(CONFIG_LM832X) += lm832x.o | ||||
| common-obj-$(CONFIG_TMP105) += tmp105.o | ||||
| common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o | ||||
| common-obj-$(CONFIG_SSD0303) += ssd0303.o | ||||
| common-obj-$(CONFIG_SSD0323) += ssd0323.o | ||||
| common-obj-$(CONFIG_ADS7846) += ads7846.o | ||||
| common-obj-$(CONFIG_MAX111X) += max111x.o | ||||
| common-obj-$(CONFIG_DS1338) += ds1338.o | ||||
| common-obj-y += i2c.o smbus.o smbus_eeprom.o | ||||
| common-obj-y += eeprom93xx.o | ||||
| common-obj-y += scsi-disk.o cdrom.o | ||||
| common-obj-y += scsi-generic.o scsi-bus.o | ||||
| common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o | ||||
| common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o | ||||
| common-obj-$(CONFIG_SSI) += ssi.o | ||||
| common-obj-$(CONFIG_SSI_SD) += ssi-sd.o | ||||
| common-obj-$(CONFIG_SD) += sd.o | ||||
| common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o | ||||
| common-obj-y += bt-hci-csr.o | ||||
| common-obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o | ||||
| common-obj-y += qemu-char.o savevm.o #aio.o | ||||
| common-obj-y += msmouse.o ps2.o | ||||
| common-obj-y += qdev.o qdev-properties.o | ||||
| common-obj-y += block-migration.o | ||||
| common-obj-y += pflib.o | ||||
|  | ||||
| common-obj-$(CONFIG_BRLAPI) += baum.o | ||||
| common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o | ||||
| common-obj-$(CONFIG_WIN32) += version.o | ||||
|  | ||||
| common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o spice-qemu-char.o | ||||
|  | ||||
| audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o | ||||
| audio-obj-$(CONFIG_SDL) += sdlaudio.o | ||||
| audio-obj-$(CONFIG_OSS) += ossaudio.o | ||||
| audio-obj-$(CONFIG_SPICE) += spiceaudio.o | ||||
| audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o | ||||
| audio-obj-$(CONFIG_ALSA) += alsaaudio.o | ||||
| audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o | ||||
| audio-obj-$(CONFIG_FMOD) += fmodaudio.o | ||||
| audio-obj-$(CONFIG_ESD) += esdaudio.o | ||||
| audio-obj-$(CONFIG_PA) += paaudio.o | ||||
| audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o | ||||
| audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o | ||||
| audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o | ||||
| audio-obj-y += wavcapture.o | ||||
| common-obj-y += $(addprefix audio/, $(audio-obj-y)) | ||||
|  | ||||
| ui-obj-y += keymaps.o | ||||
| ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o | ||||
| ui-obj-$(CONFIG_CURSES) += curses.o | ||||
| ui-obj-y += vnc.o d3des.o | ||||
| ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o | ||||
| ui-obj-y += vnc-enc-tight.o vnc-palette.o | ||||
| ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o | ||||
| ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o | ||||
| ui-obj-$(CONFIG_COCOA) += cocoa.o | ||||
| ifdef CONFIG_VNC_THREAD | ||||
| ui-obj-y += vnc-jobs-async.o | ||||
| else | ||||
| ui-obj-y += vnc-jobs-sync.o | ||||
| endif | ||||
| common-obj-y += $(addprefix ui/, $(ui-obj-y)) | ||||
|  | ||||
| common-obj-y += iov.o acl.o | ||||
| common-obj-$(CONFIG_THREAD) += qemu-thread.o | ||||
| common-obj-$(CONFIG_IOTHREAD) += compatfd.o | ||||
| common-obj-y += notify.o event_notifier.o | ||||
| common-obj-y += qemu-timer.o qemu-timer-common.o | ||||
|  | ||||
| slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o | ||||
| slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o | ||||
| slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o | ||||
| common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y)) | ||||
|  | ||||
| # xen backend driver support | ||||
| common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o | ||||
| common-obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o | ||||
|  | ||||
| ###################################################################### | ||||
| # libuser | ||||
|  | ||||
| user-obj-y = | ||||
| user-obj-y += envlist.o path.o | ||||
| user-obj-y += tcg-runtime.o host-utils.o | ||||
| user-obj-y += cutils.o cache-utils.o | ||||
|  | ||||
| ###################################################################### | ||||
| # libhw | ||||
|  | ||||
| hw-obj-y = | ||||
| hw-obj-y += vl.o loader.o | ||||
| hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o | ||||
| hw-obj-y += fw_cfg.o | ||||
| hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o | ||||
| hw-obj-$(CONFIG_PCI) += msix.o msi.o | ||||
| hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o | ||||
| hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o | ||||
| hw-obj-y += watchdog.o | ||||
| hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o | ||||
| hw-obj-$(CONFIG_ECC) += ecc.o | ||||
| hw-obj-$(CONFIG_NAND) += nand.o | ||||
| hw-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o | ||||
| hw-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o | ||||
|  | ||||
| hw-obj-$(CONFIG_M48T59) += m48t59.o | ||||
| hw-obj-$(CONFIG_ESCC) += escc.o | ||||
| hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o | ||||
|  | ||||
| hw-obj-$(CONFIG_SERIAL) += serial.o | ||||
| hw-obj-$(CONFIG_PARALLEL) += parallel.o | ||||
| hw-obj-$(CONFIG_I8254) += i8254.o | ||||
| hw-obj-$(CONFIG_PCSPK) += pcspk.o | ||||
| hw-obj-$(CONFIG_PCKBD) += pckbd.o | ||||
| hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o | ||||
| hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o | ||||
| hw-obj-$(CONFIG_FDC) += fdc.o | ||||
| hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o | ||||
| hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o | ||||
| hw-obj-$(CONFIG_DMA) += dma.o | ||||
|  | ||||
| # PPC devices | ||||
| hw-obj-$(CONFIG_OPENPIC) += openpic.o | ||||
| hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o | ||||
| # Mac shared devices | ||||
| hw-obj-$(CONFIG_MACIO) += macio.o | ||||
| hw-obj-$(CONFIG_CUDA) += cuda.o | ||||
| hw-obj-$(CONFIG_ADB) += adb.o | ||||
| hw-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o | ||||
| hw-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o | ||||
| # OldWorld PowerMac | ||||
| hw-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o | ||||
| hw-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o | ||||
| # NewWorld PowerMac | ||||
| hw-obj-$(CONFIG_UNIN_PCI) += unin_pci.o | ||||
| hw-obj-$(CONFIG_DEC_PCI) += dec_pci.o | ||||
| # PowerPC E500 boards | ||||
| hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o | ||||
|  | ||||
| # MIPS devices | ||||
| hw-obj-$(CONFIG_PIIX4) += piix4.o | ||||
|  | ||||
| # PCI watchdog devices | ||||
| hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o | ||||
|  | ||||
| hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o | ||||
|  | ||||
| # PCI network cards | ||||
| hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o | ||||
| hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o | ||||
| hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o | ||||
| hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o | ||||
| hw-obj-$(CONFIG_E1000_PCI) += e1000.o | ||||
| hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o | ||||
|  | ||||
| hw-obj-$(CONFIG_SMC91C111) += smc91c111.o | ||||
| hw-obj-$(CONFIG_LAN9118) += lan9118.o | ||||
| hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o | ||||
|  | ||||
| # IDE | ||||
| hw-obj-$(CONFIG_IDE_CORE) += ide/core.o | ||||
| hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o | ||||
| hw-obj-$(CONFIG_IDE_PCI) += ide/pci.o | ||||
| hw-obj-$(CONFIG_IDE_ISA) += ide/isa.o | ||||
| hw-obj-$(CONFIG_IDE_PIIX) += ide/piix.o | ||||
| hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o | ||||
| hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o | ||||
| hw-obj-$(CONFIG_IDE_VIA) += ide/via.o | ||||
| hw-obj-$(CONFIG_AHCI) += ide/ahci.o | ||||
| hw-obj-$(CONFIG_AHCI) += ide/ich.o | ||||
|  | ||||
| # SCSI layer | ||||
| hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o | ||||
| hw-obj-$(CONFIG_ESP) += esp.o | ||||
|  | ||||
| hw-obj-y += dma-helpers.o sysbus.o isa-bus.o | ||||
| hw-obj-y += qdev-addr.o | ||||
|  | ||||
| # VGA | ||||
| hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o | ||||
| hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o | ||||
| hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o | ||||
| hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o | ||||
|  | ||||
| hw-obj-$(CONFIG_RC4030) += rc4030.o | ||||
| hw-obj-$(CONFIG_DP8393X) += dp8393x.o | ||||
| hw-obj-$(CONFIG_DS1225Y) += ds1225y.o | ||||
| hw-obj-$(CONFIG_MIPSNET) += mipsnet.o | ||||
|  | ||||
| # Sound | ||||
| sound-obj-y = | ||||
| sound-obj-$(CONFIG_SB16) += sb16.o | ||||
| sound-obj-$(CONFIG_ES1370) += es1370.o | ||||
| sound-obj-$(CONFIG_AC97) += ac97.o | ||||
| sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o | ||||
| sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o | ||||
| sound-obj-$(CONFIG_CS4231A) += cs4231a.o | ||||
| sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o | ||||
|  | ||||
| adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 | ||||
| hw-obj-$(CONFIG_SOUND) += $(sound-obj-y) | ||||
|  | ||||
| hw-obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p-debug.o | ||||
| hw-obj-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o | ||||
| hw-obj-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o | ||||
|  | ||||
| ###################################################################### | ||||
| # libdis | ||||
| # NOTE: the disassembler code is only needed for debugging | ||||
|  | ||||
| libdis-y = | ||||
| libdis-$(CONFIG_ALPHA_DIS) += alpha-dis.o | ||||
| libdis-$(CONFIG_ARM_DIS) += arm-dis.o | ||||
| libdis-$(CONFIG_CRIS_DIS) += cris-dis.o | ||||
| libdis-$(CONFIG_HPPA_DIS) += hppa-dis.o | ||||
| libdis-$(CONFIG_I386_DIS) += i386-dis.o | ||||
| libdis-$(CONFIG_IA64_DIS) += ia64-dis.o | ||||
| libdis-$(CONFIG_M68K_DIS) += m68k-dis.o | ||||
| libdis-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o | ||||
| libdis-$(CONFIG_MIPS_DIS) += mips-dis.o | ||||
| libdis-$(CONFIG_PPC_DIS) += ppc-dis.o | ||||
| libdis-$(CONFIG_S390_DIS) += s390-dis.o | ||||
| libdis-$(CONFIG_SH4_DIS) += sh4-dis.o | ||||
| libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o | ||||
|  | ||||
| ###################################################################### | ||||
| # trace | ||||
|  | ||||
| ifeq ($(TRACE_BACKEND),dtrace) | ||||
| trace-obj-y = trace-dtrace.o | ||||
| else | ||||
| trace-obj-y = trace.o | ||||
| ifeq ($(TRACE_BACKEND),simple) | ||||
| trace-obj-y += simpletrace.o | ||||
| user-obj-y += qemu-timer-common.o | ||||
| endif | ||||
| endif | ||||
|  | ||||
| vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) | ||||
|  | ||||
| vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) | ||||
|  | ||||
							
								
								
									
										302
									
								
								Makefile.target
									
									
									
									
									
								
							
							
						
						
									
										302
									
								
								Makefile.target
									
									
									
									
									
								
							| @@ -1,23 +1,17 @@ | ||||
| # -*- Mode: makefile -*- | ||||
|  | ||||
| # This needs to be defined before rules.mak | ||||
| GENERATED_HEADERS = config-target.h | ||||
| CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y) | ||||
| CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y) | ||||
|  | ||||
| include ../config-host.mak | ||||
| include config-devices.mak | ||||
| include config-target.mak | ||||
| include $(SRC_PATH)/rules.mak | ||||
| ifneq ($(HWDIR),) | ||||
| include $(HWDIR)/config.mak | ||||
| endif | ||||
|  | ||||
| TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH) | ||||
| $(call set-vpath, $(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw) | ||||
| VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw | ||||
| QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H | ||||
|  | ||||
| include $(SRC_PATH)/Makefile.objs | ||||
|  | ||||
| ifdef CONFIG_USER_ONLY | ||||
| # user emulator name | ||||
| QEMU_PROG=qemu-$(TARGET_ARCH2) | ||||
| @@ -31,61 +25,57 @@ endif | ||||
| endif | ||||
|  | ||||
| PROGS=$(QEMU_PROG) | ||||
| STPFILES= | ||||
|  | ||||
| ifndef CONFIG_HAIKU | ||||
| LIBS+=-lm | ||||
| endif | ||||
|  | ||||
| kvm.o kvm-all.o vhost.o vhost_net.o: QEMU_CFLAGS+=$(KVM_CFLAGS) | ||||
| kvm.o kvm-all.o: QEMU_CFLAGS+=$(KVM_CFLAGS) | ||||
|  | ||||
| CFLAGS += $(KVM_CFLAGS) | ||||
|  | ||||
| config-target.h: config-target.h-timestamp | ||||
| config-target.h-timestamp: config-target.mak | ||||
|  | ||||
| ifdef CONFIG_SYSTEMTAP_TRACE | ||||
| stap: $(QEMU_PROG).stp | ||||
|  | ||||
| ifdef CONFIG_USER_ONLY | ||||
| TARGET_TYPE=user | ||||
| else | ||||
| TARGET_TYPE=system | ||||
| endif | ||||
|  | ||||
| $(QEMU_PROG).stp: | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \ | ||||
| 		--$(TRACE_BACKEND) \ | ||||
| 		--binary $(bindir)/$(QEMU_PROG) \ | ||||
| 		--target-arch $(TARGET_ARCH) \ | ||||
| 		--target-type $(TARGET_TYPE) \ | ||||
| 		--stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp,"  GEN   $(QEMU_PROG).stp") | ||||
| else | ||||
| stap: | ||||
| endif | ||||
|  | ||||
| all: $(PROGS) stap | ||||
| all: $(PROGS) | ||||
|  | ||||
| # Dummy command so that make thinks it has done something | ||||
| 	@true | ||||
|  | ||||
| ######################################################### | ||||
| # cpu emulator library | ||||
| libobj-y = exec.o translate-all.o cpu-exec.o translate.o | ||||
| libobj-y += tcg/tcg.o | ||||
| libobj-y = exec.o cpu-exec.o | ||||
| libobj-$(CONFIG_NO_CPU_EMULATION) += fake-exec.o | ||||
| libobj-$(CONFIG_CPU_EMULATION) += translate-all.o translate.o | ||||
| libobj-$(CONFIG_CPU_EMULATION) += tcg/tcg.o | ||||
| libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o | ||||
| libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o | ||||
| libobj-y += op_helper.o helper.o | ||||
| ifeq ($(TARGET_BASE_ARCH), i386) | ||||
| libobj-y += cpuid.o | ||||
| endif | ||||
| libobj-$(CONFIG_NEED_MMU) += mmu.o | ||||
|  | ||||
| libobj-$(CONFIG_KVM) += kvm-tpr-opt.o | ||||
| libobj-$(CONFIG_KVM) += qemu-kvm-helper.o | ||||
|  | ||||
| libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o | ||||
| libobj-$(TARGET_ALPHA) += alpha_palcode.o | ||||
|  | ||||
| # NOTE: the disassembler code is only needed for debugging | ||||
| libobj-y += disas.o | ||||
|  | ||||
| $(libobj-y): $(GENERATED_HEADERS) | ||||
| libobj-$(CONFIG_ALPHA_DIS) += alpha-dis.o | ||||
| libobj-$(CONFIG_ARM_DIS) += arm-dis.o | ||||
| libobj-$(CONFIG_CRIS_DIS) += cris-dis.o | ||||
| libobj-$(CONFIG_HPPA_DIS) += hppa-dis.o | ||||
| libobj-$(CONFIG_I386_DIS) += i386-dis.o | ||||
| libobj-$(CONFIG_M68K_DIS) += m68k-dis.o | ||||
| libobj-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o | ||||
| libobj-$(CONFIG_MIPS_DIS) += mips-dis.o | ||||
| libobj-$(CONFIG_PPC_DIS) += ppc-dis.o | ||||
| libobj-$(CONFIG_S390_DIS) += s390-dis.o | ||||
| libobj-$(CONFIG_SH4_DIS) += sh4-dis.o | ||||
| libobj-$(CONFIG_SPARC_DIS) += sparc-dis.o | ||||
|  | ||||
| # libqemu | ||||
|  | ||||
| libqemu.a: $(libobj-y) | ||||
|  | ||||
| translate.o: translate.c cpu.h | ||||
|  | ||||
| translate-all.o: translate-all.c cpu.h | ||||
| @@ -100,19 +90,20 @@ op_helper.o cpu-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS) | ||||
| # cpu_signal_handler() in cpu-exec.c. | ||||
| signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS) | ||||
|  | ||||
| qemu-kvm-helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS) | ||||
|  | ||||
| ######################################################### | ||||
| # Linux user emulator target | ||||
|  | ||||
| ifdef CONFIG_LINUX_USER | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)) | ||||
|  | ||||
| VPATH+=:$(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) | ||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) | ||||
| obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \ | ||||
|       elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \ | ||||
|       qemu-malloc.o $(oslib-obj-y) | ||||
|       elfload.o linuxload.o uaccess.o gdbstub.o | ||||
|  | ||||
| obj-$(TARGET_HAS_BFLT) += flatload.o | ||||
| obj-$(TARGET_HAS_ELFLOAD32) += elfload32.o | ||||
|  | ||||
| obj-$(TARGET_I386) += vm86.o | ||||
|  | ||||
| @@ -125,11 +116,7 @@ obj-arm-y += arm-semi.o | ||||
|  | ||||
| obj-m68k-y += m68k-sim.o m68k-semi.o | ||||
|  | ||||
| $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) | ||||
|  | ||||
| obj-y += $(addprefix ../libuser/, $(user-obj-y)) | ||||
| obj-y += $(addprefix ../libdis-user/, $(libdis-y)) | ||||
| obj-y += $(libobj-y) | ||||
| ARLIBS=../libuser/libuser.a libqemu.a | ||||
|  | ||||
| endif #CONFIG_LINUX_USER | ||||
|  | ||||
| @@ -138,8 +125,7 @@ endif #CONFIG_LINUX_USER | ||||
|  | ||||
| ifdef CONFIG_DARWIN_USER | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH)/darwin-user) | ||||
|  | ||||
| VPATH+=:$(SRC_PATH)/darwin-user | ||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH) | ||||
|  | ||||
| # Leave some space for the regular program loading zone | ||||
| @@ -152,11 +138,7 @@ obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \ | ||||
|  | ||||
| obj-i386-y += ioport-user.o | ||||
|  | ||||
| $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) | ||||
|  | ||||
| obj-y += $(addprefix ../libuser/, $(user-obj-y)) | ||||
| obj-y += $(addprefix ../libdis-user/, $(libdis-y)) | ||||
| obj-y += $(libobj-y) | ||||
| ARLIBS=../libuser/libuser.a libqemu.a | ||||
|  | ||||
| endif #CONFIG_DARWIN_USER | ||||
|  | ||||
| @@ -165,8 +147,7 @@ endif #CONFIG_DARWIN_USER | ||||
|  | ||||
| ifdef CONFIG_BSD_USER | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH)/bsd-user) | ||||
|  | ||||
| VPATH+=:$(SRC_PATH)/bsd-user | ||||
| QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH) | ||||
|  | ||||
| obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ | ||||
| @@ -174,11 +155,7 @@ obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ | ||||
|  | ||||
| obj-i386-y += ioport-user.o | ||||
|  | ||||
| $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) | ||||
|  | ||||
| obj-y += $(addprefix ../libuser/, $(user-obj-y)) | ||||
| obj-y += $(addprefix ../libdis-user/, $(libdis-y)) | ||||
| obj-y += $(libobj-y) | ||||
| ARLIBS=../libuser/libuser.a libqemu.a | ||||
|  | ||||
| endif #CONFIG_BSD_USER | ||||
|  | ||||
| @@ -186,74 +163,97 @@ endif #CONFIG_BSD_USER | ||||
| # System emulator target | ||||
| ifdef CONFIG_SOFTMMU | ||||
|  | ||||
| obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o | ||||
| obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.o | ||||
| # virtio has to be here due to weird dependency between PCI and virtio-net. | ||||
| # need to fix this properly | ||||
| obj-$(CONFIG_NO_PCI) += pci-stub.o | ||||
| obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o | ||||
| obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o | ||||
| obj-y += vhost_net.o | ||||
| obj-$(CONFIG_VHOST_NET) += vhost.o | ||||
| obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p.o | ||||
| obj-y += rwhandler.o | ||||
| obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o | ||||
| obj-$(CONFIG_KVM) += kvm.o kvm-all.o | ||||
| obj-$(CONFIG_NO_KVM) += kvm-stub.o | ||||
| # MSI-X depends on kvm for interrupt injection, | ||||
| # so moved it from Makefile.hw to Makefile.target for now | ||||
| obj-y += msix.o | ||||
|  | ||||
| obj-$(CONFIG_ISA_MMIO) += isa_mmio.o | ||||
| LIBS+=-lz | ||||
|  | ||||
| sound-obj-y = | ||||
| sound-obj-$(CONFIG_SB16) += sb16.o | ||||
| sound-obj-$(CONFIG_ES1370) += es1370.o | ||||
| sound-obj-$(CONFIG_AC97) += ac97.o | ||||
| sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o | ||||
| sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o | ||||
| sound-obj-$(CONFIG_CS4231A) += cs4231a.o | ||||
|  | ||||
| adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 | ||||
|  | ||||
| QEMU_CFLAGS += $(VNC_TLS_CFLAGS) | ||||
| QEMU_CFLAGS += $(VNC_SASL_CFLAGS) | ||||
| QEMU_CFLAGS += $(VNC_JPEG_CFLAGS) | ||||
| QEMU_CFLAGS += $(VNC_PNG_CFLAGS) | ||||
|  | ||||
| # xen backend driver support | ||||
| obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o | ||||
|  | ||||
| # Inter-VM PCI shared memory | ||||
| obj-$(CONFIG_KVM) += ivshmem.o | ||||
| # USB layer | ||||
| obj-$(CONFIG_USB_OHCI) += usb-ohci.o | ||||
|  | ||||
| # PCI network cards | ||||
| obj-y += eepro100.o | ||||
| obj-y += pcnet.o | ||||
| obj-y += rtl8139.o | ||||
| obj-y += e1000.o | ||||
|  | ||||
| # Hardware support | ||||
| obj-i386-y += vga.o | ||||
| obj-i386-y += mc146818rtc.o i8259.o pc.o | ||||
| obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o | ||||
| obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o | ||||
| obj-i386-y = ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o | ||||
| obj-i386-y += pckbd.o $(sound-obj-y) dma.o | ||||
| obj-i386-y += vga.o vga-pci.o vga-isa.o | ||||
| obj-i386-y += fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o | ||||
| obj-i386-y += cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o | ||||
| obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o | ||||
| obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o | ||||
| obj-i386-y += debugcon.o multiboot.o | ||||
| obj-i386-y += pc_piix.o | ||||
| obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o | ||||
| obj-i386-y += extboot.o | ||||
| obj-i386-y += ne2000-isa.o | ||||
| obj-i386-y += testdev.o | ||||
|  | ||||
| obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o | ||||
| obj-i386-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o | ||||
|  | ||||
| # Hardware support | ||||
| obj-ia64-y += ide.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) | ||||
| obj-ia64-y += fdc.o mc146818rtc.o serial.o i8259.o ipf.o | ||||
| obj-ia64-y += cirrus_vga.o parallel.o acpi.o piix_pci.o | ||||
| obj-ia64-y += usb-uhci.o | ||||
| obj-ia64-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o | ||||
|  | ||||
| # shared objects | ||||
| obj-ppc-y = ppc.o | ||||
| obj-ppc-y += vga.o | ||||
| obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o | ||||
| obj-ppc-y += ide/cmd646.o | ||||
| obj-ppc-y += vga.o vga-pci.o $(sound-obj-y) dma.o openpic.o | ||||
| obj-ppc-y += cirrus_vga.o | ||||
| # PREP target | ||||
| obj-ppc-y += i8259.o mc146818rtc.o | ||||
| obj-ppc-y += ppc_prep.o | ||||
| obj-ppc-y += pckbd.o serial.o i8259.o i8254.o fdc.o mc146818rtc.o | ||||
| obj-ppc-y += prep_pci.o ppc_prep.o ne2000-isa.o | ||||
| # Mac shared devices | ||||
| obj-ppc-y += macio.o cuda.o adb.o mac_nvram.o mac_dbdma.o | ||||
| # OldWorld PowerMac | ||||
| obj-ppc-y += ppc_oldworld.o | ||||
| obj-ppc-y += heathrow_pic.o grackle_pci.o ppc_oldworld.o | ||||
| # NewWorld PowerMac | ||||
| obj-ppc-y += ppc_newworld.o | ||||
| obj-ppc-y += unin_pci.o ppc_newworld.o | ||||
| # PowerPC 4xx boards | ||||
| obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o | ||||
| obj-ppc-y += pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o | ||||
| obj-ppc-y += ppc440.o ppc440_bamboo.o | ||||
| # PowerPC E500 boards | ||||
| obj-ppc-y += ppce500_mpc8544ds.o | ||||
| # PowerPC 440 Xilinx ML507 reference board. | ||||
| obj-ppc-y += virtex_ml507.o | ||||
| obj-ppc-y += ppce500_pci.o ppce500_mpc8544ds.o | ||||
| obj-ppc-$(CONFIG_KVM) += kvm_ppc.o | ||||
| obj-ppc-$(CONFIG_FDT) += device_tree.o | ||||
|  | ||||
| # Xilinx PPC peripherals | ||||
| obj-ppc-y += xilinx_intc.o | ||||
| obj-ppc-y += xilinx_timer.o | ||||
| obj-ppc-y += xilinx_uartlite.o | ||||
| obj-ppc-y += xilinx_ethlite.o | ||||
|  | ||||
| obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o | ||||
| obj-mips-y += mips_addr.o mips_timer.o mips_int.o | ||||
| obj-mips-y += vga.o i8259.o | ||||
| obj-mips-y += g364fb.o jazz_led.o | ||||
| obj-mips-y += gt64xxx.o mc146818rtc.o | ||||
| obj-mips-y += cirrus_vga.o | ||||
| obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o | ||||
| obj-mips-y += mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o rc4030.o | ||||
| obj-mips-y += vga-pci.o vga-isa.o vga-isa-mm.o | ||||
| obj-mips-y += g364fb.o jazz_led.o dp8393x.o | ||||
| obj-mips-y += ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o | ||||
| obj-mips-y += gt64xxx.o pckbd.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o | ||||
| obj-mips-y += piix4.o parallel.o cirrus_vga.o pcspk.o $(sound-obj-y) | ||||
| obj-mips-y += mipsnet.o ne2000-isa.o | ||||
| obj-mips-y += pflash_cfi01.o | ||||
| obj-mips-y += vmware_vga.o | ||||
|  | ||||
| obj-microblaze-y = petalogix_s3adsp1800_mmu.o | ||||
|  | ||||
| @@ -263,13 +263,12 @@ obj-microblaze-y += xilinx_timer.o | ||||
| obj-microblaze-y += xilinx_uartlite.o | ||||
| obj-microblaze-y += xilinx_ethlite.o | ||||
|  | ||||
| obj-microblaze-y += pflash_cfi02.o | ||||
|  | ||||
| obj-microblaze-$(CONFIG_FDT) += device_tree.o | ||||
|  | ||||
| # Boards | ||||
| obj-cris-y = cris_pic_cpu.o | ||||
| obj-cris-y += cris-boot.o | ||||
| obj-cris-y += etraxfs.o axis_dev88.o | ||||
| obj-cris-y += axis_dev88.o | ||||
| obj-cris-y = cris_pic_cpu.o etraxfs.o axis_dev88.o | ||||
|  | ||||
| # IO blocks | ||||
| obj-cris-y += etraxfs_dma.o | ||||
| @@ -278,18 +277,18 @@ obj-cris-y += etraxfs_eth.o | ||||
| obj-cris-y += etraxfs_timer.o | ||||
| obj-cris-y += etraxfs_ser.o | ||||
|  | ||||
| ifeq ($(TARGET_ARCH), sparc64) | ||||
| obj-sparc-y = sun4u.o apb_pci.o | ||||
| obj-sparc-y += vga.o | ||||
| obj-sparc-y += mc146818rtc.o | ||||
| obj-sparc-y += cirrus_vga.o | ||||
| else | ||||
| obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o | ||||
| obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o | ||||
| obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o | ||||
| obj-cris-y += pflash_cfi02.o | ||||
|  | ||||
| # GRLIB | ||||
| obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o | ||||
| ifeq ($(TARGET_ARCH), sparc64) | ||||
| obj-sparc-y = sun4u.o pckbd.o apb_pci.o | ||||
| obj-sparc-y += ide/core.o ide/qdev.o ide/pci.o ide/cmd646.o | ||||
| obj-sparc-y += vga.o vga-pci.o | ||||
| obj-sparc-y += fdc.o mc146818rtc.o serial.o | ||||
| obj-sparc-y += cirrus_vga.o parallel.o | ||||
| else | ||||
| obj-sparc-y = sun4m.o lance.o tcx.o iommu.o slavio_intctl.o | ||||
| obj-sparc-y += slavio_timer.o slavio_misc.o fdc.o sparc32_dma.o | ||||
| obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o | ||||
| endif | ||||
|  | ||||
| obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o | ||||
| @@ -301,79 +300,68 @@ obj-arm-y += pl061.o | ||||
| obj-arm-y += arm-semi.o | ||||
| obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o | ||||
| obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o | ||||
| obj-arm-y += gumstix.o | ||||
| obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o | ||||
| obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ | ||||
| 		omap_gpio.o omap_intc.o omap_uart.o | ||||
| obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \ | ||||
| 		omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o | ||||
| obj-arm-y += pflash_cfi01.o gumstix.o | ||||
| obj-arm-y += zaurus.o ide/core.o ide/microdrive.o serial.o spitz.o tosa.o tc6393xb.o | ||||
| obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o | ||||
| obj-arm-y += omap2.o omap_dss.o soc_dma.o | ||||
| obj-arm-y += omap_sx1.o palm.o tsc210x.o | ||||
| obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o | ||||
| obj-arm-y += mst_fpga.o mainstone.o | ||||
| obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o | ||||
| obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o marvell_88w8618_audio.o | ||||
| obj-arm-y += framebuffer.o | ||||
| obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o | ||||
| obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o | ||||
| obj-arm-y += syborg_virtio.o | ||||
|  | ||||
| obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o | ||||
| obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o | ||||
| obj-sh4-y += ide/mmio.o | ||||
| obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o serial.o | ||||
| obj-sh4-y += ide/core.o ide/mmio.o | ||||
|  | ||||
| obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o | ||||
| obj-m68k-y += m68k-semi.o dummy_m68k.o | ||||
|  | ||||
| obj-s390x-y = s390-virtio-bus.o s390-virtio.o | ||||
|  | ||||
| obj-alpha-y = alpha_palcode.o | ||||
| ifeq ($(TARGET_ARCH), ia64) | ||||
| firmware.o: firmware.c | ||||
| 	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< | ||||
| endif | ||||
|  | ||||
| main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) | ||||
| main.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) | ||||
|  | ||||
| monitor.o: hmp-commands.h qmp-commands.h | ||||
| vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) | ||||
|  | ||||
| $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) | ||||
| vl.o: qemu-options.h | ||||
|  | ||||
| obj-y += $(addprefix ../, $(common-obj-y)) | ||||
| obj-y += $(addprefix ../libdis/, $(libdis-y)) | ||||
| obj-y += $(libobj-y) | ||||
| obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y)) | ||||
| monitor.o: qemu-monitor.h | ||||
|  | ||||
| ARLIBS=../libqemu_common.a libqemu.a $(HWLIB) | ||||
|  | ||||
| endif # CONFIG_SOFTMMU | ||||
|  | ||||
| obj-y += $(addprefix ../, $(trace-obj-y)) | ||||
| obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o | ||||
|  | ||||
| $(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) | ||||
| $(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) $(ARLIBS) | ||||
| 	$(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)) | ||||
|  | ||||
|  | ||||
| gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh | ||||
| 	$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES),"  GEN   $(TARGET_DIR)$@") | ||||
| gdbstub-xml.c: $(TARGET_XML_FILES) feature_to_c.sh | ||||
| 	$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES),"  GEN   $(TARGET_DIR)$@") | ||||
|  | ||||
| hmp-commands.h: $(SRC_PATH)/hmp-commands.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||
| qemu-options.h: $(SRC_PATH)/qemu-options.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||
|  | ||||
| qmp-commands.h: $(SRC_PATH)/qmp-commands.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||
| qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx | ||||
| 	$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@,"  GEN   $(TARGET_DIR)$@") | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o | ||||
| 	rm -f *.d */*.d tcg/*.o ide/*.o | ||||
| 	rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c | ||||
| ifdef CONFIG_SYSTEMTAP_TRACE | ||||
| 	rm -f *.stp | ||||
| endif | ||||
| 	rm -f qemu-options.h qemu-monitor.h gdbstub-xml.c | ||||
|  | ||||
| install: all | ||||
| ifneq ($(PROGS),) | ||||
| 	$(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)" | ||||
| ifneq ($(STRIP),) | ||||
| 	$(STRIP) $(patsubst %,"$(DESTDIR)$(bindir)/%",$(PROGS)) | ||||
| endif | ||||
| endif | ||||
| ifdef CONFIG_SYSTEMTAP_TRACE | ||||
| 	$(INSTALL_DIR) "$(DESTDIR)$(datadir)/../systemtap/tapset" | ||||
| 	$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(datadir)/../systemtap/tapset" | ||||
| 	$(INSTALL) -m 755 $(STRIP_OPT) $(PROGS) "$(DESTDIR)$(bindir)" | ||||
| endif | ||||
|  | ||||
| # Include automatically generated dependency files | ||||
|   | ||||
| @@ -6,16 +6,25 @@ include $(SRC_PATH)/rules.mak | ||||
|  | ||||
| .PHONY: all | ||||
|  | ||||
| $(call set-vpath, $(SRC_PATH)) | ||||
| # Do not take %.o from $(SRC_PATH), only %.c and %.h | ||||
| # All %.o for user targets should be built with -fpie, when | ||||
| # configured with --enable-user-pie, so we don't want to | ||||
| # take %.o from $(SRC_PATH), since they built without -fpie | ||||
| vpath %.c %.h $(SRC_PATH) | ||||
|  | ||||
| QEMU_CFLAGS+=-I.. | ||||
|  | ||||
| include $(SRC_PATH)/Makefile.objs | ||||
| obj-y = | ||||
| obj-y += envlist.o path.o | ||||
| obj-y += tcg-runtime.o host-utils.o | ||||
| obj-y += cutils.o cache-utils.o | ||||
|  | ||||
| all: $(user-obj-y) | ||||
| all: libuser.a | ||||
| # Dummy command so that make thinks it has done something | ||||
| 	@true | ||||
|  | ||||
| libuser.a: $(obj-y) | ||||
|  | ||||
| clean: | ||||
| 	rm -f *.o *.d *.a *~ | ||||
|  | ||||
|   | ||||
							
								
								
									
										77
									
								
								QMP/README
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								QMP/README
									
									
									
									
									
								
							| @@ -7,82 +7,57 @@ Introduction | ||||
| The QEMU Monitor Protocol (QMP) allows applications to communicate with | ||||
| QEMU's Monitor. | ||||
|  | ||||
| QMP is JSON[1] based and currently has the following features: | ||||
| QMP is JSON[1] based and has the following features: | ||||
|  | ||||
| - Lightweight, text-based, easy to parse data format | ||||
| - Asynchronous messages support (ie. events) | ||||
| - Capabilities Negotiation | ||||
| - Asynchronous events support  | ||||
| - Stability | ||||
|  | ||||
| For detailed information on QMP's usage, please, refer to the following files: | ||||
| For more information, please, refer to the following files: | ||||
|  | ||||
| o qmp-spec.txt      QEMU Monitor Protocol current specification | ||||
| o qmp-commands.txt  QMP supported commands (auto-generated at build-time) | ||||
| o qmp-events.txt    List of available asynchronous events | ||||
| o qmp-spec.txt    QEMU Monitor Protocol current specification | ||||
| o qmp-events.txt  List of available asynchronous events | ||||
|  | ||||
| There is also a simple Python script called 'qmp-shell' available. | ||||
|  | ||||
| IMPORTANT: It's strongly recommended to read the 'Stability Considerations' | ||||
| section in the qmp-commands.txt file before making any serious use of QMP. | ||||
| There are also two simple Python scripts available: | ||||
|  | ||||
| o qmp-shell       A shell | ||||
| o vm-info         Show some information about the Virtual Machine | ||||
|  | ||||
| [1] http://www.json.org | ||||
|  | ||||
| Usage | ||||
| ----- | ||||
|  | ||||
| To enable QMP, you need a QEMU monitor instance in "control mode". There are | ||||
| two ways of doing this. | ||||
| To enable QMP, QEMU has to be started in "control mode". There are | ||||
| two ways of doing this, the simplest one is using the the '-qmp' | ||||
| command-line option. | ||||
|  | ||||
| The simplest one is using the '-qmp' command-line option. The following | ||||
| example makes QMP available on localhost port 4444: | ||||
| For example: | ||||
|  | ||||
|   $ qemu [...] -qmp tcp:localhost:4444,server | ||||
| $ qemu [...] -qmp tcp:localhost:4444,server | ||||
|  | ||||
| However, in order to have more complex combinations, like multiple monitors, | ||||
| the '-mon' command-line option should be used along with the '-chardev' one. | ||||
| For instance, the following example creates one user monitor on stdio and one | ||||
| QMP monitor on localhost port 4444. | ||||
| Will start QEMU in control mode, waiting for a client TCP connection | ||||
| on localhost port 4444. | ||||
|  | ||||
|    $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \ | ||||
|                 -chardev socket,id=mon1,host=localhost,port=4444,server \ | ||||
|                 -mon chardev=mon1,mode=control | ||||
|  | ||||
| Please, refer to QEMU's manpage for more information. | ||||
| It is also possible to use the '-mon' command-line option to have | ||||
| more complex combinations. Please, refer to the QEMU's manpage for | ||||
| more information. | ||||
|  | ||||
| Simple Testing | ||||
| -------------- | ||||
|  | ||||
| To manually test QMP one can connect with telnet and issue commands by hand: | ||||
| To manually test QMP one can connect with telnet and issue commands: | ||||
|  | ||||
| $ telnet localhost 4444 | ||||
| Trying 127.0.0.1... | ||||
| Connected to localhost. | ||||
| Escape character is '^]'. | ||||
| {"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}} | ||||
| { "execute": "qmp_capabilities" } | ||||
| {"return": {}} | ||||
| {"QMP": {"capabilities": []}} | ||||
| { "execute": "query-version" } | ||||
| {"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}} | ||||
| {"return": {"qemu": "0.11.50", "package": ""}} | ||||
|  | ||||
| Development Process | ||||
| ------------------- | ||||
| Contact | ||||
| ------- | ||||
|  | ||||
| When changing QMP's interface (by adding new commands, events or modifying | ||||
| existing ones) it's mandatory to update the relevant documentation, which is | ||||
| one (or more) of the files listed in the 'Introduction' section*. | ||||
|  | ||||
| Also, it's strongly recommended to send the documentation patch first, before | ||||
| doing any code change. This is so because: | ||||
|  | ||||
|   1. Avoids the code dictating the interface | ||||
|  | ||||
|   2. Review can improve your interface.  Letting that happen before | ||||
|      you implement it can save you work. | ||||
|  | ||||
| * The qmp-commands.txt file is generated from the qmp-commands.hx one, which | ||||
|   is the file that should be edited. | ||||
|  | ||||
| Homepage | ||||
| -------- | ||||
|  | ||||
| http://wiki.qemu.org/QMP | ||||
| http://www.linux-kvm.org/page/MonitorProtocol | ||||
| Luiz Fernando N. Capitulino <lcapitulino@redhat.com> | ||||
|   | ||||
| @@ -1,266 +1,26 @@ | ||||
|                    QEMU Monitor Protocol Events | ||||
|                    ============================ | ||||
|                    QEMU Monitor Protocol: Events | ||||
|                    ============================= | ||||
|  | ||||
| BLOCK_IO_ERROR | ||||
| -------------- | ||||
|  | ||||
| Emitted when a disk I/O error occurs. | ||||
|  | ||||
| Data: | ||||
|  | ||||
| - "device": device name (json-string) | ||||
| - "operation": I/O operation (json-string, "read" or "write") | ||||
| - "action": action that has been taken, it's one of the following (json-string): | ||||
|     "ignore": error has been ignored | ||||
|     "report": error has been reported to the device | ||||
|     "stop": error caused VM to be stopped | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "BLOCK_IO_ERROR", | ||||
|     "data": { "device": "ide0-hd1", | ||||
|               "operation": "write", | ||||
|               "action": "stop" }, | ||||
|     "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } | ||||
|  | ||||
| Note: If action is "stop", a STOP event will eventually follow the | ||||
| BLOCK_IO_ERROR event. | ||||
|  | ||||
| RESET | ||||
| ----- | ||||
|  | ||||
| Emitted when the Virtual Machine is reseted. | ||||
| 1 SHUTDOWN | ||||
| ----------- | ||||
|  | ||||
| Description: Issued when the Virtual Machine is powered down. | ||||
| Data: None. | ||||
|  | ||||
| Example: | ||||
| 2 RESET | ||||
| ------- | ||||
|  | ||||
| { "event": "RESET", | ||||
|     "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } | ||||
| Description: Issued when the Virtual Machine is reseted. | ||||
| Data: None. | ||||
|  | ||||
| RESUME | ||||
| 3 STOP | ||||
| ------ | ||||
|  | ||||
| Emitted when the Virtual Machine resumes execution. | ||||
|  | ||||
| Description: Issued when the Virtual Machine is stopped. | ||||
| Data: None. | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "RESUME", | ||||
|     "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } | ||||
|  | ||||
| RTC_CHANGE | ||||
| ---------- | ||||
|  | ||||
| Emitted when the guest changes the RTC time. | ||||
|  | ||||
| Data: | ||||
|  | ||||
| - "offset": delta against the host UTC in seconds (json-number) | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "RTC_CHANGE", | ||||
|     "data": { "offset": 78 }, | ||||
|     "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } | ||||
|  | ||||
| SHUTDOWN | ||||
| -------- | ||||
|  | ||||
| Emitted when the Virtual Machine is powered down. | ||||
| 4 DEBUG | ||||
| ------- | ||||
|  | ||||
| Description: Issued when the Virtual Machine enters debug mode. | ||||
| Data: None. | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "SHUTDOWN", | ||||
|     "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } | ||||
|  | ||||
| Note: If the command-line option "-no-shutdown" has been specified, a STOP | ||||
| event will eventually follow the SHUTDOWN event. | ||||
|  | ||||
| STOP | ||||
| ---- | ||||
|  | ||||
| Emitted when the Virtual Machine is stopped. | ||||
|  | ||||
| Data: None. | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "STOP", | ||||
|     "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } | ||||
|  | ||||
| VNC_CONNECTED | ||||
| ------------- | ||||
|  | ||||
| Emitted when a VNC client establishes a connection. | ||||
|  | ||||
| Data: | ||||
|  | ||||
| - "server": Server information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "service": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|   - "auth": authentication method (json-string, optional) | ||||
| - "client": Client information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "service": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "VNC_CONNECTED", | ||||
|     "data": { | ||||
|         "server": { "auth": "sasl", "family": "ipv4", | ||||
|                     "service": "5901", "host": "0.0.0.0" }, | ||||
|         "client": { "family": "ipv4", "service": "58425", | ||||
|                     "host": "127.0.0.1" } }, | ||||
|     "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } | ||||
|  | ||||
|  | ||||
| Note: This event is emitted before any authentication takes place, thus | ||||
| the authentication ID is not provided. | ||||
|  | ||||
| VNC_DISCONNECTED | ||||
| ---------------- | ||||
|  | ||||
| Emitted when the conection is closed. | ||||
|  | ||||
| Data: | ||||
|  | ||||
| - "server": Server information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "service": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|   - "auth": authentication method (json-string, optional) | ||||
| - "client": Client information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "service": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|   - "x509_dname": TLS dname (json-string, optional) | ||||
|   - "sasl_username": SASL username (json-string, optional) | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "VNC_DISCONNECTED", | ||||
|     "data": { | ||||
|         "server": { "auth": "sasl", "family": "ipv4", | ||||
|                     "service": "5901", "host": "0.0.0.0" }, | ||||
|         "client": { "family": "ipv4", "service": "58425", | ||||
|                     "host": "127.0.0.1", "sasl_username": "luiz" } }, | ||||
|     "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } | ||||
|  | ||||
| VNC_INITIALIZED | ||||
| --------------- | ||||
|  | ||||
| Emitted after authentication takes place (if any) and the VNC session is | ||||
| made active. | ||||
|  | ||||
| Data: | ||||
|  | ||||
| - "server": Server information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "service": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|   - "auth": authentication method (json-string, optional) | ||||
| - "client": Client information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "service": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|   - "x509_dname": TLS dname (json-string, optional) | ||||
|   - "sasl_username": SASL username (json-string, optional) | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "VNC_INITIALIZED", | ||||
|     "data": { | ||||
|         "server": { "auth": "sasl", "family": "ipv4", | ||||
|                     "service": "5901", "host": "0.0.0.0"}, | ||||
|         "client": { "family": "ipv4", "service": "46089", | ||||
|                     "host": "127.0.0.1", "sasl_username": "luiz" } }, | ||||
|         "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } | ||||
|  | ||||
| SPICE_CONNECTED, SPICE_DISCONNECTED | ||||
| ----------------------------------- | ||||
|  | ||||
| Emitted when a SPICE client connects or disconnects. | ||||
|  | ||||
| Data: | ||||
|  | ||||
| - "server": Server information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "port": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
| - "client": Client information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "port": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "timestamp": {"seconds": 1290688046, "microseconds": 388707}, | ||||
|   "event": "SPICE_CONNECTED", | ||||
|   "data": { | ||||
|     "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, | ||||
|     "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} | ||||
| }} | ||||
|  | ||||
|  | ||||
| SPICE_INITIALIZED | ||||
| ----------------- | ||||
|  | ||||
| Emitted after initial handshake and authentication takes place (if any) | ||||
| and the SPICE channel is up'n'running | ||||
|  | ||||
| Data: | ||||
|  | ||||
| - "server": Server information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "port": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|   - "auth": authentication method (json-string, optional) | ||||
| - "client": Client information (json-object) | ||||
|   - "host": IP address (json-string) | ||||
|   - "port": port number (json-string) | ||||
|   - "family": address family (json-string, "ipv4" or "ipv6") | ||||
|   - "connection-id": spice connection id.  All channels with the same id | ||||
|                      belong to the same spice session (json-int) | ||||
|   - "channel-type": channel type.  "1" is the main control channel, filter for | ||||
|                     this one if you want track spice sessions only (json-int) | ||||
|   - "channel-id": channel id.  Usually "0", might be different needed when | ||||
|                   multiple channels of the same type exist, such as multiple | ||||
|                   display channels in a multihead setup (json-int) | ||||
|   - "tls": whevener the channel is encrypted (json-bool) | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, | ||||
|   "event": "SPICE_INITIALIZED", | ||||
|   "data": {"server": {"auth": "spice", "port": "5921", | ||||
|                       "family": "ipv4", "host": "127.0.0.1"}, | ||||
|            "client": {"port": "49004", "family": "ipv4", "channel-type": 3, | ||||
|                       "connection-id": 1804289383, "host": "127.0.0.1", | ||||
|                       "channel-id": 0, "tls": true} | ||||
| }} | ||||
|  | ||||
|  | ||||
| WATCHDOG | ||||
| -------- | ||||
|  | ||||
| Emitted when the watchdog device's timer is expired. | ||||
|  | ||||
| Data: | ||||
|  | ||||
| - "action": Action that has been taken, it's one of the following (json-string): | ||||
|             "reset", "shutdown", "poweroff", "pause", "debug", or "none" | ||||
|  | ||||
| Example: | ||||
|  | ||||
| { "event": "WATCHDOG", | ||||
|      "data": { "action": "reset" }, | ||||
|      "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } | ||||
|  | ||||
| Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is | ||||
| followed respectively by the RESET, SHUTDOWN, or STOP events. | ||||
|   | ||||
							
								
								
									
										265
									
								
								QMP/qmp-shell
									
									
									
									
									
								
							
							
						
						
									
										265
									
								
								QMP/qmp-shell
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| #!/usr/bin/python | ||||
| # | ||||
| # Low-level QEMU shell on top of QMP. | ||||
| # Simple QEMU shell on top of QMP | ||||
| # | ||||
| # Copyright (C) 2009, 2010 Red Hat Inc. | ||||
| # Copyright (C) 2009 Red Hat Inc. | ||||
| # | ||||
| # Authors: | ||||
| #  Luiz Capitulino <lcapitulino@redhat.com> | ||||
| @@ -14,246 +14,59 @@ | ||||
| # | ||||
| # Start QEMU with: | ||||
| # | ||||
| # # qemu [...] -qmp unix:./qmp-sock,server | ||||
| # $ qemu [...] -monitor control,unix:./qmp,server | ||||
| # | ||||
| # Run the shell: | ||||
| # | ||||
| # $ qmp-shell ./qmp-sock | ||||
| # $ qmp-shell ./qmp | ||||
| # | ||||
| # Commands have the following format: | ||||
| # | ||||
| #    < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] | ||||
| # < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] | ||||
| # | ||||
| # For example: | ||||
| # | ||||
| # (QEMU) device_add driver=e1000 id=net1 | ||||
| # {u'return': {}} | ||||
| # (QEMU) | ||||
| # (QEMU) info item=network | ||||
|  | ||||
| import qmp | ||||
| import readline | ||||
| import sys | ||||
| from sys import argv,exit | ||||
|  | ||||
| class QMPCompleter(list): | ||||
|     def complete(self, text, state): | ||||
|         for cmd in self: | ||||
|             if cmd.startswith(text): | ||||
|                 if not state: | ||||
|                     return cmd | ||||
|                 else: | ||||
|                     state -= 1 | ||||
|  | ||||
| class QMPShellError(Exception): | ||||
|     pass | ||||
|  | ||||
| class QMPShellBadPort(QMPShellError): | ||||
|     pass | ||||
|  | ||||
| # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and | ||||
| #       _execute_cmd()). Let's design a better one. | ||||
| class QMPShell(qmp.QEMUMonitorProtocol): | ||||
|     def __init__(self, address): | ||||
|         qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address)) | ||||
|         self._greeting = None | ||||
|         self._completer = None | ||||
|  | ||||
|     def __get_address(self, arg): | ||||
|         """ | ||||
|         Figure out if the argument is in the port:host form, if it's not it's | ||||
|         probably a file path. | ||||
|         """ | ||||
|         addr = arg.split(':') | ||||
|         if len(addr) == 2: | ||||
|             try: | ||||
|                 port = int(addr[1]) | ||||
|             except ValueError: | ||||
|                 raise QMPShellBadPort | ||||
|             return ( addr[0], port ) | ||||
|         # socket path | ||||
|         return arg | ||||
|  | ||||
|     def _fill_completion(self): | ||||
|         for cmd in self.cmd('query-commands')['return']: | ||||
|             self._completer.append(cmd['name']) | ||||
|  | ||||
|     def __completer_setup(self): | ||||
|         self._completer = QMPCompleter() | ||||
|         self._fill_completion() | ||||
|         readline.set_completer(self._completer.complete) | ||||
|         readline.parse_and_bind("tab: complete") | ||||
|         # XXX: default delimiters conflict with some command names (eg. query-), | ||||
|         # clearing everything as it doesn't seem to matter | ||||
|         readline.set_completer_delims('') | ||||
|  | ||||
|     def __build_cmd(self, cmdline): | ||||
|         """ | ||||
|         Build a QMP input object from a user provided command-line in the | ||||
|         following format: | ||||
|      | ||||
|             < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] | ||||
|         """ | ||||
|         cmdargs = cmdline.split() | ||||
|         qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } | ||||
|         for arg in cmdargs[1:]: | ||||
|             opt = arg.split('=') | ||||
|             try: | ||||
|                 value = int(opt[1]) | ||||
|             except ValueError: | ||||
|                 value = opt[1] | ||||
|             qmpcmd['arguments'][opt[0]] = value | ||||
|         return qmpcmd | ||||
|  | ||||
|     def _execute_cmd(self, cmdline): | ||||
|         try: | ||||
|             qmpcmd = self.__build_cmd(cmdline) | ||||
|         except: | ||||
|             print 'command format: <command-name> ', | ||||
|             print '[arg-name1=arg1] ... [arg-nameN=argN]' | ||||
|             return True | ||||
|         resp = self.cmd_obj(qmpcmd) | ||||
|         if resp is None: | ||||
|             print 'Disconnected' | ||||
|             return False | ||||
|         print resp | ||||
|         return True | ||||
|  | ||||
|     def connect(self): | ||||
|         self._greeting = qmp.QEMUMonitorProtocol.connect(self) | ||||
|         self.__completer_setup() | ||||
|  | ||||
|     def show_banner(self, msg='Welcome to the QMP low-level shell!'): | ||||
|         print msg | ||||
|         version = self._greeting['QMP']['version']['qemu'] | ||||
|         print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) | ||||
|  | ||||
|     def read_exec_command(self, prompt): | ||||
|         """ | ||||
|         Read and execute a command. | ||||
|  | ||||
|         @return True if execution was ok, return False if disconnected. | ||||
|         """ | ||||
|         try: | ||||
|             cmdline = raw_input(prompt) | ||||
|         except EOFError: | ||||
|             print | ||||
|             return False | ||||
|         if cmdline == '': | ||||
|             for ev in self.get_events(): | ||||
|                 print ev | ||||
|             self.clear_events() | ||||
|             return True | ||||
|         else: | ||||
|             return self._execute_cmd(cmdline) | ||||
|  | ||||
| class HMPShell(QMPShell): | ||||
|     def __init__(self, address): | ||||
|         QMPShell.__init__(self, address) | ||||
|         self.__cpu_index = 0 | ||||
|  | ||||
|     def __cmd_completion(self): | ||||
|         for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'): | ||||
|             if cmd and cmd[0] != '[' and cmd[0] != '\t': | ||||
|                 name = cmd.split()[0] # drop help text | ||||
|                 if name == 'info': | ||||
|                     continue | ||||
|                 if name.find('|') != -1: | ||||
|                     # Command in the form 'foobar|f' or 'f|foobar', take the | ||||
|                     # full name | ||||
|                     opt = name.split('|') | ||||
|                     if len(opt[0]) == 1: | ||||
|                         name = opt[1] | ||||
|                     else: | ||||
|                         name = opt[0] | ||||
|                 self._completer.append(name) | ||||
|                 self._completer.append('help ' + name) # help completion | ||||
|  | ||||
|     def __info_completion(self): | ||||
|         for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'): | ||||
|             if cmd: | ||||
|                 self._completer.append('info ' + cmd.split()[1]) | ||||
|  | ||||
|     def __other_completion(self): | ||||
|         # special cases | ||||
|         self._completer.append('help info') | ||||
|  | ||||
|     def _fill_completion(self): | ||||
|         self.__cmd_completion() | ||||
|         self.__info_completion() | ||||
|         self.__other_completion() | ||||
|  | ||||
|     def __cmd_passthrough(self, cmdline, cpu_index = 0): | ||||
|         return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments': | ||||
|                               { 'command-line': cmdline, | ||||
|                                 'cpu-index': cpu_index } }) | ||||
|  | ||||
|     def _execute_cmd(self, cmdline): | ||||
|         if cmdline.split()[0] == "cpu": | ||||
|             # trap the cpu command, it requires special setting | ||||
|             try: | ||||
|                 idx = int(cmdline.split()[1]) | ||||
|                 if not 'return' in self.__cmd_passthrough('info version', idx): | ||||
|                     print 'bad CPU index' | ||||
|                     return True | ||||
|                 self.__cpu_index = idx | ||||
|             except ValueError: | ||||
|                 print 'cpu command takes an integer argument' | ||||
|                 return True | ||||
|         resp = self.__cmd_passthrough(cmdline, self.__cpu_index) | ||||
|         if resp is None: | ||||
|             print 'Disconnected' | ||||
|             return False | ||||
|         assert 'return' in resp or 'error' in resp | ||||
|         if 'return' in resp: | ||||
|             # Success | ||||
|             if len(resp['return']) > 0: | ||||
|                 print resp['return'], | ||||
|         else: | ||||
|             # Error | ||||
|             print '%s: %s' % (resp['error']['class'], resp['error']['desc']) | ||||
|         return True | ||||
|  | ||||
|     def show_banner(self): | ||||
|         QMPShell.show_banner(self, msg='Welcome to the HMP shell!') | ||||
|  | ||||
| def die(msg): | ||||
|     sys.stderr.write('ERROR: %s\n' % msg) | ||||
|     sys.exit(1) | ||||
|  | ||||
| def fail_cmdline(option=None): | ||||
|     if option: | ||||
|         sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option) | ||||
|     sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n') | ||||
|     sys.exit(1) | ||||
| def shell_help(): | ||||
|     print 'bye  exit from the shell' | ||||
|  | ||||
| def main(): | ||||
|     addr = '' | ||||
|     try: | ||||
|         if len(sys.argv) == 2: | ||||
|             qemu = QMPShell(sys.argv[1]) | ||||
|             addr = sys.argv[1] | ||||
|         elif len(sys.argv) == 3: | ||||
|             if sys.argv[1] != '-H': | ||||
|                 fail_cmdline(sys.argv[1]) | ||||
|             qemu = HMPShell(sys.argv[2]) | ||||
|             addr = sys.argv[2] | ||||
|     if len(argv) != 2: | ||||
|         print 'qemu-shell <unix-socket>' | ||||
|         exit(1) | ||||
|  | ||||
|     qemu = qmp.QEMUMonitorProtocol(argv[1]) | ||||
|     qemu.connect() | ||||
|  | ||||
|     print 'Connected!' | ||||
|  | ||||
|     while True: | ||||
|         try: | ||||
|             cmd = raw_input('(QEMU) ') | ||||
|         except EOFError: | ||||
|             print | ||||
|             break | ||||
|         if cmd == '': | ||||
|             continue | ||||
|         elif cmd == 'bye': | ||||
|             break | ||||
|         elif cmd == 'help': | ||||
|             shell_help() | ||||
|         else: | ||||
|                 fail_cmdline() | ||||
|     except QMPShellBadPort: | ||||
|         die('bad port number in command-line') | ||||
|  | ||||
|     try: | ||||
|         qemu.connect() | ||||
|     except qmp.QMPConnectError: | ||||
|         die('Didn\'t get QMP greeting message') | ||||
|     except qmp.QMPCapabilitiesError: | ||||
|         die('Could not negotiate capabilities') | ||||
|     except qemu.error: | ||||
|         die('Could not connect to %s' % addr) | ||||
|  | ||||
|     qemu.show_banner() | ||||
|     while qemu.read_exec_command('(QEMU) '): | ||||
|         pass | ||||
|     qemu.close() | ||||
|             try: | ||||
|                 resp = qemu.send(cmd) | ||||
|                 if resp == None: | ||||
|                     print 'Disconnected' | ||||
|                     break | ||||
|                 print resp | ||||
|             except IndexError: | ||||
|                 print '-> command format: <command-name> ', | ||||
|                 print '[arg-name1=arg1] ... [arg-nameN=argN]' | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     main() | ||||
|   | ||||
| @@ -44,17 +44,14 @@ they can be in ANY order, thus no particular order should be assumed. | ||||
|  | ||||
| Right when connected the Server will issue a greeting message, which signals | ||||
| that the connection has been successfully established and that the Server is | ||||
| ready for capabilities negotiation (for more information refer to section | ||||
| '4. Capabilities Negotiation'). | ||||
| waiting for commands. | ||||
|  | ||||
| The format is: | ||||
|  | ||||
| { "QMP": { "version": json-object, "capabilities": json-array } } | ||||
| { "QMP": { "capabilities": json-array } } | ||||
|  | ||||
|  Where, | ||||
|  | ||||
| - The "version" member contains the Server's version information (the format | ||||
|   is the same of the 'query-version' command) | ||||
| - The "capabilities" member specify the availability of features beyond the | ||||
|   baseline specification | ||||
|  | ||||
| @@ -155,7 +152,7 @@ This section provides some examples of real QMP usage, in all of them | ||||
| 3.1 Server greeting | ||||
| ------------------- | ||||
|  | ||||
| S: {"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}} | ||||
| S: {"QMP": {"capabilities": []}} | ||||
|  | ||||
| 3.2 Simple 'stop' execution | ||||
| --------------------------- | ||||
| @@ -182,91 +179,25 @@ S: {"error": {"class": "JSONParsing", "desc": "Invalid JSON syntax", "data": | ||||
| S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event": | ||||
| "POWERDOWN"} | ||||
|  | ||||
| 4. Capabilities Negotiation | ||||
| ---------------------------- | ||||
| 4. Compatibility Considerations | ||||
| -------------------------------- | ||||
|  | ||||
| When a Client successfully establishes a connection, the Server is in | ||||
| Capabilities Negotiation mode. | ||||
|  | ||||
| In this mode only the 'qmp_capabilities' command is allowed to run, all | ||||
| other commands will return the CommandNotFound error. Asynchronous messages | ||||
| are not delivered either. | ||||
|  | ||||
| Clients should use the 'qmp_capabilities' command to enable capabilities | ||||
| advertised in the Server's greeting (section '2.2 Server Greeting') they | ||||
| support. | ||||
|  | ||||
| When the 'qmp_capabilities' command is issued, and if it does not return an | ||||
| error, the Server enters in Command mode where capabilities changes take | ||||
| effect, all commands (except 'qmp_capabilities') are allowed and asynchronous | ||||
| messages are delivered. | ||||
|  | ||||
| 5 Compatibility Considerations | ||||
| ------------------------------ | ||||
|  | ||||
| All protocol changes or new features which modify the protocol format in an | ||||
| incompatible way are disabled by default and will be advertised by the | ||||
| capabilities array (section '2.2 Server Greeting'). Thus, Clients can check | ||||
| that array and enable the capabilities they support. | ||||
|  | ||||
| Additionally, Clients must not assume any particular: | ||||
| In order to achieve maximum compatibility between versions, Clients must not  | ||||
| assume any particular: | ||||
|  | ||||
| - Size of json-objects or length of json-arrays | ||||
| - Order of json-object members or json-array elements | ||||
| - Amount of errors generated by a command, that is, new errors can be added | ||||
|   to any existing command in newer versions of the Server | ||||
|  | ||||
| 6. Downstream extension of QMP | ||||
| ------------------------------ | ||||
| Additionally, Clients should always: | ||||
|  | ||||
| We recommend that downstream consumers of QEMU do *not* modify QMP. | ||||
| Management tools should be able to support both upstream and downstream | ||||
| versions of QMP without special logic, and downstream extensions are | ||||
| inherently at odds with that. | ||||
| - Check the capabilities json-array at connection time | ||||
| - Check the availability of commands with 'query-commands' before issuing them | ||||
|  | ||||
| However, we recognize that it is sometimes impossible for downstreams to | ||||
| avoid modifying QMP.  Both upstream and downstream need to take care to | ||||
| preserve long-term compatibility and interoperability. | ||||
| 5. Recommendations to Client implementors | ||||
| ----------------------------------------- | ||||
|  | ||||
| To help with that, QMP reserves JSON object member names beginning with | ||||
| '__' (double underscore) for downstream use ("downstream names").  This | ||||
| means upstream will never use any downstream names for its commands, | ||||
| arguments, errors, asynchronous events, and so forth. | ||||
|  | ||||
| Any new names downstream wishes to add must begin with '__'.  To | ||||
| ensure compatibility with other downstreams, it is strongly | ||||
| recommended that you prefix your downstram names with '__RFQDN_' where | ||||
| RFQDN is a valid, reverse fully qualified domain name which you | ||||
| control.  For example, a qemu-kvm specific monitor command would be: | ||||
|  | ||||
|     (qemu) __org.linux-kvm_enable_irqchip | ||||
|  | ||||
| Downstream must not change the server greeting (section 2.2) other than | ||||
| to offer additional capabilities.  But see below for why even that is | ||||
| discouraged. | ||||
|  | ||||
| Section '5 Compatibility Considerations' applies to downstream as well | ||||
| as to upstream, obviously.  It follows that downstream must behave | ||||
| exactly like upstream for any input not containing members with | ||||
| downstream names ("downstream members"), except it may add members | ||||
| with downstream names to its output. | ||||
|  | ||||
| Thus, a client should not be able to distinguish downstream from | ||||
| upstream as long as it doesn't send input with downstream members, and | ||||
| properly ignores any downstream members in the output it receives. | ||||
|  | ||||
| Advice on downstream modifications: | ||||
|  | ||||
| 1. Introducing new commands is okay.  If you want to extend an existing | ||||
|    command, consider introducing a new one with the new behaviour | ||||
|    instead. | ||||
|  | ||||
| 2. Introducing new asynchronous messages is okay.  If you want to extend | ||||
|    an existing message, consider adding a new one instead. | ||||
|  | ||||
| 3. Introducing new errors for use in new commands is okay.  Adding new | ||||
|    errors to existing commands counts as extension, so 1. applies. | ||||
|  | ||||
| 4. New capabilities are strongly discouraged.  Capabilities are for | ||||
|    evolving the basic protocol, and multiple diverging basic protocol | ||||
|    dialects are most undesirable. | ||||
| 5.1 The Server should be always started in pause mode, thus the Client is | ||||
|     able to perform any setup procedure without the risk of race conditions | ||||
|     and related problems | ||||
|   | ||||
							
								
								
									
										161
									
								
								QMP/qmp.py
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								QMP/qmp.py
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # QEMU Monitor Protocol Python class | ||||
| #  | ||||
| # Copyright (C) 2009, 2010 Red Hat Inc. | ||||
| # Copyright (C) 2009 Red Hat Inc. | ||||
| # | ||||
| # Authors: | ||||
| #  Luiz Capitulino <lcapitulino@redhat.com> | ||||
| @@ -8,9 +8,7 @@ | ||||
| # This work is licensed under the terms of the GNU GPL, version 2.  See | ||||
| # the COPYING file in the top-level directory. | ||||
|  | ||||
| import json | ||||
| import errno | ||||
| import socket | ||||
| import socket, json | ||||
|  | ||||
| class QMPError(Exception): | ||||
|     pass | ||||
| @@ -18,114 +16,57 @@ class QMPError(Exception): | ||||
| class QMPConnectError(QMPError): | ||||
|     pass | ||||
|  | ||||
| class QMPCapabilitiesError(QMPError): | ||||
|     pass | ||||
|  | ||||
| class QEMUMonitorProtocol: | ||||
|     def __init__(self, address): | ||||
|         """ | ||||
|         Create a QEMUMonitorProtocol class. | ||||
|  | ||||
|         @param address: QEMU address, can be either a unix socket path (string) | ||||
|                         or a tuple in the form ( address, port ) for a TCP | ||||
|                         connection | ||||
|         @note No connection is established, this is done by the connect() method | ||||
|         """ | ||||
|         self.__events = [] | ||||
|         self.__address = address | ||||
|         self.__sock = self.__get_sock() | ||||
|         self.__sockfile = self.__sock.makefile() | ||||
|  | ||||
|     def __get_sock(self): | ||||
|         if isinstance(self.__address, tuple): | ||||
|             family = socket.AF_INET | ||||
|         else: | ||||
|             family = socket.AF_UNIX | ||||
|         return socket.socket(family, socket.SOCK_STREAM) | ||||
|  | ||||
|     def __json_read(self): | ||||
|         while True: | ||||
|             data = self.__sockfile.readline() | ||||
|             if not data: | ||||
|                 return | ||||
|             resp = json.loads(data) | ||||
|             if 'event' in resp: | ||||
|                 self.__events.append(resp) | ||||
|                 continue | ||||
|             return resp | ||||
|  | ||||
|     error = socket.error | ||||
|  | ||||
|     def connect(self): | ||||
|         """ | ||||
|         Connect to the QMP Monitor and perform capabilities negotiation. | ||||
|  | ||||
|         @return QMP greeting dict | ||||
|         @raise socket.error on socket connection errors | ||||
|         @raise QMPConnectError if the greeting is not received | ||||
|         @raise QMPCapabilitiesError if fails to negotiate capabilities | ||||
|         """ | ||||
|         self.__sock.connect(self.__address) | ||||
|         greeting = self.__json_read() | ||||
|         if greeting is None or not greeting.has_key('QMP'): | ||||
|         self.sock.connect(self.filename) | ||||
|         data = self.__json_read() | ||||
|         if data == None: | ||||
|             raise QMPConnectError | ||||
|         # Greeting seems ok, negotiate capabilities | ||||
|         resp = self.cmd('qmp_capabilities') | ||||
|         if "return" in resp: | ||||
|             return greeting | ||||
|         raise QMPCapabilitiesError | ||||
|  | ||||
|     def cmd_obj(self, qmp_cmd): | ||||
|         """ | ||||
|         Send a QMP command to the QMP Monitor. | ||||
|  | ||||
|         @param qmp_cmd: QMP command to be sent as a Python dict | ||||
|         @return QMP response as a Python dict or None if the connection has | ||||
|                 been closed | ||||
|         """ | ||||
|         try: | ||||
|             self.__sock.sendall(json.dumps(qmp_cmd)) | ||||
|         except socket.error, err: | ||||
|             if err[0] == errno.EPIPE: | ||||
|                 return | ||||
|             raise socket.error(err) | ||||
|         return self.__json_read() | ||||
|  | ||||
|     def cmd(self, name, args=None, id=None): | ||||
|         """ | ||||
|         Build a QMP command and send it to the QMP Monitor. | ||||
|  | ||||
|         @param name: command name (string) | ||||
|         @param args: command arguments (dict) | ||||
|         @param id: command id (dict, list, string or int) | ||||
|         """ | ||||
|         qmp_cmd = { 'execute': name } | ||||
|         if args: | ||||
|             qmp_cmd['arguments'] = args | ||||
|         if id: | ||||
|             qmp_cmd['id'] = id | ||||
|         return self.cmd_obj(qmp_cmd) | ||||
|  | ||||
|     def get_events(self): | ||||
|         """ | ||||
|         Get a list of available QMP events. | ||||
|         """ | ||||
|         self.__sock.setblocking(0) | ||||
|         try: | ||||
|             self.__json_read() | ||||
|         except socket.error, err: | ||||
|             if err[0] == errno.EAGAIN: | ||||
|                 # No data available | ||||
|                 pass | ||||
|         self.__sock.setblocking(1) | ||||
|         return self.__events | ||||
|  | ||||
|     def clear_events(self): | ||||
|         """ | ||||
|         Clear current list of pending events. | ||||
|         """ | ||||
|         self.__events = [] | ||||
|         if not data.has_key('QMP'): | ||||
|             raise QMPConnectError | ||||
|         return data['QMP']['capabilities'] | ||||
|  | ||||
|     def close(self): | ||||
|         self.__sock.close() | ||||
|         self.__sockfile.close() | ||||
|         self.sock.close() | ||||
|  | ||||
|     def send_raw(self, line): | ||||
|         self.sock.send(str(line)) | ||||
|         return self.__json_read() | ||||
|  | ||||
|     def send(self, cmdline): | ||||
|         cmd = self.__build_cmd(cmdline) | ||||
|         self.__json_send(cmd) | ||||
|         resp = self.__json_read() | ||||
|         if resp == None: | ||||
|             return | ||||
|         elif resp.has_key('error'): | ||||
|             return resp['error'] | ||||
|         else: | ||||
|             return resp['return'] | ||||
|  | ||||
|     def __build_cmd(self, cmdline): | ||||
|         cmdargs = cmdline.split() | ||||
|         qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } | ||||
|         for arg in cmdargs[1:]: | ||||
|             opt = arg.split('=') | ||||
|             try: | ||||
|                 value = int(opt[1]) | ||||
|             except ValueError: | ||||
|                 value = opt[1] | ||||
|             qmpcmd['arguments'][opt[0]] = value | ||||
|         return qmpcmd | ||||
|  | ||||
|     def __json_send(self, cmd): | ||||
|         # XXX: We have to send any additional char, otherwise | ||||
|         # the Server won't read our input | ||||
|         self.sock.send(json.dumps(cmd) + ' ') | ||||
|  | ||||
|     def __json_read(self): | ||||
|         try: | ||||
|             return json.loads(self.sock.recv(1024)) | ||||
|         except ValueError: | ||||
|             return | ||||
|  | ||||
|     def __init__(self, filename): | ||||
|         self.filename = filename | ||||
|         self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||||
|   | ||||
							
								
								
									
										32
									
								
								QMP/vm-info
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								QMP/vm-info
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| #!/usr/bin/python | ||||
| # | ||||
| # Print Virtual Machine information | ||||
| # | ||||
| # Usage: | ||||
| # | ||||
| # Start QEMU with: | ||||
| # | ||||
| # $ qemu [...] -monitor control,unix:./qmp,server | ||||
| # | ||||
| # Run vm-info: | ||||
| # | ||||
| # $ vm-info ./qmp | ||||
| # | ||||
| # Luiz Capitulino <lcapitulino@redhat.com> | ||||
|  | ||||
| import qmp | ||||
| from sys import argv,exit | ||||
|  | ||||
| def main(): | ||||
|     if len(argv) != 2: | ||||
|         print 'vm-info <unix-socket>' | ||||
|         exit(1) | ||||
|  | ||||
|     qemu = qmp.QEMUMonitorProtocol(argv[1]) | ||||
|     qemu.connect() | ||||
|  | ||||
|     for cmd in [ 'version', 'hpet', 'kvm', 'status', 'uuid', 'balloon' ]: | ||||
|         print cmd + ': ' + str(qemu.send('query-' + cmd)) | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     main() | ||||
| @@ -22,9 +22,6 @@ along with this file; see the file COPYING.  If not, see | ||||
| #include <stdio.h> | ||||
| #include "dis-asm.h" | ||||
|  | ||||
| /* MAX is redefined below, so remove any previous definition. */ | ||||
| #undef MAX | ||||
|  | ||||
| /* The opcode table is an array of struct alpha_opcode.  */ | ||||
|  | ||||
| struct alpha_opcode | ||||
|   | ||||
							
								
								
									
										726
									
								
								arch_init.c
									
									
									
									
									
								
							
							
						
						
									
										726
									
								
								arch_init.c
									
									
									
									
									
								
							| @@ -1,726 +0,0 @@ | ||||
| /* | ||||
|  * QEMU System Emulator | ||||
|  * | ||||
|  * Copyright (c) 2003-2008 Fabrice Bellard | ||||
|  * | ||||
|  * 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 <stdint.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdlib.h> | ||||
| #ifndef _WIN32 | ||||
| #include <sys/types.h> | ||||
| #include <sys/mman.h> | ||||
| #endif | ||||
| #include "config.h" | ||||
| #include "monitor.h" | ||||
| #include "sysemu.h" | ||||
| #include "arch_init.h" | ||||
| #include "audio/audio.h" | ||||
| #include "hw/pc.h" | ||||
| #include "hw/pci.h" | ||||
| #include "hw/audiodev.h" | ||||
| #include "kvm.h" | ||||
| #include "migration.h" | ||||
| #include "net.h" | ||||
| #include "gdbstub.h" | ||||
| #include "hw/smbios.h" | ||||
|  | ||||
| #ifdef TARGET_SPARC | ||||
| int graphic_width = 1024; | ||||
| int graphic_height = 768; | ||||
| int graphic_depth = 8; | ||||
| #else | ||||
| int graphic_width = 800; | ||||
| int graphic_height = 600; | ||||
| int graphic_depth = 15; | ||||
| #endif | ||||
|  | ||||
| const char arch_config_name[] = CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf"; | ||||
|  | ||||
| #if defined(TARGET_ALPHA) | ||||
| #define QEMU_ARCH QEMU_ARCH_ALPHA | ||||
| #elif defined(TARGET_ARM) | ||||
| #define QEMU_ARCH QEMU_ARCH_ARM | ||||
| #elif defined(TARGET_CRIS) | ||||
| #define QEMU_ARCH QEMU_ARCH_CRIS | ||||
| #elif defined(TARGET_I386) | ||||
| #define QEMU_ARCH QEMU_ARCH_I386 | ||||
| #elif defined(TARGET_M68K) | ||||
| #define QEMU_ARCH QEMU_ARCH_M68K | ||||
| #elif defined(TARGET_MICROBLAZE) | ||||
| #define QEMU_ARCH QEMU_ARCH_MICROBLAZE | ||||
| #elif defined(TARGET_MIPS) | ||||
| #define QEMU_ARCH QEMU_ARCH_MIPS | ||||
| #elif defined(TARGET_PPC) | ||||
| #define QEMU_ARCH QEMU_ARCH_PPC | ||||
| #elif defined(TARGET_S390X) | ||||
| #define QEMU_ARCH QEMU_ARCH_S390X | ||||
| #elif defined(TARGET_SH4) | ||||
| #define QEMU_ARCH QEMU_ARCH_SH4 | ||||
| #elif defined(TARGET_SPARC) | ||||
| #define QEMU_ARCH QEMU_ARCH_SPARC | ||||
| #endif | ||||
|  | ||||
| const uint32_t arch_type = QEMU_ARCH; | ||||
|  | ||||
| /***********************************************************/ | ||||
| /* ram save/restore */ | ||||
|  | ||||
| #define RAM_SAVE_FLAG_FULL     0x01 /* Obsolete, not used anymore */ | ||||
| #define RAM_SAVE_FLAG_COMPRESS 0x02 | ||||
| #define RAM_SAVE_FLAG_MEM_SIZE 0x04 | ||||
| #define RAM_SAVE_FLAG_PAGE     0x08 | ||||
| #define RAM_SAVE_FLAG_EOS      0x10 | ||||
| #define RAM_SAVE_FLAG_CONTINUE 0x20 | ||||
|  | ||||
| static int is_dup_page(uint8_t *page, uint8_t ch) | ||||
| { | ||||
|     uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch; | ||||
|     uint32_t *array = (uint32_t *)page; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) { | ||||
|         if (array[i] != val) { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| static RAMBlock *last_block; | ||||
| static ram_addr_t last_offset; | ||||
|  | ||||
| static int ram_save_block(QEMUFile *f) | ||||
| { | ||||
|     RAMBlock *block = last_block; | ||||
|     ram_addr_t offset = last_offset; | ||||
|     ram_addr_t current_addr; | ||||
|     int bytes_sent = 0; | ||||
|  | ||||
|     if (!block) | ||||
|         block = QLIST_FIRST(&ram_list.blocks); | ||||
|  | ||||
|     current_addr = block->offset + offset; | ||||
|  | ||||
|     do { | ||||
|         if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { | ||||
|             uint8_t *p; | ||||
|             int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0; | ||||
|  | ||||
|             cpu_physical_memory_reset_dirty(current_addr, | ||||
|                                             current_addr + TARGET_PAGE_SIZE, | ||||
|                                             MIGRATION_DIRTY_FLAG); | ||||
|  | ||||
|             p = block->host + offset; | ||||
|  | ||||
|             if (is_dup_page(p, *p)) { | ||||
|                 qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS); | ||||
|                 if (!cont) { | ||||
|                     qemu_put_byte(f, strlen(block->idstr)); | ||||
|                     qemu_put_buffer(f, (uint8_t *)block->idstr, | ||||
|                                     strlen(block->idstr)); | ||||
|                 } | ||||
|                 qemu_put_byte(f, *p); | ||||
|                 bytes_sent = 1; | ||||
|             } else { | ||||
|                 qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE); | ||||
|                 if (!cont) { | ||||
|                     qemu_put_byte(f, strlen(block->idstr)); | ||||
|                     qemu_put_buffer(f, (uint8_t *)block->idstr, | ||||
|                                     strlen(block->idstr)); | ||||
|                 } | ||||
|                 qemu_put_buffer(f, p, TARGET_PAGE_SIZE); | ||||
|                 bytes_sent = TARGET_PAGE_SIZE; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         offset += TARGET_PAGE_SIZE; | ||||
|         if (offset >= block->length) { | ||||
|             offset = 0; | ||||
|             block = QLIST_NEXT(block, next); | ||||
|             if (!block) | ||||
|                 block = QLIST_FIRST(&ram_list.blocks); | ||||
|         } | ||||
|  | ||||
|         current_addr = block->offset + offset; | ||||
|  | ||||
|     } while (current_addr != last_block->offset + last_offset); | ||||
|  | ||||
|     last_block = block; | ||||
|     last_offset = offset; | ||||
|  | ||||
|     return bytes_sent; | ||||
| } | ||||
|  | ||||
| static uint64_t bytes_transferred; | ||||
|  | ||||
| static ram_addr_t ram_save_remaining(void) | ||||
| { | ||||
|     RAMBlock *block; | ||||
|     ram_addr_t count = 0; | ||||
|  | ||||
|     QLIST_FOREACH(block, &ram_list.blocks, next) { | ||||
|         ram_addr_t addr; | ||||
|         for (addr = block->offset; addr < block->offset + block->length; | ||||
|              addr += TARGET_PAGE_SIZE) { | ||||
|             if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) { | ||||
|                 count++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return count; | ||||
| } | ||||
|  | ||||
| uint64_t ram_bytes_remaining(void) | ||||
| { | ||||
|     return ram_save_remaining() * TARGET_PAGE_SIZE; | ||||
| } | ||||
|  | ||||
| uint64_t ram_bytes_transferred(void) | ||||
| { | ||||
|     return bytes_transferred; | ||||
| } | ||||
|  | ||||
| uint64_t ram_bytes_total(void) | ||||
| { | ||||
|     RAMBlock *block; | ||||
|     uint64_t total = 0; | ||||
|  | ||||
|     QLIST_FOREACH(block, &ram_list.blocks, next) | ||||
|         total += block->length; | ||||
|  | ||||
|     return total; | ||||
| } | ||||
|  | ||||
| static int block_compar(const void *a, const void *b) | ||||
| { | ||||
|     RAMBlock * const *ablock = a; | ||||
|     RAMBlock * const *bblock = b; | ||||
|     if ((*ablock)->offset < (*bblock)->offset) { | ||||
|         return -1; | ||||
|     } else if ((*ablock)->offset > (*bblock)->offset) { | ||||
|         return 1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void sort_ram_list(void) | ||||
| { | ||||
|     RAMBlock *block, *nblock, **blocks; | ||||
|     int n; | ||||
|     n = 0; | ||||
|     QLIST_FOREACH(block, &ram_list.blocks, next) { | ||||
|         ++n; | ||||
|     } | ||||
|     blocks = qemu_malloc(n * sizeof *blocks); | ||||
|     n = 0; | ||||
|     QLIST_FOREACH_SAFE(block, &ram_list.blocks, next, nblock) { | ||||
|         blocks[n++] = block; | ||||
|         QLIST_REMOVE(block, next); | ||||
|     } | ||||
|     qsort(blocks, n, sizeof *blocks, block_compar); | ||||
|     while (--n >= 0) { | ||||
|         QLIST_INSERT_HEAD(&ram_list.blocks, blocks[n], next); | ||||
|     } | ||||
|     qemu_free(blocks); | ||||
| } | ||||
|  | ||||
| int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) | ||||
| { | ||||
|     ram_addr_t addr; | ||||
|     uint64_t bytes_transferred_last; | ||||
|     double bwidth = 0; | ||||
|     uint64_t expected_time = 0; | ||||
|  | ||||
|     if (stage < 0) { | ||||
|         cpu_physical_memory_set_dirty_tracking(0); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { | ||||
|         qemu_file_set_error(f); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (stage == 1) { | ||||
|         RAMBlock *block; | ||||
|         bytes_transferred = 0; | ||||
|         last_block = NULL; | ||||
|         last_offset = 0; | ||||
|         sort_ram_list(); | ||||
|  | ||||
|         /* Make sure all dirty bits are set */ | ||||
|         QLIST_FOREACH(block, &ram_list.blocks, next) { | ||||
|             for (addr = block->offset; addr < block->offset + block->length; | ||||
|                  addr += TARGET_PAGE_SIZE) { | ||||
|                 if (!cpu_physical_memory_get_dirty(addr, | ||||
|                                                    MIGRATION_DIRTY_FLAG)) { | ||||
|                     cpu_physical_memory_set_dirty(addr); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* Enable dirty memory tracking */ | ||||
|         cpu_physical_memory_set_dirty_tracking(1); | ||||
|  | ||||
|         qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); | ||||
|  | ||||
|         QLIST_FOREACH(block, &ram_list.blocks, next) { | ||||
|             qemu_put_byte(f, strlen(block->idstr)); | ||||
|             qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); | ||||
|             qemu_put_be64(f, block->length); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bytes_transferred_last = bytes_transferred; | ||||
|     bwidth = qemu_get_clock_ns(rt_clock); | ||||
|  | ||||
|     while (!qemu_file_rate_limit(f)) { | ||||
|         int bytes_sent; | ||||
|  | ||||
|         bytes_sent = ram_save_block(f); | ||||
|         bytes_transferred += bytes_sent; | ||||
|         if (bytes_sent == 0) { /* no more blocks */ | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bwidth = qemu_get_clock_ns(rt_clock) - bwidth; | ||||
|     bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; | ||||
|  | ||||
|     /* if we haven't transferred anything this round, force expected_time to a | ||||
|      * a very high value, but without crashing */ | ||||
|     if (bwidth == 0) { | ||||
|         bwidth = 0.000001; | ||||
|     } | ||||
|  | ||||
|     /* try transferring iterative blocks of memory */ | ||||
|     if (stage == 3) { | ||||
|         int bytes_sent; | ||||
|  | ||||
|         /* flush all remaining blocks regardless of rate limiting */ | ||||
|         while ((bytes_sent = ram_save_block(f)) != 0) { | ||||
|             bytes_transferred += bytes_sent; | ||||
|         } | ||||
|         cpu_physical_memory_set_dirty_tracking(0); | ||||
|     } | ||||
|  | ||||
|     qemu_put_be64(f, RAM_SAVE_FLAG_EOS); | ||||
|  | ||||
|     expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth; | ||||
|  | ||||
|     return (stage == 2) && (expected_time <= migrate_max_downtime()); | ||||
| } | ||||
|  | ||||
| static inline void *host_from_stream_offset(QEMUFile *f, | ||||
|                                             ram_addr_t offset, | ||||
|                                             int flags) | ||||
| { | ||||
|     static RAMBlock *block = NULL; | ||||
|     char id[256]; | ||||
|     uint8_t len; | ||||
|  | ||||
|     if (flags & RAM_SAVE_FLAG_CONTINUE) { | ||||
|         if (!block) { | ||||
|             fprintf(stderr, "Ack, bad migration stream!\n"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         return block->host + offset; | ||||
|     } | ||||
|  | ||||
|     len = qemu_get_byte(f); | ||||
|     qemu_get_buffer(f, (uint8_t *)id, len); | ||||
|     id[len] = 0; | ||||
|  | ||||
|     QLIST_FOREACH(block, &ram_list.blocks, next) { | ||||
|         if (!strncmp(id, block->idstr, sizeof(id))) | ||||
|             return block->host + offset; | ||||
|     } | ||||
|  | ||||
|     fprintf(stderr, "Can't find block %s!\n", id); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| int ram_load(QEMUFile *f, void *opaque, int version_id) | ||||
| { | ||||
|     ram_addr_t addr; | ||||
|     int flags; | ||||
|  | ||||
|     if (version_id < 3 || version_id > 4) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     do { | ||||
|         addr = qemu_get_be64(f); | ||||
|  | ||||
|         flags = addr & ~TARGET_PAGE_MASK; | ||||
|         addr &= TARGET_PAGE_MASK; | ||||
|  | ||||
|         if (flags & RAM_SAVE_FLAG_MEM_SIZE) { | ||||
|             if (version_id == 3) { | ||||
|                 if (addr != ram_bytes_total()) { | ||||
|                     return -EINVAL; | ||||
|                 } | ||||
|             } else { | ||||
|                 /* Synchronize RAM block list */ | ||||
|                 char id[256]; | ||||
|                 ram_addr_t length; | ||||
|                 ram_addr_t total_ram_bytes = addr; | ||||
|  | ||||
|                 while (total_ram_bytes) { | ||||
|                     RAMBlock *block; | ||||
|                     uint8_t len; | ||||
|  | ||||
|                     len = qemu_get_byte(f); | ||||
|                     qemu_get_buffer(f, (uint8_t *)id, len); | ||||
|                     id[len] = 0; | ||||
|                     length = qemu_get_be64(f); | ||||
|  | ||||
|                     QLIST_FOREACH(block, &ram_list.blocks, next) { | ||||
|                         if (!strncmp(id, block->idstr, sizeof(id))) { | ||||
|                             if (block->length != length) | ||||
|                                 return -EINVAL; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if (!block) { | ||||
|                         fprintf(stderr, "Unknown ramblock \"%s\", cannot " | ||||
|                                 "accept migration\n", id); | ||||
|                         return -EINVAL; | ||||
|                     } | ||||
|  | ||||
|                     total_ram_bytes -= length; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (flags & RAM_SAVE_FLAG_COMPRESS) { | ||||
|             void *host; | ||||
|             uint8_t ch; | ||||
|  | ||||
|             if (version_id == 3) | ||||
|                 host = qemu_get_ram_ptr(addr); | ||||
|             else | ||||
|                 host = host_from_stream_offset(f, addr, flags); | ||||
|             if (!host) { | ||||
|                 return -EINVAL; | ||||
|             } | ||||
|  | ||||
|             ch = qemu_get_byte(f); | ||||
|             memset(host, ch, TARGET_PAGE_SIZE); | ||||
| #ifndef _WIN32 | ||||
|             if (ch == 0 && | ||||
|                 (!kvm_enabled() || kvm_has_sync_mmu())) { | ||||
|                 qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED); | ||||
|             } | ||||
| #endif | ||||
|         } else if (flags & RAM_SAVE_FLAG_PAGE) { | ||||
|             void *host; | ||||
|  | ||||
|             if (version_id == 3) | ||||
|                 host = qemu_get_ram_ptr(addr); | ||||
|             else | ||||
|                 host = host_from_stream_offset(f, addr, flags); | ||||
|  | ||||
|             qemu_get_buffer(f, host, TARGET_PAGE_SIZE); | ||||
|         } | ||||
|         if (qemu_file_has_error(f)) { | ||||
|             return -EIO; | ||||
|         } | ||||
|     } while (!(flags & RAM_SAVE_FLAG_EOS)); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void qemu_service_io(void) | ||||
| { | ||||
|     qemu_notify_event(); | ||||
| } | ||||
|  | ||||
| #ifdef HAS_AUDIO | ||||
| struct soundhw { | ||||
|     const char *name; | ||||
|     const char *descr; | ||||
|     int enabled; | ||||
|     int isa; | ||||
|     union { | ||||
|         int (*init_isa) (qemu_irq *pic); | ||||
|         int (*init_pci) (PCIBus *bus); | ||||
|     } init; | ||||
| }; | ||||
|  | ||||
| static struct soundhw soundhw[] = { | ||||
| #ifdef HAS_AUDIO_CHOICE | ||||
| #if defined(TARGET_I386) || defined(TARGET_MIPS) | ||||
|     { | ||||
|         "pcspk", | ||||
|         "PC speaker", | ||||
|         0, | ||||
|         1, | ||||
|         { .init_isa = pcspk_audio_init } | ||||
|     }, | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_SB16 | ||||
|     { | ||||
|         "sb16", | ||||
|         "Creative Sound Blaster 16", | ||||
|         0, | ||||
|         1, | ||||
|         { .init_isa = SB16_init } | ||||
|     }, | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_CS4231A | ||||
|     { | ||||
|         "cs4231a", | ||||
|         "CS4231A", | ||||
|         0, | ||||
|         1, | ||||
|         { .init_isa = cs4231a_init } | ||||
|     }, | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_ADLIB | ||||
|     { | ||||
|         "adlib", | ||||
| #ifdef HAS_YMF262 | ||||
|         "Yamaha YMF262 (OPL3)", | ||||
| #else | ||||
|         "Yamaha YM3812 (OPL2)", | ||||
| #endif | ||||
|         0, | ||||
|         1, | ||||
|         { .init_isa = Adlib_init } | ||||
|     }, | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_GUS | ||||
|     { | ||||
|         "gus", | ||||
|         "Gravis Ultrasound GF1", | ||||
|         0, | ||||
|         1, | ||||
|         { .init_isa = GUS_init } | ||||
|     }, | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_AC97 | ||||
|     { | ||||
|         "ac97", | ||||
|         "Intel 82801AA AC97 Audio", | ||||
|         0, | ||||
|         0, | ||||
|         { .init_pci = ac97_init } | ||||
|     }, | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_ES1370 | ||||
|     { | ||||
|         "es1370", | ||||
|         "ENSONIQ AudioPCI ES1370", | ||||
|         0, | ||||
|         0, | ||||
|         { .init_pci = es1370_init } | ||||
|     }, | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_HDA | ||||
|     { | ||||
|         "hda", | ||||
|         "Intel HD Audio", | ||||
|         0, | ||||
|         0, | ||||
|         { .init_pci = intel_hda_and_codec_init } | ||||
|     }, | ||||
| #endif | ||||
|  | ||||
| #endif /* HAS_AUDIO_CHOICE */ | ||||
|  | ||||
|     { NULL, NULL, 0, 0, { NULL } } | ||||
| }; | ||||
|  | ||||
| void select_soundhw(const char *optarg) | ||||
| { | ||||
|     struct soundhw *c; | ||||
|  | ||||
|     if (*optarg == '?') { | ||||
|     show_valid_cards: | ||||
|  | ||||
|         printf("Valid sound card names (comma separated):\n"); | ||||
|         for (c = soundhw; c->name; ++c) { | ||||
|             printf ("%-11s %s\n", c->name, c->descr); | ||||
|         } | ||||
|         printf("\n-soundhw all will enable all of the above\n"); | ||||
|         exit(*optarg != '?'); | ||||
|     } | ||||
|     else { | ||||
|         size_t l; | ||||
|         const char *p; | ||||
|         char *e; | ||||
|         int bad_card = 0; | ||||
|  | ||||
|         if (!strcmp(optarg, "all")) { | ||||
|             for (c = soundhw; c->name; ++c) { | ||||
|                 c->enabled = 1; | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         p = optarg; | ||||
|         while (*p) { | ||||
|             e = strchr(p, ','); | ||||
|             l = !e ? strlen(p) : (size_t) (e - p); | ||||
|  | ||||
|             for (c = soundhw; c->name; ++c) { | ||||
|                 if (!strncmp(c->name, p, l) && !c->name[l]) { | ||||
|                     c->enabled = 1; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (!c->name) { | ||||
|                 if (l > 80) { | ||||
|                     fprintf(stderr, | ||||
|                             "Unknown sound card name (too big to show)\n"); | ||||
|                 } | ||||
|                 else { | ||||
|                     fprintf(stderr, "Unknown sound card name `%.*s'\n", | ||||
|                             (int) l, p); | ||||
|                 } | ||||
|                 bad_card = 1; | ||||
|             } | ||||
|             p += l + (e != NULL); | ||||
|         } | ||||
|  | ||||
|         if (bad_card) { | ||||
|             goto show_valid_cards; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) | ||||
| { | ||||
|     struct soundhw *c; | ||||
|  | ||||
|     for (c = soundhw; c->name; ++c) { | ||||
|         if (c->enabled) { | ||||
|             if (c->isa) { | ||||
|                 if (isa_pic) { | ||||
|                     c->init.init_isa(isa_pic); | ||||
|                 } | ||||
|             } else { | ||||
|                 if (pci_bus) { | ||||
|                     c->init.init_pci(pci_bus); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #else | ||||
| void select_soundhw(const char *optarg) | ||||
| { | ||||
| } | ||||
| void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) | ||||
| { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| int qemu_uuid_parse(const char *str, uint8_t *uuid) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     if (strlen(str) != 36) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     ret = sscanf(str, UUID_FMT, &uuid[0], &uuid[1], &uuid[2], &uuid[3], | ||||
|                  &uuid[4], &uuid[5], &uuid[6], &uuid[7], &uuid[8], &uuid[9], | ||||
|                  &uuid[10], &uuid[11], &uuid[12], &uuid[13], &uuid[14], | ||||
|                  &uuid[15]); | ||||
|  | ||||
|     if (ret != 16) { | ||||
|         return -1; | ||||
|     } | ||||
| #ifdef TARGET_I386 | ||||
|     smbios_add_field(1, offsetof(struct smbios_type_1, uuid), 16, uuid); | ||||
| #endif | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void do_acpitable_option(const char *optarg) | ||||
| { | ||||
| #ifdef TARGET_I386 | ||||
|     if (acpi_table_add(optarg) < 0) { | ||||
|         fprintf(stderr, "Wrong acpi table provided\n"); | ||||
|         exit(1); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void do_smbios_option(const char *optarg) | ||||
| { | ||||
| #ifdef TARGET_I386 | ||||
|     if (smbios_entry_add(optarg) < 0) { | ||||
|         fprintf(stderr, "Wrong smbios provided\n"); | ||||
|         exit(1); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void cpudef_init(void) | ||||
| { | ||||
| #if defined(cpudef_setup) | ||||
|     cpudef_setup(); /* parse cpu definitions in target config file */ | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int audio_available(void) | ||||
| { | ||||
| #ifdef HAS_AUDIO | ||||
|     return 1; | ||||
| #else | ||||
|     return 0; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int kvm_available(void) | ||||
| { | ||||
| #ifdef CONFIG_KVM | ||||
|     return 1; | ||||
| #else | ||||
|     return 0; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| int xen_available(void) | ||||
| { | ||||
| #ifdef CONFIG_XEN | ||||
|     return 1; | ||||
| #else | ||||
|     return 0; | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										34
									
								
								arch_init.h
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								arch_init.h
									
									
									
									
									
								
							| @@ -1,34 +0,0 @@ | ||||
| #ifndef QEMU_ARCH_INIT_H | ||||
| #define QEMU_ARCH_INIT_H | ||||
|  | ||||
| extern const char arch_config_name[]; | ||||
|  | ||||
| enum { | ||||
|     QEMU_ARCH_ALL = -1, | ||||
|     QEMU_ARCH_ALPHA = 1, | ||||
|     QEMU_ARCH_ARM = 2, | ||||
|     QEMU_ARCH_CRIS = 4, | ||||
|     QEMU_ARCH_I386 = 8, | ||||
|     QEMU_ARCH_M68K = 16, | ||||
|     QEMU_ARCH_MICROBLAZE = 32, | ||||
|     QEMU_ARCH_MIPS = 64, | ||||
|     QEMU_ARCH_PPC = 128, | ||||
|     QEMU_ARCH_S390X = 256, | ||||
|     QEMU_ARCH_SH4 = 512, | ||||
|     QEMU_ARCH_SPARC = 1024, | ||||
| }; | ||||
|  | ||||
| extern const uint32_t arch_type; | ||||
|  | ||||
| void select_soundhw(const char *optarg); | ||||
| int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque); | ||||
| int ram_load(QEMUFile *f, void *opaque, int version_id); | ||||
| void do_acpitable_option(const char *optarg); | ||||
| void do_smbios_option(const char *optarg); | ||||
| void cpudef_init(void); | ||||
| int audio_available(void); | ||||
| void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus); | ||||
| int kvm_available(void); | ||||
| int xen_available(void); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										55
									
								
								arm-dis.c
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								arm-dis.c
									
									
									
									
									
								
							| @@ -60,8 +60,10 @@ | ||||
| #define FPU_VFP_EXT_V3	 0 | ||||
| #define FPU_NEON_EXT_V1	 0 | ||||
|  | ||||
| int floatformat_ieee_single_little; | ||||
| /* Assume host uses ieee float.  */ | ||||
| static void floatformat_to_double (unsigned char *data, double *dest) | ||||
| static void floatformat_to_double (int *ignored, unsigned char *data, | ||||
|                                    double *dest) | ||||
| { | ||||
|     union { | ||||
|         uint32_t i; | ||||
| @@ -1587,7 +1589,7 @@ arm_decode_bitfield (const char *ptr, unsigned long insn, | ||||
| } | ||||
|  | ||||
| static void | ||||
| arm_decode_shift (long given, fprintf_function func, void *stream, | ||||
| arm_decode_shift (long given, fprintf_ftype func, void *stream, | ||||
| 		  int print_shift) | ||||
| { | ||||
|   func (stream, "%s", arm_regnames[given & 0xf]); | ||||
| @@ -1633,7 +1635,7 @@ print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given, | ||||
| { | ||||
|   const struct opcode32 *insn; | ||||
|   void *stream = info->stream; | ||||
|   fprintf_function func = info->fprintf_func; | ||||
|   fprintf_ftype func = info->fprintf_func; | ||||
|   unsigned long mask; | ||||
|   unsigned long value; | ||||
|   int cond; | ||||
| @@ -2127,7 +2129,7 @@ static void | ||||
| print_arm_address (bfd_vma pc, struct disassemble_info *info, long given) | ||||
| { | ||||
|   void *stream = info->stream; | ||||
|   fprintf_function func = info->fprintf_func; | ||||
|   fprintf_ftype func = info->fprintf_func; | ||||
|  | ||||
|   if (((given & 0x000f0000) == 0x000f0000) | ||||
|       && ((given & 0x02000000) == 0)) | ||||
| @@ -2222,7 +2224,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb) | ||||
| { | ||||
|   const struct opcode32 *insn; | ||||
|   void *stream = info->stream; | ||||
|   fprintf_function func = info->fprintf_func; | ||||
|   fprintf_ftype func = info->fprintf_func; | ||||
|  | ||||
|   if (thumb) | ||||
|     { | ||||
| @@ -2515,6 +2517,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb) | ||||
| 			  { | ||||
| 			    func (stream, "<illegal constant %.8x:%x:%x>", | ||||
|                                   bits, cmode, op); | ||||
|                             size = 32; | ||||
| 			    break; | ||||
| 			  } | ||||
|                         switch (size) | ||||
| @@ -2540,7 +2543,9 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb) | ||||
|                                 valbytes[2] = (value >> 16) & 0xff; | ||||
|                                 valbytes[3] = (value >> 24) & 0xff; | ||||
|  | ||||
|                                 floatformat_to_double (valbytes, &fvalue); | ||||
|                                 floatformat_to_double | ||||
|                                   (&floatformat_ieee_single_little, valbytes, | ||||
|                                   &fvalue); | ||||
|  | ||||
|                                 func (stream, "#%.7g\t; 0x%.8lx", fvalue, | ||||
|                                       value); | ||||
| @@ -2676,7 +2681,7 @@ print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given) | ||||
| { | ||||
|   const struct opcode32 *insn; | ||||
|   void *stream = info->stream; | ||||
|   fprintf_function func = info->fprintf_func; | ||||
|   fprintf_ftype func = info->fprintf_func; | ||||
|  | ||||
|   if (print_insn_coprocessor (pc, info, given, false)) | ||||
|     return; | ||||
| @@ -3036,7 +3041,7 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given) | ||||
| { | ||||
|   const struct opcode16 *insn; | ||||
|   void *stream = info->stream; | ||||
|   fprintf_function func = info->fprintf_func; | ||||
|   fprintf_ftype func = info->fprintf_func; | ||||
|  | ||||
|   for (insn = thumb_opcodes; insn->assembler; insn++) | ||||
|     if ((given & insn->mask) == insn->value) | ||||
| @@ -3148,14 +3153,14 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given) | ||||
| 		      if (started) | ||||
| 			func (stream, ", "); | ||||
| 		      started = 1; | ||||
| 		      func (stream, "%s", arm_regnames[14] /* "lr" */); | ||||
| 		      func (stream, arm_regnames[14] /* "lr" */); | ||||
| 		    } | ||||
|  | ||||
| 		  if (domaskpc) | ||||
| 		    { | ||||
| 		      if (started) | ||||
| 			func (stream, ", "); | ||||
| 		      func (stream, "%s", arm_regnames[15] /* "pc" */); | ||||
| 		      func (stream, arm_regnames[15] /* "pc" */); | ||||
| 		    } | ||||
|  | ||||
| 		  func (stream, "}"); | ||||
| @@ -3312,7 +3317,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) | ||||
| { | ||||
|   const struct opcode32 *insn; | ||||
|   void *stream = info->stream; | ||||
|   fprintf_function func = info->fprintf_func; | ||||
|   fprintf_ftype func = info->fprintf_func; | ||||
|  | ||||
|   if (print_insn_coprocessor (pc, info, given, true)) | ||||
|     return; | ||||
| @@ -3698,7 +3703,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) | ||||
| 		  } | ||||
| 		else | ||||
| 		  { | ||||
| 		    func (stream, "%s", psr_name (given & 0xff)); | ||||
| 		    func (stream, psr_name (given & 0xff)); | ||||
| 		  } | ||||
| 		break; | ||||
|  | ||||
| @@ -3706,7 +3711,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given) | ||||
| 		if ((given & 0xff) == 0) | ||||
| 		  func (stream, "%cPSR", (given & 0x100000) ? 'S' : 'C'); | ||||
| 		else | ||||
| 		  func (stream, "%s", psr_name (given & 0xff)); | ||||
| 		  func (stream, psr_name (given & 0xff)); | ||||
| 		break; | ||||
|  | ||||
| 	      case '0': case '1': case '2': case '3': case '4': | ||||
| @@ -4101,30 +4106,6 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info) | ||||
|        addresses, since the addend is not currently pc-relative.  */ | ||||
|     pc = 0; | ||||
|  | ||||
|   /* We include the hexdump of the instruction. The format here | ||||
|      matches that used by objdump and the ARM ARM (in particular, | ||||
|      32 bit Thumb instructions are displayed as pairs of halfwords, | ||||
|      not as a single word.)  */ | ||||
|   if (is_thumb) | ||||
|     { | ||||
|       if (size == 2) | ||||
| 	{ | ||||
| 	  info->fprintf_func(info->stream, "%04lx       ", | ||||
| 			     ((unsigned long)given) & 0xffff); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  info->fprintf_func(info->stream, "%04lx %04lx  ", | ||||
| 			     (((unsigned long)given) >> 16) & 0xffff, | ||||
| 			     ((unsigned long)given) & 0xffff); | ||||
| 	} | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       info->fprintf_func(info->stream, "%08lx      ", | ||||
| 			 ((unsigned long)given) & 0xffffffff); | ||||
|     } | ||||
|  | ||||
|   printer (pc, info, given); | ||||
|  | ||||
|   if (is_thumb) | ||||
|   | ||||
							
								
								
									
										80
									
								
								arm-semi.c
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								arm-semi.c
									
									
									
									
									
								
							| @@ -373,64 +373,45 @@ uint32_t do_arm_semihosting(CPUState *env) | ||||
| #ifdef CONFIG_USER_ONLY | ||||
|         /* Build a commandline from the original argv.  */ | ||||
|         { | ||||
|             char *arm_cmdline_buffer; | ||||
|             const char *host_cmdline_buffer; | ||||
|             char **arg = ts->info->host_argv; | ||||
|             int len = ARG(1); | ||||
|             /* lock the buffer on the ARM side */ | ||||
|             char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0); | ||||
|  | ||||
|             unsigned int i; | ||||
|             unsigned int arm_cmdline_len = ARG(1); | ||||
|             unsigned int host_cmdline_len = | ||||
|                 ts->info->arg_end-ts->info->arg_start; | ||||
|             if (!cmdline_buffer) | ||||
|                 /* FIXME - should this error code be -TARGET_EFAULT ? */ | ||||
|                 return (uint32_t)-1; | ||||
|  | ||||
|             if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) { | ||||
|                 return -1; /* not enough space to store command line */ | ||||
|             } | ||||
|             s = cmdline_buffer; | ||||
|             while (*arg && len > 2) { | ||||
|                 int n = strlen(*arg); | ||||
|  | ||||
|             if (!host_cmdline_len) { | ||||
|                 /* We special-case the "empty command line" case (argc==0). | ||||
|                    Just provide the terminating 0. */ | ||||
|                 arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0); | ||||
|                 arm_cmdline_buffer[0] = 0; | ||||
|                 unlock_user(arm_cmdline_buffer, ARG(0), 1); | ||||
|  | ||||
|                 /* Adjust the commandline length argument. */ | ||||
|                 SET_ARG(1, 0); | ||||
|                 return 0; | ||||
|             } | ||||
|  | ||||
|             /* lock the buffers on the ARM side */ | ||||
|             arm_cmdline_buffer = | ||||
|                 lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0); | ||||
|             host_cmdline_buffer = | ||||
|                 lock_user(VERIFY_READ, ts->info->arg_start, | ||||
|                                        host_cmdline_len, 1); | ||||
|  | ||||
|             if (arm_cmdline_buffer && host_cmdline_buffer) | ||||
|             { | ||||
|                 /* the last argument is zero-terminated; | ||||
|                    no need for additional termination */ | ||||
|                 memcpy(arm_cmdline_buffer, host_cmdline_buffer, | ||||
|                        host_cmdline_len); | ||||
|  | ||||
|                 /* separate arguments by white spaces */ | ||||
|                 for (i = 0; i < host_cmdline_len-1; i++) { | ||||
|                     if (arm_cmdline_buffer[i] == 0) { | ||||
|                         arm_cmdline_buffer[i] = ' '; | ||||
|                     } | ||||
|                 if (s != cmdline_buffer) { | ||||
|                     *(s++) = ' '; | ||||
|                     len--; | ||||
|                 } | ||||
|  | ||||
|                 /* Adjust the commandline length argument. */ | ||||
|                 SET_ARG(1, host_cmdline_len-1); | ||||
|                 if (n >= len) | ||||
|                     n = len - 1; | ||||
|                 memcpy(s, *arg, n); | ||||
|                 s += n; | ||||
|                 len -= n; | ||||
|                 arg++; | ||||
|             } | ||||
|             /* Null terminate the string.  */ | ||||
|             *s = 0; | ||||
|             len = s - cmdline_buffer; | ||||
|  | ||||
|             /* Unlock the buffers on the ARM side.  */ | ||||
|             unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len); | ||||
|             unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0); | ||||
|             /* Unlock the buffer on the ARM side.  */ | ||||
|             unlock_user(cmdline_buffer, ARG(0), len); | ||||
|  | ||||
|             /* Return success if we could return a commandline.  */ | ||||
|             return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1; | ||||
|             /* Adjust the commandline length argument.  */ | ||||
|             SET_ARG(1, len); | ||||
|  | ||||
|             /* Return success if commandline fit into buffer.  */ | ||||
|             return *arg ? -1 : 0; | ||||
|         } | ||||
| #else | ||||
|         return -1; | ||||
|       return -1; | ||||
| #endif | ||||
|     case SYS_HEAPINFO: | ||||
|         { | ||||
| @@ -478,7 +459,6 @@ uint32_t do_arm_semihosting(CPUState *env) | ||||
|             return 0; | ||||
|         } | ||||
|     case SYS_EXIT: | ||||
|         gdb_exit(env, 0); | ||||
|         exit(0); | ||||
|     default: | ||||
|         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); | ||||
|   | ||||
| @@ -318,7 +318,7 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len) | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) | ||||
| static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt) | ||||
| { | ||||
|     switch (fmt) { | ||||
|     case AUD_FMT_S8: | ||||
| @@ -328,36 +328,16 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) | ||||
|         return SND_PCM_FORMAT_U8; | ||||
|  | ||||
|     case AUD_FMT_S16: | ||||
|         if (endianness) { | ||||
|             return SND_PCM_FORMAT_S16_BE; | ||||
|         } | ||||
|         else { | ||||
|             return SND_PCM_FORMAT_S16_LE; | ||||
|         } | ||||
|         return SND_PCM_FORMAT_S16_LE; | ||||
|  | ||||
|     case AUD_FMT_U16: | ||||
|         if (endianness) { | ||||
|             return SND_PCM_FORMAT_U16_BE; | ||||
|         } | ||||
|         else { | ||||
|             return SND_PCM_FORMAT_U16_LE; | ||||
|         } | ||||
|         return SND_PCM_FORMAT_U16_LE; | ||||
|  | ||||
|     case AUD_FMT_S32: | ||||
|         if (endianness) { | ||||
|             return SND_PCM_FORMAT_S32_BE; | ||||
|         } | ||||
|         else { | ||||
|             return SND_PCM_FORMAT_S32_LE; | ||||
|         } | ||||
|         return SND_PCM_FORMAT_S32_LE; | ||||
|  | ||||
|     case AUD_FMT_U32: | ||||
|         if (endianness) { | ||||
|             return SND_PCM_FORMAT_U32_BE; | ||||
|         } | ||||
|         else { | ||||
|             return SND_PCM_FORMAT_U32_LE; | ||||
|         } | ||||
|         return SND_PCM_FORMAT_U32_LE; | ||||
|  | ||||
|     default: | ||||
|         dolog ("Internal logic error: Bad audio format %d\n", fmt); | ||||
| @@ -431,11 +411,10 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, | ||||
| } | ||||
|  | ||||
| static void alsa_dump_info (struct alsa_params_req *req, | ||||
|                             struct alsa_params_obt *obt, | ||||
|                             snd_pcm_format_t obtfmt) | ||||
|                             struct alsa_params_obt *obt) | ||||
| { | ||||
|     dolog ("parameter | requested value | obtained value\n"); | ||||
|     dolog ("format    |      %10d |     %10d\n", req->fmt, obtfmt); | ||||
|     dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt); | ||||
|     dolog ("channels  |      %10d |     %10d\n", | ||||
|            req->nchannels, obt->nchannels); | ||||
|     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq); | ||||
| @@ -687,15 +666,15 @@ static int alsa_open (int in, struct alsa_params_req *req, | ||||
|     *handlep = handle; | ||||
|  | ||||
|     if (conf.verbose && | ||||
|         (obtfmt != req->fmt || | ||||
|         (obt->fmt != req->fmt || | ||||
|          obt->nchannels != req->nchannels || | ||||
|          obt->freq != req->freq)) { | ||||
|         dolog ("Audio parameters for %s\n", typ); | ||||
|         alsa_dump_info (req, obt, obtfmt); | ||||
|         alsa_dump_info (req, obt); | ||||
|     } | ||||
|  | ||||
| #ifdef DEBUG | ||||
|     alsa_dump_info (req, obt, obtfmt); | ||||
|     alsa_dump_info (req, obt); | ||||
| #endif | ||||
|     return 0; | ||||
|  | ||||
| @@ -829,7 +808,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|     snd_pcm_t *handle; | ||||
|     struct audsettings obt_as; | ||||
|  | ||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); | ||||
|     req.fmt = aud_to_alsafmt (as->fmt); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.period_size = conf.period_size_out; | ||||
| @@ -863,15 +842,11 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #define VOICE_CTL_PAUSE 0 | ||||
| #define VOICE_CTL_PREPARE 1 | ||||
| #define VOICE_CTL_START 2 | ||||
|  | ||||
| static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) | ||||
| static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) | ||||
| { | ||||
|     int err; | ||||
|  | ||||
|     if (ctl == VOICE_CTL_PAUSE) { | ||||
|     if (pause) { | ||||
|         err = snd_pcm_drop (handle); | ||||
|         if (err < 0) { | ||||
|             alsa_logerr (err, "Could not stop %s\n", typ); | ||||
| @@ -884,13 +859,6 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) | ||||
|             alsa_logerr (err, "Could not prepare handle for %s\n", typ); | ||||
|             return -1; | ||||
|         } | ||||
|         if (ctl == VOICE_CTL_START) { | ||||
|             err = snd_pcm_start(handle); | ||||
|             if (err < 0) { | ||||
|                 alsa_logerr (err, "Could not start handle for %s\n", typ); | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -915,16 +883,12 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) | ||||
|                 poll_mode = 0; | ||||
|             } | ||||
|             hw->poll_mode = poll_mode; | ||||
|             return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE); | ||||
|             return alsa_voice_ctl (alsa->handle, "playback", 0); | ||||
|         } | ||||
|  | ||||
|     case VOICE_DISABLE: | ||||
|         ldebug ("disabling voice\n"); | ||||
|         if (hw->poll_mode) { | ||||
|             hw->poll_mode = 0; | ||||
|             alsa_fini_poll (&alsa->pollhlp); | ||||
|         } | ||||
|         return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE); | ||||
|         return alsa_voice_ctl (alsa->handle, "playback", 1); | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| @@ -938,7 +902,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
|     snd_pcm_t *handle; | ||||
|     struct audsettings obt_as; | ||||
|  | ||||
|     req.fmt = aud_to_alsafmt (as->fmt, as->endianness); | ||||
|     req.fmt = aud_to_alsafmt (as->fmt); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.period_size = conf.period_size_in; | ||||
| @@ -1097,7 +1061,7 @@ static int alsa_run_in (HWVoiceIn *hw) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             hw->conv (dst, src, nread); | ||||
|             hw->conv (dst, src, nread, &nominal_volume); | ||||
|  | ||||
|             src = advance (src, nread << hwshift); | ||||
|             dst += nread; | ||||
| @@ -1137,7 +1101,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|             } | ||||
|             hw->poll_mode = poll_mode; | ||||
|  | ||||
|             return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START); | ||||
|             return alsa_voice_ctl (alsa->handle, "capture", 0); | ||||
|         } | ||||
|  | ||||
|     case VOICE_DISABLE: | ||||
| @@ -1146,7 +1110,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|             hw->poll_mode = 0; | ||||
|             alsa_fini_poll (&alsa->pollhlp); | ||||
|         } | ||||
|         return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE); | ||||
|         return alsa_voice_ctl (alsa->handle, "capture", 1); | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
|   | ||||
| @@ -44,9 +44,6 @@ | ||||
|     that we generate the list. | ||||
| */ | ||||
| static struct audio_driver *drvtab[] = { | ||||
| #ifdef CONFIG_SPICE | ||||
|     &spice_audio_driver, | ||||
| #endif | ||||
|     CONFIG_AUDIO_DRIVERS | ||||
|     &no_audio_driver, | ||||
|     &wav_audio_driver | ||||
| @@ -104,7 +101,7 @@ static struct { | ||||
|  | ||||
| static AudioState glob_audio_state; | ||||
|  | ||||
| const struct mixeng_volume nominal_volume = { | ||||
| struct mixeng_volume nominal_volume = { | ||||
|     .mute = 0, | ||||
| #ifdef FLOAT_MIXENG | ||||
|     .r = 1.0, | ||||
| @@ -118,9 +115,6 @@ const struct mixeng_volume nominal_volume = { | ||||
| #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED | ||||
| #error No its not | ||||
| #else | ||||
| static void audio_print_options (const char *prefix, | ||||
|                                  struct audio_option *opt); | ||||
|  | ||||
| int audio_bug (const char *funcname, int cond) | ||||
| { | ||||
|     if (cond) { | ||||
| @@ -128,16 +122,10 @@ int audio_bug (const char *funcname, int cond) | ||||
|  | ||||
|         AUD_log (NULL, "A bug was just triggered in %s\n", funcname); | ||||
|         if (!shown) { | ||||
|             struct audio_driver *d; | ||||
|  | ||||
|             shown = 1; | ||||
|             AUD_log (NULL, "Save all your work and restart without audio\n"); | ||||
|             AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); | ||||
|             AUD_log (NULL, "I am sorry\n"); | ||||
|             d = glob_audio_state.drv; | ||||
|             if (d) { | ||||
|                 audio_print_options (d->name, d->options); | ||||
|             } | ||||
|         } | ||||
|         AUD_log (NULL, "Context:\n"); | ||||
|  | ||||
| @@ -333,10 +321,10 @@ void AUD_vlog (const char *cap, const char *fmt, va_list ap) | ||||
| { | ||||
|     if (conf.log_to_monitor) { | ||||
|         if (cap) { | ||||
|             monitor_printf(default_mon, "%s: ", cap); | ||||
|             monitor_printf(cur_mon, "%s: ", cap); | ||||
|         } | ||||
|  | ||||
|         monitor_vprintf(default_mon, fmt, ap); | ||||
|         monitor_vprintf(cur_mon, fmt, ap); | ||||
|     } | ||||
|     else { | ||||
|         if (cap) { | ||||
| @@ -702,11 +690,13 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) | ||||
| /* | ||||
|  * Capture | ||||
|  */ | ||||
| static void noop_conv (struct st_sample *dst, const void *src, int samples) | ||||
| static void noop_conv (struct st_sample *dst, const void *src, | ||||
|                        int samples, struct mixeng_volume *vol) | ||||
| { | ||||
|     (void) src; | ||||
|     (void) dst; | ||||
|     (void) samples; | ||||
|     (void) vol; | ||||
| } | ||||
|  | ||||
| static CaptureVoiceOut *audio_pcm_capture_find_specific ( | ||||
| @@ -954,8 +944,6 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) | ||||
|         total += isamp; | ||||
|     } | ||||
|  | ||||
|     mixeng_volume (sw->buf, ret, &sw->vol); | ||||
|  | ||||
|     sw->clip (buf, sw->buf, ret); | ||||
|     sw->total_hw_samples_acquired += total; | ||||
|     return ret << sw->info.shift; | ||||
| @@ -1037,8 +1025,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) | ||||
|     swlim = ((int64_t) dead << 32) / sw->ratio; | ||||
|     swlim = audio_MIN (swlim, samples); | ||||
|     if (swlim) { | ||||
|         sw->conv (sw->buf, buf, swlim); | ||||
|         mixeng_volume (sw->buf, swlim, &sw->vol); | ||||
|         sw->conv (sw->buf, buf, swlim, &sw->vol); | ||||
|     } | ||||
|  | ||||
|     while (swlim) { | ||||
| @@ -1097,6 +1084,15 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) | ||||
| /* | ||||
|  * Timer | ||||
|  */ | ||||
| static void audio_timer (void *opaque) | ||||
| { | ||||
|     AudioState *s = opaque; | ||||
|  | ||||
|     audio_run ("timer"); | ||||
|     qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int audio_is_timer_needed (void) | ||||
| { | ||||
|     HWVoiceIn *hwi = NULL; | ||||
| @@ -1111,8 +1107,10 @@ static int audio_is_timer_needed (void) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void audio_reset_timer (AudioState *s) | ||||
| static void audio_reset_timer (void) | ||||
| { | ||||
|     AudioState *s = &glob_audio_state; | ||||
|  | ||||
|     if (audio_is_timer_needed ()) { | ||||
|         qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); | ||||
|     } | ||||
| @@ -1121,12 +1119,6 @@ static void audio_reset_timer (AudioState *s) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void audio_timer (void *opaque) | ||||
| { | ||||
|     audio_run ("timer"); | ||||
|     audio_reset_timer (opaque); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Public API | ||||
|  */ | ||||
| @@ -1191,7 +1183,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | ||||
|                 hw->enabled = 1; | ||||
|                 if (s->vm_running) { | ||||
|                     hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out); | ||||
|                     audio_reset_timer (s); | ||||
|                     audio_reset_timer (); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -1236,7 +1228,6 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) | ||||
|                 hw->enabled = 1; | ||||
|                 if (s->vm_running) { | ||||
|                     hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in); | ||||
|                     audio_reset_timer (s); | ||||
|                 } | ||||
|             } | ||||
|             sw->total_hw_samples_acquired = hw->total_samples_captured; | ||||
| @@ -1758,7 +1749,7 @@ static void audio_vm_change_state_handler (void *opaque, int running, | ||||
|     while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { | ||||
|         hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); | ||||
|     } | ||||
|     audio_reset_timer (s); | ||||
|     audio_reset_timer (); | ||||
| } | ||||
|  | ||||
| static void audio_atexit (void) | ||||
| @@ -1901,7 +1892,7 @@ static void audio_init (void) | ||||
|     } | ||||
|  | ||||
|     QLIST_INIT (&s->card_head); | ||||
|     vmstate_register (NULL, 0, &vmstate_audio, s); | ||||
|     vmstate_register (0, &vmstate_audio, s); | ||||
| } | ||||
|  | ||||
| void AUD_register_card (const char *name, QEMUSoundCard *card) | ||||
|   | ||||
| @@ -86,8 +86,12 @@ typedef struct QEMUAudioTimeStamp { | ||||
|     uint64_t old_ts; | ||||
| } QEMUAudioTimeStamp; | ||||
|  | ||||
| void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); | ||||
| void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); | ||||
| void AUD_vlog (const char *cap, const char *fmt, va_list ap); | ||||
| void AUD_log (const char *cap, const char *fmt, ...) | ||||
| #ifdef __GNUC__ | ||||
|     __attribute__ ((__format__ (__printf__, 2, 3))) | ||||
| #endif | ||||
|     ; | ||||
|  | ||||
| void AUD_help (void); | ||||
| void AUD_register_card (const char *name, QEMUSoundCard *card); | ||||
|   | ||||
| @@ -209,9 +209,8 @@ extern struct audio_driver coreaudio_audio_driver; | ||||
| extern struct audio_driver dsound_audio_driver; | ||||
| extern struct audio_driver esd_audio_driver; | ||||
| extern struct audio_driver pa_audio_driver; | ||||
| extern struct audio_driver spice_audio_driver; | ||||
| extern struct audio_driver winwave_audio_driver; | ||||
| extern const struct mixeng_volume nominal_volume; | ||||
| extern struct mixeng_volume nominal_volume; | ||||
|  | ||||
| void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); | ||||
| void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); | ||||
| @@ -237,6 +236,14 @@ static inline int audio_ring_dist (int dst, int src, int len) | ||||
|     return (dst >= src) ? (dst - src) : (len - src + dst); | ||||
| } | ||||
|  | ||||
| #if defined __GNUC__ | ||||
| #define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2))) | ||||
| #define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m))) | ||||
| #else | ||||
| #define GCC_ATTR /**/ | ||||
| #define GCC_FMT_ATTR(n, m) | ||||
| #endif | ||||
|  | ||||
| static void GCC_ATTR dolog (const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|   | ||||
| @@ -6,10 +6,7 @@ | ||||
| #include "audio_int.h" | ||||
| #include "audio_pt_int.h" | ||||
|  | ||||
| #include <signal.h> | ||||
|  | ||||
| static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err, | ||||
|                                        const char *fmt, ...) | ||||
| static void logerr (struct audio_pt *pt, int err, const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
| @@ -26,16 +23,9 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *), | ||||
| { | ||||
|     int err, err2; | ||||
|     const char *efunc; | ||||
|     sigset_t set, old_set; | ||||
|  | ||||
|     p->drv = drv; | ||||
|  | ||||
|     err = sigfillset (&set); | ||||
|     if (err) { | ||||
|         logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     err = pthread_mutex_init (&p->mutex, NULL); | ||||
|     if (err) { | ||||
|         efunc = "pthread_mutex_init"; | ||||
| @@ -48,23 +38,7 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *), | ||||
|         goto err1; | ||||
|     } | ||||
|  | ||||
|     err = pthread_sigmask (SIG_BLOCK, &set, &old_set); | ||||
|     if (err) { | ||||
|         efunc = "pthread_sigmask"; | ||||
|         goto err2; | ||||
|     } | ||||
|  | ||||
|     err = pthread_create (&p->thread, NULL, func, opaque); | ||||
|  | ||||
|     err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||
|     if (err2) { | ||||
|         logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed", | ||||
|                 cap, AUDIO_FUNC); | ||||
|         /* We have failed to restore original signal mask, all bets are off, | ||||
|            so terminate the process */ | ||||
|         exit (EXIT_FAILURE); | ||||
|     } | ||||
|  | ||||
|     if (err) { | ||||
|         efunc = "pthread_create"; | ||||
|         goto err2; | ||||
|   | ||||
| @@ -108,7 +108,11 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) | ||||
| { | ||||
|     int samples; | ||||
|  | ||||
| #ifdef DAC | ||||
|     samples = sw->hw->samples; | ||||
| #else | ||||
|     samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; | ||||
| #endif | ||||
|  | ||||
|     sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample)); | ||||
|     if (!sw->buf) { | ||||
| @@ -537,7 +541,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) | ||||
|  | ||||
|     cur_ts = sw->hw->ts_helper; | ||||
|     old_ts = ts->old_ts; | ||||
|     /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */ | ||||
|     /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ | ||||
|  | ||||
|     if (cur_ts >= old_ts) { | ||||
|         delta = cur_ts - old_ts; | ||||
|   | ||||
| @@ -831,11 +831,11 @@ static int dsound_run_in (HWVoiceIn *hw) | ||||
|     decr = len1 + len2; | ||||
|  | ||||
|     if (p1 && len1) { | ||||
|         hw->conv (hw->conv_buf + hw->wpos, p1, len1); | ||||
|         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); | ||||
|     } | ||||
|  | ||||
|     if (p2 && len2) { | ||||
|         hw->conv (hw->conv_buf, p2, len2); | ||||
|         hw->conv (hw->conv_buf, p2, len2, &nominal_volume); | ||||
|     } | ||||
|  | ||||
|     dsound_unlock_in (dscb, p1, p2, blen1, blen2); | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include <esd.h> | ||||
| #include "qemu-common.h" | ||||
| #include "audio.h" | ||||
| #include <signal.h> | ||||
|  | ||||
| #define AUDIO_CAP "esd" | ||||
| #include "audio_int.h" | ||||
| @@ -189,6 +190,10 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|     ESDVoiceOut *esd = (ESDVoiceOut *) hw; | ||||
|     struct audsettings obt_as = *as; | ||||
|     int esdfmt = ESD_STREAM | ESD_PLAY; | ||||
|     int err; | ||||
|     sigset_t set, old_set; | ||||
|  | ||||
|     sigfillset (&set); | ||||
|  | ||||
|     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; | ||||
|     switch (as->fmt) { | ||||
| @@ -226,25 +231,43 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); | ||||
|     if (esd->fd < 0) { | ||||
|         qesd_logerr (errno, "esd_play_stream failed\n"); | ||||
|     esd->fd = -1; | ||||
|     err = pthread_sigmask (SIG_BLOCK, &set, &old_set); | ||||
|     if (err) { | ||||
|         qesd_logerr (err, "pthread_sigmask failed\n"); | ||||
|         goto fail1; | ||||
|     } | ||||
|  | ||||
|     if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { | ||||
|     esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); | ||||
|     if (esd->fd < 0) { | ||||
|         qesd_logerr (errno, "esd_play_stream failed\n"); | ||||
|         goto fail2; | ||||
|     } | ||||
|  | ||||
|     if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { | ||||
|         goto fail3; | ||||
|     } | ||||
|  | ||||
|     err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||
|     if (err) { | ||||
|         qesd_logerr (err, "pthread_sigmask(restore) failed\n"); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
|  fail2: | ||||
|  fail3: | ||||
|     if (close (esd->fd)) { | ||||
|         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", | ||||
|                      AUDIO_FUNC, esd->fd); | ||||
|     } | ||||
|     esd->fd = -1; | ||||
|  | ||||
|  fail2: | ||||
|     err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||
|     if (err) { | ||||
|         qesd_logerr (err, "pthread_sigmask(restore) failed\n"); | ||||
|     } | ||||
|  | ||||
|  fail1: | ||||
|     qemu_free (esd->pcm_buf); | ||||
|     esd->pcm_buf = NULL; | ||||
| @@ -346,7 +369,8 @@ static void *qesd_thread_in (void *arg) | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift); | ||||
|             hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift, | ||||
|                       &nominal_volume); | ||||
|             wpos = (wpos + chunk) % hw->samples; | ||||
|             to_grab -= chunk; | ||||
|         } | ||||
| @@ -399,6 +423,10 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
|     ESDVoiceIn *esd = (ESDVoiceIn *) hw; | ||||
|     struct audsettings obt_as = *as; | ||||
|     int esdfmt = ESD_STREAM | ESD_RECORD; | ||||
|     int err; | ||||
|     sigset_t set, old_set; | ||||
|  | ||||
|     sigfillset (&set); | ||||
|  | ||||
|     esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; | ||||
|     switch (as->fmt) { | ||||
| @@ -433,25 +461,44 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); | ||||
|     if (esd->fd < 0) { | ||||
|         qesd_logerr (errno, "esd_record_stream failed\n"); | ||||
|     esd->fd = -1; | ||||
|  | ||||
|     err = pthread_sigmask (SIG_BLOCK, &set, &old_set); | ||||
|     if (err) { | ||||
|         qesd_logerr (err, "pthread_sigmask failed\n"); | ||||
|         goto fail1; | ||||
|     } | ||||
|  | ||||
|     if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { | ||||
|     esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); | ||||
|     if (esd->fd < 0) { | ||||
|         qesd_logerr (errno, "esd_record_stream failed\n"); | ||||
|         goto fail2; | ||||
|     } | ||||
|  | ||||
|     if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { | ||||
|         goto fail3; | ||||
|     } | ||||
|  | ||||
|     err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||
|     if (err) { | ||||
|         qesd_logerr (err, "pthread_sigmask(restore) failed\n"); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
|  fail2: | ||||
|  fail3: | ||||
|     if (close (esd->fd)) { | ||||
|         qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", | ||||
|                      AUDIO_FUNC, esd->fd); | ||||
|     } | ||||
|     esd->fd = -1; | ||||
|  | ||||
|  fail2: | ||||
|     err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); | ||||
|     if (err) { | ||||
|         qesd_logerr (err, "pthread_sigmask(restore) failed\n"); | ||||
|     } | ||||
|  | ||||
|  fail1: | ||||
|     qemu_free (esd->pcm_buf); | ||||
|     esd->pcm_buf = NULL; | ||||
|   | ||||
| @@ -488,10 +488,10 @@ static int fmod_run_in (HWVoiceIn *hw) | ||||
|     decr = len1 + len2; | ||||
|  | ||||
|     if (p1 && blen1) { | ||||
|         hw->conv (hw->conv_buf + hw->wpos, p1, len1); | ||||
|         hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); | ||||
|     } | ||||
|     if (p2 && len2) { | ||||
|         hw->conv (hw->conv_buf, p2, len2); | ||||
|         hw->conv (hw->conv_buf, p2, len2, &nominal_volume); | ||||
|     } | ||||
|  | ||||
|     fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); | ||||
|   | ||||
| @@ -123,7 +123,7 @@ | ||||
| #undef IN_T | ||||
| #undef SHIFT | ||||
|  | ||||
| /* Unsigned 32 bit */ | ||||
| /* Unsigned 16 bit */ | ||||
| #define IN_T uint32_t | ||||
| #define IN_MIN 0 | ||||
| #define IN_MAX UINT32_MAX | ||||
| @@ -333,28 +333,3 @@ void mixeng_clear (struct st_sample *buf, int len) | ||||
| { | ||||
|     memset (buf, 0, len * sizeof (struct st_sample)); | ||||
| } | ||||
|  | ||||
| void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) | ||||
| { | ||||
| #ifdef CONFIG_MIXEMU | ||||
|     if (vol->mute) { | ||||
|         mixeng_clear (buf, len); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     while (len--) { | ||||
| #ifdef FLOAT_MIXENG | ||||
|         buf->l = buf->l * vol->l; | ||||
|         buf->r = buf->r * vol->r; | ||||
| #else | ||||
|         buf->l = (buf->l * vol->l) >> 32; | ||||
|         buf->r = (buf->r * vol->r) >> 32; | ||||
| #endif | ||||
|         buf += 1; | ||||
|     } | ||||
| #else | ||||
|     (void) buf; | ||||
|     (void) len; | ||||
|     (void) vol; | ||||
| #endif | ||||
| } | ||||
|   | ||||
| @@ -33,7 +33,8 @@ struct mixeng_volume { int mute; int64_t r; int64_t l; }; | ||||
| struct st_sample { int64_t l; int64_t r; }; | ||||
| #endif | ||||
|  | ||||
| typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); | ||||
| typedef void (t_sample) (struct st_sample *dst, const void *src, | ||||
|                          int samples, struct mixeng_volume *vol); | ||||
| typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); | ||||
|  | ||||
| extern t_sample *mixeng_conv[2][2][2][3]; | ||||
| @@ -46,6 +47,5 @@ void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *o | ||||
|                        int *isamp, int *osamp); | ||||
| void st_rate_stop (void *opaque); | ||||
| void mixeng_clear (struct st_sample *buf, int len); | ||||
| void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); | ||||
|  | ||||
| #endif  /* mixeng.h */ | ||||
|   | ||||
| @@ -31,6 +31,16 @@ | ||||
| #define HALF (IN_MAX >> 1) | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_MIXEMU | ||||
| #ifdef FLOAT_MIXENG | ||||
| #define VOL(a, b) ((a) * (b)) | ||||
| #else | ||||
| #define VOL(a, b) ((a) * (b)) >> 32 | ||||
| #endif | ||||
| #else | ||||
| #define VOL(a, b) a | ||||
| #endif | ||||
|  | ||||
| #define ET glue (ENDIAN_CONVERSION, glue (_, IN_T)) | ||||
|  | ||||
| #ifdef FLOAT_MIXENG | ||||
| @@ -99,26 +109,40 @@ static inline IN_T glue (clip_, ET) (int64_t v) | ||||
| #endif | ||||
|  | ||||
| static void glue (glue (conv_, ET), _to_stereo) | ||||
|     (struct st_sample *dst, const void *src, int samples) | ||||
|     (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol) | ||||
| { | ||||
|     struct st_sample *out = dst; | ||||
|     IN_T *in = (IN_T *) src; | ||||
|  | ||||
| #ifdef CONFIG_MIXEMU | ||||
|     if (vol->mute) { | ||||
|         mixeng_clear (dst, samples); | ||||
|         return; | ||||
|     } | ||||
| #else | ||||
|     (void) vol; | ||||
| #endif | ||||
|     while (samples--) { | ||||
|         out->l = glue (conv_, ET) (*in++); | ||||
|         out->r = glue (conv_, ET) (*in++); | ||||
|         out->l = VOL (glue (conv_, ET) (*in++), vol->l); | ||||
|         out->r = VOL (glue (conv_, ET) (*in++), vol->r); | ||||
|         out += 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void glue (glue (conv_, ET), _to_mono) | ||||
|     (struct st_sample *dst, const void *src, int samples) | ||||
|     (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol) | ||||
| { | ||||
|     struct st_sample *out = dst; | ||||
|     IN_T *in = (IN_T *) src; | ||||
|  | ||||
| #ifdef CONFIG_MIXEMU | ||||
|     if (vol->mute) { | ||||
|         mixeng_clear (dst, samples); | ||||
|         return; | ||||
|     } | ||||
| #else | ||||
|     (void) vol; | ||||
| #endif | ||||
|     while (samples--) { | ||||
|         out->l = glue (conv_, ET) (in[0]); | ||||
|         out->l = VOL (glue (conv_, ET) (in[0]), vol->l); | ||||
|         out->r = out->l; | ||||
|         out += 1; | ||||
|         in += 1; | ||||
| @@ -150,3 +174,4 @@ static void glue (glue (clip_, ET), _from_mono) | ||||
|  | ||||
| #undef ET | ||||
| #undef HALF | ||||
| #undef VOL | ||||
|   | ||||
| @@ -117,14 +117,11 @@ static int no_run_in (HWVoiceIn *hw) | ||||
|  | ||||
| static int no_read (SWVoiceIn *sw, void *buf, int size) | ||||
| { | ||||
|     /* use custom code here instead of audio_pcm_sw_read() to avoid | ||||
|      * useless resampling/mixing */ | ||||
|     int samples = size >> sw->info.shift; | ||||
|     int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; | ||||
|     int to_clear = audio_MIN (samples, total); | ||||
|     sw->total_hw_samples_acquired += total; | ||||
|     audio_pcm_info_clear_buf (&sw->info, buf, to_clear); | ||||
|     return to_clear << sw->info.shift; | ||||
|     return to_clear; | ||||
| } | ||||
|  | ||||
| static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) | ||||
|   | ||||
| @@ -161,7 +161,7 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len) | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static int aud_to_ossfmt (audfmt_e fmt, int endianness) | ||||
| static int aud_to_ossfmt (audfmt_e fmt) | ||||
| { | ||||
|     switch (fmt) { | ||||
|     case AUD_FMT_S8: | ||||
| @@ -171,20 +171,10 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness) | ||||
|         return AFMT_U8; | ||||
|  | ||||
|     case AUD_FMT_S16: | ||||
|         if (endianness) { | ||||
|             return AFMT_S16_BE; | ||||
|         } | ||||
|         else { | ||||
|             return AFMT_S16_LE; | ||||
|         } | ||||
|         return AFMT_S16_LE; | ||||
|  | ||||
|     case AUD_FMT_U16: | ||||
|         if (endianness) { | ||||
|             return AFMT_U16_BE; | ||||
|         } | ||||
|         else { | ||||
|             return AFMT_U16_LE; | ||||
|         } | ||||
|         return AFMT_U16_LE; | ||||
|  | ||||
|     default: | ||||
|         dolog ("Internal logic error: Bad audio format %d\n", fmt); | ||||
| @@ -526,7 +516,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|  | ||||
|     oss->fd = -1; | ||||
|  | ||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||
|     req.fmt = aud_to_ossfmt (as->fmt); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.fragsize = conf.fragsize; | ||||
| @@ -692,7 +682,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
|  | ||||
|     oss->fd = -1; | ||||
|  | ||||
|     req.fmt = aud_to_ossfmt (as->fmt, as->endianness); | ||||
|     req.fmt = aud_to_ossfmt (as->fmt); | ||||
|     req.freq = as->freq; | ||||
|     req.nchannels = as->nchannels; | ||||
|     req.fragsize = conf.fragsize; | ||||
| @@ -788,7 +778,8 @@ static int oss_run_in (HWVoiceIn *hw) | ||||
|                            hw->info.align + 1); | ||||
|                 } | ||||
|                 read_samples += nread >> hwshift; | ||||
|                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift); | ||||
|                 hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift, | ||||
|                           &nominal_volume); | ||||
|             } | ||||
|  | ||||
|             if (bufs[i].len - nread) { | ||||
|   | ||||
| @@ -33,11 +33,13 @@ typedef struct { | ||||
|  | ||||
| static struct { | ||||
|     int samples; | ||||
|     int divisor; | ||||
|     char *server; | ||||
|     char *sink; | ||||
|     char *source; | ||||
| } conf = { | ||||
|     .samples = 4096, | ||||
|     .samples = 1024, | ||||
|     .divisor = 2, | ||||
| }; | ||||
|  | ||||
| static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) | ||||
| @@ -55,6 +57,9 @@ static void *qpa_thread_out (void *arg) | ||||
| { | ||||
|     PAVoiceOut *pa = arg; | ||||
|     HWVoiceOut *hw = &pa->hw; | ||||
|     int threshold; | ||||
|  | ||||
|     threshold = conf.divisor ? hw->samples / conf.divisor : 0; | ||||
|  | ||||
|     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { | ||||
|         return NULL; | ||||
| @@ -68,7 +73,7 @@ static void *qpa_thread_out (void *arg) | ||||
|                 goto exit; | ||||
|             } | ||||
|  | ||||
|             if (pa->live > 0) { | ||||
|             if (pa->live > threshold) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
| @@ -77,8 +82,8 @@ static void *qpa_thread_out (void *arg) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         decr = to_mix = audio_MIN (pa->live, conf.samples >> 2); | ||||
|         rpos = pa->rpos; | ||||
|         decr = to_mix = pa->live; | ||||
|         rpos = hw->rpos; | ||||
|  | ||||
|         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { | ||||
|             return NULL; | ||||
| @@ -147,6 +152,9 @@ static void *qpa_thread_in (void *arg) | ||||
| { | ||||
|     PAVoiceIn *pa = arg; | ||||
|     HWVoiceIn *hw = &pa->hw; | ||||
|     int threshold; | ||||
|  | ||||
|     threshold = conf.divisor ? hw->samples / conf.divisor : 0; | ||||
|  | ||||
|     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { | ||||
|         return NULL; | ||||
| @@ -160,7 +168,7 @@ static void *qpa_thread_in (void *arg) | ||||
|                 goto exit; | ||||
|             } | ||||
|  | ||||
|             if (pa->dead > 0) { | ||||
|             if (pa->dead > threshold) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
| @@ -169,8 +177,8 @@ static void *qpa_thread_in (void *arg) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2); | ||||
|         wpos = pa->wpos; | ||||
|         incr = to_grab = pa->dead; | ||||
|         wpos = hw->wpos; | ||||
|  | ||||
|         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { | ||||
|             return NULL; | ||||
| @@ -187,7 +195,7 @@ static void *qpa_thread_in (void *arg) | ||||
|                 return NULL; | ||||
|             } | ||||
|  | ||||
|             hw->conv (hw->conv_buf + wpos, buf, chunk); | ||||
|             hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume); | ||||
|             wpos = (wpos + chunk) % hw->samples; | ||||
|             to_grab -= chunk; | ||||
|         } | ||||
| @@ -287,7 +295,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     int error; | ||||
|     static pa_sample_spec ss; | ||||
|     static pa_buffer_attr ba; | ||||
|     struct audsettings obt_as = *as; | ||||
|     PAVoiceOut *pa = (PAVoiceOut *) hw; | ||||
|  | ||||
| @@ -295,15 +302,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|     ss.channels = as->nchannels; | ||||
|     ss.rate = as->freq; | ||||
|  | ||||
|     /* | ||||
|      * qemu audio tick runs at 250 Hz (by default), so processing | ||||
|      * data chunks worth 4 ms of sound should be a good fit. | ||||
|      */ | ||||
|     ba.tlength = pa_usec_to_bytes (4 * 1000, &ss); | ||||
|     ba.minreq = pa_usec_to_bytes (2 * 1000, &ss); | ||||
|     ba.maxlength = -1; | ||||
|     ba.prebuf = -1; | ||||
|  | ||||
|     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); | ||||
|  | ||||
|     pa->s = pa_simple_new ( | ||||
| @@ -314,7 +312,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|         "pcm.playback", | ||||
|         &ss, | ||||
|         NULL,                   /* channel map */ | ||||
|         &ba,                    /* buffering attributes */ | ||||
|         NULL,                   /* buffering attributes */ | ||||
|         &error | ||||
|         ); | ||||
|     if (!pa->s) { | ||||
| @@ -325,7 +323,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|     hw->samples = conf.samples; | ||||
|     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||
|     pa->rpos = hw->rpos; | ||||
|     if (!pa->pcm_buf) { | ||||
|         dolog ("Could not allocate buffer (%d bytes)\n", | ||||
|                hw->samples << hw->info.shift); | ||||
| @@ -380,7 +377,6 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) | ||||
|     audio_pcm_init_info (&hw->info, &obt_as); | ||||
|     hw->samples = conf.samples; | ||||
|     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||||
|     pa->wpos = hw->wpos; | ||||
|     if (!pa->pcm_buf) { | ||||
|         dolog ("Could not allocate buffer (%d bytes)\n", | ||||
|                hw->samples << hw->info.shift); | ||||
| @@ -475,6 +471,12 @@ struct audio_option qpa_options[] = { | ||||
|         .valp  = &conf.samples, | ||||
|         .descr = "buffer size in samples" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "DIVISOR", | ||||
|         .tag   = AUD_OPT_INT, | ||||
|         .valp  = &conf.divisor, | ||||
|         .descr = "threshold divisor" | ||||
|     }, | ||||
|     { | ||||
|         .name  = "SERVER", | ||||
|         .tag   = AUD_OPT_STR, | ||||
|   | ||||
							
								
								
									
										115
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								audio/sdlaudio.c
									
									
									
									
									
								
							| @@ -41,8 +41,8 @@ | ||||
| typedef struct SDLVoiceOut { | ||||
|     HWVoiceOut hw; | ||||
|     int live; | ||||
|     int rpos; | ||||
|     int decr; | ||||
|     int pending; | ||||
| } SDLVoiceOut; | ||||
|  | ||||
| static struct { | ||||
| @@ -115,19 +115,23 @@ static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) | ||||
|     return sdl_post (s, forfn); | ||||
| } | ||||
|  | ||||
| static int aud_to_sdlfmt (audfmt_e fmt) | ||||
| static int aud_to_sdlfmt (audfmt_e fmt, int *shift) | ||||
| { | ||||
|     switch (fmt) { | ||||
|     case AUD_FMT_S8: | ||||
|         *shift = 0; | ||||
|         return AUDIO_S8; | ||||
|  | ||||
|     case AUD_FMT_U8: | ||||
|         *shift = 0; | ||||
|         return AUDIO_U8; | ||||
|  | ||||
|     case AUD_FMT_S16: | ||||
|         *shift = 1; | ||||
|         return AUDIO_S16LSB; | ||||
|  | ||||
|     case AUD_FMT_U16: | ||||
|         *shift = 1; | ||||
|         return AUDIO_U16LSB; | ||||
|  | ||||
|     default: | ||||
| @@ -184,20 +188,11 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | ||||
| { | ||||
|     int status; | ||||
| #ifndef _WIN32 | ||||
|     int err; | ||||
|     sigset_t new, old; | ||||
|  | ||||
|     /* Make sure potential threads created by SDL don't hog signals.  */ | ||||
|     err = sigfillset (&new); | ||||
|     if (err) { | ||||
|         dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); | ||||
|         return -1; | ||||
|     } | ||||
|     err = pthread_sigmask (SIG_BLOCK, &new, &old); | ||||
|     if (err) { | ||||
|         dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); | ||||
|         return -1; | ||||
|     } | ||||
|     sigfillset (&new); | ||||
|     pthread_sigmask (SIG_BLOCK, &new, &old); | ||||
| #endif | ||||
|  | ||||
|     status = SDL_OpenAudio (req, obt); | ||||
| @@ -206,14 +201,7 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) | ||||
|     } | ||||
|  | ||||
| #ifndef _WIN32 | ||||
|     err = pthread_sigmask (SIG_SETMASK, &old, NULL); | ||||
|     if (err) { | ||||
|         dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", | ||||
|                strerror (errno)); | ||||
|         /* We have failed to restore original signal mask, all bets are off, | ||||
|            so exit the process */ | ||||
|         exit (EXIT_FAILURE); | ||||
|     } | ||||
|     pthread_sigmask (SIG_SETMASK, &old, NULL); | ||||
| #endif | ||||
|     return status; | ||||
| } | ||||
| @@ -237,6 +225,10 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | ||||
|     HWVoiceOut *hw = &sdl->hw; | ||||
|     int samples = len >> hw->info.shift; | ||||
|  | ||||
|     if (sdl_lock (s, "sdl_callback")) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (s->exit) { | ||||
|         return; | ||||
|     } | ||||
| @@ -244,49 +236,34 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | ||||
|     while (samples) { | ||||
|         int to_mix, decr; | ||||
|  | ||||
|         /* dolog ("in callback samples=%d\n", samples); */ | ||||
|         sdl_wait (s, "sdl_callback"); | ||||
|         if (s->exit) { | ||||
|             return; | ||||
|         while (!sdl->pending) { | ||||
|             if (sdl_unlock (s, "sdl_callback")) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             sdl_wait (s, "sdl_callback"); | ||||
|             if (s->exit) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (sdl_lock (s, "sdl_callback")) { | ||||
|                 return; | ||||
|             } | ||||
|             sdl->pending += sdl->live; | ||||
|             sdl->live = 0; | ||||
|         } | ||||
|  | ||||
|         if (sdl_lock (s, "sdl_callback")) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { | ||||
|             dolog ("sdl->live=%d hw->samples=%d\n", | ||||
|                    sdl->live, hw->samples); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!sdl->live) { | ||||
|             goto again; | ||||
|         } | ||||
|  | ||||
|         /* dolog ("in callback live=%d\n", live); */ | ||||
|         to_mix = audio_MIN (samples, sdl->live); | ||||
|         decr = to_mix; | ||||
|         while (to_mix) { | ||||
|             int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); | ||||
|             struct st_sample *src = hw->mix_buf + hw->rpos; | ||||
|  | ||||
|             /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ | ||||
|             hw->clip (buf, src, chunk); | ||||
|             sdl->rpos = (sdl->rpos + chunk) % hw->samples; | ||||
|             to_mix -= chunk; | ||||
|             buf += chunk << hw->info.shift; | ||||
|         } | ||||
|         to_mix = audio_MIN (samples, sdl->pending); | ||||
|         decr = audio_pcm_hw_clip_out (hw, buf, to_mix, 0); | ||||
|         buf += decr << hw->info.shift; | ||||
|         samples -= decr; | ||||
|         sdl->live -= decr; | ||||
|         sdl->decr += decr; | ||||
|  | ||||
|     again: | ||||
|         if (sdl_unlock (s, "sdl_callback")) { | ||||
|             return; | ||||
|         } | ||||
|         sdl->pending -= decr; | ||||
|     } | ||||
|  | ||||
|     if (sdl_unlock (s, "sdl_callback")) { | ||||
|         return; | ||||
|     } | ||||
|     /* dolog ("done len=%d\n", len); */ | ||||
| } | ||||
|  | ||||
| static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) | ||||
| @@ -304,18 +281,9 @@ static int sdl_run_out (HWVoiceOut *hw, int live) | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (sdl->decr > live) { | ||||
|         ldebug ("sdl->decr %d live %d sdl->live %d\n", | ||||
|                 sdl->decr, | ||||
|                 live, | ||||
|                 sdl->live); | ||||
|     } | ||||
|  | ||||
|     decr = audio_MIN (sdl->decr, live); | ||||
|     sdl->decr -= decr; | ||||
|  | ||||
|     sdl->live = live - decr; | ||||
|     hw->rpos = sdl->rpos; | ||||
|     sdl->live = live; | ||||
|     decr = sdl->decr; | ||||
|     sdl->decr = 0; | ||||
|  | ||||
|     if (sdl->live > 0) { | ||||
|         sdl_unlock_and_post (s, "sdl_run_out"); | ||||
| @@ -338,13 +306,16 @@ static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as) | ||||
|     SDLVoiceOut *sdl = (SDLVoiceOut *) hw; | ||||
|     SDLAudioState *s = &glob_sdl; | ||||
|     SDL_AudioSpec req, obt; | ||||
|     int shift; | ||||
|     int endianess; | ||||
|     int err; | ||||
|     audfmt_e effective_fmt; | ||||
|     struct audsettings obt_as; | ||||
|  | ||||
|     shift <<= as->nchannels == 2; | ||||
|  | ||||
|     req.freq = as->freq; | ||||
|     req.format = aud_to_sdlfmt (as->fmt); | ||||
|     req.format = aud_to_sdlfmt (as->fmt, &shift); | ||||
|     req.channels = as->nchannels; | ||||
|     req.samples = conf.nb_samples; | ||||
|     req.callback = sdl_callback; | ||||
|   | ||||
| @@ -1,345 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 2010 Red Hat, Inc. | ||||
|  * | ||||
|  * maintained by Gerd Hoffmann <kraxel@redhat.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU General Public License as | ||||
|  * published by the Free Software Foundation; either version 2 or | ||||
|  * (at your option) version 3 of the License. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include "hw/hw.h" | ||||
| #include "qemu-timer.h" | ||||
| #include "ui/qemu-spice.h" | ||||
|  | ||||
| #define AUDIO_CAP "spice" | ||||
| #include "audio.h" | ||||
| #include "audio_int.h" | ||||
|  | ||||
| #define LINE_IN_SAMPLES 1024 | ||||
| #define LINE_OUT_SAMPLES 1024 | ||||
|  | ||||
| typedef struct SpiceRateCtl { | ||||
|     int64_t               start_ticks; | ||||
|     int64_t               bytes_sent; | ||||
| } SpiceRateCtl; | ||||
|  | ||||
| typedef struct SpiceVoiceOut { | ||||
|     HWVoiceOut            hw; | ||||
|     SpicePlaybackInstance sin; | ||||
|     SpiceRateCtl          rate; | ||||
|     int                   active; | ||||
|     uint32_t              *frame; | ||||
|     uint32_t              *fpos; | ||||
|     uint32_t              fsize; | ||||
| } SpiceVoiceOut; | ||||
|  | ||||
| typedef struct SpiceVoiceIn { | ||||
|     HWVoiceIn             hw; | ||||
|     SpiceRecordInstance   sin; | ||||
|     SpiceRateCtl          rate; | ||||
|     int                   active; | ||||
|     uint32_t              samples[LINE_IN_SAMPLES]; | ||||
| } SpiceVoiceIn; | ||||
|  | ||||
| static const SpicePlaybackInterface playback_sif = { | ||||
|     .base.type          = SPICE_INTERFACE_PLAYBACK, | ||||
|     .base.description   = "playback", | ||||
|     .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR, | ||||
|     .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR, | ||||
| }; | ||||
|  | ||||
| static const SpiceRecordInterface record_sif = { | ||||
|     .base.type          = SPICE_INTERFACE_RECORD, | ||||
|     .base.description   = "record", | ||||
|     .base.major_version = SPICE_INTERFACE_RECORD_MAJOR, | ||||
|     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, | ||||
| }; | ||||
|  | ||||
| static void *spice_audio_init (void) | ||||
| { | ||||
|     if (!using_spice) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return &spice_audio_init; | ||||
| } | ||||
|  | ||||
| static void spice_audio_fini (void *opaque) | ||||
| { | ||||
|     /* nothing */ | ||||
| } | ||||
|  | ||||
| static void rate_start (SpiceRateCtl *rate) | ||||
| { | ||||
|     memset (rate, 0, sizeof (*rate)); | ||||
|     rate->start_ticks = qemu_get_clock (vm_clock); | ||||
| } | ||||
|  | ||||
| static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) | ||||
| { | ||||
|     int64_t now; | ||||
|     int64_t ticks; | ||||
|     int64_t bytes; | ||||
|     int64_t samples; | ||||
|  | ||||
|     now = qemu_get_clock (vm_clock); | ||||
|     ticks = now - rate->start_ticks; | ||||
|     bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ()); | ||||
|     samples = (bytes - rate->bytes_sent) >> info->shift; | ||||
|     if (samples < 0 || samples > 65536) { | ||||
|         fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples); | ||||
|         rate_start (rate); | ||||
|         samples = 0; | ||||
|     } | ||||
|     rate->bytes_sent += samples << info->shift; | ||||
|     return samples; | ||||
| } | ||||
|  | ||||
| /* playback */ | ||||
|  | ||||
| static int line_out_init (HWVoiceOut *hw, struct audsettings *as) | ||||
| { | ||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); | ||||
|     struct audsettings settings; | ||||
|  | ||||
|     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ; | ||||
|     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN; | ||||
|     settings.fmt        = AUD_FMT_S16; | ||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &settings); | ||||
|     hw->samples = LINE_OUT_SAMPLES; | ||||
|     out->active = 0; | ||||
|  | ||||
|     out->sin.base.sif = &playback_sif.base; | ||||
|     qemu_spice_add_interface (&out->sin.base); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void line_out_fini (HWVoiceOut *hw) | ||||
| { | ||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); | ||||
|  | ||||
|     spice_server_remove_interface (&out->sin.base); | ||||
| } | ||||
|  | ||||
| static int line_out_run (HWVoiceOut *hw, int live) | ||||
| { | ||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); | ||||
|     int rpos, decr; | ||||
|     int samples; | ||||
|  | ||||
|     if (!live) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     decr = rate_get_samples (&hw->info, &out->rate); | ||||
|     decr = audio_MIN (live, decr); | ||||
|  | ||||
|     samples = decr; | ||||
|     rpos = hw->rpos; | ||||
|     while (samples) { | ||||
|         int left_till_end_samples = hw->samples - rpos; | ||||
|         int len = audio_MIN (samples, left_till_end_samples); | ||||
|  | ||||
|         if (!out->frame) { | ||||
|             spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize); | ||||
|             out->fpos = out->frame; | ||||
|         } | ||||
|         if (out->frame) { | ||||
|             len = audio_MIN (len, out->fsize); | ||||
|             hw->clip (out->fpos, hw->mix_buf + rpos, len); | ||||
|             out->fsize -= len; | ||||
|             out->fpos  += len; | ||||
|             if (out->fsize == 0) { | ||||
|                 spice_server_playback_put_samples (&out->sin, out->frame); | ||||
|                 out->frame = out->fpos = NULL; | ||||
|             } | ||||
|         } | ||||
|         rpos = (rpos + len) % hw->samples; | ||||
|         samples -= len; | ||||
|     } | ||||
|     hw->rpos = rpos; | ||||
|     return decr; | ||||
| } | ||||
|  | ||||
| static int line_out_write (SWVoiceOut *sw, void *buf, int len) | ||||
| { | ||||
|     return audio_pcm_sw_write (sw, buf, len); | ||||
| } | ||||
|  | ||||
| static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) | ||||
| { | ||||
|     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         if (out->active) { | ||||
|             break; | ||||
|         } | ||||
|         out->active = 1; | ||||
|         rate_start (&out->rate); | ||||
|         spice_server_playback_start (&out->sin); | ||||
|         break; | ||||
|     case VOICE_DISABLE: | ||||
|         if (!out->active) { | ||||
|             break; | ||||
|         } | ||||
|         out->active = 0; | ||||
|         if (out->frame) { | ||||
|             memset (out->fpos, 0, out->fsize << 2); | ||||
|             spice_server_playback_put_samples (&out->sin, out->frame); | ||||
|             out->frame = out->fpos = NULL; | ||||
|         } | ||||
|         spice_server_playback_stop (&out->sin); | ||||
|         break; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* record */ | ||||
|  | ||||
| static int line_in_init (HWVoiceIn *hw, struct audsettings *as) | ||||
| { | ||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); | ||||
|     struct audsettings settings; | ||||
|  | ||||
|     settings.freq       = SPICE_INTERFACE_RECORD_FREQ; | ||||
|     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN; | ||||
|     settings.fmt        = AUD_FMT_S16; | ||||
|     settings.endianness = AUDIO_HOST_ENDIANNESS; | ||||
|  | ||||
|     audio_pcm_init_info (&hw->info, &settings); | ||||
|     hw->samples = LINE_IN_SAMPLES; | ||||
|     in->active = 0; | ||||
|  | ||||
|     in->sin.base.sif = &record_sif.base; | ||||
|     qemu_spice_add_interface (&in->sin.base); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void line_in_fini (HWVoiceIn *hw) | ||||
| { | ||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); | ||||
|  | ||||
|     spice_server_remove_interface (&in->sin.base); | ||||
| } | ||||
|  | ||||
| static int line_in_run (HWVoiceIn *hw) | ||||
| { | ||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); | ||||
|     int num_samples; | ||||
|     int ready; | ||||
|     int len[2]; | ||||
|     uint64_t delta_samp; | ||||
|     const uint32_t *samples; | ||||
|  | ||||
|     if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     delta_samp = rate_get_samples (&hw->info, &in->rate); | ||||
|     num_samples = audio_MIN (num_samples, delta_samp); | ||||
|  | ||||
|     ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples); | ||||
|     samples = in->samples; | ||||
|     if (ready == 0) { | ||||
|         static const uint32_t silence[LINE_IN_SAMPLES]; | ||||
|         samples = silence; | ||||
|         ready = LINE_IN_SAMPLES; | ||||
|     } | ||||
|  | ||||
|     num_samples = audio_MIN (ready, num_samples); | ||||
|  | ||||
|     if (hw->wpos + num_samples > hw->samples) { | ||||
|         len[0] = hw->samples - hw->wpos; | ||||
|         len[1] = num_samples - len[0]; | ||||
|     } else { | ||||
|         len[0] = num_samples; | ||||
|         len[1] = 0; | ||||
|     } | ||||
|  | ||||
|     hw->conv (hw->conv_buf + hw->wpos, samples, len[0]); | ||||
|  | ||||
|     if (len[1]) { | ||||
|         hw->conv (hw->conv_buf, samples + len[0], len[1]); | ||||
|     } | ||||
|  | ||||
|     hw->wpos = (hw->wpos + num_samples) % hw->samples; | ||||
|  | ||||
|     return num_samples; | ||||
| } | ||||
|  | ||||
| static int line_in_read (SWVoiceIn *sw, void *buf, int size) | ||||
| { | ||||
|     return audio_pcm_sw_read (sw, buf, size); | ||||
| } | ||||
|  | ||||
| static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) | ||||
| { | ||||
|     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); | ||||
|  | ||||
|     switch (cmd) { | ||||
|     case VOICE_ENABLE: | ||||
|         if (in->active) { | ||||
|             break; | ||||
|         } | ||||
|         in->active = 1; | ||||
|         rate_start (&in->rate); | ||||
|         spice_server_record_start (&in->sin); | ||||
|         break; | ||||
|     case VOICE_DISABLE: | ||||
|         if (!in->active) { | ||||
|             break; | ||||
|         } | ||||
|         in->active = 0; | ||||
|         spice_server_record_stop (&in->sin); | ||||
|         break; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static struct audio_option audio_options[] = { | ||||
|     { /* end of list */ }, | ||||
| }; | ||||
|  | ||||
| static struct audio_pcm_ops audio_callbacks = { | ||||
|     .init_out = line_out_init, | ||||
|     .fini_out = line_out_fini, | ||||
|     .run_out  = line_out_run, | ||||
|     .write    = line_out_write, | ||||
|     .ctl_out  = line_out_ctl, | ||||
|  | ||||
|     .init_in  = line_in_init, | ||||
|     .fini_in  = line_in_fini, | ||||
|     .run_in   = line_in_run, | ||||
|     .read     = line_in_read, | ||||
|     .ctl_in   = line_in_ctl, | ||||
| }; | ||||
|  | ||||
| struct audio_driver spice_audio_driver = { | ||||
|     .name           = "spice", | ||||
|     .descr          = "spice audio driver", | ||||
|     .options        = audio_options, | ||||
|     .init           = spice_audio_init, | ||||
|     .fini           = spice_audio_fini, | ||||
|     .pcm_ops        = &audio_callbacks, | ||||
|     .max_voices_out = 1, | ||||
|     .max_voices_in  = 1, | ||||
|     .voice_size_out = sizeof (SpiceVoiceOut), | ||||
|     .voice_size_in  = sizeof (SpiceVoiceIn), | ||||
| }; | ||||
|  | ||||
| void qemu_spice_audio_init (void) | ||||
| { | ||||
|     spice_audio_driver.can_be_default = 1; | ||||
| } | ||||
| @@ -581,7 +581,8 @@ static int winwave_run_in (HWVoiceIn *hw) | ||||
|         int conv = audio_MIN (left, decr); | ||||
|         hw->conv (hw->conv_buf + hw->wpos, | ||||
|                   advance (wave->pcm_buf, wave->rpos << hw->info.shift), | ||||
|                   conv); | ||||
|                   conv, | ||||
|                   &nominal_volume); | ||||
|  | ||||
|         wave->rpos = (wave->rpos + conv) % hw->samples; | ||||
|         hw->wpos = (hw->wpos + conv) % hw->samples; | ||||
|   | ||||
							
								
								
									
										148
									
								
								balloon.c
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								balloon.c
									
									
									
									
									
								
							| @@ -1,148 +0,0 @@ | ||||
| /* | ||||
|  * QEMU System Emulator | ||||
|  * | ||||
|  * Copyright (c) 2003-2008 Fabrice Bellard | ||||
|  * | ||||
|  * 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 "sysemu.h" | ||||
| #include "monitor.h" | ||||
| #include "qjson.h" | ||||
| #include "qint.h" | ||||
| #include "cpu-common.h" | ||||
| #include "kvm.h" | ||||
| #include "balloon.h" | ||||
| #include "trace.h" | ||||
|  | ||||
|  | ||||
| static QEMUBalloonEvent *qemu_balloon_event; | ||||
| void *qemu_balloon_event_opaque; | ||||
|  | ||||
| void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) | ||||
| { | ||||
|     qemu_balloon_event = func; | ||||
|     qemu_balloon_event_opaque = opaque; | ||||
| } | ||||
|  | ||||
| int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque) | ||||
| { | ||||
|     if (qemu_balloon_event) { | ||||
|         trace_balloon_event(qemu_balloon_event_opaque, target); | ||||
|         qemu_balloon_event(qemu_balloon_event_opaque, target, cb, opaque); | ||||
|         return 1; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| int qemu_balloon_status(MonitorCompletion cb, void *opaque) | ||||
| { | ||||
|     if (qemu_balloon_event) { | ||||
|         qemu_balloon_event(qemu_balloon_event_opaque, 0, cb, opaque); | ||||
|         return 1; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void print_balloon_stat(const char *key, QObject *obj, void *opaque) | ||||
| { | ||||
|     Monitor *mon = opaque; | ||||
|  | ||||
|     if (strcmp(key, "actual")) | ||||
|         monitor_printf(mon, ",%s=%" PRId64, key, | ||||
|                        qint_get_int(qobject_to_qint(obj))); | ||||
| } | ||||
|  | ||||
| void monitor_print_balloon(Monitor *mon, const QObject *data) | ||||
| { | ||||
|     QDict *qdict; | ||||
|  | ||||
|     qdict = qobject_to_qdict(data); | ||||
|     if (!qdict_haskey(qdict, "actual")) | ||||
|         return; | ||||
|  | ||||
|     monitor_printf(mon, "balloon: actual=%" PRId64, | ||||
|                    qdict_get_int(qdict, "actual") >> 20); | ||||
|     qdict_iter(qdict, print_balloon_stat, mon); | ||||
|     monitor_printf(mon, "\n"); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * do_info_balloon(): Balloon information | ||||
|  * | ||||
|  * Make an asynchronous request for balloon info.  When the request completes | ||||
|  * a QDict will be returned according to the following specification: | ||||
|  * | ||||
|  * - "actual": current balloon value in bytes | ||||
|  * The following fields may or may not be present: | ||||
|  * - "mem_swapped_in": Amount of memory swapped in (bytes) | ||||
|  * - "mem_swapped_out": Amount of memory swapped out (bytes) | ||||
|  * - "major_page_faults": Number of major faults | ||||
|  * - "minor_page_faults": Number of minor faults | ||||
|  * - "free_mem": Total amount of free and unused memory (bytes) | ||||
|  * - "total_mem": Total amount of available memory (bytes) | ||||
|  * | ||||
|  * Example: | ||||
|  * | ||||
|  * { "actual": 1073741824, "mem_swapped_in": 0, "mem_swapped_out": 0, | ||||
|  *   "major_page_faults": 142, "minor_page_faults": 239245, | ||||
|  *   "free_mem": 1014185984, "total_mem": 1044668416 } | ||||
|  */ | ||||
| int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     if (kvm_enabled() && !kvm_has_sync_mmu()) { | ||||
|         qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     ret = qemu_balloon_status(cb, opaque); | ||||
|     if (!ret) { | ||||
|         qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * do_balloon(): Request VM to change its memory allocation | ||||
|  */ | ||||
| int do_balloon(Monitor *mon, const QDict *params, | ||||
| 	       MonitorCompletion cb, void *opaque) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     if (kvm_enabled() && !kvm_has_sync_mmu()) { | ||||
|         qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque); | ||||
|     if (ret == 0) { | ||||
|         qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     cb(opaque, NULL); | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										14
									
								
								balloon.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								balloon.h
									
									
									
									
									
								
							| @@ -14,20 +14,14 @@ | ||||
| #ifndef _QEMU_BALLOON_H | ||||
| #define _QEMU_BALLOON_H | ||||
|  | ||||
| #include "monitor.h" | ||||
| #include "cpu-defs.h" | ||||
|  | ||||
| typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target, | ||||
|                                 MonitorCompletion cb, void *cb_data); | ||||
| typedef ram_addr_t (QEMUBalloonEvent)(void *opaque, ram_addr_t target); | ||||
|  | ||||
| void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); | ||||
|  | ||||
| int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque); | ||||
| void qemu_balloon(ram_addr_t target); | ||||
|  | ||||
| int qemu_balloon_status(MonitorCompletion cb, void *opaque); | ||||
|  | ||||
| void monitor_print_balloon(Monitor *mon, const QObject *data); | ||||
| int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque); | ||||
| int do_balloon(Monitor *mon, const QDict *params, | ||||
|                MonitorCompletion cb, void *opaque); | ||||
| ram_addr_t qemu_balloon_status(void); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -15,11 +15,8 @@ | ||||
| #include "block_int.h" | ||||
| #include "hw/hw.h" | ||||
| #include "qemu-queue.h" | ||||
| #include "qemu-timer.h" | ||||
| #include "monitor.h" | ||||
| #include "block-migration.h" | ||||
| #include "migration.h" | ||||
| #include "blockdev.h" | ||||
| #include <assert.h> | ||||
|  | ||||
| #define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) | ||||
| @@ -29,14 +26,17 @@ | ||||
| #define BLK_MIG_FLAG_PROGRESS           0x04 | ||||
|  | ||||
| #define MAX_IS_ALLOCATED_SEARCH 65536 | ||||
| #define MAX_BLOCKS_READ 10000 | ||||
| #define BLOCKS_READ_CHANGE 100 | ||||
| #define INITIAL_BLOCKS_READ 100 | ||||
|  | ||||
| //#define DEBUG_BLK_MIGRATION | ||||
|  | ||||
| #ifdef DEBUG_BLK_MIGRATION | ||||
| #define DPRINTF(fmt, ...) \ | ||||
| #define dprintf(fmt, ...) \ | ||||
|     do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0) | ||||
| #else | ||||
| #define DPRINTF(fmt, ...) \ | ||||
| #define dprintf(fmt, ...) \ | ||||
|     do { } while (0) | ||||
| #endif | ||||
|  | ||||
| @@ -45,24 +45,20 @@ typedef struct BlkMigDevState { | ||||
|     int bulk_completed; | ||||
|     int shared_base; | ||||
|     int64_t cur_sector; | ||||
|     int64_t cur_dirty; | ||||
|     int64_t completed_sectors; | ||||
|     int64_t total_sectors; | ||||
|     int64_t dirty; | ||||
|     QSIMPLEQ_ENTRY(BlkMigDevState) entry; | ||||
|     unsigned long *aio_bitmap; | ||||
| } BlkMigDevState; | ||||
|  | ||||
| typedef struct BlkMigBlock { | ||||
|     uint8_t *buf; | ||||
|     BlkMigDevState *bmds; | ||||
|     int64_t sector; | ||||
|     int nr_sectors; | ||||
|     struct iovec iov; | ||||
|     QEMUIOVector qiov; | ||||
|     BlockDriverAIOCB *aiocb; | ||||
|     int ret; | ||||
|     int64_t time; | ||||
|     QSIMPLEQ_ENTRY(BlkMigBlock) entry; | ||||
| } BlkMigBlock; | ||||
|  | ||||
| @@ -76,9 +72,6 @@ typedef struct BlkMigState { | ||||
|     int transferred; | ||||
|     int64_t total_sector_sum; | ||||
|     int prev_progress; | ||||
|     int bulk_completed; | ||||
|     long double total_time; | ||||
|     int reads; | ||||
| } BlkMigState; | ||||
|  | ||||
| static BlkMigState block_mig_state; | ||||
| @@ -131,76 +124,13 @@ uint64_t blk_mig_bytes_total(void) | ||||
|     return sum << BDRV_SECTOR_BITS; | ||||
| } | ||||
|  | ||||
| static inline void add_avg_read_time(int64_t time) | ||||
| { | ||||
|     block_mig_state.reads++; | ||||
|     block_mig_state.total_time += time; | ||||
| } | ||||
|  | ||||
| static inline long double compute_read_bwidth(void) | ||||
| { | ||||
|     assert(block_mig_state.total_time != 0); | ||||
|     return  (block_mig_state.reads * BLOCK_SIZE)/ block_mig_state.total_time; | ||||
| } | ||||
|  | ||||
| static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) | ||||
| { | ||||
|     int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; | ||||
|  | ||||
|     if ((sector << BDRV_SECTOR_BITS) < bdrv_getlength(bmds->bs)) { | ||||
|         return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] & | ||||
|             (1UL << (chunk % (sizeof(unsigned long) * 8)))); | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num, | ||||
|                              int nb_sectors, int set) | ||||
| { | ||||
|     int64_t start, end; | ||||
|     unsigned long val, idx, bit; | ||||
|  | ||||
|     start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; | ||||
|     end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK; | ||||
|  | ||||
|     for (; start <= end; start++) { | ||||
|         idx = start / (sizeof(unsigned long) * 8); | ||||
|         bit = start % (sizeof(unsigned long) * 8); | ||||
|         val = bmds->aio_bitmap[idx]; | ||||
|         if (set) { | ||||
|             val |= 1UL << bit; | ||||
|         } else { | ||||
|             val &= ~(1UL << bit); | ||||
|         } | ||||
|         bmds->aio_bitmap[idx] = val; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void alloc_aio_bitmap(BlkMigDevState *bmds) | ||||
| { | ||||
|     BlockDriverState *bs = bmds->bs; | ||||
|     int64_t bitmap_size; | ||||
|  | ||||
|     bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) + | ||||
|             BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1; | ||||
|     bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8; | ||||
|  | ||||
|     bmds->aio_bitmap = qemu_mallocz(bitmap_size); | ||||
| } | ||||
|  | ||||
| static void blk_mig_read_cb(void *opaque, int ret) | ||||
| { | ||||
|     BlkMigBlock *blk = opaque; | ||||
|  | ||||
|     blk->ret = ret; | ||||
|  | ||||
|     blk->time = qemu_get_clock_ns(rt_clock) - blk->time; | ||||
|  | ||||
|     add_avg_read_time(blk->time); | ||||
|  | ||||
|     QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); | ||||
|     bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0); | ||||
|  | ||||
|     block_mig_state.submitted--; | ||||
|     block_mig_state.read_done++; | ||||
| @@ -208,7 +138,7 @@ static void blk_mig_read_cb(void *opaque, int ret) | ||||
| } | ||||
|  | ||||
| static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, | ||||
|                                 BlkMigDevState *bmds) | ||||
|                                 BlkMigDevState *bmds, int is_async) | ||||
| { | ||||
|     int64_t total_sectors = bmds->total_sectors; | ||||
|     int64_t cur_sector = bmds->cur_sector; | ||||
| @@ -244,20 +174,27 @@ static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, | ||||
|     blk->buf = qemu_malloc(BLOCK_SIZE); | ||||
|     blk->bmds = bmds; | ||||
|     blk->sector = cur_sector; | ||||
|     blk->nr_sectors = nr_sectors; | ||||
|  | ||||
|     blk->iov.iov_base = blk->buf; | ||||
|     blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; | ||||
|     qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); | ||||
|     if (is_async) { | ||||
|         blk->iov.iov_base = blk->buf; | ||||
|         blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; | ||||
|         qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); | ||||
|  | ||||
|     blk->time = qemu_get_clock_ns(rt_clock); | ||||
|         blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, | ||||
|                                     nr_sectors, blk_mig_read_cb, blk); | ||||
|         if (!blk->aiocb) { | ||||
|             goto error; | ||||
|         } | ||||
|         block_mig_state.submitted++; | ||||
|     } else { | ||||
|         if (bdrv_read(bs, cur_sector, blk->buf, nr_sectors) < 0) { | ||||
|             goto error; | ||||
|         } | ||||
|         blk_send(f, blk); | ||||
|  | ||||
|     blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, | ||||
|                                 nr_sectors, blk_mig_read_cb, blk); | ||||
|     if (!blk->aiocb) { | ||||
|         goto error; | ||||
|         qemu_free(blk->buf); | ||||
|         qemu_free(blk); | ||||
|     } | ||||
|     block_mig_state.submitted++; | ||||
|  | ||||
|     bdrv_reset_dirty(bs, cur_sector, nr_sectors); | ||||
|     bmds->cur_sector = cur_sector + nr_sectors; | ||||
| @@ -281,58 +218,49 @@ static void set_dirty_tracking(int enable) | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void init_blk_migration_it(void *opaque, BlockDriverState *bs) | ||||
| { | ||||
|     Monitor *mon = opaque; | ||||
|     BlkMigDevState *bmds; | ||||
|     int64_t sectors; | ||||
|  | ||||
|     if (!bdrv_is_read_only(bs)) { | ||||
|         sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; | ||||
|         if (sectors <= 0) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         bmds = qemu_mallocz(sizeof(BlkMigDevState)); | ||||
|         bmds->bs = bs; | ||||
|         bmds->bulk_completed = 0; | ||||
|         bmds->total_sectors = sectors; | ||||
|         bmds->completed_sectors = 0; | ||||
|         bmds->shared_base = block_mig_state.shared_base; | ||||
|         alloc_aio_bitmap(bmds); | ||||
|         drive_get_ref(drive_get_by_blockdev(bs)); | ||||
|         bdrv_set_in_use(bs, 1); | ||||
|  | ||||
|         block_mig_state.total_sector_sum += sectors; | ||||
|  | ||||
|         if (bmds->shared_base) { | ||||
|             monitor_printf(mon, "Start migration for %s with shared base " | ||||
|                                 "image\n", | ||||
|                            bs->device_name); | ||||
|         } else { | ||||
|             monitor_printf(mon, "Start full migration for %s\n", | ||||
|                            bs->device_name); | ||||
|         } | ||||
|  | ||||
|         QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void init_blk_migration(Monitor *mon, QEMUFile *f) | ||||
| { | ||||
|     BlkMigDevState *bmds; | ||||
|     BlockDriverState *bs; | ||||
|     int64_t sectors; | ||||
|  | ||||
|     block_mig_state.submitted = 0; | ||||
|     block_mig_state.read_done = 0; | ||||
|     block_mig_state.transferred = 0; | ||||
|     block_mig_state.total_sector_sum = 0; | ||||
|     block_mig_state.prev_progress = -1; | ||||
|     block_mig_state.bulk_completed = 0; | ||||
|     block_mig_state.total_time = 0; | ||||
|     block_mig_state.reads = 0; | ||||
|  | ||||
|     bdrv_iterate(init_blk_migration_it, mon); | ||||
|     for (bs = bdrv_first; bs != NULL; bs = bs->next) { | ||||
|         if (bs->type == BDRV_TYPE_HD) { | ||||
|             sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; | ||||
|             if (sectors == 0) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             bmds = qemu_mallocz(sizeof(BlkMigDevState)); | ||||
|             bmds->bs = bs; | ||||
|             bmds->bulk_completed = 0; | ||||
|             bmds->total_sectors = sectors; | ||||
|             bmds->completed_sectors = 0; | ||||
|             bmds->shared_base = block_mig_state.shared_base; | ||||
|  | ||||
|             block_mig_state.total_sector_sum += sectors; | ||||
|  | ||||
|             if (bmds->shared_base) { | ||||
|                 monitor_printf(mon, "Start migration for %s with shared base " | ||||
|                                     "image\n", | ||||
|                                bs->device_name); | ||||
|             } else { | ||||
|                 monitor_printf(mon, "Start full migration for %s\n", | ||||
|                                bs->device_name); | ||||
|             } | ||||
|  | ||||
|             QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) | ||||
| static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f, int is_async) | ||||
| { | ||||
|     int64_t completed_sector_sum = 0; | ||||
|     BlkMigDevState *bmds; | ||||
| @@ -341,7 +269,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) | ||||
|  | ||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||||
|         if (bmds->bulk_completed == 0) { | ||||
|             if (mig_save_device_bulk(mon, f, bmds) == 1) { | ||||
|             if (mig_save_device_bulk(mon, f, bmds, is_async) == 1) { | ||||
|                 /* completed bulk section for this device */ | ||||
|                 bmds->bulk_completed = 1; | ||||
|             } | ||||
| @@ -353,12 +281,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (block_mig_state.total_sector_sum != 0) { | ||||
|         progress = completed_sector_sum * 100 / | ||||
|                    block_mig_state.total_sector_sum; | ||||
|     } else { | ||||
|         progress = 100; | ||||
|     } | ||||
|     progress = completed_sector_sum * 100 / block_mig_state.total_sector_sum; | ||||
|     if (progress != block_mig_state.prev_progress) { | ||||
|         block_mig_state.prev_progress = progress; | ||||
|         qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) | ||||
| @@ -370,102 +293,46 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static void blk_mig_reset_dirty_cursor(void) | ||||
| #define MAX_NUM_BLOCKS 4 | ||||
|  | ||||
| static void blk_mig_save_dirty_blocks(Monitor *mon, QEMUFile *f) | ||||
| { | ||||
|     BlkMigDevState *bmds; | ||||
|  | ||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||||
|         bmds->cur_dirty = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, | ||||
|                                  BlkMigDevState *bmds, int is_async) | ||||
| { | ||||
|     BlkMigBlock *blk; | ||||
|     int64_t total_sectors = bmds->total_sectors; | ||||
|     BlkMigBlock blk; | ||||
|     int64_t sector; | ||||
|     int nr_sectors; | ||||
|  | ||||
|     for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) { | ||||
|         if (bmds_aio_inflight(bmds, sector)) { | ||||
|             qemu_aio_flush(); | ||||
|         } | ||||
|         if (bdrv_get_dirty(bmds->bs, sector)) { | ||||
|  | ||||
|             if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { | ||||
|                 nr_sectors = total_sectors - sector; | ||||
|             } else { | ||||
|                 nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; | ||||
|             } | ||||
|             blk = qemu_malloc(sizeof(BlkMigBlock)); | ||||
|             blk->buf = qemu_malloc(BLOCK_SIZE); | ||||
|             blk->bmds = bmds; | ||||
|             blk->sector = sector; | ||||
|             blk->nr_sectors = nr_sectors; | ||||
|  | ||||
|             if (is_async) { | ||||
|                 blk->iov.iov_base = blk->buf; | ||||
|                 blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; | ||||
|                 qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); | ||||
|  | ||||
|                 blk->time = qemu_get_clock_ns(rt_clock); | ||||
|  | ||||
|                 blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov, | ||||
|                                             nr_sectors, blk_mig_read_cb, blk); | ||||
|                 if (!blk->aiocb) { | ||||
|                     goto error; | ||||
|                 } | ||||
|                 block_mig_state.submitted++; | ||||
|                 bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); | ||||
|             } else { | ||||
|                 if (bdrv_read(bmds->bs, sector, blk->buf, | ||||
|                               nr_sectors) < 0) { | ||||
|                     goto error; | ||||
|                 } | ||||
|                 blk_send(f, blk); | ||||
|  | ||||
|                 qemu_free(blk->buf); | ||||
|                 qemu_free(blk); | ||||
|             } | ||||
|  | ||||
|             bdrv_reset_dirty(bmds->bs, sector, nr_sectors); | ||||
|             break; | ||||
|         } | ||||
|         sector += BDRV_SECTORS_PER_DIRTY_CHUNK; | ||||
|         bmds->cur_dirty = sector; | ||||
|     } | ||||
|  | ||||
|     return (bmds->cur_dirty >= bmds->total_sectors); | ||||
|  | ||||
| error: | ||||
|     monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector); | ||||
|     qemu_file_set_error(f); | ||||
|     qemu_free(blk->buf); | ||||
|     qemu_free(blk); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async) | ||||
| { | ||||
|     BlkMigDevState *bmds; | ||||
|     int ret = 0; | ||||
|     blk.buf = qemu_malloc(BLOCK_SIZE); | ||||
|  | ||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||||
|         if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) { | ||||
|             ret = 1; | ||||
|             break; | ||||
|         for (sector = 0; sector < bmds->cur_sector;) { | ||||
|             if (bdrv_get_dirty(bmds->bs, sector)) { | ||||
|                 if (bdrv_read(bmds->bs, sector, blk.buf, | ||||
|                               BDRV_SECTORS_PER_DIRTY_CHUNK) < 0) { | ||||
|                     monitor_printf(mon, "Error reading sector %" PRId64 "\n", | ||||
|                                    sector); | ||||
|                     qemu_file_set_error(f); | ||||
|                     qemu_free(blk.buf); | ||||
|                     return; | ||||
|                 } | ||||
|                 blk.bmds = bmds; | ||||
|                 blk.sector = sector; | ||||
|                 blk_send(f, &blk); | ||||
|  | ||||
|                 bdrv_reset_dirty(bmds->bs, sector, | ||||
|                                  BDRV_SECTORS_PER_DIRTY_CHUNK); | ||||
|             } | ||||
|             sector += BDRV_SECTORS_PER_DIRTY_CHUNK; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
|     qemu_free(blk.buf); | ||||
| } | ||||
|  | ||||
| static void flush_blks(QEMUFile* f) | ||||
| { | ||||
|     BlkMigBlock *blk; | ||||
|  | ||||
|     DPRINTF("%s Enter submitted %d read_done %d transferred %d\n", | ||||
|     dprintf("%s Enter submitted %d read_done %d transferred %d\n", | ||||
|             __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, | ||||
|             block_mig_state.transferred); | ||||
|  | ||||
| @@ -488,47 +355,26 @@ static void flush_blks(QEMUFile* f) | ||||
|         assert(block_mig_state.read_done >= 0); | ||||
|     } | ||||
|  | ||||
|     DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, | ||||
|     dprintf("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, | ||||
|             block_mig_state.submitted, block_mig_state.read_done, | ||||
|             block_mig_state.transferred); | ||||
| } | ||||
|  | ||||
| static int64_t get_remaining_dirty(void) | ||||
| { | ||||
|     BlkMigDevState *bmds; | ||||
|     int64_t dirty = 0; | ||||
|  | ||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||||
|         dirty += bdrv_get_dirty_count(bmds->bs); | ||||
|     } | ||||
|  | ||||
|     return dirty * BLOCK_SIZE; | ||||
| } | ||||
|  | ||||
| static int is_stage2_completed(void) | ||||
| { | ||||
|     int64_t remaining_dirty; | ||||
|     long double bwidth; | ||||
|     BlkMigDevState *bmds; | ||||
|  | ||||
|     if (block_mig_state.bulk_completed == 1) { | ||||
|     if (block_mig_state.submitted > 0) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|         remaining_dirty = get_remaining_dirty(); | ||||
|         if (remaining_dirty == 0) { | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         bwidth = compute_read_bwidth(); | ||||
|  | ||||
|         if ((remaining_dirty / bwidth) <= | ||||
|             migrate_max_downtime()) { | ||||
|             /* finish stage2 because we think that we can finish remaing work | ||||
|                below max_downtime */ | ||||
|  | ||||
|             return 1; | ||||
|     QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { | ||||
|         if (bmds->bulk_completed == 0) { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| static void blk_mig_cleanup(Monitor *mon) | ||||
| @@ -536,13 +382,8 @@ static void blk_mig_cleanup(Monitor *mon) | ||||
|     BlkMigDevState *bmds; | ||||
|     BlkMigBlock *blk; | ||||
|  | ||||
|     set_dirty_tracking(0); | ||||
|  | ||||
|     while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { | ||||
|         QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); | ||||
|         bdrv_set_in_use(bmds->bs, 0); | ||||
|         drive_put_ref(drive_get_by_blockdev(bmds->bs)); | ||||
|         qemu_free(bmds->aio_bitmap); | ||||
|         qemu_free(bmds); | ||||
|     } | ||||
|  | ||||
| @@ -552,12 +393,14 @@ static void blk_mig_cleanup(Monitor *mon) | ||||
|         qemu_free(blk); | ||||
|     } | ||||
|  | ||||
|     set_dirty_tracking(0); | ||||
|  | ||||
|     monitor_printf(mon, "\n"); | ||||
| } | ||||
|  | ||||
| static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) | ||||
| { | ||||
|     DPRINTF("Enter save live stage %d submitted %d transferred %d\n", | ||||
|     dprintf("Enter save live stage %d submitted %d transferred %d\n", | ||||
|             stage, block_mig_state.submitted, block_mig_state.transferred); | ||||
|  | ||||
|     if (stage < 0) { | ||||
| @@ -585,41 +428,29 @@ static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     blk_mig_reset_dirty_cursor(); | ||||
|  | ||||
|     if (stage == 2) { | ||||
|         /* control the rate of transfer */ | ||||
|         while ((block_mig_state.submitted + | ||||
|                 block_mig_state.read_done) * BLOCK_SIZE < | ||||
|                qemu_file_get_rate_limit(f)) { | ||||
|             if (block_mig_state.bulk_completed == 0) { | ||||
|                 /* first finish the bulk phase */ | ||||
|                 if (blk_mig_save_bulked_block(mon, f) == 0) { | ||||
|                     /* finished saving bulk on all devices */ | ||||
|                     block_mig_state.bulk_completed = 1; | ||||
|                 } | ||||
|             } else { | ||||
|                 if (blk_mig_save_dirty_block(mon, f, 1) == 0) { | ||||
|                     /* no more dirty blocks */ | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         flush_blks(f); | ||||
|  | ||||
|         if (qemu_file_has_error(f)) { | ||||
|             blk_mig_cleanup(mon); | ||||
|             return 0; | ||||
|     /* control the rate of transfer */ | ||||
|     while ((block_mig_state.submitted + | ||||
|             block_mig_state.read_done) * BLOCK_SIZE < | ||||
|            qemu_file_get_rate_limit(f)) { | ||||
|         if (blk_mig_save_bulked_block(mon, f, 1) == 0) { | ||||
|             /* no more bulk blocks for now */ | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (stage == 3) { | ||||
|         /* we know for sure that save bulk is completed and | ||||
|            all async read completed */ | ||||
|         assert(block_mig_state.submitted == 0); | ||||
|     flush_blks(f); | ||||
|  | ||||
|         while (blk_mig_save_dirty_block(mon, f, 0) != 0); | ||||
|     if (qemu_file_has_error(f)) { | ||||
|         blk_mig_cleanup(mon); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (stage == 3) { | ||||
|         while (blk_mig_save_bulked_block(mon, f, 0) != 0) { | ||||
|             /* empty */ | ||||
|         } | ||||
|  | ||||
|         blk_mig_save_dirty_blocks(mon, f); | ||||
|         blk_mig_cleanup(mon); | ||||
|  | ||||
|         /* report completion */ | ||||
| @@ -643,10 +474,8 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) | ||||
|     int len, flags; | ||||
|     char device_name[256]; | ||||
|     int64_t addr; | ||||
|     BlockDriverState *bs, *bs_prev = NULL; | ||||
|     BlockDriverState *bs; | ||||
|     uint8_t *buf; | ||||
|     int64_t total_sectors = 0; | ||||
|     int nr_sectors; | ||||
|  | ||||
|     do { | ||||
|         addr = qemu_get_be64(f); | ||||
| @@ -655,7 +484,6 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) | ||||
|         addr >>= BDRV_SECTOR_BITS; | ||||
|  | ||||
|         if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) { | ||||
|             int ret; | ||||
|             /* get device name */ | ||||
|             len = qemu_get_byte(f); | ||||
|             qemu_get_buffer(f, (uint8_t *)device_name, len); | ||||
| @@ -668,31 +496,12 @@ static int block_load(QEMUFile *f, void *opaque, int version_id) | ||||
|                 return -EINVAL; | ||||
|             } | ||||
|  | ||||
|             if (bs != bs_prev) { | ||||
|                 bs_prev = bs; | ||||
|                 total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; | ||||
|                 if (total_sectors <= 0) { | ||||
|                     error_report("Error getting length of block device %s\n", | ||||
|                                  device_name); | ||||
|                     return -EINVAL; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) { | ||||
|                 nr_sectors = total_sectors - addr; | ||||
|             } else { | ||||
|                 nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; | ||||
|             } | ||||
|  | ||||
|             buf = qemu_malloc(BLOCK_SIZE); | ||||
|  | ||||
|             qemu_get_buffer(f, buf, BLOCK_SIZE); | ||||
|             ret = bdrv_write(bs, addr, buf, nr_sectors); | ||||
|             bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK); | ||||
|  | ||||
|             qemu_free(buf); | ||||
|             if (ret < 0) { | ||||
|                 return ret; | ||||
|             } | ||||
|         } else if (flags & BLK_MIG_FLAG_PROGRESS) { | ||||
|             if (!banner_printed) { | ||||
|                 printf("Receiving block device images\n"); | ||||
| @@ -727,6 +536,6 @@ void blk_mig_init(void) | ||||
|     QSIMPLEQ_INIT(&block_mig_state.bmds_list); | ||||
|     QSIMPLEQ_INIT(&block_mig_state.blk_list); | ||||
|  | ||||
|     register_savevm_live(NULL, "block", 0, 1, block_set_params, | ||||
|                          block_save_live, NULL, block_load, &block_mig_state); | ||||
|     register_savevm_live("block", 0, 1, block_set_params, block_save_live, | ||||
|                          NULL, block_load, &block_mig_state); | ||||
| } | ||||
|   | ||||
							
								
								
									
										133
									
								
								block.h
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								block.h
									
									
									
									
									
								
							| @@ -27,31 +27,25 @@ typedef struct QEMUSnapshotInfo { | ||||
|     uint64_t vm_clock_nsec; /* VM clock relative to boot */ | ||||
| } QEMUSnapshotInfo; | ||||
|  | ||||
| #define BDRV_O_RDONLY      0x0000 | ||||
| #define BDRV_O_RDWR        0x0002 | ||||
| #define BDRV_O_ACCESS      0x0003 | ||||
| #define BDRV_O_CREAT       0x0004 /* create an empty file */ | ||||
| #define BDRV_O_SNAPSHOT    0x0008 /* open the file read only and save writes in a snapshot */ | ||||
| #define BDRV_O_FILE        0x0010 /* open as a raw file (do not try to | ||||
|                                      use a disk image format on top of | ||||
|                                      it (default for | ||||
|                                      bdrv_file_open()) */ | ||||
| #define BDRV_O_NOCACHE     0x0020 /* do not use the host page cache */ | ||||
| #define BDRV_O_CACHE_WB    0x0040 /* use write-back caching */ | ||||
| #define BDRV_O_NATIVE_AIO  0x0080 /* use native AIO instead of the thread pool */ | ||||
| #define BDRV_O_NO_BACKING  0x0100 /* don't open the backing file */ | ||||
| #define BDRV_O_NO_FLUSH    0x0200 /* disable flushing on this disk */ | ||||
|  | ||||
| #define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) | ||||
| #define BDRV_O_CACHE_MASK  (BDRV_O_NOCACHE | BDRV_O_CACHE_WB) | ||||
|  | ||||
| #define BDRV_SECTOR_BITS   9 | ||||
| #define BDRV_SECTOR_SIZE   (1ULL << BDRV_SECTOR_BITS) | ||||
| #define BDRV_SECTOR_MASK   ~(BDRV_SECTOR_SIZE - 1) | ||||
| #define BDRV_SECTOR_SIZE   (1 << BDRV_SECTOR_BITS) | ||||
| #define BDRV_SECTOR_MASK   ~(BDRV_SECTOR_SIZE - 1); | ||||
|  | ||||
| typedef enum { | ||||
|     BLOCK_ERR_REPORT, BLOCK_ERR_IGNORE, BLOCK_ERR_STOP_ENOSPC, | ||||
|     BLOCK_ERR_STOP_ANY | ||||
| } BlockErrorAction; | ||||
|  | ||||
| typedef enum { | ||||
|     BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP | ||||
| } BlockMonEventAction; | ||||
|  | ||||
| void bdrv_mon_event(const BlockDriverState *bdrv, | ||||
|                     BlockMonEventAction action, int is_read); | ||||
| void bdrv_info_print(Monitor *mon, const QObject *data); | ||||
| void bdrv_info(Monitor *mon, QObject **ret_data); | ||||
| void bdrv_stats_print(Monitor *mon, const QObject *data); | ||||
| @@ -59,22 +53,22 @@ void bdrv_info_stats(Monitor *mon, QObject **ret_data); | ||||
|  | ||||
| void bdrv_init(void); | ||||
| void bdrv_init_with_whitelist(void); | ||||
| BlockDriver *bdrv_find_protocol(const char *filename); | ||||
| BlockDriver *bdrv_find_format(const char *format_name); | ||||
| BlockDriver *bdrv_find_whitelisted_format(const char *format_name); | ||||
| int bdrv_create(BlockDriver *drv, const char* filename, | ||||
|     QEMUOptionParameter *options); | ||||
| int bdrv_create_file(const char* filename, QEMUOptionParameter *options); | ||||
| int bdrv_create2(BlockDriver *drv, | ||||
|                  const char *filename, int64_t size_in_sectors, | ||||
|                  const char *backing_file, const char *backing_format, | ||||
|                  int flags); | ||||
| BlockDriverState *bdrv_new(const char *device_name); | ||||
| void bdrv_make_anon(BlockDriverState *bs); | ||||
| void bdrv_delete(BlockDriverState *bs); | ||||
| int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags); | ||||
| int bdrv_open(BlockDriverState *bs, const char *filename, int flags, | ||||
|               BlockDriver *drv); | ||||
| int bdrv_open(BlockDriverState *bs, const char *filename, int flags); | ||||
| int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, | ||||
|                BlockDriver *drv); | ||||
| void bdrv_close(BlockDriverState *bs); | ||||
| int bdrv_attach(BlockDriverState *bs, DeviceState *qdev); | ||||
| void bdrv_detach(BlockDriverState *bs, DeviceState *qdev); | ||||
| DeviceState *bdrv_get_attached(BlockDriverState *bs); | ||||
| int bdrv_check(BlockDriverState *bs); | ||||
| int bdrv_read(BlockDriverState *bs, int64_t sector_num, | ||||
|               uint8_t *buf, int nb_sectors); | ||||
| int bdrv_write(BlockDriverState *bs, int64_t sector_num, | ||||
| @@ -92,20 +86,8 @@ int64_t bdrv_getlength(BlockDriverState *bs); | ||||
| void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); | ||||
| void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); | ||||
| int bdrv_commit(BlockDriverState *bs); | ||||
| void bdrv_commit_all(void); | ||||
| int bdrv_change_backing_file(BlockDriverState *bs, | ||||
|     const char *backing_file, const char *backing_fmt); | ||||
| void bdrv_register(BlockDriver *bdrv); | ||||
|  | ||||
|  | ||||
| typedef struct BdrvCheckResult { | ||||
|     int corruptions; | ||||
|     int leaks; | ||||
|     int check_errors; | ||||
| } BdrvCheckResult; | ||||
|  | ||||
| int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res); | ||||
|  | ||||
| /* async block I/O */ | ||||
| typedef struct BlockDriverAIOCB BlockDriverAIOCB; | ||||
| typedef void BlockDriverCompletionFunc(void *opaque, int ret); | ||||
| @@ -143,12 +125,9 @@ BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs, | ||||
|         BlockDriverCompletionFunc *cb, void *opaque); | ||||
|  | ||||
| /* Ensure contents are flushed to disk.  */ | ||||
| int bdrv_flush(BlockDriverState *bs); | ||||
| void bdrv_flush(BlockDriverState *bs); | ||||
| void bdrv_flush_all(void); | ||||
| void bdrv_close_all(void); | ||||
|  | ||||
| int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); | ||||
| int bdrv_has_zero_init(BlockDriverState *bs); | ||||
| int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, | ||||
| 	int *pnum); | ||||
|  | ||||
| @@ -169,12 +148,9 @@ void bdrv_get_geometry_hint(BlockDriverState *bs, | ||||
|                             int *pcyls, int *pheads, int *psecs); | ||||
| int bdrv_get_type_hint(BlockDriverState *bs); | ||||
| int bdrv_get_translation_hint(BlockDriverState *bs); | ||||
| void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error, | ||||
|                        BlockErrorAction on_write_error); | ||||
| BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read); | ||||
| void bdrv_set_removable(BlockDriverState *bs, int removable); | ||||
| int bdrv_is_removable(BlockDriverState *bs); | ||||
| int bdrv_is_read_only(BlockDriverState *bs); | ||||
| int bdrv_set_read_only(BlockDriverState *bs, int read_only); | ||||
| int bdrv_is_sg(BlockDriverState *bs); | ||||
| int bdrv_enable_write_cache(BlockDriverState *bs); | ||||
| int bdrv_is_inserted(BlockDriverState *bs); | ||||
| @@ -183,11 +159,9 @@ int bdrv_is_locked(BlockDriverState *bs); | ||||
| void bdrv_set_locked(BlockDriverState *bs, int locked); | ||||
| int bdrv_eject(BlockDriverState *bs, int eject_flag); | ||||
| void bdrv_set_change_cb(BlockDriverState *bs, | ||||
|                         void (*change_cb)(void *opaque, int reason), | ||||
|                         void *opaque); | ||||
|                         void (*change_cb)(void *opaque), void *opaque); | ||||
| void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size); | ||||
| BlockDriverState *bdrv_find(const char *name); | ||||
| BlockDriverState *bdrv_next(BlockDriverState *bs); | ||||
| void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), | ||||
|                   void *opaque); | ||||
| int bdrv_is_encrypted(BlockDriverState *bs); | ||||
| @@ -204,9 +178,6 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); | ||||
| const char *bdrv_get_encrypted_filename(BlockDriverState *bs); | ||||
| void bdrv_get_backing_filename(BlockDriverState *bs, | ||||
|                                char *filename, int filename_size); | ||||
| int bdrv_can_snapshot(BlockDriverState *bs); | ||||
| int bdrv_is_snapshot(BlockDriverState *bs); | ||||
| BlockDriverState *bdrv_snapshots(void); | ||||
| int bdrv_snapshot_create(BlockDriverState *bs, | ||||
|                          QEMUSnapshotInfo *sn_info); | ||||
| int bdrv_snapshot_goto(BlockDriverState *bs, | ||||
| @@ -214,8 +185,6 @@ int bdrv_snapshot_goto(BlockDriverState *bs, | ||||
| int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); | ||||
| int bdrv_snapshot_list(BlockDriverState *bs, | ||||
|                        QEMUSnapshotInfo **psn_info); | ||||
| int bdrv_snapshot_load_tmp(BlockDriverState *bs, | ||||
|                            const char *snapshot_name); | ||||
| char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); | ||||
|  | ||||
| char *get_human_readable_size(char *buf, int buf_size, int64_t size); | ||||
| @@ -230,70 +199,10 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, | ||||
| int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, | ||||
|                       int64_t pos, int size); | ||||
|  | ||||
| int bdrv_img_create(const char *filename, const char *fmt, | ||||
|                     const char *base_filename, const char *base_fmt, | ||||
|                     char *options, uint64_t img_size, int flags); | ||||
|  | ||||
| #define BDRV_SECTORS_PER_DIRTY_CHUNK 2048 | ||||
|  | ||||
| void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable); | ||||
| int bdrv_get_dirty(BlockDriverState *bs, int64_t sector); | ||||
| void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, | ||||
|                       int nr_sectors); | ||||
| int64_t bdrv_get_dirty_count(BlockDriverState *bs); | ||||
|  | ||||
| void bdrv_set_in_use(BlockDriverState *bs, int in_use); | ||||
| int bdrv_in_use(BlockDriverState *bs); | ||||
|  | ||||
| typedef enum { | ||||
|     BLKDBG_L1_UPDATE, | ||||
|  | ||||
|     BLKDBG_L1_GROW_ALLOC_TABLE, | ||||
|     BLKDBG_L1_GROW_WRITE_TABLE, | ||||
|     BLKDBG_L1_GROW_ACTIVATE_TABLE, | ||||
|  | ||||
|     BLKDBG_L2_LOAD, | ||||
|     BLKDBG_L2_UPDATE, | ||||
|     BLKDBG_L2_UPDATE_COMPRESSED, | ||||
|     BLKDBG_L2_ALLOC_COW_READ, | ||||
|     BLKDBG_L2_ALLOC_WRITE, | ||||
|  | ||||
|     BLKDBG_READ, | ||||
|     BLKDBG_READ_AIO, | ||||
|     BLKDBG_READ_BACKING, | ||||
|     BLKDBG_READ_BACKING_AIO, | ||||
|     BLKDBG_READ_COMPRESSED, | ||||
|  | ||||
|     BLKDBG_WRITE_AIO, | ||||
|     BLKDBG_WRITE_COMPRESSED, | ||||
|  | ||||
|     BLKDBG_VMSTATE_LOAD, | ||||
|     BLKDBG_VMSTATE_SAVE, | ||||
|  | ||||
|     BLKDBG_COW_READ, | ||||
|     BLKDBG_COW_WRITE, | ||||
|  | ||||
|     BLKDBG_REFTABLE_LOAD, | ||||
|     BLKDBG_REFTABLE_GROW, | ||||
|  | ||||
|     BLKDBG_REFBLOCK_LOAD, | ||||
|     BLKDBG_REFBLOCK_UPDATE, | ||||
|     BLKDBG_REFBLOCK_UPDATE_PART, | ||||
|     BLKDBG_REFBLOCK_ALLOC, | ||||
|     BLKDBG_REFBLOCK_ALLOC_HOOKUP, | ||||
|     BLKDBG_REFBLOCK_ALLOC_WRITE, | ||||
|     BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS, | ||||
|     BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE, | ||||
|     BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE, | ||||
|  | ||||
|     BLKDBG_CLUSTER_ALLOC, | ||||
|     BLKDBG_CLUSTER_ALLOC_BYTES, | ||||
|     BLKDBG_CLUSTER_FREE, | ||||
|  | ||||
|     BLKDBG_EVENT_MAX, | ||||
| } BlkDebugEvent; | ||||
|  | ||||
| #define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt) | ||||
| void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										471
									
								
								block/blkdebug.c
									
									
									
									
									
								
							
							
						
						
									
										471
									
								
								block/blkdebug.c
									
									
									
									
									
								
							| @@ -1,471 +0,0 @@ | ||||
| /* | ||||
|  * Block protocol for I/O error injection | ||||
|  * | ||||
|  * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com> | ||||
|  * | ||||
|  * 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 "qemu-common.h" | ||||
| #include "block_int.h" | ||||
| #include "module.h" | ||||
|  | ||||
| typedef struct BlkdebugVars { | ||||
|     int state; | ||||
|  | ||||
|     /* If inject_errno != 0, an error is injected for requests */ | ||||
|     int inject_errno; | ||||
|  | ||||
|     /* Decides if all future requests fail (false) or only the next one and | ||||
|      * after the next request inject_errno is reset to 0 (true) */ | ||||
|     bool inject_once; | ||||
|  | ||||
|     /* Decides if aio_readv/writev fails right away (true) or returns an error | ||||
|      * return value only in the callback (false) */ | ||||
|     bool inject_immediately; | ||||
| } BlkdebugVars; | ||||
|  | ||||
| typedef struct BDRVBlkdebugState { | ||||
|     BlkdebugVars vars; | ||||
|     QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; | ||||
| } BDRVBlkdebugState; | ||||
|  | ||||
| typedef struct BlkdebugAIOCB { | ||||
|     BlockDriverAIOCB common; | ||||
|     QEMUBH *bh; | ||||
|     int ret; | ||||
| } BlkdebugAIOCB; | ||||
|  | ||||
| static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb); | ||||
|  | ||||
| static AIOPool blkdebug_aio_pool = { | ||||
|     .aiocb_size = sizeof(BlkdebugAIOCB), | ||||
|     .cancel     = blkdebug_aio_cancel, | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|     ACTION_INJECT_ERROR, | ||||
|     ACTION_SET_STATE, | ||||
| }; | ||||
|  | ||||
| typedef struct BlkdebugRule { | ||||
|     BlkDebugEvent event; | ||||
|     int action; | ||||
|     int state; | ||||
|     union { | ||||
|         struct { | ||||
|             int error; | ||||
|             int immediately; | ||||
|             int once; | ||||
|         } inject; | ||||
|         struct { | ||||
|             int new_state; | ||||
|         } set_state; | ||||
|     } options; | ||||
|     QLIST_ENTRY(BlkdebugRule) next; | ||||
| } BlkdebugRule; | ||||
|  | ||||
| static QemuOptsList inject_error_opts = { | ||||
|     .name = "inject-error", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), | ||||
|     .desc = { | ||||
|         { | ||||
|             .name = "event", | ||||
|             .type = QEMU_OPT_STRING, | ||||
|         }, | ||||
|         { | ||||
|             .name = "state", | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|         }, | ||||
|         { | ||||
|             .name = "errno", | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|         }, | ||||
|         { | ||||
|             .name = "once", | ||||
|             .type = QEMU_OPT_BOOL, | ||||
|         }, | ||||
|         { | ||||
|             .name = "immediately", | ||||
|             .type = QEMU_OPT_BOOL, | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static QemuOptsList set_state_opts = { | ||||
|     .name = "set-state", | ||||
|     .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head), | ||||
|     .desc = { | ||||
|         { | ||||
|             .name = "event", | ||||
|             .type = QEMU_OPT_STRING, | ||||
|         }, | ||||
|         { | ||||
|             .name = "state", | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|         }, | ||||
|         { | ||||
|             .name = "new_state", | ||||
|             .type = QEMU_OPT_NUMBER, | ||||
|         }, | ||||
|         { /* end of list */ } | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| static QemuOptsList *config_groups[] = { | ||||
|     &inject_error_opts, | ||||
|     &set_state_opts, | ||||
|     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]                           = "read", | ||||
|     [BLKDBG_READ_AIO]                       = "read_aio", | ||||
|     [BLKDBG_READ_BACKING]                   = "read_backing", | ||||
|     [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_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", | ||||
| }; | ||||
|  | ||||
| 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)) { | ||||
|             *event = i; | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| struct add_rule_data { | ||||
|     BDRVBlkdebugState *s; | ||||
|     int action; | ||||
| }; | ||||
|  | ||||
| static int add_rule(QemuOpts *opts, void *opaque) | ||||
| { | ||||
|     struct add_rule_data *d = opaque; | ||||
|     BDRVBlkdebugState *s = d->s; | ||||
|     const char* event_name; | ||||
|     BlkDebugEvent event; | ||||
|     struct BlkdebugRule *rule; | ||||
|  | ||||
|     /* Find the right event for the rule */ | ||||
|     event_name = qemu_opt_get(opts, "event"); | ||||
|     if (!event_name || get_event_by_name(event_name, &event) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* Set attributes common for all actions */ | ||||
|     rule = qemu_mallocz(sizeof(*rule)); | ||||
|     *rule = (struct BlkdebugRule) { | ||||
|         .event  = event, | ||||
|         .action = d->action, | ||||
|         .state  = qemu_opt_get_number(opts, "state", 0), | ||||
|     }; | ||||
|  | ||||
|     /* Parse action-specific options */ | ||||
|     switch (d->action) { | ||||
|     case ACTION_INJECT_ERROR: | ||||
|         rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO); | ||||
|         rule->options.inject.once  = qemu_opt_get_bool(opts, "once", 0); | ||||
|         rule->options.inject.immediately = | ||||
|             qemu_opt_get_bool(opts, "immediately", 0); | ||||
|         break; | ||||
|  | ||||
|     case ACTION_SET_STATE: | ||||
|         rule->options.set_state.new_state = | ||||
|             qemu_opt_get_number(opts, "new_state", 0); | ||||
|         break; | ||||
|     }; | ||||
|  | ||||
|     /* Add the rule */ | ||||
|     QLIST_INSERT_HEAD(&s->rules[event], rule, next); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int read_config(BDRVBlkdebugState *s, const char *filename) | ||||
| { | ||||
|     FILE *f; | ||||
|     int ret; | ||||
|     struct add_rule_data d; | ||||
|  | ||||
|     f = fopen(filename, "r"); | ||||
|     if (f == NULL) { | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     ret = qemu_config_parse(f, config_groups, filename); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     d.s = s; | ||||
|     d.action = ACTION_INJECT_ERROR; | ||||
|     qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0); | ||||
|  | ||||
|     d.action = ACTION_SET_STATE; | ||||
|     qemu_opts_foreach(&set_state_opts, add_rule, &d, 0); | ||||
|  | ||||
|     ret = 0; | ||||
| fail: | ||||
|     qemu_opts_reset(&inject_error_opts); | ||||
|     qemu_opts_reset(&set_state_opts); | ||||
|     fclose(f); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ | ||||
| static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|     int ret; | ||||
|     char *config, *c; | ||||
|  | ||||
|     /* Parse the blkdebug: prefix */ | ||||
|     if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     filename += strlen("blkdebug:"); | ||||
|  | ||||
|     /* Read rules from config file */ | ||||
|     c = strchr(filename, ':'); | ||||
|     if (c == NULL) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     config = strdup(filename); | ||||
|     config[c - filename] = '\0'; | ||||
|     ret = read_config(s, config); | ||||
|     free(config); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|     filename = c + 1; | ||||
|  | ||||
|     /* Set initial state */ | ||||
|     s->vars.state = 1; | ||||
|  | ||||
|     /* Open the backing file */ | ||||
|     ret = bdrv_file_open(&bs->file, filename, flags); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void error_callback_bh(void *opaque) | ||||
| { | ||||
|     struct BlkdebugAIOCB *acb = opaque; | ||||
|     qemu_bh_delete(acb->bh); | ||||
|     acb->common.cb(acb->common.opaque, acb->ret); | ||||
|     qemu_aio_release(acb); | ||||
| } | ||||
|  | ||||
| static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb) | ||||
| { | ||||
|     BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common); | ||||
|     qemu_aio_release(acb); | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *inject_error(BlockDriverState *bs, | ||||
|     BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|     int error = s->vars.inject_errno; | ||||
|     struct BlkdebugAIOCB *acb; | ||||
|     QEMUBH *bh; | ||||
|  | ||||
|     if (s->vars.inject_once) { | ||||
|         s->vars.inject_errno = 0; | ||||
|     } | ||||
|  | ||||
|     if (s->vars.inject_immediately) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     acb = qemu_aio_get(&blkdebug_aio_pool, bs, cb, opaque); | ||||
|     acb->ret = -error; | ||||
|  | ||||
|     bh = qemu_bh_new(error_callback_bh, acb); | ||||
|     acb->bh = bh; | ||||
|     qemu_bh_schedule(bh); | ||||
|  | ||||
|     return &acb->common; | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs, | ||||
|     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|     BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|  | ||||
|     if (s->vars.inject_errno) { | ||||
|         return inject_error(bs, cb, opaque); | ||||
|     } | ||||
|  | ||||
|     BlockDriverAIOCB *acb = | ||||
|         bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); | ||||
|     return acb; | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs, | ||||
|     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|     BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|  | ||||
|     if (s->vars.inject_errno) { | ||||
|         return inject_error(bs, cb, opaque); | ||||
|     } | ||||
|  | ||||
|     BlockDriverAIOCB *acb = | ||||
|         bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); | ||||
|     return acb; | ||||
| } | ||||
|  | ||||
| static void blkdebug_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|     BlkdebugRule *rule, *next; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < BLKDBG_EVENT_MAX; i++) { | ||||
|         QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { | ||||
|             QLIST_REMOVE(rule, next); | ||||
|             qemu_free(rule); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int blkdebug_flush(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_flush(bs->file); | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs, | ||||
|     BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     return bdrv_aio_flush(bs->file, cb, opaque); | ||||
| } | ||||
|  | ||||
| static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, | ||||
|     BlkdebugVars *old_vars) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|     BlkdebugVars *vars = &s->vars; | ||||
|  | ||||
|     /* Only process rules for the current state */ | ||||
|     if (rule->state && rule->state != old_vars->state) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /* Take the action */ | ||||
|     switch (rule->action) { | ||||
|     case ACTION_INJECT_ERROR: | ||||
|         vars->inject_errno       = rule->options.inject.error; | ||||
|         vars->inject_once        = rule->options.inject.once; | ||||
|         vars->inject_immediately = rule->options.inject.immediately; | ||||
|         break; | ||||
|  | ||||
|     case ACTION_SET_STATE: | ||||
|         vars->state              = rule->options.set_state.new_state; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) | ||||
| { | ||||
|     BDRVBlkdebugState *s = bs->opaque; | ||||
|     struct BlkdebugRule *rule; | ||||
|     BlkdebugVars old_vars = s->vars; | ||||
|  | ||||
|     assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); | ||||
|  | ||||
|     QLIST_FOREACH(rule, &s->rules[event], next) { | ||||
|         process_rule(bs, rule, &old_vars); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_blkdebug = { | ||||
|     .format_name        = "blkdebug", | ||||
|     .protocol_name      = "blkdebug", | ||||
|  | ||||
|     .instance_size      = sizeof(BDRVBlkdebugState), | ||||
|  | ||||
|     .bdrv_file_open     = blkdebug_open, | ||||
|     .bdrv_close         = blkdebug_close, | ||||
|     .bdrv_flush         = blkdebug_flush, | ||||
|  | ||||
|     .bdrv_aio_readv     = blkdebug_aio_readv, | ||||
|     .bdrv_aio_writev    = blkdebug_aio_writev, | ||||
|     .bdrv_aio_flush     = blkdebug_aio_flush, | ||||
|  | ||||
|     .bdrv_debug_event   = blkdebug_debug_event, | ||||
| }; | ||||
|  | ||||
| static void bdrv_blkdebug_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_blkdebug); | ||||
| } | ||||
|  | ||||
| block_init(bdrv_blkdebug_init); | ||||
| @@ -1,383 +0,0 @@ | ||||
| /* | ||||
|  * Block protocol for block driver correctness testing | ||||
|  * | ||||
|  * Copyright (C) 2010 IBM, Corp. | ||||
|  * | ||||
|  * 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 <stdarg.h> | ||||
| #include "qemu_socket.h" /* for EINPROGRESS on Windows */ | ||||
| #include "block_int.h" | ||||
|  | ||||
| typedef struct { | ||||
|     BlockDriverState *test_file; | ||||
| } BDRVBlkverifyState; | ||||
|  | ||||
| typedef struct BlkverifyAIOCB BlkverifyAIOCB; | ||||
| struct BlkverifyAIOCB { | ||||
|     BlockDriverAIOCB common; | ||||
|     QEMUBH *bh; | ||||
|  | ||||
|     /* Request metadata */ | ||||
|     bool is_write; | ||||
|     int64_t sector_num; | ||||
|     int nb_sectors; | ||||
|  | ||||
|     int ret;                    /* first completed request's result */ | ||||
|     unsigned int done;          /* completion counter */ | ||||
|     bool *finished;             /* completion signal for cancel */ | ||||
|  | ||||
|     QEMUIOVector *qiov;         /* user I/O vector */ | ||||
|     QEMUIOVector raw_qiov;      /* cloned I/O vector for raw file */ | ||||
|     void *buf;                  /* buffer for raw file I/O */ | ||||
|  | ||||
|     void (*verify)(BlkverifyAIOCB *acb); | ||||
| }; | ||||
|  | ||||
| static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb) | ||||
| { | ||||
|     BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb; | ||||
|     bool finished = false; | ||||
|  | ||||
|     /* Wait until request completes, invokes its callback, and frees itself */ | ||||
|     acb->finished = &finished; | ||||
|     while (!finished) { | ||||
|         qemu_aio_wait(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static AIOPool blkverify_aio_pool = { | ||||
|     .aiocb_size         = sizeof(BlkverifyAIOCB), | ||||
|     .cancel             = blkverify_aio_cancel, | ||||
| }; | ||||
|  | ||||
| static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb, | ||||
|                                              const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
|     va_start(ap, fmt); | ||||
|     fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ", | ||||
|             acb->is_write ? "write" : "read", acb->sector_num, | ||||
|             acb->nb_sectors); | ||||
|     vfprintf(stderr, fmt, ap); | ||||
|     fprintf(stderr, "\n"); | ||||
|     va_end(ap); | ||||
|     exit(1); | ||||
| } | ||||
|  | ||||
| /* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */ | ||||
| static int blkverify_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|     int ret; | ||||
|     char *raw, *c; | ||||
|  | ||||
|     /* Parse the blkverify: prefix */ | ||||
|     if (strncmp(filename, "blkverify:", strlen("blkverify:"))) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|     filename += strlen("blkverify:"); | ||||
|  | ||||
|     /* Parse the raw image filename */ | ||||
|     c = strchr(filename, ':'); | ||||
|     if (c == NULL) { | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     raw = strdup(filename); | ||||
|     raw[c - filename] = '\0'; | ||||
|     ret = bdrv_file_open(&bs->file, raw, flags); | ||||
|     free(raw); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|     filename = c + 1; | ||||
|  | ||||
|     /* Open the test file */ | ||||
|     s->test_file = bdrv_new(""); | ||||
|     ret = bdrv_open(s->test_file, filename, flags, NULL); | ||||
|     if (ret < 0) { | ||||
|         bdrv_delete(s->test_file); | ||||
|         s->test_file = NULL; | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void blkverify_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     bdrv_delete(s->test_file); | ||||
|     s->test_file = NULL; | ||||
| } | ||||
|  | ||||
| static int blkverify_flush(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     /* Only flush test file, the raw file is not important */ | ||||
|     return bdrv_flush(s->test_file); | ||||
| } | ||||
|  | ||||
| static int64_t blkverify_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     return bdrv_getlength(s->test_file); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Check that I/O vector contents are identical | ||||
|  * | ||||
|  * @a:          I/O vector | ||||
|  * @b:          I/O vector | ||||
|  * @ret:        Offset to first mismatching byte or -1 if match | ||||
|  */ | ||||
| static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) | ||||
| { | ||||
|     int i; | ||||
|     ssize_t offset = 0; | ||||
|  | ||||
|     assert(a->niov == b->niov); | ||||
|     for (i = 0; i < a->niov; i++) { | ||||
|         size_t len = 0; | ||||
|         uint8_t *p = (uint8_t *)a->iov[i].iov_base; | ||||
|         uint8_t *q = (uint8_t *)b->iov[i].iov_base; | ||||
|  | ||||
|         assert(a->iov[i].iov_len == b->iov[i].iov_len); | ||||
|         while (len < a->iov[i].iov_len && *p++ == *q++) { | ||||
|             len++; | ||||
|         } | ||||
|  | ||||
|         offset += len; | ||||
|  | ||||
|         if (len != a->iov[i].iov_len) { | ||||
|             return offset; | ||||
|         } | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     int src_index; | ||||
|     struct iovec *src_iov; | ||||
|     void *dest_base; | ||||
| } IOVectorSortElem; | ||||
|  | ||||
| static int sortelem_cmp_src_base(const void *a, const void *b) | ||||
| { | ||||
|     const IOVectorSortElem *elem_a = a; | ||||
|     const IOVectorSortElem *elem_b = b; | ||||
|  | ||||
|     /* Don't overflow */ | ||||
|     if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) { | ||||
|         return -1; | ||||
|     } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) { | ||||
|         return 1; | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int sortelem_cmp_src_index(const void *a, const void *b) | ||||
| { | ||||
|     const IOVectorSortElem *elem_a = a; | ||||
|     const IOVectorSortElem *elem_b = b; | ||||
|  | ||||
|     return elem_a->src_index - elem_b->src_index; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Copy contents of I/O vector | ||||
|  * | ||||
|  * The relative relationships of overlapping iovecs are preserved.  This is | ||||
|  * necessary to ensure identical semantics in the cloned I/O vector. | ||||
|  */ | ||||
| static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, | ||||
|                                   void *buf) | ||||
| { | ||||
|     IOVectorSortElem sortelems[src->niov]; | ||||
|     void *last_end; | ||||
|     int i; | ||||
|  | ||||
|     /* Sort by source iovecs by base address */ | ||||
|     for (i = 0; i < src->niov; i++) { | ||||
|         sortelems[i].src_index = i; | ||||
|         sortelems[i].src_iov = &src->iov[i]; | ||||
|     } | ||||
|     qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base); | ||||
|  | ||||
|     /* Allocate buffer space taking into account overlapping iovecs */ | ||||
|     last_end = NULL; | ||||
|     for (i = 0; i < src->niov; i++) { | ||||
|         struct iovec *cur = sortelems[i].src_iov; | ||||
|         ptrdiff_t rewind = 0; | ||||
|  | ||||
|         /* Detect overlap */ | ||||
|         if (last_end && last_end > cur->iov_base) { | ||||
|             rewind = last_end - cur->iov_base; | ||||
|         } | ||||
|  | ||||
|         sortelems[i].dest_base = buf - rewind; | ||||
|         buf += cur->iov_len - MIN(rewind, cur->iov_len); | ||||
|         last_end = MAX(cur->iov_base + cur->iov_len, last_end); | ||||
|     } | ||||
|  | ||||
|     /* Sort by source iovec index and build destination iovec */ | ||||
|     qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index); | ||||
|     for (i = 0; i < src->niov; i++) { | ||||
|         qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, | ||||
|                                          int64_t sector_num, QEMUIOVector *qiov, | ||||
|                                          int nb_sectors, | ||||
|                                          BlockDriverCompletionFunc *cb, | ||||
|                                          void *opaque) | ||||
| { | ||||
|     BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aio_pool, bs, cb, opaque); | ||||
|  | ||||
|     acb->bh = NULL; | ||||
|     acb->is_write = is_write; | ||||
|     acb->sector_num = sector_num; | ||||
|     acb->nb_sectors = nb_sectors; | ||||
|     acb->ret = -EINPROGRESS; | ||||
|     acb->done = 0; | ||||
|     acb->qiov = qiov; | ||||
|     acb->buf = NULL; | ||||
|     acb->verify = NULL; | ||||
|     acb->finished = NULL; | ||||
|     return acb; | ||||
| } | ||||
|  | ||||
| static void blkverify_aio_bh(void *opaque) | ||||
| { | ||||
|     BlkverifyAIOCB *acb = opaque; | ||||
|  | ||||
|     qemu_bh_delete(acb->bh); | ||||
|     if (acb->buf) { | ||||
|         qemu_iovec_destroy(&acb->raw_qiov); | ||||
|         qemu_vfree(acb->buf); | ||||
|     } | ||||
|     acb->common.cb(acb->common.opaque, acb->ret); | ||||
|     if (acb->finished) { | ||||
|         *acb->finished = true; | ||||
|     } | ||||
|     qemu_aio_release(acb); | ||||
| } | ||||
|  | ||||
| static void blkverify_aio_cb(void *opaque, int ret) | ||||
| { | ||||
|     BlkverifyAIOCB *acb = opaque; | ||||
|  | ||||
|     switch (++acb->done) { | ||||
|     case 1: | ||||
|         acb->ret = ret; | ||||
|         break; | ||||
|  | ||||
|     case 2: | ||||
|         if (acb->ret != ret) { | ||||
|             blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret); | ||||
|         } | ||||
|  | ||||
|         if (acb->verify) { | ||||
|             acb->verify(acb); | ||||
|         } | ||||
|  | ||||
|         acb->bh = qemu_bh_new(blkverify_aio_bh, acb); | ||||
|         qemu_bh_schedule(acb->bh); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void blkverify_verify_readv(BlkverifyAIOCB *acb) | ||||
| { | ||||
|     ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov); | ||||
|     if (offset != -1) { | ||||
|         blkverify_err(acb, "contents mismatch in sector %" PRId64, | ||||
|                       acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs, | ||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|         BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|     BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov, | ||||
|                                             nb_sectors, cb, opaque); | ||||
|  | ||||
|     acb->verify = blkverify_verify_readv; | ||||
|     acb->buf = qemu_blockalign(bs->file, qiov->size); | ||||
|     qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); | ||||
|     blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf); | ||||
|  | ||||
|     if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, | ||||
|                         blkverify_aio_cb, acb)) { | ||||
|         blkverify_aio_cb(acb, -EIO); | ||||
|     } | ||||
|     if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, | ||||
|                         blkverify_aio_cb, acb)) { | ||||
|         blkverify_aio_cb(acb, -EIO); | ||||
|     } | ||||
|     return &acb->common; | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs, | ||||
|         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|         BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|     BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, | ||||
|                                             nb_sectors, cb, opaque); | ||||
|  | ||||
|     if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, | ||||
|                          blkverify_aio_cb, acb)) { | ||||
|         blkverify_aio_cb(acb, -EIO); | ||||
|     } | ||||
|     if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, | ||||
|                          blkverify_aio_cb, acb)) { | ||||
|         blkverify_aio_cb(acb, -EIO); | ||||
|     } | ||||
|     return &acb->common; | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs, | ||||
|                                              BlockDriverCompletionFunc *cb, | ||||
|                                              void *opaque) | ||||
| { | ||||
|     BDRVBlkverifyState *s = bs->opaque; | ||||
|  | ||||
|     /* Only flush test file, the raw file is not important */ | ||||
|     return bdrv_aio_flush(s->test_file, cb, opaque); | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_blkverify = { | ||||
|     .format_name        = "blkverify", | ||||
|     .protocol_name      = "blkverify", | ||||
|  | ||||
|     .instance_size      = sizeof(BDRVBlkverifyState), | ||||
|  | ||||
|     .bdrv_getlength     = blkverify_getlength, | ||||
|  | ||||
|     .bdrv_file_open     = blkverify_open, | ||||
|     .bdrv_close         = blkverify_close, | ||||
|     .bdrv_flush         = blkverify_flush, | ||||
|  | ||||
|     .bdrv_aio_readv     = blkverify_aio_readv, | ||||
|     .bdrv_aio_writev    = blkverify_aio_writev, | ||||
|     .bdrv_aio_flush     = blkverify_aio_flush, | ||||
| }; | ||||
|  | ||||
| static void bdrv_blkverify_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_blkverify); | ||||
| } | ||||
|  | ||||
| block_init(bdrv_blkverify_init); | ||||
| @@ -80,6 +80,8 @@ struct bochs_header { | ||||
| }; | ||||
|  | ||||
| typedef struct BDRVBochsState { | ||||
|     int fd; | ||||
|  | ||||
|     uint32_t *catalog_bitmap; | ||||
|     int catalog_size; | ||||
|  | ||||
| @@ -107,16 +109,25 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int bochs_open(BlockDriverState *bs, int flags) | ||||
| static int bochs_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVBochsState *s = bs->opaque; | ||||
|     int i; | ||||
|     int fd, i; | ||||
|     struct bochs_header bochs; | ||||
|     struct bochs_header_v1 header_v1; | ||||
|  | ||||
|     fd = open(filename, O_RDWR | O_BINARY); | ||||
|     if (fd < 0) { | ||||
|         fd = open(filename, O_RDONLY | O_BINARY); | ||||
|         if (fd < 0) | ||||
|             return -1; | ||||
|     } | ||||
|  | ||||
|     bs->read_only = 1; // no write support yet | ||||
|  | ||||
|     if (bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)) != sizeof(bochs)) { | ||||
|     s->fd = fd; | ||||
|  | ||||
|     if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
| @@ -135,10 +146,12 @@ static int bochs_open(BlockDriverState *bs, int flags) | ||||
|       bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; | ||||
|     } | ||||
|  | ||||
|     lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET); | ||||
|  | ||||
|     s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog); | ||||
|     s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); | ||||
|     if (bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, | ||||
|                    s->catalog_size * 4) != s->catalog_size * 4) | ||||
|     if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) != | ||||
| 	s->catalog_size * 4) | ||||
| 	goto fail; | ||||
|     for (i = 0; i < s->catalog_size; i++) | ||||
| 	le32_to_cpus(&s->catalog_bitmap[i]); | ||||
| @@ -152,53 +165,68 @@ static int bochs_open(BlockDriverState *bs, int flags) | ||||
|  | ||||
|     return 0; | ||||
|  fail: | ||||
|     close(fd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
| static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
| { | ||||
|     BDRVBochsState *s = bs->opaque; | ||||
|     int64_t offset = sector_num * 512; | ||||
|     int64_t extent_index, extent_offset, bitmap_offset; | ||||
|     int64_t extent_index, extent_offset, bitmap_offset, block_offset; | ||||
|     char bitmap_entry; | ||||
|  | ||||
|     // seek to sector | ||||
|     extent_index = offset / s->extent_size; | ||||
|     extent_offset = (offset % s->extent_size) / 512; | ||||
|  | ||||
|     if (s->catalog_bitmap[extent_index] == 0xffffffff) { | ||||
| 	return -1; /* not allocated */ | ||||
|     if (s->catalog_bitmap[extent_index] == 0xffffffff) | ||||
|     { | ||||
| //	fprintf(stderr, "page not allocated [%x - %x:%x]\n", | ||||
| //	    sector_num, extent_index, extent_offset); | ||||
| 	return -1; // not allocated | ||||
|     } | ||||
|  | ||||
|     bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] * | ||||
| 	(s->extent_blocks + s->bitmap_blocks)); | ||||
|     block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); | ||||
|  | ||||
|     /* read in bitmap for current extent */ | ||||
|     if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), | ||||
|                    &bitmap_entry, 1) != 1) { | ||||
|         return -1; | ||||
| //    fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n", | ||||
| //	sector_num, extent_index, extent_offset, | ||||
| //	le32_to_cpu(s->catalog_bitmap[extent_index]), | ||||
| //	bitmap_offset, block_offset); | ||||
|  | ||||
|     // read in bitmap for current extent | ||||
|     lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET); | ||||
|  | ||||
|     read(s->fd, &bitmap_entry, 1); | ||||
|  | ||||
|     if (!((bitmap_entry >> (extent_offset % 8)) & 1)) | ||||
|     { | ||||
| //	fprintf(stderr, "sector (%x) in bitmap not allocated\n", | ||||
| //	    sector_num); | ||||
| 	return -1; // not allocated | ||||
|     } | ||||
|  | ||||
|     if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { | ||||
| 	return -1; /* not allocated */ | ||||
|     } | ||||
|     lseek(s->fd, block_offset, SEEK_SET); | ||||
|  | ||||
|     return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int bochs_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                     uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     BDRVBochsState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
|         int64_t block_offset = seek_to_sector(bs, sector_num); | ||||
|         if (block_offset >= 0) { | ||||
|             ret = bdrv_pread(bs->file, block_offset, buf, 512); | ||||
|             if (ret != 512) { | ||||
|                 return -1; | ||||
|             } | ||||
|         } else | ||||
| 	if (!seek_to_sector(bs, sector_num)) | ||||
| 	{ | ||||
| 	    ret = read(s->fd, buf, 512); | ||||
| 	    if (ret != 512) | ||||
| 		return -1; | ||||
| 	} | ||||
| 	else | ||||
|             memset(buf, 0, 512); | ||||
|         nb_sectors--; | ||||
|         sector_num++; | ||||
| @@ -211,6 +239,7 @@ static void bochs_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVBochsState *s = bs->opaque; | ||||
|     qemu_free(s->catalog_bitmap); | ||||
|     close(s->fd); | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_bochs = { | ||||
|   | ||||
| @@ -27,6 +27,7 @@ | ||||
| #include <zlib.h> | ||||
|  | ||||
| typedef struct BDRVCloopState { | ||||
|     int fd; | ||||
|     uint32_t block_size; | ||||
|     uint32_t n_blocks; | ||||
|     uint64_t* offsets; | ||||
| @@ -50,31 +51,34 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int cloop_open(BlockDriverState *bs, int flags) | ||||
| static int cloop_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVCloopState *s = bs->opaque; | ||||
|     uint32_t offsets_size,max_compressed_block_size=1,i; | ||||
|  | ||||
|     s->fd = open(filename, O_RDONLY | O_BINARY); | ||||
|     if (s->fd < 0) | ||||
|         return -errno; | ||||
|     bs->read_only = 1; | ||||
|  | ||||
|     /* read header */ | ||||
|     if (bdrv_pread(bs->file, 128, &s->block_size, 4) < 4) { | ||||
|         goto cloop_close; | ||||
|     if(lseek(s->fd,128,SEEK_SET)<0) { | ||||
| cloop_close: | ||||
| 	close(s->fd); | ||||
| 	return -1; | ||||
|     } | ||||
|     s->block_size = be32_to_cpu(s->block_size); | ||||
|  | ||||
|     if (bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4) < 4) { | ||||
|         goto cloop_close; | ||||
|     } | ||||
|     s->n_blocks = be32_to_cpu(s->n_blocks); | ||||
|     if(read(s->fd,&s->block_size,4)<4) | ||||
| 	goto cloop_close; | ||||
|     s->block_size=be32_to_cpu(s->block_size); | ||||
|     if(read(s->fd,&s->n_blocks,4)<4) | ||||
| 	goto cloop_close; | ||||
|     s->n_blocks=be32_to_cpu(s->n_blocks); | ||||
|  | ||||
|     /* read offsets */ | ||||
|     offsets_size = s->n_blocks * sizeof(uint64_t); | ||||
|     s->offsets = qemu_malloc(offsets_size); | ||||
|     if (bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size) < | ||||
|             offsets_size) { | ||||
|     offsets_size=s->n_blocks*sizeof(uint64_t); | ||||
|     s->offsets=(uint64_t*)qemu_malloc(offsets_size); | ||||
|     if(read(s->fd,s->offsets,offsets_size)<offsets_size) | ||||
| 	goto cloop_close; | ||||
|     } | ||||
|     for(i=0;i<s->n_blocks;i++) { | ||||
| 	s->offsets[i]=be64_to_cpu(s->offsets[i]); | ||||
| 	if(i>0) { | ||||
| @@ -94,21 +98,16 @@ static int cloop_open(BlockDriverState *bs, int flags) | ||||
|     s->sectors_per_block = s->block_size/512; | ||||
|     bs->total_sectors = s->n_blocks*s->sectors_per_block; | ||||
|     return 0; | ||||
|  | ||||
| cloop_close: | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static inline int cloop_read_block(BlockDriverState *bs, int block_num) | ||||
| static inline int cloop_read_block(BDRVCloopState *s,int block_num) | ||||
| { | ||||
|     BDRVCloopState *s = bs->opaque; | ||||
|  | ||||
|     if(s->current_block != block_num) { | ||||
| 	int ret; | ||||
|         uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num]; | ||||
|  | ||||
|         ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block, | ||||
|                          bytes); | ||||
| 	lseek(s->fd, s->offsets[block_num], SEEK_SET); | ||||
|         ret = read(s->fd, s->compressed_block, bytes); | ||||
|         if (ret != bytes) | ||||
|             return -1; | ||||
|  | ||||
| @@ -137,7 +136,7 @@ static int cloop_read(BlockDriverState *bs, int64_t sector_num, | ||||
|     for(i=0;i<nb_sectors;i++) { | ||||
| 	uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block), | ||||
| 	    block_num=(sector_num+i)/s->sectors_per_block; | ||||
| 	if(cloop_read_block(bs, block_num) != 0) | ||||
| 	if(cloop_read_block(s, block_num) != 0) | ||||
| 	    return -1; | ||||
| 	memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512); | ||||
|     } | ||||
| @@ -147,6 +146,7 @@ static int cloop_read(BlockDriverState *bs, int64_t sector_num, | ||||
| static void cloop_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVCloopState *s = bs->opaque; | ||||
|     close(s->fd); | ||||
|     if(s->n_blocks>0) | ||||
| 	free(s->offsets); | ||||
|     free(s->compressed_block); | ||||
|   | ||||
							
								
								
									
										149
									
								
								block/cow.c
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								block/cow.c
									
									
									
									
									
								
							| @@ -21,9 +21,11 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #ifndef _WIN32 | ||||
| #include "qemu-common.h" | ||||
| #include "block_int.h" | ||||
| #include "module.h" | ||||
| #include <sys/mman.h> | ||||
|  | ||||
| /**************************************************************/ | ||||
| /* COW block driver using file system holes */ | ||||
| @@ -42,6 +44,10 @@ struct cow_header_v2 { | ||||
| }; | ||||
|  | ||||
| typedef struct BDRVCowState { | ||||
|     int fd; | ||||
|     uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ | ||||
|     uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ | ||||
|     int cow_bitmap_size; | ||||
|     int64_t cow_sectors_offset; | ||||
| } BDRVCowState; | ||||
|  | ||||
| @@ -57,16 +63,22 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static int cow_open(BlockDriverState *bs, int flags) | ||||
| static int cow_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVCowState *s = bs->opaque; | ||||
|     int fd; | ||||
|     struct cow_header_v2 cow_header; | ||||
|     int bitmap_size; | ||||
|     int64_t size; | ||||
|  | ||||
|     fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); | ||||
|     if (fd < 0) { | ||||
|         fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | ||||
|         if (fd < 0) | ||||
|             return -1; | ||||
|     } | ||||
|     s->fd = fd; | ||||
|     /* see if it is a cow image */ | ||||
|     if (bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)) != | ||||
|             sizeof(cow_header)) { | ||||
|     if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
| @@ -82,91 +94,61 @@ static int cow_open(BlockDriverState *bs, int flags) | ||||
|     pstrcpy(bs->backing_file, sizeof(bs->backing_file), | ||||
|             cow_header.backing_file); | ||||
|  | ||||
|     bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); | ||||
|     s->cow_sectors_offset = (bitmap_size + 511) & ~511; | ||||
|     /* mmap the bitmap */ | ||||
|     s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); | ||||
|     s->cow_bitmap_addr = (void *)mmap(get_mmap_addr(s->cow_bitmap_size), | ||||
|                                       s->cow_bitmap_size, | ||||
|                                       PROT_READ | PROT_WRITE, | ||||
|                                       MAP_SHARED, s->fd, 0); | ||||
|     if (s->cow_bitmap_addr == MAP_FAILED) | ||||
|         goto fail; | ||||
|     s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header); | ||||
|     s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511; | ||||
|     return 0; | ||||
|  fail: | ||||
|     close(fd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * XXX(hch): right now these functions are extremly ineffcient. | ||||
|  * We should just read the whole bitmap we'll need in one go instead. | ||||
|  */ | ||||
| static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) | ||||
| static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum) | ||||
| { | ||||
|     uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; | ||||
|     uint8_t bitmap; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap)); | ||||
|     if (ret < 0) { | ||||
|        return ret; | ||||
|     } | ||||
|  | ||||
|     bitmap |= (1 << (bitnum % 8)); | ||||
|  | ||||
|     ret = bdrv_pwrite_sync(bs->file, offset, &bitmap, sizeof(bitmap)); | ||||
|     if (ret < 0) { | ||||
|        return ret; | ||||
|     } | ||||
|     return 0; | ||||
|     bitmap[bitnum / 8] |= (1 << (bitnum%8)); | ||||
| } | ||||
|  | ||||
| static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum) | ||||
| static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum) | ||||
| { | ||||
|     uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; | ||||
|     uint8_t bitmap; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap)); | ||||
|     if (ret < 0) { | ||||
|        return ret; | ||||
|     } | ||||
|  | ||||
|     return !!(bitmap & (1 << (bitnum % 8))); | ||||
|     return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Return true if first block has been changed (ie. current version is | ||||
|  * in COW file).  Set the number of continuous blocks for which that | ||||
|  * is true. */ | ||||
| static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, | ||||
|         int nb_sectors, int *num_same) | ||||
| static inline int is_changed(uint8_t *bitmap, | ||||
|                              int64_t sector_num, int nb_sectors, | ||||
|                              int *num_same) | ||||
| { | ||||
|     int changed; | ||||
|  | ||||
|     if (nb_sectors == 0) { | ||||
|     if (!bitmap || nb_sectors == 0) { | ||||
| 	*num_same = nb_sectors; | ||||
| 	return 0; | ||||
|     } | ||||
|  | ||||
|     changed = is_bit_set(bs, sector_num); | ||||
|     if (changed < 0) { | ||||
|         return 0; /* XXX: how to return I/O errors? */ | ||||
|     } | ||||
|  | ||||
|     changed = is_bit_set(bitmap, sector_num); | ||||
|     for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { | ||||
| 	if (is_bit_set(bs, sector_num + *num_same) != changed) | ||||
| 	if (is_bit_set(bitmap, sector_num + *num_same) != changed) | ||||
| 	    break; | ||||
|     } | ||||
|  | ||||
|     return changed; | ||||
| } | ||||
|  | ||||
| static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num, | ||||
|         int nb_sectors) | ||||
| static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, | ||||
|                             int nb_sectors, int *pnum) | ||||
| { | ||||
|     int error = 0; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < nb_sectors; i++) { | ||||
|         error = cow_set_bit(bs, sector_num + i); | ||||
|         if (error) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return error; | ||||
|     BDRVCowState *s = bs->opaque; | ||||
|     return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum); | ||||
| } | ||||
|  | ||||
| static int cow_read(BlockDriverState *bs, int64_t sector_num, | ||||
| @@ -176,10 +158,9 @@ static int cow_read(BlockDriverState *bs, int64_t sector_num, | ||||
|     int ret, n; | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
|         if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) { | ||||
|             ret = bdrv_pread(bs->file, | ||||
|                         s->cow_sectors_offset + sector_num * 512, | ||||
|                         buf, n * 512); | ||||
|         if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) { | ||||
|             lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); | ||||
|             ret = read(s->fd, buf, n * 512); | ||||
|             if (ret != n * 512) | ||||
|                 return -1; | ||||
|         } else { | ||||
| @@ -203,18 +184,22 @@ static int cow_write(BlockDriverState *bs, int64_t sector_num, | ||||
|                      const uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     BDRVCowState *s = bs->opaque; | ||||
|     int ret; | ||||
|     int ret, i; | ||||
|  | ||||
|     ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512, | ||||
|                       buf, nb_sectors * 512); | ||||
|     lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); | ||||
|     ret = write(s->fd, buf, nb_sectors * 512); | ||||
|     if (ret != nb_sectors * 512) | ||||
|         return -1; | ||||
|  | ||||
|     return cow_update_bitmap(bs, sector_num, nb_sectors); | ||||
|     for (i = 0; i < nb_sectors; i++) | ||||
|         cow_set_bit(s->cow_bitmap, sector_num + i); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void cow_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVCowState *s = bs->opaque; | ||||
|     munmap((void *)s->cow_bitmap_addr, s->cow_bitmap_size); | ||||
|     close(s->fd); | ||||
| } | ||||
|  | ||||
| static int cow_create(const char *filename, QEMUOptionParameter *options) | ||||
| @@ -224,7 +209,6 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) | ||||
|     struct stat st; | ||||
|     int64_t image_sectors = 0; | ||||
|     const char *image_filename = NULL; | ||||
|     int ret; | ||||
|  | ||||
|     /* Read out options */ | ||||
|     while (options && options->name) { | ||||
| @@ -239,7 +223,7 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) | ||||
|     cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, | ||||
|               0644); | ||||
|     if (cow_fd < 0) | ||||
|         return -errno; | ||||
|         return -1; | ||||
|     memset(&cow_header, 0, sizeof(cow_header)); | ||||
|     cow_header.magic = cpu_to_be32(COW_MAGIC); | ||||
|     cow_header.version = cpu_to_be32(COW_VERSION); | ||||
| @@ -264,27 +248,17 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) | ||||
|     } | ||||
|     cow_header.sectorsize = cpu_to_be32(512); | ||||
|     cow_header.size = cpu_to_be64(image_sectors * 512); | ||||
|     ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header)); | ||||
|     if (ret != sizeof(cow_header)) { | ||||
|         ret = -errno; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     write(cow_fd, &cow_header, sizeof(cow_header)); | ||||
|     /* resize to include at least all the bitmap */ | ||||
|     ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); | ||||
|     if (ret) { | ||||
|         ret = -errno; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
| exit: | ||||
|     ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); | ||||
|     close(cow_fd); | ||||
|     return ret; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int cow_flush(BlockDriverState *bs) | ||||
| static void cow_flush(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_flush(bs->file); | ||||
|     BDRVCowState *s = bs->opaque; | ||||
|     qemu_fdatasync(s->fd); | ||||
| } | ||||
|  | ||||
| static QEMUOptionParameter cow_create_options[] = { | ||||
| @@ -322,3 +296,4 @@ static void bdrv_cow_init(void) | ||||
| } | ||||
|  | ||||
| block_init(bdrv_cow_init); | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										36
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -29,9 +29,9 @@ | ||||
| // #define DEBUG_VERBOSE | ||||
|  | ||||
| #ifdef DEBUG_CURL | ||||
| #define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) | ||||
| #define dprintf(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) | ||||
| #else | ||||
| #define DPRINTF(fmt, ...) do { } while (0) | ||||
| #define dprintf(fmt, ...) do { } while (0) | ||||
| #endif | ||||
|  | ||||
| #define CURL_NUM_STATES 8 | ||||
| @@ -80,7 +80,7 @@ static void curl_multi_do(void *arg); | ||||
| static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, | ||||
|                         void *s, void *sp) | ||||
| { | ||||
|     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); | ||||
|     dprintf("CURL (AIO): Sock action %d on fd %d\n", action, fd); | ||||
|     switch (action) { | ||||
|         case CURL_POLL_IN: | ||||
|             qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, NULL, NULL, s); | ||||
| @@ -104,11 +104,10 @@ static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | ||||
| { | ||||
|     CURLState *s = ((CURLState*)opaque); | ||||
|     size_t realsize = size * nmemb; | ||||
|     size_t fsize; | ||||
|     long long fsize; | ||||
|  | ||||
|     if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) { | ||||
|     if(sscanf(ptr, "Content-Length: %lld", &fsize) == 1) | ||||
|         s->s->len = fsize; | ||||
|     } | ||||
|  | ||||
|     return realsize; | ||||
| } | ||||
| @@ -119,7 +118,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | ||||
|     size_t realsize = size * nmemb; | ||||
|     int i; | ||||
|  | ||||
|     DPRINTF("CURL: Just reading %zd bytes\n", realsize); | ||||
|     dprintf("CURL: Just reading %lld bytes\n", (unsigned long long)realsize); | ||||
|  | ||||
|     if (!s || !s->orig_buf) | ||||
|         goto read_end; | ||||
| @@ -340,7 +339,7 @@ static int curl_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|     } | ||||
|  | ||||
|     if ((s->readahead_size & 0x1ff) != 0) { | ||||
|         fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n", | ||||
|         fprintf(stderr, "HTTP_READAHEAD_SIZE %Zd is not a multiple of 512\n", | ||||
|                 s->readahead_size); | ||||
|         goto out_noclean; | ||||
|     } | ||||
| @@ -350,7 +349,7 @@ static int curl_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|         inited = 1; | ||||
|     } | ||||
|  | ||||
|     DPRINTF("CURL: Opening %s\n", file); | ||||
|     dprintf("CURL: Opening %s\n", file); | ||||
|     s->url = file; | ||||
|     state = curl_init_state(s); | ||||
|     if (!state) | ||||
| @@ -369,7 +368,7 @@ static int curl_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|         s->len = (size_t)d; | ||||
|     else if(!s->len) | ||||
|         goto out; | ||||
|     DPRINTF("CURL: Size = %zd\n", s->len); | ||||
|     dprintf("CURL: Size = %lld\n", (long long)s->len); | ||||
|  | ||||
|     curl_clean_state(state); | ||||
|     curl_easy_cleanup(state->curl); | ||||
| @@ -451,9 +450,8 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, | ||||
|     state->orig_buf = qemu_malloc(state->buf_len); | ||||
|     state->acb[0] = acb; | ||||
|  | ||||
|     snprintf(state->range, 127, "%zd-%zd", start, end); | ||||
|     DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n", | ||||
|             (nb_sectors * SECTOR_SIZE), start, state->range); | ||||
|     snprintf(state->range, 127, "%lld-%lld", (long long)start, (long long)end); | ||||
|     dprintf("CURL (AIO): Reading %d at %lld (%s)\n", (nb_sectors * SECTOR_SIZE), start, state->range); | ||||
|     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); | ||||
|  | ||||
|     curl_multi_add_handle(s->multi, state->curl); | ||||
| @@ -467,7 +465,7 @@ static void curl_close(BlockDriverState *bs) | ||||
|     BDRVCURLState *s = bs->opaque; | ||||
|     int i; | ||||
|  | ||||
|     DPRINTF("CURL: Close\n"); | ||||
|     dprintf("CURL: Close\n"); | ||||
|     for (i=0; i<CURL_NUM_STATES; i++) { | ||||
|         if (s->states[i].in_use) | ||||
|             curl_clean_state(&s->states[i]); | ||||
| @@ -497,7 +495,7 @@ static BlockDriver bdrv_http = { | ||||
|     .protocol_name   = "http", | ||||
|  | ||||
|     .instance_size   = sizeof(BDRVCURLState), | ||||
|     .bdrv_file_open  = curl_open, | ||||
|     .bdrv_open       = curl_open, | ||||
|     .bdrv_close      = curl_close, | ||||
|     .bdrv_getlength  = curl_getlength, | ||||
|  | ||||
| @@ -509,7 +507,7 @@ static BlockDriver bdrv_https = { | ||||
|     .protocol_name   = "https", | ||||
|  | ||||
|     .instance_size   = sizeof(BDRVCURLState), | ||||
|     .bdrv_file_open  = curl_open, | ||||
|     .bdrv_open       = curl_open, | ||||
|     .bdrv_close      = curl_close, | ||||
|     .bdrv_getlength  = curl_getlength, | ||||
|  | ||||
| @@ -521,7 +519,7 @@ static BlockDriver bdrv_ftp = { | ||||
|     .protocol_name   = "ftp", | ||||
|  | ||||
|     .instance_size   = sizeof(BDRVCURLState), | ||||
|     .bdrv_file_open  = curl_open, | ||||
|     .bdrv_open       = curl_open, | ||||
|     .bdrv_close      = curl_close, | ||||
|     .bdrv_getlength  = curl_getlength, | ||||
|  | ||||
| @@ -533,7 +531,7 @@ static BlockDriver bdrv_ftps = { | ||||
|     .protocol_name   = "ftps", | ||||
|  | ||||
|     .instance_size   = sizeof(BDRVCURLState), | ||||
|     .bdrv_file_open  = curl_open, | ||||
|     .bdrv_open       = curl_open, | ||||
|     .bdrv_close      = curl_close, | ||||
|     .bdrv_getlength  = curl_getlength, | ||||
|  | ||||
| @@ -545,7 +543,7 @@ static BlockDriver bdrv_tftp = { | ||||
|     .protocol_name   = "tftp", | ||||
|  | ||||
|     .instance_size   = sizeof(BDRVCURLState), | ||||
|     .bdrv_file_open  = curl_open, | ||||
|     .bdrv_open       = curl_open, | ||||
|     .bdrv_close      = curl_close, | ||||
|     .bdrv_getlength  = curl_getlength, | ||||
|  | ||||
|   | ||||
							
								
								
									
										107
									
								
								block/dmg.c
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								block/dmg.c
									
									
									
									
									
								
							| @@ -28,6 +28,8 @@ | ||||
| #include <zlib.h> | ||||
|  | ||||
| typedef struct BDRVDMGState { | ||||
|     int fd; | ||||
|  | ||||
|     /* each chunk contains a certain number of sectors, | ||||
|      * offsets[i] is the offset in the .dmg file, | ||||
|      * lengths[i] is the length of the compressed chunk, | ||||
| @@ -56,75 +58,69 @@ static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static off_t read_off(BlockDriverState *bs, int64_t offset) | ||||
| static off_t read_off(int fd) | ||||
| { | ||||
| 	uint64_t buffer; | ||||
| 	if (bdrv_pread(bs->file, offset, &buffer, 8) < 8) | ||||
| 	if(read(fd,&buffer,8)<8) | ||||
| 		return 0; | ||||
| 	return be64_to_cpu(buffer); | ||||
| } | ||||
|  | ||||
| static off_t read_uint32(BlockDriverState *bs, int64_t offset) | ||||
| static off_t read_uint32(int fd) | ||||
| { | ||||
| 	uint32_t buffer; | ||||
| 	if (bdrv_pread(bs->file, offset, &buffer, 4) < 4) | ||||
| 	if(read(fd,&buffer,4)<4) | ||||
| 		return 0; | ||||
| 	return be32_to_cpu(buffer); | ||||
| } | ||||
|  | ||||
| static int dmg_open(BlockDriverState *bs, int flags) | ||||
| static int dmg_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVDMGState *s = bs->opaque; | ||||
|     off_t info_begin,info_end,last_in_offset,last_out_offset; | ||||
|     uint32_t count; | ||||
|     uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; | ||||
|     int64_t offset; | ||||
|  | ||||
|     s->fd = open(filename, O_RDONLY | O_BINARY); | ||||
|     if (s->fd < 0) | ||||
|         return -errno; | ||||
|     bs->read_only = 1; | ||||
|     s->n_chunks = 0; | ||||
|     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; | ||||
|  | ||||
|     /* read offset of info blocks */ | ||||
|     offset = bdrv_getlength(bs->file); | ||||
|     if (offset < 0) { | ||||
|     if(lseek(s->fd,-0x1d8,SEEK_END)<0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     offset -= 0x1d8; | ||||
|  | ||||
|     info_begin = read_off(bs, offset); | ||||
|     if (info_begin == 0) { | ||||
|     info_begin=read_off(s->fd); | ||||
|     if(info_begin==0) | ||||
| 	goto fail; | ||||
|     if(lseek(s->fd,info_begin,SEEK_SET)<0) | ||||
| 	goto fail; | ||||
|     if(read_uint32(s->fd)!=0x100) | ||||
| 	goto fail; | ||||
|     if((count = read_uint32(s->fd))==0) | ||||
| 	goto fail; | ||||
|     info_end = info_begin+count; | ||||
|     if(lseek(s->fd,0xf8,SEEK_CUR)<0) | ||||
| 	goto fail; | ||||
|     } | ||||
|  | ||||
|     if (read_uint32(bs, info_begin) != 0x100) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     count = read_uint32(bs, info_begin + 4); | ||||
|     if (count == 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     info_end = info_begin + count; | ||||
|  | ||||
|     offset = info_begin + 0x100; | ||||
|  | ||||
|     /* read offsets */ | ||||
|     last_in_offset = last_out_offset = 0; | ||||
|     while (offset < info_end) { | ||||
|     while(lseek(s->fd,0,SEEK_CUR)<info_end) { | ||||
|         uint32_t type; | ||||
|  | ||||
| 	count = read_uint32(bs, offset); | ||||
| 	count = read_uint32(s->fd); | ||||
| 	if(count==0) | ||||
| 	    goto fail; | ||||
|         offset += 4; | ||||
|  | ||||
| 	type = read_uint32(bs, offset); | ||||
| 	if (type == 0x6d697368 && count >= 244) { | ||||
| 	type = read_uint32(s->fd); | ||||
| 	if(type!=0x6d697368 || count<244) | ||||
| 	    lseek(s->fd,count-4,SEEK_CUR); | ||||
| 	else { | ||||
| 	    int new_size, chunk_count; | ||||
|  | ||||
|             offset += 4; | ||||
|             offset += 200; | ||||
|  | ||||
| 	    if(lseek(s->fd,200,SEEK_CUR)<0) | ||||
| 	        goto fail; | ||||
| 	    chunk_count = (count-204)/40; | ||||
| 	    new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); | ||||
| 	    s->types = qemu_realloc(s->types, new_size/2); | ||||
| @@ -134,8 +130,7 @@ static int dmg_open(BlockDriverState *bs, int flags) | ||||
| 	    s->sectorcounts = qemu_realloc(s->sectorcounts, new_size); | ||||
|  | ||||
| 	    for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) { | ||||
| 		s->types[i] = read_uint32(bs, offset); | ||||
| 		offset += 4; | ||||
| 		s->types[i] = read_uint32(s->fd); | ||||
| 		if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { | ||||
| 		    if(s->types[i]==0xffffffff) { | ||||
| 			last_in_offset = s->offsets[i-1]+s->lengths[i-1]; | ||||
| @@ -143,23 +138,15 @@ static int dmg_open(BlockDriverState *bs, int flags) | ||||
| 		    } | ||||
| 		    chunk_count--; | ||||
| 		    i--; | ||||
| 		    offset += 36; | ||||
| 		    if(lseek(s->fd,36,SEEK_CUR)<0) | ||||
| 			goto fail; | ||||
| 		    continue; | ||||
| 		} | ||||
| 		offset += 4; | ||||
|  | ||||
| 		s->sectors[i] = last_out_offset+read_off(bs, offset); | ||||
| 		offset += 8; | ||||
|  | ||||
| 		s->sectorcounts[i] = read_off(bs, offset); | ||||
| 		offset += 8; | ||||
|  | ||||
| 		s->offsets[i] = last_in_offset+read_off(bs, offset); | ||||
| 		offset += 8; | ||||
|  | ||||
| 		s->lengths[i] = read_off(bs, offset); | ||||
| 		offset += 8; | ||||
|  | ||||
| 		read_uint32(s->fd); | ||||
| 		s->sectors[i] = last_out_offset+read_off(s->fd); | ||||
| 		s->sectorcounts[i] = read_off(s->fd); | ||||
| 		s->offsets[i] = last_in_offset+read_off(s->fd); | ||||
| 		s->lengths[i] = read_off(s->fd); | ||||
| 		if(s->lengths[i]>max_compressed_size) | ||||
| 		    max_compressed_size = s->lengths[i]; | ||||
| 		if(s->sectorcounts[i]>max_sectors_per_chunk) | ||||
| @@ -179,6 +166,7 @@ static int dmg_open(BlockDriverState *bs, int flags) | ||||
|  | ||||
|     return 0; | ||||
| fail: | ||||
|     close(s->fd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| @@ -208,10 +196,8 @@ static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num) | ||||
|     return s->n_chunks; /* error */ | ||||
| } | ||||
|  | ||||
| static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) | ||||
| static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num) | ||||
| { | ||||
|     BDRVDMGState *s = bs->opaque; | ||||
|  | ||||
|     if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) { | ||||
| 	int ret; | ||||
| 	uint32_t chunk = search_chunk(s,sector_num); | ||||
| @@ -224,12 +210,15 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) | ||||
| 	case 0x80000005: { /* zlib compressed */ | ||||
| 	    int i; | ||||
|  | ||||
| 	    ret = lseek(s->fd, s->offsets[chunk], SEEK_SET); | ||||
| 	    if(ret<0) | ||||
| 		return -1; | ||||
|  | ||||
| 	    /* we need to buffer, because only the chunk as whole can be | ||||
| 	     * inflated. */ | ||||
| 	    i=0; | ||||
| 	    do { | ||||
|                 ret = bdrv_pread(bs->file, s->offsets[chunk] + i, | ||||
|                                  s->compressed_chunk+i, s->lengths[chunk]-i); | ||||
| 		ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i); | ||||
| 		if(ret<0 && errno==EINTR) | ||||
| 		    ret=0; | ||||
| 		i+=ret; | ||||
| @@ -250,8 +239,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) | ||||
| 		return -1; | ||||
| 	    break; } | ||||
| 	case 1: /* copy */ | ||||
| 	    ret = bdrv_pread(bs->file, s->offsets[chunk], | ||||
|                              s->uncompressed_chunk, s->lengths[chunk]); | ||||
| 	    ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]); | ||||
| 	    if (ret != s->lengths[chunk]) | ||||
| 		return -1; | ||||
| 	    break; | ||||
| @@ -272,7 +260,7 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num, | ||||
|  | ||||
|     for(i=0;i<nb_sectors;i++) { | ||||
| 	uint32_t sector_offset_in_chunk; | ||||
| 	if(dmg_read_chunk(bs, sector_num+i) != 0) | ||||
| 	if(dmg_read_chunk(s, sector_num+i) != 0) | ||||
| 	    return -1; | ||||
| 	sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk]; | ||||
| 	memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512); | ||||
| @@ -283,6 +271,7 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num, | ||||
| static void dmg_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVDMGState *s = bs->opaque; | ||||
|     close(s->fd); | ||||
|     if(s->n_chunks>0) { | ||||
| 	free(s->types); | ||||
| 	free(s->offsets); | ||||
|   | ||||
							
								
								
									
										67
									
								
								block/nbd.c
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								block/nbd.c
									
									
									
									
									
								
							| @@ -33,8 +33,6 @@ | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #define EN_OPTSTR ":exportname=" | ||||
|  | ||||
| typedef struct BDRVNBDState { | ||||
|     int sock; | ||||
|     off_t size; | ||||
| @@ -44,81 +42,58 @@ typedef struct BDRVNBDState { | ||||
| static int nbd_open(BlockDriverState *bs, const char* filename, int flags) | ||||
| { | ||||
|     BDRVNBDState *s = bs->opaque; | ||||
|     uint32_t nbdflags; | ||||
|  | ||||
|     char *file; | ||||
|     char *name; | ||||
|     const char *host; | ||||
|     const char *unixpath; | ||||
|     int sock; | ||||
|     off_t size; | ||||
|     size_t blocksize; | ||||
|     int ret; | ||||
|     int err = -EINVAL; | ||||
|  | ||||
|     file = qemu_strdup(filename); | ||||
|     if ((flags & BDRV_O_CREAT)) | ||||
|         return -EINVAL; | ||||
|  | ||||
|     name = strstr(file, EN_OPTSTR); | ||||
|     if (name) { | ||||
|         if (name[strlen(EN_OPTSTR)] == 0) { | ||||
|             goto out; | ||||
|         } | ||||
|         name[0] = 0; | ||||
|         name += strlen(EN_OPTSTR); | ||||
|     } | ||||
|  | ||||
|     if (!strstart(file, "nbd:", &host)) { | ||||
|         goto out; | ||||
|     } | ||||
|     if (!strstart(filename, "nbd:", &host)) | ||||
|         return -EINVAL; | ||||
|  | ||||
|     if (strstart(host, "unix:", &unixpath)) { | ||||
|  | ||||
|         if (unixpath[0] != '/') { | ||||
|             goto out; | ||||
|         } | ||||
|         if (unixpath[0] != '/') | ||||
|             return -EINVAL; | ||||
|  | ||||
|         sock = unix_socket_outgoing(unixpath); | ||||
|  | ||||
|     } else { | ||||
|         uint16_t port = NBD_DEFAULT_PORT; | ||||
|         uint16_t port; | ||||
|         char *p, *r; | ||||
|         char hostname[128]; | ||||
|  | ||||
|         pstrcpy(hostname, 128, host); | ||||
|  | ||||
|         p = strchr(hostname, ':'); | ||||
|         if (p != NULL) { | ||||
|             *p = '\0'; | ||||
|             p++; | ||||
|         if (p == NULL) | ||||
|             return -EINVAL; | ||||
|  | ||||
|             port = strtol(p, &r, 0); | ||||
|             if (r == p) { | ||||
|                 goto out; | ||||
|             } | ||||
|         } | ||||
|         *p = '\0'; | ||||
|         p++; | ||||
|  | ||||
|         port = strtol(p, &r, 0); | ||||
|         if (r == p) | ||||
|             return -EINVAL; | ||||
|         sock = tcp_socket_outgoing(hostname, port); | ||||
|     } | ||||
|  | ||||
|     if (sock == -1) { | ||||
|         err = -errno; | ||||
|         goto out; | ||||
|     } | ||||
|     if (sock == -1) | ||||
|         return -errno; | ||||
|  | ||||
|     ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize); | ||||
|     if (ret == -1) { | ||||
|         err = -errno; | ||||
|         goto out; | ||||
|     } | ||||
|     ret = nbd_receive_negotiate(sock, &size, &blocksize); | ||||
|     if (ret == -1) | ||||
|         return -errno; | ||||
|  | ||||
|     s->sock = sock; | ||||
|     s->size = size; | ||||
|     s->blocksize = blocksize; | ||||
|     err = 0; | ||||
|  | ||||
| out: | ||||
|     qemu_free(file); | ||||
|     return err; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int nbd_read(BlockDriverState *bs, int64_t sector_num, | ||||
| @@ -205,7 +180,7 @@ static int64_t nbd_getlength(BlockDriverState *bs) | ||||
| static BlockDriver bdrv_nbd = { | ||||
|     .format_name	= "nbd", | ||||
|     .instance_size	= sizeof(BDRVNBDState), | ||||
|     .bdrv_file_open	= nbd_open, | ||||
|     .bdrv_open		= nbd_open, | ||||
|     .bdrv_read		= nbd_read, | ||||
|     .bdrv_write		= nbd_write, | ||||
|     .bdrv_close		= nbd_close, | ||||
|   | ||||
| @@ -46,6 +46,7 @@ struct parallels_header { | ||||
| } __attribute__((packed)); | ||||
|  | ||||
| typedef struct BDRVParallelsState { | ||||
|     int fd; | ||||
|  | ||||
|     uint32_t *catalog_bitmap; | ||||
|     int catalog_size; | ||||
| @@ -67,15 +68,24 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int parallels_open(BlockDriverState *bs, int flags) | ||||
| static int parallels_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     int i; | ||||
|     int fd, i; | ||||
|     struct parallels_header ph; | ||||
|  | ||||
|     fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); | ||||
|     if (fd < 0) { | ||||
|         fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); | ||||
|         if (fd < 0) | ||||
|             return -1; | ||||
|     } | ||||
|  | ||||
|     bs->read_only = 1; // no write support yet | ||||
|  | ||||
|     if (bdrv_pread(bs->file, 0, &ph, sizeof(ph)) != sizeof(ph)) | ||||
|     s->fd = fd; | ||||
|  | ||||
|     if (read(fd, &ph, sizeof(ph)) != sizeof(ph)) | ||||
|         goto fail; | ||||
|  | ||||
|     if (memcmp(ph.magic, HEADER_MAGIC, 16) || | ||||
| @@ -85,11 +95,14 @@ static int parallels_open(BlockDriverState *bs, int flags) | ||||
|  | ||||
|     bs->total_sectors = le32_to_cpu(ph.nb_sectors); | ||||
|  | ||||
|     if (lseek(s->fd, 64, SEEK_SET) != 64) | ||||
| 	goto fail; | ||||
|  | ||||
|     s->tracks = le32_to_cpu(ph.tracks); | ||||
|  | ||||
|     s->catalog_size = le32_to_cpu(ph.catalog_entries); | ||||
|     s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); | ||||
|     if (bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4) != | ||||
|     if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) != | ||||
| 	s->catalog_size * 4) | ||||
| 	goto fail; | ||||
|     for (i = 0; i < s->catalog_size; i++) | ||||
| @@ -99,34 +112,45 @@ static int parallels_open(BlockDriverState *bs, int flags) | ||||
| fail: | ||||
|     if (s->catalog_bitmap) | ||||
| 	qemu_free(s->catalog_bitmap); | ||||
|     close(fd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
| static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     uint32_t index, offset; | ||||
|     uint64_t position; | ||||
|  | ||||
|     index = sector_num / s->tracks; | ||||
|     offset = sector_num % s->tracks; | ||||
|  | ||||
|     /* not allocated */ | ||||
|     // not allocated | ||||
|     if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0)) | ||||
| 	return -1; | ||||
|     return (uint64_t)(s->catalog_bitmap[index] + offset) * 512; | ||||
|  | ||||
|     position = (uint64_t)(s->catalog_bitmap[index] + offset) * 512; | ||||
|  | ||||
| //    fprintf(stderr, "sector: %llx index=%x offset=%x pointer=%x position=%x\n", | ||||
| //	sector_num, index, offset, s->catalog_bitmap[index], position); | ||||
|  | ||||
|     if (lseek(s->fd, position, SEEK_SET) != position) | ||||
| 	return -1; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int parallels_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                     uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
|         int64_t position = seek_to_sector(bs, sector_num); | ||||
|         if (position >= 0) { | ||||
|             if (bdrv_pread(bs->file, position, buf, 512) != 512) | ||||
|                 return -1; | ||||
|         } else { | ||||
| 	if (!seek_to_sector(bs, sector_num)) { | ||||
| 	    if (read(s->fd, buf, 512) != 512) | ||||
| 		return -1; | ||||
| 	} else | ||||
|             memset(buf, 0, 512); | ||||
|         } | ||||
|         nb_sectors--; | ||||
|         sector_num++; | ||||
|         buf += 512; | ||||
| @@ -138,6 +162,7 @@ static void parallels_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVParallelsState *s = bs->opaque; | ||||
|     qemu_free(s->catalog_bitmap); | ||||
|     close(s->fd); | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_parallels = { | ||||
|   | ||||
							
								
								
									
										103
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								block/qcow.c
									
									
									
									
									
								
							| @@ -54,6 +54,7 @@ typedef struct QCowHeader { | ||||
| #define L2_CACHE_SIZE 16 | ||||
|  | ||||
| typedef struct BDRVQcowState { | ||||
|     BlockDriverState *hd; | ||||
|     int cluster_bits; | ||||
|     int cluster_size; | ||||
|     int cluster_sectors; | ||||
| @@ -75,7 +76,7 @@ typedef struct BDRVQcowState { | ||||
|     AES_KEY aes_decrypt_key; | ||||
| } BDRVQcowState; | ||||
|  | ||||
| static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | ||||
| static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); | ||||
|  | ||||
| static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
| { | ||||
| @@ -89,13 +90,16 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|         return 0; | ||||
| } | ||||
|  | ||||
| static int qcow_open(BlockDriverState *bs, int flags) | ||||
| static int qcow_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int len, i, shift; | ||||
|     int len, i, shift, ret; | ||||
|     QCowHeader header; | ||||
|  | ||||
|     if (bdrv_pread(bs->file, 0, &header, sizeof(header)) != sizeof(header)) | ||||
|     ret = bdrv_file_open(&s->hd, filename, flags); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|     if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header)) | ||||
|         goto fail; | ||||
|     be32_to_cpus(&header.magic); | ||||
|     be32_to_cpus(&header.version); | ||||
| @@ -131,7 +135,7 @@ static int qcow_open(BlockDriverState *bs, int flags) | ||||
|     s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); | ||||
|     if (!s->l1_table) | ||||
|         goto fail; | ||||
|     if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != | ||||
|     if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != | ||||
|         s->l1_size * sizeof(uint64_t)) | ||||
|         goto fail; | ||||
|     for(i = 0;i < s->l1_size; i++) { | ||||
| @@ -154,7 +158,7 @@ static int qcow_open(BlockDriverState *bs, int flags) | ||||
|         len = header.backing_file_size; | ||||
|         if (len > 1023) | ||||
|             len = 1023; | ||||
|         if (bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len) != len) | ||||
|         if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len) | ||||
|             goto fail; | ||||
|         bs->backing_file[len] = '\0'; | ||||
|     } | ||||
| @@ -165,6 +169,7 @@ static int qcow_open(BlockDriverState *bs, int flags) | ||||
|     qemu_free(s->l2_cache); | ||||
|     qemu_free(s->cluster_cache); | ||||
|     qemu_free(s->cluster_data); | ||||
|     bdrv_delete(s->hd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| @@ -266,13 +271,13 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|         if (!allocate) | ||||
|             return 0; | ||||
|         /* allocate a new l2 entry */ | ||||
|         l2_offset = bdrv_getlength(bs->file); | ||||
|         l2_offset = bdrv_getlength(s->hd); | ||||
|         /* round to cluster size */ | ||||
|         l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); | ||||
|         /* update the L1 entry */ | ||||
|         s->l1_table[l1_index] = l2_offset; | ||||
|         tmp = cpu_to_be64(l2_offset); | ||||
|         if (bdrv_pwrite_sync(bs->file, | ||||
|         if (bdrv_pwrite_sync(s->hd, | ||||
|                 s->l1_table_offset + l1_index * sizeof(tmp), | ||||
|                 &tmp, sizeof(tmp)) < 0) | ||||
|             return 0; | ||||
| @@ -302,11 +307,11 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|     l2_table = s->l2_cache + (min_index << s->l2_bits); | ||||
|     if (new_l2_table) { | ||||
|         memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||||
|         if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, | ||||
|         if (bdrv_pwrite_sync(s->hd, l2_offset, l2_table, | ||||
|                 s->l2_size * sizeof(uint64_t)) < 0) | ||||
|             return 0; | ||||
|     } else { | ||||
|         if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != | ||||
|         if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != | ||||
|             s->l2_size * sizeof(uint64_t)) | ||||
|             return 0; | ||||
|     } | ||||
| @@ -325,22 +330,22 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|             /* if the cluster is already compressed, we must | ||||
|                decompress it in the case it is not completely | ||||
|                overwritten */ | ||||
|             if (decompress_cluster(bs, cluster_offset) < 0) | ||||
|             if (decompress_cluster(s, cluster_offset) < 0) | ||||
|                 return 0; | ||||
|             cluster_offset = bdrv_getlength(bs->file); | ||||
|             cluster_offset = bdrv_getlength(s->hd); | ||||
|             cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||||
|                 ~(s->cluster_size - 1); | ||||
|             /* write the cluster content */ | ||||
|             if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) != | ||||
|             if (bdrv_pwrite(s->hd, cluster_offset, s->cluster_cache, s->cluster_size) != | ||||
|                 s->cluster_size) | ||||
|                 return -1; | ||||
|         } else { | ||||
|             cluster_offset = bdrv_getlength(bs->file); | ||||
|             cluster_offset = bdrv_getlength(s->hd); | ||||
|             if (allocate == 1) { | ||||
|                 /* round to cluster size */ | ||||
|                 cluster_offset = (cluster_offset + s->cluster_size - 1) & | ||||
|                     ~(s->cluster_size - 1); | ||||
|                 bdrv_truncate(bs->file, cluster_offset + s->cluster_size); | ||||
|                 bdrv_truncate(s->hd, cluster_offset + s->cluster_size); | ||||
|                 /* if encrypted, we must initialize the cluster | ||||
|                    content which won't be written */ | ||||
|                 if (s->crypt_method && | ||||
| @@ -354,7 +359,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|                                             s->cluster_data, | ||||
|                                             s->cluster_data + 512, 1, 1, | ||||
|                                             &s->aes_encrypt_key); | ||||
|                             if (bdrv_pwrite(bs->file, cluster_offset + i * 512, | ||||
|                             if (bdrv_pwrite(s->hd, cluster_offset + i * 512, | ||||
|                                             s->cluster_data, 512) != 512) | ||||
|                                 return -1; | ||||
|                         } | ||||
| @@ -368,7 +373,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, | ||||
|         /* update L2 table */ | ||||
|         tmp = cpu_to_be64(cluster_offset); | ||||
|         l2_table[l2_index] = tmp; | ||||
|         if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), | ||||
|         if (bdrv_pwrite_sync(s->hd, l2_offset + l2_index * sizeof(tmp), | ||||
|                 &tmp, sizeof(tmp)) < 0) | ||||
|             return 0; | ||||
|     } | ||||
| @@ -418,9 +423,8 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
| static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret, csize; | ||||
|     uint64_t coffset; | ||||
|  | ||||
| @@ -428,7 +432,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
|     if (s->cluster_cache_offset != coffset) { | ||||
|         csize = cluster_offset >> (63 - s->cluster_bits); | ||||
|         csize &= (s->cluster_size - 1); | ||||
|         ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); | ||||
|         ret = bdrv_pread(s->hd, coffset, s->cluster_data, csize); | ||||
|         if (ret != csize) | ||||
|             return -1; | ||||
|         if (decompress_buffer(s->cluster_cache, s->cluster_size, | ||||
| @@ -465,11 +469,11 @@ static int qcow_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                 memset(buf, 0, 512 * n); | ||||
|             } | ||||
|         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||||
|             if (decompress_cluster(bs, cluster_offset) < 0) | ||||
|             if (decompress_cluster(s, cluster_offset) < 0) | ||||
|                 return -1; | ||||
|             memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); | ||||
|         } else { | ||||
|             ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512); | ||||
|             ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); | ||||
|             if (ret != n * 512) | ||||
|                 return -1; | ||||
|             if (s->crypt_method) { | ||||
| @@ -502,7 +506,7 @@ typedef struct QCowAIOCB { | ||||
|  | ||||
| static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) | ||||
| { | ||||
|     QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common); | ||||
|     QCowAIOCB *acb = (QCowAIOCB *)blockacb; | ||||
|     if (acb->hd_aiocb) | ||||
|         bdrv_aio_cancel(acb->hd_aiocb); | ||||
|     qemu_aio_release(acb); | ||||
| @@ -598,7 +602,7 @@ static void qcow_aio_read_cb(void *opaque, int ret) | ||||
|         } | ||||
|     } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||||
|         /* add AIO support for compressed blocks ? */ | ||||
|         if (decompress_cluster(bs, acb->cluster_offset) < 0) | ||||
|         if (decompress_cluster(s, acb->cluster_offset) < 0) | ||||
|             goto done; | ||||
|         memcpy(acb->buf, | ||||
|                s->cluster_cache + index_in_cluster * 512, 512 * acb->n); | ||||
| @@ -611,7 +615,7 @@ static void qcow_aio_read_cb(void *opaque, int ret) | ||||
|         acb->hd_iov.iov_base = (void *)acb->buf; | ||||
|         acb->hd_iov.iov_len = acb->n * 512; | ||||
|         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||
|         acb->hd_aiocb = bdrv_aio_readv(bs->file, | ||||
|         acb->hd_aiocb = bdrv_aio_readv(s->hd, | ||||
|                             (acb->cluster_offset >> 9) + index_in_cluster, | ||||
|                             &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb); | ||||
|         if (acb->hd_aiocb == NULL) | ||||
| @@ -696,7 +700,7 @@ static void qcow_aio_write_cb(void *opaque, int ret) | ||||
|     acb->hd_iov.iov_base = (void *)src_buf; | ||||
|     acb->hd_iov.iov_len = acb->n * 512; | ||||
|     qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||
|     acb->hd_aiocb = bdrv_aio_writev(bs->file, | ||||
|     acb->hd_aiocb = bdrv_aio_writev(s->hd, | ||||
|                                     (cluster_offset >> 9) + index_in_cluster, | ||||
|                                     &acb->hd_qiov, acb->n, | ||||
|                                     qcow_aio_write_cb, acb); | ||||
| @@ -736,6 +740,7 @@ static void qcow_close(BlockDriverState *bs) | ||||
|     qemu_free(s->l2_cache); | ||||
|     qemu_free(s->cluster_cache); | ||||
|     qemu_free(s->cluster_data); | ||||
|     bdrv_delete(s->hd); | ||||
| } | ||||
|  | ||||
| static int qcow_create(const char *filename, QEMUOptionParameter *options) | ||||
| @@ -746,7 +751,6 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) | ||||
|     int64_t total_size = 0; | ||||
|     const char *backing_file = NULL; | ||||
|     int flags = 0; | ||||
|     int ret; | ||||
|  | ||||
|     /* Read out options */ | ||||
|     while (options && options->name) { | ||||
| @@ -762,7 +766,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) | ||||
|  | ||||
|     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); | ||||
|     if (fd < 0) | ||||
|         return -errno; | ||||
|         return -1; | ||||
|     memset(&header, 0, sizeof(header)); | ||||
|     header.magic = cpu_to_be32(QCOW_MAGIC); | ||||
|     header.version = cpu_to_be32(QCOW_VERSION); | ||||
| @@ -798,34 +802,17 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options) | ||||
|     } | ||||
|  | ||||
|     /* write all the data */ | ||||
|     ret = qemu_write_full(fd, &header, sizeof(header)); | ||||
|     if (ret != sizeof(header)) { | ||||
|         ret = -errno; | ||||
|         goto exit; | ||||
|     } | ||||
|  | ||||
|     write(fd, &header, sizeof(header)); | ||||
|     if (backing_file) { | ||||
|         ret = qemu_write_full(fd, backing_file, backing_filename_len); | ||||
|         if (ret != backing_filename_len) { | ||||
|             ret = -errno; | ||||
|             goto exit; | ||||
|         } | ||||
|  | ||||
|         write(fd, backing_file, backing_filename_len); | ||||
|     } | ||||
|     lseek(fd, header_size, SEEK_SET); | ||||
|     tmp = 0; | ||||
|     for(i = 0;i < l1_size; i++) { | ||||
|         ret = qemu_write_full(fd, &tmp, sizeof(tmp)); | ||||
|         if (ret != sizeof(tmp)) { | ||||
|             ret = -errno; | ||||
|             goto exit; | ||||
|         } | ||||
|         write(fd, &tmp, sizeof(tmp)); | ||||
|     } | ||||
|  | ||||
|     ret = 0; | ||||
| exit: | ||||
|     close(fd); | ||||
|     return ret; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int qcow_make_empty(BlockDriverState *bs) | ||||
| @@ -835,10 +822,10 @@ static int qcow_make_empty(BlockDriverState *bs) | ||||
|     int ret; | ||||
|  | ||||
|     memset(s->l1_table, 0, l1_length); | ||||
|     if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, | ||||
|     if (bdrv_pwrite_sync(s->hd, s->l1_table_offset, s->l1_table, | ||||
|             l1_length) < 0) | ||||
|         return -1; | ||||
|     ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); | ||||
|     ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|  | ||||
| @@ -899,7 +886,7 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, | ||||
|         cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, | ||||
|                                             out_len, 0, 0); | ||||
|         cluster_offset &= s->cluster_offset_mask; | ||||
|         if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) { | ||||
|         if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) { | ||||
|             qemu_free(out_buf); | ||||
|             return -1; | ||||
|         } | ||||
| @@ -909,15 +896,10 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int qcow_flush(BlockDriverState *bs) | ||||
| static void qcow_flush(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_flush(bs->file); | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs, | ||||
|         BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     return bdrv_aio_flush(bs->file, cb, opaque); | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     bdrv_flush(s->hd); | ||||
| } | ||||
|  | ||||
| static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||||
| @@ -960,7 +942,6 @@ static BlockDriver bdrv_qcow = { | ||||
|     .bdrv_make_empty	= qcow_make_empty, | ||||
|     .bdrv_aio_readv	= qcow_aio_readv, | ||||
|     .bdrv_aio_writev	= qcow_aio_writev, | ||||
|     .bdrv_aio_flush	= qcow_aio_flush, | ||||
|     .bdrv_write_compressed = qcow_write_compressed, | ||||
|     .bdrv_get_info	= qcow_get_info, | ||||
|  | ||||
|   | ||||
| @@ -1,314 +0,0 @@ | ||||
| /* | ||||
|  * L2/refcount table cache for the QCOW2 format | ||||
|  * | ||||
|  * Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com> | ||||
|  * | ||||
|  * 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 "block_int.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qcow2.h" | ||||
|  | ||||
| typedef struct Qcow2CachedTable { | ||||
|     void*   table; | ||||
|     int64_t offset; | ||||
|     bool    dirty; | ||||
|     int     cache_hits; | ||||
|     int     ref; | ||||
| } Qcow2CachedTable; | ||||
|  | ||||
| struct Qcow2Cache { | ||||
|     Qcow2CachedTable*       entries; | ||||
|     struct Qcow2Cache*      depends; | ||||
|     int                     size; | ||||
|     bool                    depends_on_flush; | ||||
|     bool                    writethrough; | ||||
| }; | ||||
|  | ||||
| Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, | ||||
|     bool writethrough) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     Qcow2Cache *c; | ||||
|     int i; | ||||
|  | ||||
|     c = qemu_mallocz(sizeof(*c)); | ||||
|     c->size = num_tables; | ||||
|     c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables); | ||||
|     c->writethrough = writethrough; | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         c->entries[i].table = qemu_blockalign(bs, s->cluster_size); | ||||
|     } | ||||
|  | ||||
|     return c; | ||||
| } | ||||
|  | ||||
| int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         assert(c->entries[i].ref == 0); | ||||
|         qemu_vfree(c->entries[i].table); | ||||
|     } | ||||
|  | ||||
|     qemu_free(c->entries); | ||||
|     qemu_free(c); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = qcow2_cache_flush(bs, c->depends); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     c->depends = NULL; | ||||
|     c->depends_on_flush = false; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret = 0; | ||||
|  | ||||
|     if (!c->entries[i].dirty || !c->entries[i].offset) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     if (c->depends) { | ||||
|         ret = qcow2_cache_flush_dependency(bs, c); | ||||
|     } else if (c->depends_on_flush) { | ||||
|         ret = bdrv_flush(bs->file); | ||||
|         if (ret >= 0) { | ||||
|             c->depends_on_flush = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     if (c == s->refcount_block_cache) { | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); | ||||
|     } else if (c == s->l2_table_cache) { | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table, | ||||
|         s->cluster_size); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     c->entries[i].dirty = false; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) | ||||
| { | ||||
|     int result = 0; | ||||
|     int ret; | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         ret = qcow2_cache_entry_flush(bs, c, i); | ||||
|         if (ret < 0 && result != -ENOSPC) { | ||||
|             result = ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (result == 0) { | ||||
|         ret = bdrv_flush(bs->file); | ||||
|         if (ret < 0) { | ||||
|             result = ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, | ||||
|     Qcow2Cache *dependency) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     if (dependency->depends) { | ||||
|         ret = qcow2_cache_flush_dependency(bs, dependency); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (c->depends && (c->depends != dependency)) { | ||||
|         ret = qcow2_cache_flush_dependency(bs, c); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     c->depends = dependency; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| void qcow2_cache_depends_on_flush(Qcow2Cache *c) | ||||
| { | ||||
|     c->depends_on_flush = true; | ||||
| } | ||||
|  | ||||
| static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c) | ||||
| { | ||||
|     int i; | ||||
|     int min_count = INT_MAX; | ||||
|     int min_index = -1; | ||||
|  | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         if (c->entries[i].ref) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (c->entries[i].cache_hits < min_count) { | ||||
|             min_index = i; | ||||
|             min_count = c->entries[i].cache_hits; | ||||
|         } | ||||
|  | ||||
|         /* Give newer hits priority */ | ||||
|         /* TODO Check how to optimize the replacement strategy */ | ||||
|         c->entries[i].cache_hits /= 2; | ||||
|     } | ||||
|  | ||||
|     if (min_index == -1) { | ||||
|         /* This can't happen in current synchronous code, but leave the check | ||||
|          * here as a reminder for whoever starts using AIO with the cache */ | ||||
|         abort(); | ||||
|     } | ||||
|     return min_index; | ||||
| } | ||||
|  | ||||
| static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, | ||||
|     uint64_t offset, void **table, bool read_from_disk) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int i; | ||||
|     int ret; | ||||
|  | ||||
|     /* Check if the table is already cached */ | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         if (c->entries[i].offset == offset) { | ||||
|             goto found; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* If not, write a table back and replace it */ | ||||
|     i = qcow2_cache_find_entry_to_replace(c); | ||||
|     if (i < 0) { | ||||
|         return i; | ||||
|     } | ||||
|  | ||||
|     ret = qcow2_cache_entry_flush(bs, c, i); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     c->entries[i].offset = 0; | ||||
|     if (read_from_disk) { | ||||
|         if (c == s->l2_table_cache) { | ||||
|             BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); | ||||
|         } | ||||
|  | ||||
|         ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Give the table some hits for the start so that it won't be replaced | ||||
|      * immediately. The number 32 is completely arbitrary. */ | ||||
|     c->entries[i].cache_hits = 32; | ||||
|     c->entries[i].offset = offset; | ||||
|  | ||||
|     /* And return the right table */ | ||||
| found: | ||||
|     c->entries[i].cache_hits++; | ||||
|     c->entries[i].ref++; | ||||
|     *table = c->entries[i].table; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||||
|     void **table) | ||||
| { | ||||
|     return qcow2_cache_do_get(bs, c, offset, table, true); | ||||
| } | ||||
|  | ||||
| int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||||
|     void **table) | ||||
| { | ||||
|     return qcow2_cache_do_get(bs, c, offset, table, false); | ||||
| } | ||||
|  | ||||
| int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         if (c->entries[i].table == *table) { | ||||
|             goto found; | ||||
|         } | ||||
|     } | ||||
|     return -ENOENT; | ||||
|  | ||||
| found: | ||||
|     c->entries[i].ref--; | ||||
|     *table = NULL; | ||||
|  | ||||
|     assert(c->entries[i].ref >= 0); | ||||
|  | ||||
|     if (c->writethrough) { | ||||
|         return qcow2_cache_entry_flush(bs, c, i); | ||||
|     } else { | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < c->size; i++) { | ||||
|         if (c->entries[i].table == table) { | ||||
|             goto found; | ||||
|         } | ||||
|     } | ||||
|     abort(); | ||||
|  | ||||
| found: | ||||
|     c->entries[i].dirty = true; | ||||
| } | ||||
|  | ||||
| @@ -28,7 +28,7 @@ | ||||
| #include "block_int.h" | ||||
| #include "block/qcow2.h" | ||||
|  | ||||
| int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | ||||
| int qcow2_grow_l1_table(BlockDriverState *bs, int min_size) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int new_l1_size, new_l1_size2, ret, i; | ||||
| @@ -36,22 +36,15 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | ||||
|     int64_t new_l1_table_offset; | ||||
|     uint8_t data[12]; | ||||
|  | ||||
|     if (min_size <= s->l1_size) | ||||
|     new_l1_size = s->l1_size; | ||||
|     if (min_size <= new_l1_size) | ||||
|         return 0; | ||||
|  | ||||
|     if (exact_size) { | ||||
|         new_l1_size = min_size; | ||||
|     } else { | ||||
|         /* Bump size up to reduce the number of times we have to grow */ | ||||
|         new_l1_size = s->l1_size; | ||||
|         if (new_l1_size == 0) { | ||||
|             new_l1_size = 1; | ||||
|         } | ||||
|         while (min_size > new_l1_size) { | ||||
|             new_l1_size = (new_l1_size * 3 + 1) / 2; | ||||
|         } | ||||
|     if (new_l1_size == 0) { | ||||
|         new_l1_size = 1; | ||||
|     } | ||||
|     while (min_size > new_l1_size) { | ||||
|         new_l1_size = (new_l1_size * 3 + 1) / 2; | ||||
|     } | ||||
|  | ||||
| #ifdef DEBUG_ALLOC2 | ||||
|     printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size); | ||||
| #endif | ||||
| @@ -61,32 +54,24 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | ||||
|     memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); | ||||
|  | ||||
|     /* write new table (align to cluster) */ | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE); | ||||
|     new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); | ||||
|     if (new_l1_table_offset < 0) { | ||||
|         qemu_free(new_l1_table); | ||||
|         return new_l1_table_offset; | ||||
|     } | ||||
|  | ||||
|     ret = qcow2_cache_flush(bs, s->refcount_block_cache); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); | ||||
|     for(i = 0; i < s->l1_size; i++) | ||||
|         new_l1_table[i] = cpu_to_be64(new_l1_table[i]); | ||||
|     ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2); | ||||
|     ret = bdrv_pwrite_sync(s->hd, new_l1_table_offset, new_l1_table, new_l1_size2); | ||||
|     if (ret < 0) | ||||
|         goto fail; | ||||
|     for(i = 0; i < s->l1_size; i++) | ||||
|         new_l1_table[i] = be64_to_cpu(new_l1_table[i]); | ||||
|  | ||||
|     /* set new table */ | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); | ||||
|     cpu_to_be32w((uint32_t*)data, new_l1_size); | ||||
|     cpu_to_be64wu((uint64_t*)(data + 4), new_l1_table_offset); | ||||
|     ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); | ||||
|     cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset); | ||||
|     ret = bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, l1_size), data,sizeof(data)); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
| @@ -102,6 +87,63 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| void qcow2_l2_cache_reset(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|  | ||||
|     memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); | ||||
|     memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t)); | ||||
|     memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t)); | ||||
| } | ||||
|  | ||||
| static inline int l2_cache_new_entry(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint32_t min_count; | ||||
|     int min_index, i; | ||||
|  | ||||
|     /* find a new entry in the least used one */ | ||||
|     min_index = 0; | ||||
|     min_count = 0xffffffff; | ||||
|     for(i = 0; i < L2_CACHE_SIZE; i++) { | ||||
|         if (s->l2_cache_counts[i] < min_count) { | ||||
|             min_count = s->l2_cache_counts[i]; | ||||
|             min_index = i; | ||||
|         } | ||||
|     } | ||||
|     return min_index; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * seek_l2_table | ||||
|  * | ||||
|  * seek l2_offset in the l2_cache table | ||||
|  * if not found, return NULL, | ||||
|  * if found, | ||||
|  *   increments the l2 cache hit count of the entry, | ||||
|  *   if counter overflow, divide by two all counters | ||||
|  *   return the pointer to the l2 cache entry | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset) | ||||
| { | ||||
|     int i, j; | ||||
|  | ||||
|     for(i = 0; i < L2_CACHE_SIZE; i++) { | ||||
|         if (l2_offset == s->l2_cache_offsets[i]) { | ||||
|             /* increment the hit count */ | ||||
|             if (++s->l2_cache_counts[i] == 0xffffffff) { | ||||
|                 for(j = 0; j < L2_CACHE_SIZE; j++) { | ||||
|                     s->l2_cache_counts[j] >>= 1; | ||||
|                 } | ||||
|             } | ||||
|             return s->l2_cache + (i << s->l2_bits); | ||||
|         } | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * l2_load | ||||
|  * | ||||
| @@ -112,15 +154,29 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) | ||||
|  * the image file failed. | ||||
|  */ | ||||
|  | ||||
| static int l2_load(BlockDriverState *bs, uint64_t l2_offset, | ||||
|     uint64_t **l2_table) | ||||
| static uint64_t *l2_load(BlockDriverState *bs, uint64_t l2_offset) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret; | ||||
|     int min_index; | ||||
|     uint64_t *l2_table; | ||||
|  | ||||
|     ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); | ||||
|     /* seek if the table for the given offset is in the cache */ | ||||
|  | ||||
|     return ret; | ||||
|     l2_table = seek_l2_table(s, l2_offset); | ||||
|     if (l2_table != NULL) | ||||
|         return l2_table; | ||||
|  | ||||
|     /* not found: load a new entry in the least used one */ | ||||
|  | ||||
|     min_index = l2_cache_new_entry(bs); | ||||
|     l2_table = s->l2_cache + (min_index << s->l2_bits); | ||||
|     if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != | ||||
|         s->l2_size * sizeof(uint64_t)) | ||||
|         return NULL; | ||||
|     s->l2_cache_offsets[min_index] = l2_offset; | ||||
|     s->l2_cache_counts[min_index] = 1; | ||||
|  | ||||
|     return l2_table; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -128,9 +184,8 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset, | ||||
|  * and we really don't want bdrv_pread to perform a read-modify-write) | ||||
|  */ | ||||
| #define L1_ENTRIES_PER_SECTOR (512 / 8) | ||||
| static int write_l1_entry(BlockDriverState *bs, int l1_index) | ||||
| static int write_l1_entry(BDRVQcowState *s, int l1_index) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t buf[L1_ENTRIES_PER_SECTOR]; | ||||
|     int l1_start_index; | ||||
|     int i, ret; | ||||
| @@ -140,8 +195,7 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index) | ||||
|         buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); | ||||
|     } | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); | ||||
|     ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, | ||||
|     ret = bdrv_pwrite_sync(s->hd, s->l1_table_offset + 8 * l1_start_index, | ||||
|         buf, sizeof(buf)); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
| @@ -160,9 +214,10 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index) | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | ||||
| static uint64_t *l2_allocate(BlockDriverState *bs, int l1_index) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int min_index; | ||||
|     uint64_t old_l2_offset; | ||||
|     uint64_t *l2_table; | ||||
|     int64_t l2_offset; | ||||
| @@ -174,68 +229,48 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) | ||||
|  | ||||
|     l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); | ||||
|     if (l2_offset < 0) { | ||||
|         return l2_offset; | ||||
|     } | ||||
|  | ||||
|     ret = qcow2_cache_flush(bs, s->refcount_block_cache); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     /* allocate a new entry in the l2 cache */ | ||||
|  | ||||
|     ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     l2_table = *table; | ||||
|     min_index = l2_cache_new_entry(bs); | ||||
|     l2_table = s->l2_cache + (min_index << s->l2_bits); | ||||
|  | ||||
|     if (old_l2_offset == 0) { | ||||
|         /* if there was no old l2 table, clear the new table */ | ||||
|         memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); | ||||
|     } else { | ||||
|         uint64_t* old_table; | ||||
|  | ||||
|         /* if there was an old l2 table, read it from the disk */ | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); | ||||
|         ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset, | ||||
|             (void**) &old_table); | ||||
|         if (ret < 0) { | ||||
|         if (bdrv_pread(s->hd, old_l2_offset, | ||||
|                        l2_table, s->l2_size * sizeof(uint64_t)) != | ||||
|             s->l2_size * sizeof(uint64_t)) | ||||
|             goto fail; | ||||
|         } | ||||
|  | ||||
|         memcpy(l2_table, old_table, s->cluster_size); | ||||
|  | ||||
|         ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table); | ||||
|         if (ret < 0) { | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* write the l2 table to the file */ | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); | ||||
|  | ||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|     ret = qcow2_cache_flush(bs, s->l2_table_cache); | ||||
|     ret = bdrv_pwrite_sync(s->hd, l2_offset, l2_table, | ||||
|         s->l2_size * sizeof(uint64_t)); | ||||
|     if (ret < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* update the L1 entry */ | ||||
|     s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; | ||||
|     ret = write_l1_entry(bs, l1_index); | ||||
|     if (ret < 0) { | ||||
|     if (write_l1_entry(s, l1_index) < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     *table = l2_table; | ||||
|     return 0; | ||||
|     /* update the l2 cache entry */ | ||||
|  | ||||
|     s->l2_cache_offsets[min_index] = l2_offset; | ||||
|     s->l2_cache_counts[min_index] = 1; | ||||
|  | ||||
|     return l2_table; | ||||
|  | ||||
| fail: | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void**) table); | ||||
|     s->l1_table[l1_index] = old_l2_offset; | ||||
|     return ret; | ||||
|     qcow2_l2_cache_reset(bs); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, | ||||
| @@ -290,35 +325,22 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
| } | ||||
|  | ||||
|  | ||||
| static int qcow2_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                       uint8_t *buf, int nb_sectors) | ||||
| static int qcow_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                      uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret, index_in_cluster, n, n1; | ||||
|     uint64_t cluster_offset; | ||||
|     struct iovec iov; | ||||
|     QEMUIOVector qiov; | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
|         n = nb_sectors; | ||||
|  | ||||
|         ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n, | ||||
|             &cluster_offset); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
|         cluster_offset = qcow2_get_cluster_offset(bs, sector_num << 9, &n); | ||||
|         index_in_cluster = sector_num & (s->cluster_sectors - 1); | ||||
|         if (!cluster_offset) { | ||||
|             if (bs->backing_hd) { | ||||
|                 /* read from the base image */ | ||||
|                 iov.iov_base = buf; | ||||
|                 iov.iov_len = n * 512; | ||||
|                 qemu_iovec_init_external(&qiov, &iov, 1); | ||||
|  | ||||
|                 n1 = qcow2_backing_read1(bs->backing_hd, &qiov, sector_num, n); | ||||
|                 n1 = qcow2_backing_read1(bs->backing_hd, sector_num, buf, n); | ||||
|                 if (n1 > 0) { | ||||
|                     BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING); | ||||
|                     ret = bdrv_read(bs->backing_hd, sector_num, buf, n1); | ||||
|                     if (ret < 0) | ||||
|                         return -1; | ||||
| @@ -327,12 +349,11 @@ static int qcow2_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                 memset(buf, 0, 512 * n); | ||||
|             } | ||||
|         } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { | ||||
|             if (qcow2_decompress_cluster(bs, cluster_offset) < 0) | ||||
|             if (qcow2_decompress_cluster(s, cluster_offset) < 0) | ||||
|                 return -1; | ||||
|             memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); | ||||
|         } else { | ||||
|             BLKDBG_EVENT(bs->file, BLKDBG_READ); | ||||
|             ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512); | ||||
|             ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); | ||||
|             if (ret != n * 512) | ||||
|                 return -1; | ||||
|             if (s->crypt_method) { | ||||
| @@ -356,8 +377,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, | ||||
|     n = n_end - n_start; | ||||
|     if (n <= 0) | ||||
|         return 0; | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_COW_READ); | ||||
|     ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n); | ||||
|     ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|     if (s->crypt_method) { | ||||
| @@ -366,8 +386,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, | ||||
|                         s->cluster_data, n, 1, | ||||
|                         &s->aes_encrypt_key); | ||||
|     } | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); | ||||
|     ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start, | ||||
|     ret = bdrv_write_sync(s->hd, (cluster_offset >> 9) + n_start, | ||||
|         s->cluster_data, n); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
| @@ -378,29 +397,28 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, | ||||
| /* | ||||
|  * get_cluster_offset | ||||
|  * | ||||
|  * For a given offset of the disk image, find the cluster offset in | ||||
|  * qcow2 file. The offset is stored in *cluster_offset. | ||||
|  * For a given offset of the disk image, return cluster offset in | ||||
|  * qcow2 file. | ||||
|  * | ||||
|  * on entry, *num is the number of contiguous clusters we'd like to | ||||
|  * access following offset. | ||||
|  * | ||||
|  * on exit, *num is the number of contiguous clusters we can read. | ||||
|  * | ||||
|  * Return 0, if the offset is found | ||||
|  * Return -errno, otherwise. | ||||
|  * Return 1, if the offset is found | ||||
|  * Return 0, otherwise. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int *num, uint64_t *cluster_offset) | ||||
| uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int *num) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     unsigned int l1_index, l2_index; | ||||
|     uint64_t l2_offset, *l2_table; | ||||
|     uint64_t l2_offset, *l2_table, cluster_offset; | ||||
|     int l1_bits, c; | ||||
|     unsigned int index_in_cluster, nb_clusters; | ||||
|     uint64_t nb_available, nb_needed; | ||||
|     int ret; | ||||
|  | ||||
|     index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1); | ||||
|     nb_needed = *num + index_in_cluster; | ||||
| @@ -421,7 +439,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|         nb_needed = nb_available; | ||||
|     } | ||||
|  | ||||
|     *cluster_offset = 0; | ||||
|     cluster_offset = 0; | ||||
|  | ||||
|     /* seek the the l2 offset in the l1 table */ | ||||
|  | ||||
| @@ -439,18 +457,17 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     /* load the l2 table in memory */ | ||||
|  | ||||
|     l2_offset &= ~QCOW_OFLAG_COPIED; | ||||
|     ret = l2_load(bs, l2_offset, &l2_table); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|     l2_table = l2_load(bs, l2_offset); | ||||
|     if (l2_table == NULL) | ||||
|         return 0; | ||||
|  | ||||
|     /* find the cluster offset for the given disk offset */ | ||||
|  | ||||
|     l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); | ||||
|     *cluster_offset = be64_to_cpu(l2_table[l2_index]); | ||||
|     cluster_offset = be64_to_cpu(l2_table[l2_index]); | ||||
|     nb_clusters = size_to_clusters(s, nb_needed << 9); | ||||
|  | ||||
|     if (!*cluster_offset) { | ||||
|     if (!cluster_offset) { | ||||
|         /* how many empty clusters ? */ | ||||
|         c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); | ||||
|     } else { | ||||
| @@ -459,8 +476,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|                 &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); | ||||
|     } | ||||
|  | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|  | ||||
|    nb_available = (c * s->cluster_sectors); | ||||
| out: | ||||
|     if (nb_available > nb_needed) | ||||
| @@ -468,8 +483,7 @@ out: | ||||
|  | ||||
|     *num = nb_available - index_in_cluster; | ||||
|  | ||||
|     *cluster_offset &=~QCOW_OFLAG_COPIED; | ||||
|     return 0; | ||||
|     return cluster_offset & ~QCOW_OFLAG_COPIED; | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -490,15 +504,14 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     unsigned int l1_index, l2_index; | ||||
|     uint64_t l2_offset; | ||||
|     uint64_t *l2_table = NULL; | ||||
|     uint64_t l2_offset, *l2_table; | ||||
|     int ret; | ||||
|  | ||||
|     /* seek the the l2 offset in the l1 table */ | ||||
|  | ||||
|     l1_index = offset >> (s->l2_bits + s->cluster_bits); | ||||
|     if (l1_index >= s->l1_size) { | ||||
|         ret = qcow2_grow_l1_table(bs, l1_index + 1, false); | ||||
|         ret = qcow2_grow_l1_table(bs, l1_index + 1); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
| @@ -510,20 +523,16 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, | ||||
|     if (l2_offset & QCOW_OFLAG_COPIED) { | ||||
|         /* load the l2 table in memory */ | ||||
|         l2_offset &= ~QCOW_OFLAG_COPIED; | ||||
|         ret = l2_load(bs, l2_offset, &l2_table); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         l2_table = l2_load(bs, l2_offset); | ||||
|         if (l2_table == NULL) { | ||||
|             return -EIO; | ||||
|         } | ||||
|     } else { | ||||
|         /* First allocate a new L2 table (and do COW if needed) */ | ||||
|         ret = l2_allocate(bs, l1_index, &l2_table); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
|         /* Then decrease the refcount of the old table */ | ||||
|         if (l2_offset) { | ||||
|         if (l2_offset) | ||||
|             qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); | ||||
|         l2_table = l2_allocate(bs, l1_index); | ||||
|         if (l2_table == NULL) { | ||||
|             return -EIO; | ||||
|         } | ||||
|         l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; | ||||
|     } | ||||
| @@ -576,7 +585,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||||
|  | ||||
|     cluster_offset = qcow2_alloc_bytes(bs, compressed_size); | ||||
|     if (cluster_offset < 0) { | ||||
|         qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
| @@ -590,24 +598,45 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||||
|  | ||||
|     /* compressed clusters never have the copied flag */ | ||||
|  | ||||
|     BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); | ||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|     l2_table[l2_index] = cpu_to_be64(cluster_offset); | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (ret < 0) { | ||||
|     if (bdrv_pwrite_sync(s->hd, | ||||
|                     l2_offset + l2_index * sizeof(uint64_t), | ||||
|                     l2_table + l2_index, | ||||
|                     sizeof(uint64_t)) < 0) | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return cluster_offset; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Write L2 table updates to disk, writing whole sectors to avoid a | ||||
|  * read-modify-write in bdrv_pwrite | ||||
|  */ | ||||
| #define L2_ENTRIES_PER_SECTOR (512 / 8) | ||||
| static int write_l2_entries(BDRVQcowState *s, uint64_t *l2_table, | ||||
|     uint64_t l2_offset, int l2_index, int num) | ||||
| { | ||||
|     int l2_start_index = l2_index & ~(L1_ENTRIES_PER_SECTOR - 1); | ||||
|     int start_offset = (8 * l2_index) & ~511; | ||||
|     int end_offset = (8 * (l2_index + num) + 511) & ~511; | ||||
|     size_t len = end_offset - start_offset; | ||||
|     int ret; | ||||
|  | ||||
|     ret = bdrv_pwrite_sync(s->hd, l2_offset + start_offset, | ||||
|         &l2_table[l2_start_index], len); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int i, j = 0, l2_index, ret; | ||||
|     uint64_t *old_cluster, start_sect, l2_offset, *l2_table; | ||||
|     uint64_t cluster_offset = m->cluster_offset; | ||||
|     bool cow = false; | ||||
|  | ||||
|     if (m->nb_clusters == 0) | ||||
|         return 0; | ||||
| @@ -617,7 +646,6 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||||
|     /* copy content of unmodified sectors */ | ||||
|     start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9; | ||||
|     if (m->n_start) { | ||||
|         cow = true; | ||||
|         ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start); | ||||
|         if (ret < 0) | ||||
|             goto err; | ||||
| @@ -625,30 +653,17 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||||
|  | ||||
|     if (m->nb_available & (s->cluster_sectors - 1)) { | ||||
|         uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1); | ||||
|         cow = true; | ||||
|         ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9), | ||||
|                 m->nb_available - end, s->cluster_sectors); | ||||
|         if (ret < 0) | ||||
|             goto err; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Update L2 table. | ||||
|      * | ||||
|      * Before we update the L2 table to actually point to the new cluster, we | ||||
|      * need to be sure that the refcounts have been increased and COW was | ||||
|      * handled. | ||||
|      */ | ||||
|     if (cow) { | ||||
|         qcow2_cache_depends_on_flush(s->l2_table_cache); | ||||
|     } | ||||
|  | ||||
|     qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); | ||||
|     /* update L2 table */ | ||||
|     ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index); | ||||
|     if (ret < 0) { | ||||
|         goto err; | ||||
|     } | ||||
|     qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|  | ||||
|     for (i = 0; i < m->nb_clusters; i++) { | ||||
|         /* if two concurrent writes happen to the same unallocated cluster | ||||
| @@ -664,22 +679,15 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) | ||||
|                     (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); | ||||
|      } | ||||
|  | ||||
|  | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     ret = write_l2_entries(s, l2_table, l2_offset, l2_index, m->nb_clusters); | ||||
|     if (ret < 0) { | ||||
|         qcow2_l2_cache_reset(bs); | ||||
|         goto err; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * If this was a COW, we need to decrease the refcount of the old cluster. | ||||
|      * Also flush bs->file to get the right order for L2 and refcount update. | ||||
|      */ | ||||
|     if (j != 0) { | ||||
|         for (i = 0; i < j; i++) { | ||||
|             qcow2_free_any_clusters(bs, | ||||
|                 be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); | ||||
|         } | ||||
|     } | ||||
|     for (i = 0; i < j; i++) | ||||
|         qcow2_free_any_clusters(bs, | ||||
|             be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); | ||||
|  | ||||
|     ret = 0; | ||||
| err: | ||||
| @@ -796,8 +804,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|                 m->depends_on = old_alloc; | ||||
|                 m->nb_clusters = 0; | ||||
|                 *num = 0; | ||||
|                 ret = 0; | ||||
|                 goto fail; | ||||
|                 return 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -813,8 +820,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size); | ||||
|     if (cluster_offset < 0) { | ||||
|         QLIST_REMOVE(m, next_in_flight); | ||||
|         ret = cluster_offset; | ||||
|         goto fail; | ||||
|         return cluster_offset; | ||||
|     } | ||||
|  | ||||
|     /* save info needed for meta data update */ | ||||
| @@ -823,21 +829,12 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     m->nb_clusters = nb_clusters; | ||||
|  | ||||
| out: | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end); | ||||
|     m->cluster_offset = cluster_offset; | ||||
|  | ||||
|     *num = m->nb_available - n_start; | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| fail: | ||||
|     qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | ||||
| @@ -867,9 +864,8 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
| int qcow2_decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     int ret, csize, nb_csectors, sector_offset; | ||||
|     uint64_t coffset; | ||||
|  | ||||
| @@ -878,98 +874,15 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) | ||||
|         nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; | ||||
|         sector_offset = coffset & 511; | ||||
|         csize = nb_csectors * 512 - sector_offset; | ||||
|         BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); | ||||
|         ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); | ||||
|         ret = bdrv_read(s->hd, coffset >> 9, s->cluster_data, nb_csectors); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|             return -1; | ||||
|         } | ||||
|         if (decompress_buffer(s->cluster_cache, s->cluster_size, | ||||
|                               s->cluster_data + sector_offset, csize) < 0) { | ||||
|             return -EIO; | ||||
|             return -1; | ||||
|         } | ||||
|         s->cluster_cache_offset = coffset; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * This discards as many clusters of nb_clusters as possible at once (i.e. | ||||
|  * all clusters in the same L2 table) and returns the number of discarded | ||||
|  * clusters. | ||||
|  */ | ||||
| static int discard_single_l2(BlockDriverState *bs, uint64_t offset, | ||||
|     unsigned int nb_clusters) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t l2_offset, *l2_table; | ||||
|     int l2_index; | ||||
|     int ret; | ||||
|     int i; | ||||
|  | ||||
|     ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     /* Limit nb_clusters to one L2 table */ | ||||
|     nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); | ||||
|  | ||||
|     for (i = 0; i < nb_clusters; i++) { | ||||
|         uint64_t old_offset; | ||||
|  | ||||
|         old_offset = be64_to_cpu(l2_table[l2_index + i]); | ||||
|         old_offset &= ~QCOW_OFLAG_COPIED; | ||||
|  | ||||
|         if (old_offset == 0) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         /* First remove L2 entries */ | ||||
|         qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); | ||||
|         l2_table[l2_index + i] = cpu_to_be64(0); | ||||
|  | ||||
|         /* Then decrease the refcount */ | ||||
|         qcow2_free_any_clusters(bs, old_offset, 1); | ||||
|     } | ||||
|  | ||||
|     ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return nb_clusters; | ||||
| } | ||||
|  | ||||
| int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, | ||||
|     int nb_sectors) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     uint64_t end_offset; | ||||
|     unsigned int nb_clusters; | ||||
|     int ret; | ||||
|  | ||||
|     end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); | ||||
|  | ||||
|     /* Round start up and end down */ | ||||
|     offset = align_offset(offset, s->cluster_size); | ||||
|     end_offset &= ~(s->cluster_size - 1); | ||||
|  | ||||
|     if (offset > end_offset) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     nb_clusters = size_to_clusters(s, end_offset - offset); | ||||
|  | ||||
|     /* Each L2 table is handled by its own loop iteration */ | ||||
|     while (nb_clusters > 0) { | ||||
|         ret = discard_single_l2(bs, offset, nb_clusters); | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
|         nb_clusters -= ret; | ||||
|         offset += (ret * s->cluster_size); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -79,7 +79,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | ||||
|     s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot)); | ||||
|     for(i = 0; i < s->nb_snapshots; i++) { | ||||
|         offset = align_offset(offset, 8); | ||||
|         if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h)) | ||||
|         if (bdrv_pread(s->hd, offset, &h, sizeof(h)) != sizeof(h)) | ||||
|             goto fail; | ||||
|         offset += sizeof(h); | ||||
|         sn = s->snapshots + i; | ||||
| @@ -97,13 +97,13 @@ int qcow2_read_snapshots(BlockDriverState *bs) | ||||
|         offset += extra_data_size; | ||||
|  | ||||
|         sn->id_str = qemu_malloc(id_str_size + 1); | ||||
|         if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size) | ||||
|         if (bdrv_pread(s->hd, offset, sn->id_str, id_str_size) != id_str_size) | ||||
|             goto fail; | ||||
|         offset += id_str_size; | ||||
|         sn->id_str[id_str_size] = '\0'; | ||||
|  | ||||
|         sn->name = qemu_malloc(name_size + 1); | ||||
|         if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size) | ||||
|         if (bdrv_pread(s->hd, offset, sn->name, name_size) != name_size) | ||||
|             goto fail; | ||||
|         offset += name_size; | ||||
|         sn->name[name_size] = '\0'; | ||||
| @@ -116,7 +116,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) | ||||
| } | ||||
|  | ||||
| /* add at the end of the file a new list of snapshots */ | ||||
| static int qcow2_write_snapshots(BlockDriverState *bs) | ||||
| static int qcow_write_snapshots(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshot *sn; | ||||
| @@ -138,7 +138,6 @@ static int qcow2_write_snapshots(BlockDriverState *bs) | ||||
|     snapshots_size = offset; | ||||
|  | ||||
|     snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); | ||||
|     bdrv_flush(bs->file); | ||||
|     offset = snapshots_offset; | ||||
|     if (offset < 0) { | ||||
|         return offset; | ||||
| @@ -159,24 +158,24 @@ static int qcow2_write_snapshots(BlockDriverState *bs) | ||||
|         h.id_str_size = cpu_to_be16(id_str_size); | ||||
|         h.name_size = cpu_to_be16(name_size); | ||||
|         offset = align_offset(offset, 8); | ||||
|         if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0) | ||||
|         if (bdrv_pwrite_sync(s->hd, offset, &h, sizeof(h)) < 0) | ||||
|             goto fail; | ||||
|         offset += sizeof(h); | ||||
|         if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0) | ||||
|         if (bdrv_pwrite_sync(s->hd, offset, sn->id_str, id_str_size) < 0) | ||||
|             goto fail; | ||||
|         offset += id_str_size; | ||||
|         if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0) | ||||
|         if (bdrv_pwrite_sync(s->hd, offset, sn->name, name_size) < 0) | ||||
|             goto fail; | ||||
|         offset += name_size; | ||||
|     } | ||||
|  | ||||
|     /* update the various header fields */ | ||||
|     data64 = cpu_to_be64(snapshots_offset); | ||||
|     if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset), | ||||
|     if (bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, snapshots_offset), | ||||
|                     &data64, sizeof(data64)) < 0) | ||||
|         goto fail; | ||||
|     data32 = cpu_to_be32(s->nb_snapshots); | ||||
|     if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), | ||||
|     if (bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, nb_snapshots), | ||||
|                     &data32, sizeof(data32)) < 0) | ||||
|         goto fail; | ||||
|  | ||||
| @@ -272,7 +271,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|     if (l1_table_offset < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     bdrv_flush(bs->file); | ||||
|  | ||||
|     sn->l1_table_offset = l1_table_offset; | ||||
|     sn->l1_size = s->l1_size; | ||||
| @@ -286,7 +284,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|     for(i = 0; i < s->l1_size; i++) { | ||||
|         l1_table[i] = cpu_to_be64(s->l1_table[i]); | ||||
|     } | ||||
|     if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset, | ||||
|     if (bdrv_pwrite_sync(s->hd, sn->l1_table_offset, | ||||
|                     l1_table, s->l1_size * sizeof(uint64_t)) < 0) | ||||
|         goto fail; | ||||
|     qemu_free(l1_table); | ||||
| @@ -300,7 +298,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) | ||||
|     s->snapshots = snapshots1; | ||||
|     s->snapshots[s->nb_snapshots++] = *sn; | ||||
|  | ||||
|     if (qcow2_write_snapshots(bs) < 0) | ||||
|     if (qcow_write_snapshots(bs) < 0) | ||||
|         goto fail; | ||||
| #ifdef DEBUG_ALLOC | ||||
|     qcow2_check_refcounts(bs); | ||||
| @@ -327,16 +325,16 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) | ||||
|     if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0) | ||||
|         goto fail; | ||||
|  | ||||
|     if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0) | ||||
|     if (qcow2_grow_l1_table(bs, sn->l1_size) < 0) | ||||
|         goto fail; | ||||
|  | ||||
|     s->l1_size = sn->l1_size; | ||||
|     l1_size2 = s->l1_size * sizeof(uint64_t); | ||||
|     /* copy the snapshot l1 table to the current l1 table */ | ||||
|     if (bdrv_pread(bs->file, sn->l1_table_offset, | ||||
|     if (bdrv_pread(s->hd, sn->l1_table_offset, | ||||
|                    s->l1_table, l1_size2) != l1_size2) | ||||
|         goto fail; | ||||
|     if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, | ||||
|     if (bdrv_pwrite_sync(s->hd, s->l1_table_offset, | ||||
|                     s->l1_table, l1_size2) < 0) | ||||
|         goto fail; | ||||
|     for(i = 0;i < s->l1_size; i++) { | ||||
| @@ -378,7 +376,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) | ||||
|     qemu_free(sn->name); | ||||
|     memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn)); | ||||
|     s->nb_snapshots--; | ||||
|     ret = qcow2_write_snapshots(bs); | ||||
|     ret = qcow_write_snapshots(bs); | ||||
|     if (ret < 0) { | ||||
|         /* XXX: restore snapshot if error ? */ | ||||
|         return ret; | ||||
| @@ -418,34 +416,3 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) | ||||
|     return s->nb_snapshots; | ||||
| } | ||||
|  | ||||
| int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name) | ||||
| { | ||||
|     int i, snapshot_index, l1_size2; | ||||
|     BDRVQcowState *s = bs->opaque; | ||||
|     QCowSnapshot *sn; | ||||
|  | ||||
|     snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name); | ||||
|     if (snapshot_index < 0) { | ||||
|         return -ENOENT; | ||||
|     } | ||||
|  | ||||
|     sn = &s->snapshots[snapshot_index]; | ||||
|     s->l1_size = sn->l1_size; | ||||
|     l1_size2 = s->l1_size * sizeof(uint64_t); | ||||
|     if (s->l1_table != NULL) { | ||||
|         qemu_free(s->l1_table); | ||||
|     } | ||||
|  | ||||
|     s->l1_table_offset = sn->l1_table_offset; | ||||
|     s->l1_table = qemu_mallocz(align_offset(l1_size2, 512)); | ||||
|  | ||||
|     if (bdrv_pread(bs->file, sn->l1_table_offset, | ||||
|                    s->l1_table, l1_size2) != l1_size2) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     for(i = 0;i < s->l1_size; i++) { | ||||
|         be64_to_cpus(&s->l1_table[i]); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
							
								
								
									
										922
									
								
								block/qcow2.c
									
									
									
									
									
								
							
							
						
						
									
										922
									
								
								block/qcow2.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -51,9 +51,6 @@ | ||||
|  | ||||
| #define L2_CACHE_SIZE 16 | ||||
|  | ||||
| /* Must be at least 4 to cover all cases of refcount table growth */ | ||||
| #define REFCOUNT_CACHE_SIZE 4 | ||||
|  | ||||
| typedef struct QCowHeader { | ||||
|     uint32_t magic; | ||||
|     uint32_t version; | ||||
| @@ -81,10 +78,8 @@ typedef struct QCowSnapshot { | ||||
|     uint64_t vm_clock_nsec; | ||||
| } QCowSnapshot; | ||||
|  | ||||
| struct Qcow2Cache; | ||||
| typedef struct Qcow2Cache Qcow2Cache; | ||||
|  | ||||
| typedef struct BDRVQcowState { | ||||
|     BlockDriverState *hd; | ||||
|     int cluster_bits; | ||||
|     int cluster_size; | ||||
|     int cluster_sectors; | ||||
| @@ -97,10 +92,9 @@ typedef struct BDRVQcowState { | ||||
|     uint64_t cluster_offset_mask; | ||||
|     uint64_t l1_table_offset; | ||||
|     uint64_t *l1_table; | ||||
|  | ||||
|     Qcow2Cache* l2_table_cache; | ||||
|     Qcow2Cache* refcount_block_cache; | ||||
|  | ||||
|     uint64_t *l2_cache; | ||||
|     uint64_t l2_cache_offsets[L2_CACHE_SIZE]; | ||||
|     uint32_t l2_cache_counts[L2_CACHE_SIZE]; | ||||
|     uint8_t *cluster_cache; | ||||
|     uint8_t *cluster_data; | ||||
|     uint64_t cluster_cache_offset; | ||||
| @@ -109,6 +103,8 @@ typedef struct BDRVQcowState { | ||||
|     uint64_t *refcount_table; | ||||
|     uint64_t refcount_table_offset; | ||||
|     uint32_t refcount_table_size; | ||||
|     uint64_t refcount_block_cache_offset; | ||||
|     uint16_t *refcount_block_cache; | ||||
|     int64_t free_cluster_index; | ||||
|     int64_t free_byte_offset; | ||||
|  | ||||
| @@ -154,12 +150,6 @@ static inline int size_to_clusters(BDRVQcowState *s, int64_t size) | ||||
|     return (size + (s->cluster_size - 1)) >> s->cluster_bits; | ||||
| } | ||||
|  | ||||
| static inline int size_to_l1(BDRVQcowState *s, int64_t size) | ||||
| { | ||||
|     int shift = s->cluster_bits + s->l2_bits; | ||||
|     return (size + (1ULL << shift) - 1) >> shift; | ||||
| } | ||||
|  | ||||
| static inline int64_t align_offset(int64_t offset, int n) | ||||
| { | ||||
|     offset = (offset + n - 1) & ~(n - 1); | ||||
| @@ -170,8 +160,8 @@ static inline int64_t align_offset(int64_t offset, int n) | ||||
| // FIXME Need qcow2_ prefix to global functions | ||||
|  | ||||
| /* qcow2.c functions */ | ||||
| int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, | ||||
|                   int64_t sector_num, int nb_sectors); | ||||
| int qcow2_backing_read1(BlockDriverState *bs, | ||||
|                   int64_t sector_num, uint8_t *buf, int nb_sectors); | ||||
|  | ||||
| /* qcow2-refcount.c functions */ | ||||
| int qcow2_refcount_init(BlockDriverState *bs); | ||||
| @@ -189,19 +179,19 @@ void qcow2_create_refcount_update(QCowCreateState *s, int64_t offset, | ||||
| int qcow2_update_snapshot_refcount(BlockDriverState *bs, | ||||
|     int64_t l1_table_offset, int l1_size, int addend); | ||||
|  | ||||
| int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res); | ||||
| int qcow2_check_refcounts(BlockDriverState *bs); | ||||
|  | ||||
| /* qcow2-cluster.c functions */ | ||||
| int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size); | ||||
| int qcow2_grow_l1_table(BlockDriverState *bs, int min_size); | ||||
| void qcow2_l2_cache_reset(BlockDriverState *bs); | ||||
| int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); | ||||
| int qcow2_decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); | ||||
| void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, | ||||
|                      uint8_t *out_buf, const uint8_t *in_buf, | ||||
|                      int nb_sectors, int enc, | ||||
|                      const AES_KEY *key); | ||||
|  | ||||
| int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int *num, uint64_t *cluster_offset); | ||||
| uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int *num); | ||||
| int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, | ||||
|     int n_start, int n_end, int *num, QCowL2Meta *m); | ||||
| uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||||
| @@ -209,34 +199,14 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, | ||||
|                                          int compressed_size); | ||||
|  | ||||
| int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); | ||||
| int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, | ||||
|     int nb_sectors); | ||||
|  | ||||
| /* qcow2-snapshot.c functions */ | ||||
| int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); | ||||
| int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); | ||||
| int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); | ||||
| int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); | ||||
| int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name); | ||||
|  | ||||
| void qcow2_free_snapshots(BlockDriverState *bs); | ||||
| int qcow2_read_snapshots(BlockDriverState *bs); | ||||
|  | ||||
| /* qcow2-cache.c functions */ | ||||
| Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, | ||||
|     bool writethrough); | ||||
| int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); | ||||
|  | ||||
| void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); | ||||
| int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); | ||||
| int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, | ||||
|     Qcow2Cache *dependency); | ||||
| void qcow2_cache_depends_on_flush(Qcow2Cache *c); | ||||
|  | ||||
| int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||||
|     void **table); | ||||
| int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, | ||||
|     void **table); | ||||
| int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,210 +0,0 @@ | ||||
| /* | ||||
|  * QEMU Enhanced Disk Format Consistency Check | ||||
|  * | ||||
|  * Copyright IBM, Corp. 2010 | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qed.h" | ||||
|  | ||||
| typedef struct { | ||||
|     BDRVQEDState *s; | ||||
|     BdrvCheckResult *result; | ||||
|     bool fix;                           /* whether to fix invalid offsets */ | ||||
|  | ||||
|     uint64_t nclusters; | ||||
|     uint32_t *used_clusters;            /* referenced cluster bitmap */ | ||||
|  | ||||
|     QEDRequest request; | ||||
| } QEDCheck; | ||||
|  | ||||
| static bool qed_test_bit(uint32_t *bitmap, uint64_t n) { | ||||
|     return !!(bitmap[n / 32] & (1 << (n % 32))); | ||||
| } | ||||
|  | ||||
| static void qed_set_bit(uint32_t *bitmap, uint64_t n) { | ||||
|     bitmap[n / 32] |= 1 << (n % 32); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Set bitmap bits for clusters | ||||
|  * | ||||
|  * @check:          Check structure | ||||
|  * @offset:         Starting offset in bytes | ||||
|  * @n:              Number of clusters | ||||
|  */ | ||||
| static bool qed_set_used_clusters(QEDCheck *check, uint64_t offset, | ||||
|                                   unsigned int n) | ||||
| { | ||||
|     uint64_t cluster = qed_bytes_to_clusters(check->s, offset); | ||||
|     unsigned int corruptions = 0; | ||||
|  | ||||
|     while (n-- != 0) { | ||||
|         /* Clusters should only be referenced once */ | ||||
|         if (qed_test_bit(check->used_clusters, cluster)) { | ||||
|             corruptions++; | ||||
|         } | ||||
|  | ||||
|         qed_set_bit(check->used_clusters, cluster); | ||||
|         cluster++; | ||||
|     } | ||||
|  | ||||
|     check->result->corruptions += corruptions; | ||||
|     return corruptions == 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Check an L2 table | ||||
|  * | ||||
|  * @ret:            Number of invalid cluster offsets | ||||
|  */ | ||||
| static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table) | ||||
| { | ||||
|     BDRVQEDState *s = check->s; | ||||
|     unsigned int i, num_invalid = 0; | ||||
|  | ||||
|     for (i = 0; i < s->table_nelems; i++) { | ||||
|         uint64_t offset = table->offsets[i]; | ||||
|  | ||||
|         if (!offset) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         /* Detect invalid cluster offset */ | ||||
|         if (!qed_check_cluster_offset(s, offset)) { | ||||
|             if (check->fix) { | ||||
|                 table->offsets[i] = 0; | ||||
|             } else { | ||||
|                 check->result->corruptions++; | ||||
|             } | ||||
|  | ||||
|             num_invalid++; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         qed_set_used_clusters(check, offset, 1); | ||||
|     } | ||||
|  | ||||
|     return num_invalid; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Descend tables and check each cluster is referenced once only | ||||
|  */ | ||||
| static int qed_check_l1_table(QEDCheck *check, QEDTable *table) | ||||
| { | ||||
|     BDRVQEDState *s = check->s; | ||||
|     unsigned int i, num_invalid_l1 = 0; | ||||
|     int ret, last_error = 0; | ||||
|  | ||||
|     /* Mark L1 table clusters used */ | ||||
|     qed_set_used_clusters(check, s->header.l1_table_offset, | ||||
|                           s->header.table_size); | ||||
|  | ||||
|     for (i = 0; i < s->table_nelems; i++) { | ||||
|         unsigned int num_invalid_l2; | ||||
|         uint64_t offset = table->offsets[i]; | ||||
|  | ||||
|         if (!offset) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         /* Detect invalid L2 offset */ | ||||
|         if (!qed_check_table_offset(s, offset)) { | ||||
|             /* Clear invalid offset */ | ||||
|             if (check->fix) { | ||||
|                 table->offsets[i] = 0; | ||||
|             } else { | ||||
|                 check->result->corruptions++; | ||||
|             } | ||||
|  | ||||
|             num_invalid_l1++; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (!qed_set_used_clusters(check, offset, s->header.table_size)) { | ||||
|             continue; /* skip an invalid table */ | ||||
|         } | ||||
|  | ||||
|         ret = qed_read_l2_table_sync(s, &check->request, offset); | ||||
|         if (ret) { | ||||
|             check->result->check_errors++; | ||||
|             last_error = ret; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         num_invalid_l2 = qed_check_l2_table(check, | ||||
|                                             check->request.l2_table->table); | ||||
|  | ||||
|         /* Write out fixed L2 table */ | ||||
|         if (num_invalid_l2 > 0 && check->fix) { | ||||
|             ret = qed_write_l2_table_sync(s, &check->request, 0, | ||||
|                                           s->table_nelems, false); | ||||
|             if (ret) { | ||||
|                 check->result->check_errors++; | ||||
|                 last_error = ret; | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Drop reference to final table */ | ||||
|     qed_unref_l2_cache_entry(check->request.l2_table); | ||||
|     check->request.l2_table = NULL; | ||||
|  | ||||
|     /* Write out fixed L1 table */ | ||||
|     if (num_invalid_l1 > 0 && check->fix) { | ||||
|         ret = qed_write_l1_table_sync(s, 0, s->table_nelems); | ||||
|         if (ret) { | ||||
|             check->result->check_errors++; | ||||
|             last_error = ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return last_error; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Check for unreferenced (leaked) clusters | ||||
|  */ | ||||
| static void qed_check_for_leaks(QEDCheck *check) | ||||
| { | ||||
|     BDRVQEDState *s = check->s; | ||||
|     uint64_t i; | ||||
|  | ||||
|     for (i = s->header.header_size; i < check->nclusters; i++) { | ||||
|         if (!qed_test_bit(check->used_clusters, i)) { | ||||
|             check->result->leaks++; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix) | ||||
| { | ||||
|     QEDCheck check = { | ||||
|         .s = s, | ||||
|         .result = result, | ||||
|         .nclusters = qed_bytes_to_clusters(s, s->file_size), | ||||
|         .request = { .l2_table = NULL }, | ||||
|         .fix = fix, | ||||
|     }; | ||||
|     int ret; | ||||
|  | ||||
|     check.used_clusters = qemu_mallocz(((check.nclusters + 31) / 32) * | ||||
|                                        sizeof(check.used_clusters[0])); | ||||
|  | ||||
|     ret = qed_check_l1_table(&check, s->l1_table); | ||||
|     if (ret == 0) { | ||||
|         /* Only check for leaks if entire image was scanned successfully */ | ||||
|         qed_check_for_leaks(&check); | ||||
|     } | ||||
|  | ||||
|     qemu_free(check.used_clusters); | ||||
|     return ret; | ||||
| } | ||||
| @@ -1,154 +0,0 @@ | ||||
| /* | ||||
|  * QEMU Enhanced Disk Format Cluster functions | ||||
|  * | ||||
|  * Copyright IBM, Corp. 2010 | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> | ||||
|  *  Anthony Liguori   <aliguori@us.ibm.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qed.h" | ||||
|  | ||||
| /** | ||||
|  * Count the number of contiguous data clusters | ||||
|  * | ||||
|  * @s:              QED state | ||||
|  * @table:          L2 table | ||||
|  * @index:          First cluster index | ||||
|  * @n:              Maximum number of clusters | ||||
|  * @offset:         Set to first cluster offset | ||||
|  * | ||||
|  * This function scans tables for contiguous allocated or free clusters. | ||||
|  */ | ||||
| static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s, | ||||
|                                                   QEDTable *table, | ||||
|                                                   unsigned int index, | ||||
|                                                   unsigned int n, | ||||
|                                                   uint64_t *offset) | ||||
| { | ||||
|     unsigned int end = MIN(index + n, s->table_nelems); | ||||
|     uint64_t last = table->offsets[index]; | ||||
|     unsigned int i; | ||||
|  | ||||
|     *offset = last; | ||||
|  | ||||
|     for (i = index + 1; i < end; i++) { | ||||
|         if (last == 0) { | ||||
|             /* Counting free clusters */ | ||||
|             if (table->offsets[i] != 0) { | ||||
|                 break; | ||||
|             } | ||||
|         } else { | ||||
|             /* Counting allocated clusters */ | ||||
|             if (table->offsets[i] != last + s->header.cluster_size) { | ||||
|                 break; | ||||
|             } | ||||
|             last = table->offsets[i]; | ||||
|         } | ||||
|     } | ||||
|     return i - index; | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     BDRVQEDState *s; | ||||
|     uint64_t pos; | ||||
|     size_t len; | ||||
|  | ||||
|     QEDRequest *request; | ||||
|  | ||||
|     /* User callback */ | ||||
|     QEDFindClusterFunc *cb; | ||||
|     void *opaque; | ||||
| } QEDFindClusterCB; | ||||
|  | ||||
| static void qed_find_cluster_cb(void *opaque, int ret) | ||||
| { | ||||
|     QEDFindClusterCB *find_cluster_cb = opaque; | ||||
|     BDRVQEDState *s = find_cluster_cb->s; | ||||
|     QEDRequest *request = find_cluster_cb->request; | ||||
|     uint64_t offset = 0; | ||||
|     size_t len = 0; | ||||
|     unsigned int index; | ||||
|     unsigned int n; | ||||
|  | ||||
|     if (ret) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     index = qed_l2_index(s, find_cluster_cb->pos); | ||||
|     n = qed_bytes_to_clusters(s, | ||||
|                               qed_offset_into_cluster(s, find_cluster_cb->pos) + | ||||
|                               find_cluster_cb->len); | ||||
|     n = qed_count_contiguous_clusters(s, request->l2_table->table, | ||||
|                                       index, n, &offset); | ||||
|  | ||||
|     ret = offset ? QED_CLUSTER_FOUND : QED_CLUSTER_L2; | ||||
|     len = MIN(find_cluster_cb->len, n * s->header.cluster_size - | ||||
|               qed_offset_into_cluster(s, find_cluster_cb->pos)); | ||||
|  | ||||
|     if (offset && !qed_check_cluster_offset(s, offset)) { | ||||
|         ret = -EINVAL; | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len); | ||||
|     qemu_free(find_cluster_cb); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Find the offset of a data cluster | ||||
|  * | ||||
|  * @s:          QED state | ||||
|  * @request:    L2 cache entry | ||||
|  * @pos:        Byte position in device | ||||
|  * @len:        Number of bytes | ||||
|  * @cb:         Completion function | ||||
|  * @opaque:     User data for completion function | ||||
|  * | ||||
|  * This function translates a position in the block device to an offset in the | ||||
|  * image file.  It invokes the cb completion callback to report back the | ||||
|  * translated offset or unallocated range in the image file. | ||||
|  * | ||||
|  * If the L2 table exists, request->l2_table points to the L2 table cache entry | ||||
|  * and the caller must free the reference when they are finished.  The cache | ||||
|  * entry is exposed in this way to avoid callers having to read the L2 table | ||||
|  * again later during request processing.  If request->l2_table is non-NULL it | ||||
|  * will be unreferenced before taking on the new cache entry. | ||||
|  */ | ||||
| void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, | ||||
|                       size_t len, QEDFindClusterFunc *cb, void *opaque) | ||||
| { | ||||
|     QEDFindClusterCB *find_cluster_cb; | ||||
|     uint64_t l2_offset; | ||||
|  | ||||
|     /* Limit length to L2 boundary.  Requests are broken up at the L2 boundary | ||||
|      * so that a request acts on one L2 table at a time. | ||||
|      */ | ||||
|     len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos); | ||||
|  | ||||
|     l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)]; | ||||
|     if (!l2_offset) { | ||||
|         cb(opaque, QED_CLUSTER_L1, 0, len); | ||||
|         return; | ||||
|     } | ||||
|     if (!qed_check_table_offset(s, l2_offset)) { | ||||
|         cb(opaque, -EINVAL, 0, 0); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     find_cluster_cb = qemu_malloc(sizeof(*find_cluster_cb)); | ||||
|     find_cluster_cb->s = s; | ||||
|     find_cluster_cb->pos = pos; | ||||
|     find_cluster_cb->len = len; | ||||
|     find_cluster_cb->cb = cb; | ||||
|     find_cluster_cb->opaque = opaque; | ||||
|     find_cluster_cb->request = request; | ||||
|  | ||||
|     qed_read_l2_table(s, request, l2_offset, | ||||
|                       qed_find_cluster_cb, find_cluster_cb); | ||||
| } | ||||
| @@ -1,32 +0,0 @@ | ||||
| /* | ||||
|  * QEMU Enhanced Disk Format | ||||
|  * | ||||
|  * Copyright IBM, Corp. 2010 | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "qed.h" | ||||
|  | ||||
| void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     GenericCB *gencb = qemu_malloc(len); | ||||
|     gencb->cb = cb; | ||||
|     gencb->opaque = opaque; | ||||
|     return gencb; | ||||
| } | ||||
|  | ||||
| void gencb_complete(void *opaque, int ret) | ||||
| { | ||||
|     GenericCB *gencb = opaque; | ||||
|     BlockDriverCompletionFunc *cb = gencb->cb; | ||||
|     void *user_opaque = gencb->opaque; | ||||
|  | ||||
|     qemu_free(gencb); | ||||
|     cb(user_opaque, ret); | ||||
| } | ||||
| @@ -1,173 +0,0 @@ | ||||
| /* | ||||
|  * QEMU Enhanced Disk Format L2 Cache | ||||
|  * | ||||
|  * Copyright IBM, Corp. 2010 | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Anthony Liguori   <aliguori@us.ibm.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * L2 table cache usage is as follows: | ||||
|  * | ||||
|  * An open image has one L2 table cache that is used to avoid accessing the | ||||
|  * image file for recently referenced L2 tables. | ||||
|  * | ||||
|  * Cluster offset lookup translates the logical offset within the block device | ||||
|  * to a cluster offset within the image file.  This is done by indexing into | ||||
|  * the L1 and L2 tables which store cluster offsets.  It is here where the L2 | ||||
|  * table cache serves up recently referenced L2 tables. | ||||
|  * | ||||
|  * If there is a cache miss, that L2 table is read from the image file and | ||||
|  * committed to the cache.  Subsequent accesses to that L2 table will be served | ||||
|  * from the cache until the table is evicted from the cache. | ||||
|  * | ||||
|  * L2 tables are also committed to the cache when new L2 tables are allocated | ||||
|  * in the image file.  Since the L2 table cache is write-through, the new L2 | ||||
|  * table is first written out to the image file and then committed to the | ||||
|  * cache. | ||||
|  * | ||||
|  * Multiple I/O requests may be using an L2 table cache entry at any given | ||||
|  * time.  That means an entry may be in use across several requests and | ||||
|  * reference counting is needed to free the entry at the correct time.  In | ||||
|  * particular, an entry evicted from the cache will only be freed once all | ||||
|  * references are dropped. | ||||
|  * | ||||
|  * An in-flight I/O request will hold a reference to a L2 table cache entry for | ||||
|  * the period during which it needs to access the L2 table.  This includes | ||||
|  * cluster offset lookup, L2 table allocation, and L2 table update when a new | ||||
|  * data cluster has been allocated. | ||||
|  * | ||||
|  * An interesting case occurs when two requests need to access an L2 table that | ||||
|  * is not in the cache.  Since the operation to read the table from the image | ||||
|  * file takes some time to complete, both requests may see a cache miss and | ||||
|  * start reading the L2 table from the image file.  The first to finish will | ||||
|  * commit its L2 table into the cache.  When the second tries to commit its | ||||
|  * table will be deleted in favor of the existing cache entry. | ||||
|  */ | ||||
|  | ||||
| #include "trace.h" | ||||
| #include "qed.h" | ||||
|  | ||||
| /* Each L2 holds 2GB so this let's us fully cache a 100GB disk */ | ||||
| #define MAX_L2_CACHE_SIZE 50 | ||||
|  | ||||
| /** | ||||
|  * Initialize the L2 cache | ||||
|  */ | ||||
| void qed_init_l2_cache(L2TableCache *l2_cache) | ||||
| { | ||||
|     QTAILQ_INIT(&l2_cache->entries); | ||||
|     l2_cache->n_entries = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Free the L2 cache | ||||
|  */ | ||||
| void qed_free_l2_cache(L2TableCache *l2_cache) | ||||
| { | ||||
|     CachedL2Table *entry, *next_entry; | ||||
|  | ||||
|     QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next_entry) { | ||||
|         qemu_vfree(entry->table); | ||||
|         qemu_free(entry); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Allocate an uninitialized entry from the cache | ||||
|  * | ||||
|  * The returned entry has a reference count of 1 and is owned by the caller. | ||||
|  * The caller must allocate the actual table field for this entry and it must | ||||
|  * be freeable using qemu_vfree(). | ||||
|  */ | ||||
| CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache) | ||||
| { | ||||
|     CachedL2Table *entry; | ||||
|  | ||||
|     entry = qemu_mallocz(sizeof(*entry)); | ||||
|     entry->ref++; | ||||
|  | ||||
|     trace_qed_alloc_l2_cache_entry(l2_cache, entry); | ||||
|  | ||||
|     return entry; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Decrease an entry's reference count and free if necessary when the reference | ||||
|  * count drops to zero. | ||||
|  */ | ||||
| void qed_unref_l2_cache_entry(CachedL2Table *entry) | ||||
| { | ||||
|     if (!entry) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     entry->ref--; | ||||
|     trace_qed_unref_l2_cache_entry(entry, entry->ref); | ||||
|     if (entry->ref == 0) { | ||||
|         qemu_vfree(entry->table); | ||||
|         qemu_free(entry); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Find an entry in the L2 cache.  This may return NULL and it's up to the | ||||
|  * caller to satisfy the cache miss. | ||||
|  * | ||||
|  * For a cached entry, this function increases the reference count and returns | ||||
|  * the entry. | ||||
|  */ | ||||
| CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset) | ||||
| { | ||||
|     CachedL2Table *entry; | ||||
|  | ||||
|     QTAILQ_FOREACH(entry, &l2_cache->entries, node) { | ||||
|         if (entry->offset == offset) { | ||||
|             trace_qed_find_l2_cache_entry(l2_cache, entry, offset, entry->ref); | ||||
|             entry->ref++; | ||||
|             return entry; | ||||
|         } | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Commit an L2 cache entry into the cache.  This is meant to be used as part of | ||||
|  * the process to satisfy a cache miss.  A caller would allocate an entry which | ||||
|  * is not actually in the L2 cache and then once the entry was valid and | ||||
|  * present on disk, the entry can be committed into the cache. | ||||
|  * | ||||
|  * Since the cache is write-through, it's important that this function is not | ||||
|  * called until the entry is present on disk and the L1 has been updated to | ||||
|  * point to the entry. | ||||
|  * | ||||
|  * N.B. This function steals a reference to the l2_table from the caller so the | ||||
|  * caller must obtain a new reference by issuing a call to | ||||
|  * qed_find_l2_cache_entry(). | ||||
|  */ | ||||
| void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table) | ||||
| { | ||||
|     CachedL2Table *entry; | ||||
|  | ||||
|     entry = qed_find_l2_cache_entry(l2_cache, l2_table->offset); | ||||
|     if (entry) { | ||||
|         qed_unref_l2_cache_entry(entry); | ||||
|         qed_unref_l2_cache_entry(l2_table); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) { | ||||
|         entry = QTAILQ_FIRST(&l2_cache->entries); | ||||
|         QTAILQ_REMOVE(&l2_cache->entries, entry, node); | ||||
|         l2_cache->n_entries--; | ||||
|         qed_unref_l2_cache_entry(entry); | ||||
|     } | ||||
|  | ||||
|     l2_cache->n_entries++; | ||||
|     QTAILQ_INSERT_TAIL(&l2_cache->entries, l2_table, node); | ||||
| } | ||||
| @@ -1,319 +0,0 @@ | ||||
| /* | ||||
|  * QEMU Enhanced Disk Format Table I/O | ||||
|  * | ||||
|  * Copyright IBM, Corp. 2010 | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> | ||||
|  *  Anthony Liguori   <aliguori@us.ibm.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "trace.h" | ||||
| #include "qemu_socket.h" /* for EINPROGRESS on Windows */ | ||||
| #include "qed.h" | ||||
|  | ||||
| typedef struct { | ||||
|     GenericCB gencb; | ||||
|     BDRVQEDState *s; | ||||
|     QEDTable *table; | ||||
|  | ||||
|     struct iovec iov; | ||||
|     QEMUIOVector qiov; | ||||
| } QEDReadTableCB; | ||||
|  | ||||
| static void qed_read_table_cb(void *opaque, int ret) | ||||
| { | ||||
|     QEDReadTableCB *read_table_cb = opaque; | ||||
|     QEDTable *table = read_table_cb->table; | ||||
|     int noffsets = read_table_cb->iov.iov_len / sizeof(uint64_t); | ||||
|     int i; | ||||
|  | ||||
|     /* Handle I/O error */ | ||||
|     if (ret) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     /* Byteswap offsets */ | ||||
|     for (i = 0; i < noffsets; i++) { | ||||
|         table->offsets[i] = le64_to_cpu(table->offsets[i]); | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     /* Completion */ | ||||
|     trace_qed_read_table_cb(read_table_cb->s, read_table_cb->table, ret); | ||||
|     gencb_complete(&read_table_cb->gencb, ret); | ||||
| } | ||||
|  | ||||
| static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, | ||||
|                            BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb), | ||||
|                                                 cb, opaque); | ||||
|     QEMUIOVector *qiov = &read_table_cb->qiov; | ||||
|     BlockDriverAIOCB *aiocb; | ||||
|  | ||||
|     trace_qed_read_table(s, offset, table); | ||||
|  | ||||
|     read_table_cb->s = s; | ||||
|     read_table_cb->table = table; | ||||
|     read_table_cb->iov.iov_base = table->offsets, | ||||
|     read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size, | ||||
|  | ||||
|     qemu_iovec_init_external(qiov, &read_table_cb->iov, 1); | ||||
|     aiocb = bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, | ||||
|                            read_table_cb->iov.iov_len / BDRV_SECTOR_SIZE, | ||||
|                            qed_read_table_cb, read_table_cb); | ||||
|     if (!aiocb) { | ||||
|         qed_read_table_cb(read_table_cb, -EIO); | ||||
|     } | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     GenericCB gencb; | ||||
|     BDRVQEDState *s; | ||||
|     QEDTable *orig_table; | ||||
|     QEDTable *table; | ||||
|     bool flush;             /* flush after write? */ | ||||
|  | ||||
|     struct iovec iov; | ||||
|     QEMUIOVector qiov; | ||||
| } QEDWriteTableCB; | ||||
|  | ||||
| static void qed_write_table_cb(void *opaque, int ret) | ||||
| { | ||||
|     QEDWriteTableCB *write_table_cb = opaque; | ||||
|  | ||||
|     trace_qed_write_table_cb(write_table_cb->s, | ||||
|                              write_table_cb->orig_table, | ||||
|                              write_table_cb->flush, | ||||
|                              ret); | ||||
|  | ||||
|     if (ret) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (write_table_cb->flush) { | ||||
|         /* We still need to flush first */ | ||||
|         write_table_cb->flush = false; | ||||
|         bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb, | ||||
|                        write_table_cb); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| out: | ||||
|     qemu_vfree(write_table_cb->table); | ||||
|     gencb_complete(&write_table_cb->gencb, ret); | ||||
|     return; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Write out an updated part or all of a table | ||||
|  * | ||||
|  * @s:          QED state | ||||
|  * @offset:     Offset of table in image file, in bytes | ||||
|  * @table:      Table | ||||
|  * @index:      Index of first element | ||||
|  * @n:          Number of elements | ||||
|  * @flush:      Whether or not to sync to disk | ||||
|  * @cb:         Completion function | ||||
|  * @opaque:     Argument for completion function | ||||
|  */ | ||||
| static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, | ||||
|                             unsigned int index, unsigned int n, bool flush, | ||||
|                             BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     QEDWriteTableCB *write_table_cb; | ||||
|     BlockDriverAIOCB *aiocb; | ||||
|     unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1; | ||||
|     unsigned int start, end, i; | ||||
|     size_t len_bytes; | ||||
|  | ||||
|     trace_qed_write_table(s, offset, table, index, n); | ||||
|  | ||||
|     /* Calculate indices of the first and one after last elements */ | ||||
|     start = index & ~sector_mask; | ||||
|     end = (index + n + sector_mask) & ~sector_mask; | ||||
|  | ||||
|     len_bytes = (end - start) * sizeof(uint64_t); | ||||
|  | ||||
|     write_table_cb = gencb_alloc(sizeof(*write_table_cb), cb, opaque); | ||||
|     write_table_cb->s = s; | ||||
|     write_table_cb->orig_table = table; | ||||
|     write_table_cb->flush = flush; | ||||
|     write_table_cb->table = qemu_blockalign(s->bs, len_bytes); | ||||
|     write_table_cb->iov.iov_base = write_table_cb->table->offsets; | ||||
|     write_table_cb->iov.iov_len = len_bytes; | ||||
|     qemu_iovec_init_external(&write_table_cb->qiov, &write_table_cb->iov, 1); | ||||
|  | ||||
|     /* Byteswap table */ | ||||
|     for (i = start; i < end; i++) { | ||||
|         uint64_t le_offset = cpu_to_le64(table->offsets[i]); | ||||
|         write_table_cb->table->offsets[i - start] = le_offset; | ||||
|     } | ||||
|  | ||||
|     /* Adjust for offset into table */ | ||||
|     offset += start * sizeof(uint64_t); | ||||
|  | ||||
|     aiocb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, | ||||
|                             &write_table_cb->qiov, | ||||
|                             write_table_cb->iov.iov_len / BDRV_SECTOR_SIZE, | ||||
|                             qed_write_table_cb, write_table_cb); | ||||
|     if (!aiocb) { | ||||
|         qed_write_table_cb(write_table_cb, -EIO); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Propagate return value from async callback | ||||
|  */ | ||||
| static void qed_sync_cb(void *opaque, int ret) | ||||
| { | ||||
|     *(int *)opaque = ret; | ||||
| } | ||||
|  | ||||
| int qed_read_l1_table_sync(BDRVQEDState *s) | ||||
| { | ||||
|     int ret = -EINPROGRESS; | ||||
|  | ||||
|     async_context_push(); | ||||
|  | ||||
|     qed_read_table(s, s->header.l1_table_offset, | ||||
|                    s->l1_table, qed_sync_cb, &ret); | ||||
|     while (ret == -EINPROGRESS) { | ||||
|         qemu_aio_wait(); | ||||
|     } | ||||
|  | ||||
|     async_context_pop(); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, | ||||
|                         BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE); | ||||
|     qed_write_table(s, s->header.l1_table_offset, | ||||
|                     s->l1_table, index, n, false, cb, opaque); | ||||
| } | ||||
|  | ||||
| int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, | ||||
|                             unsigned int n) | ||||
| { | ||||
|     int ret = -EINPROGRESS; | ||||
|  | ||||
|     async_context_push(); | ||||
|  | ||||
|     qed_write_l1_table(s, index, n, qed_sync_cb, &ret); | ||||
|     while (ret == -EINPROGRESS) { | ||||
|         qemu_aio_wait(); | ||||
|     } | ||||
|  | ||||
|     async_context_pop(); | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     GenericCB gencb; | ||||
|     BDRVQEDState *s; | ||||
|     uint64_t l2_offset; | ||||
|     QEDRequest *request; | ||||
| } QEDReadL2TableCB; | ||||
|  | ||||
| static void qed_read_l2_table_cb(void *opaque, int ret) | ||||
| { | ||||
|     QEDReadL2TableCB *read_l2_table_cb = opaque; | ||||
|     QEDRequest *request = read_l2_table_cb->request; | ||||
|     BDRVQEDState *s = read_l2_table_cb->s; | ||||
|     CachedL2Table *l2_table = request->l2_table; | ||||
|  | ||||
|     if (ret) { | ||||
|         /* can't trust loaded L2 table anymore */ | ||||
|         qed_unref_l2_cache_entry(l2_table); | ||||
|         request->l2_table = NULL; | ||||
|     } else { | ||||
|         l2_table->offset = read_l2_table_cb->l2_offset; | ||||
|  | ||||
|         qed_commit_l2_cache_entry(&s->l2_cache, l2_table); | ||||
|  | ||||
|         /* This is guaranteed to succeed because we just committed the entry | ||||
|          * to the cache. | ||||
|          */ | ||||
|         request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, | ||||
|                                                     l2_table->offset); | ||||
|         assert(request->l2_table != NULL); | ||||
|     } | ||||
|  | ||||
|     gencb_complete(&read_l2_table_cb->gencb, ret); | ||||
| } | ||||
|  | ||||
| void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, | ||||
|                        BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     QEDReadL2TableCB *read_l2_table_cb; | ||||
|  | ||||
|     qed_unref_l2_cache_entry(request->l2_table); | ||||
|  | ||||
|     /* Check for cached L2 entry */ | ||||
|     request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset); | ||||
|     if (request->l2_table) { | ||||
|         cb(opaque, 0); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); | ||||
|     request->l2_table->table = qed_alloc_table(s); | ||||
|  | ||||
|     read_l2_table_cb = gencb_alloc(sizeof(*read_l2_table_cb), cb, opaque); | ||||
|     read_l2_table_cb->s = s; | ||||
|     read_l2_table_cb->l2_offset = offset; | ||||
|     read_l2_table_cb->request = request; | ||||
|  | ||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD); | ||||
|     qed_read_table(s, offset, request->l2_table->table, | ||||
|                    qed_read_l2_table_cb, read_l2_table_cb); | ||||
| } | ||||
|  | ||||
| int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset) | ||||
| { | ||||
|     int ret = -EINPROGRESS; | ||||
|  | ||||
|     async_context_push(); | ||||
|  | ||||
|     qed_read_l2_table(s, request, offset, qed_sync_cb, &ret); | ||||
|     while (ret == -EINPROGRESS) { | ||||
|         qemu_aio_wait(); | ||||
|     } | ||||
|  | ||||
|     async_context_pop(); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, | ||||
|                         unsigned int index, unsigned int n, bool flush, | ||||
|                         BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE); | ||||
|     qed_write_table(s, request->l2_table->offset, | ||||
|                     request->l2_table->table, index, n, flush, cb, opaque); | ||||
| } | ||||
|  | ||||
| int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, | ||||
|                             unsigned int index, unsigned int n, bool flush) | ||||
| { | ||||
|     int ret = -EINPROGRESS; | ||||
|  | ||||
|     async_context_push(); | ||||
|  | ||||
|     qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret); | ||||
|     while (ret == -EINPROGRESS) { | ||||
|         qemu_aio_wait(); | ||||
|     } | ||||
|  | ||||
|     async_context_pop(); | ||||
|     return ret; | ||||
| } | ||||
							
								
								
									
										1372
									
								
								block/qed.c
									
									
									
									
									
								
							
							
						
						
									
										1372
									
								
								block/qed.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										301
									
								
								block/qed.h
									
									
									
									
									
								
							
							
						
						
									
										301
									
								
								block/qed.h
									
									
									
									
									
								
							| @@ -1,301 +0,0 @@ | ||||
| /* | ||||
|  * QEMU Enhanced Disk Format | ||||
|  * | ||||
|  * Copyright IBM, Corp. 2010 | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com> | ||||
|  *  Anthony Liguori   <aliguori@us.ibm.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef BLOCK_QED_H | ||||
| #define BLOCK_QED_H | ||||
|  | ||||
| #include "block_int.h" | ||||
|  | ||||
| /* The layout of a QED file is as follows: | ||||
|  * | ||||
|  * +--------+----------+----------+----------+-----+ | ||||
|  * | header | L1 table | cluster0 | cluster1 | ... | | ||||
|  * +--------+----------+----------+----------+-----+ | ||||
|  * | ||||
|  * There is a 2-level pagetable for cluster allocation: | ||||
|  * | ||||
|  *                     +----------+ | ||||
|  *                     | L1 table | | ||||
|  *                     +----------+ | ||||
|  *                ,------'  |  '------. | ||||
|  *           +----------+   |    +----------+ | ||||
|  *           | L2 table |  ...   | L2 table | | ||||
|  *           +----------+        +----------+ | ||||
|  *       ,------'  |  '------. | ||||
|  *  +----------+   |    +----------+ | ||||
|  *  |   Data   |  ...   |   Data   | | ||||
|  *  +----------+        +----------+ | ||||
|  * | ||||
|  * The L1 table is fixed size and always present.  L2 tables are allocated on | ||||
|  * demand.  The L1 table size determines the maximum possible image size; it | ||||
|  * can be influenced using the cluster_size and table_size values. | ||||
|  * | ||||
|  * All fields are little-endian on disk. | ||||
|  */ | ||||
|  | ||||
| enum { | ||||
|     QED_MAGIC = 'Q' | 'E' << 8 | 'D' << 16 | '\0' << 24, | ||||
|  | ||||
|     /* The image supports a backing file */ | ||||
|     QED_F_BACKING_FILE = 0x01, | ||||
|  | ||||
|     /* The image needs a consistency check before use */ | ||||
|     QED_F_NEED_CHECK = 0x02, | ||||
|  | ||||
|     /* The backing file format must not be probed, treat as raw image */ | ||||
|     QED_F_BACKING_FORMAT_NO_PROBE = 0x04, | ||||
|  | ||||
|     /* Feature bits must be used when the on-disk format changes */ | ||||
|     QED_FEATURE_MASK = QED_F_BACKING_FILE | /* supported feature bits */ | ||||
|                        QED_F_NEED_CHECK | | ||||
|                        QED_F_BACKING_FORMAT_NO_PROBE, | ||||
|     QED_COMPAT_FEATURE_MASK = 0,            /* supported compat feature bits */ | ||||
|     QED_AUTOCLEAR_FEATURE_MASK = 0,         /* supported autoclear feature bits */ | ||||
|  | ||||
|     /* Data is stored in groups of sectors called clusters.  Cluster size must | ||||
|      * be large to avoid keeping too much metadata.  I/O requests that have | ||||
|      * sub-cluster size will require read-modify-write. | ||||
|      */ | ||||
|     QED_MIN_CLUSTER_SIZE = 4 * 1024, /* in bytes */ | ||||
|     QED_MAX_CLUSTER_SIZE = 64 * 1024 * 1024, | ||||
|     QED_DEFAULT_CLUSTER_SIZE = 64 * 1024, | ||||
|  | ||||
|     /* Allocated clusters are tracked using a 2-level pagetable.  Table size is | ||||
|      * a multiple of clusters so large maximum image sizes can be supported | ||||
|      * without jacking up the cluster size too much. | ||||
|      */ | ||||
|     QED_MIN_TABLE_SIZE = 1,        /* in clusters */ | ||||
|     QED_MAX_TABLE_SIZE = 16, | ||||
|     QED_DEFAULT_TABLE_SIZE = 4, | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     uint32_t magic;                 /* QED\0 */ | ||||
|  | ||||
|     uint32_t cluster_size;          /* in bytes */ | ||||
|     uint32_t table_size;            /* for L1 and L2 tables, in clusters */ | ||||
|     uint32_t header_size;           /* in clusters */ | ||||
|  | ||||
|     uint64_t features;              /* format feature bits */ | ||||
|     uint64_t compat_features;       /* compatible feature bits */ | ||||
|     uint64_t autoclear_features;    /* self-resetting feature bits */ | ||||
|  | ||||
|     uint64_t l1_table_offset;       /* in bytes */ | ||||
|     uint64_t image_size;            /* total logical image size, in bytes */ | ||||
|  | ||||
|     /* if (features & QED_F_BACKING_FILE) */ | ||||
|     uint32_t backing_filename_offset; /* in bytes from start of header */ | ||||
|     uint32_t backing_filename_size;   /* in bytes */ | ||||
| } QEDHeader; | ||||
|  | ||||
| typedef struct { | ||||
|     uint64_t offsets[0];            /* in bytes */ | ||||
| } QEDTable; | ||||
|  | ||||
| /* The L2 cache is a simple write-through cache for L2 structures */ | ||||
| typedef struct CachedL2Table { | ||||
|     QEDTable *table; | ||||
|     uint64_t offset;    /* offset=0 indicates an invalidate entry */ | ||||
|     QTAILQ_ENTRY(CachedL2Table) node; | ||||
|     int ref; | ||||
| } CachedL2Table; | ||||
|  | ||||
| typedef struct { | ||||
|     QTAILQ_HEAD(, CachedL2Table) entries; | ||||
|     unsigned int n_entries; | ||||
| } L2TableCache; | ||||
|  | ||||
| typedef struct QEDRequest { | ||||
|     CachedL2Table *l2_table; | ||||
| } QEDRequest; | ||||
|  | ||||
| typedef struct QEDAIOCB { | ||||
|     BlockDriverAIOCB common; | ||||
|     QEMUBH *bh; | ||||
|     int bh_ret;                     /* final return status for completion bh */ | ||||
|     QSIMPLEQ_ENTRY(QEDAIOCB) next;  /* next request */ | ||||
|     bool is_write;                  /* false - read, true - write */ | ||||
|     bool *finished;                 /* signal for cancel completion */ | ||||
|     uint64_t end_pos;               /* request end on block device, in bytes */ | ||||
|  | ||||
|     /* User scatter-gather list */ | ||||
|     QEMUIOVector *qiov; | ||||
|     size_t qiov_offset;             /* byte count already processed */ | ||||
|  | ||||
|     /* Current cluster scatter-gather list */ | ||||
|     QEMUIOVector cur_qiov; | ||||
|     uint64_t cur_pos;               /* position on block device, in bytes */ | ||||
|     uint64_t cur_cluster;           /* cluster offset in image file */ | ||||
|     unsigned int cur_nclusters;     /* number of clusters being accessed */ | ||||
|     int find_cluster_ret;           /* used for L1/L2 update */ | ||||
|  | ||||
|     QEDRequest request; | ||||
| } QEDAIOCB; | ||||
|  | ||||
| typedef struct { | ||||
|     BlockDriverState *bs;           /* device */ | ||||
|     uint64_t file_size;             /* length of image file, in bytes */ | ||||
|  | ||||
|     QEDHeader header;               /* always cpu-endian */ | ||||
|     QEDTable *l1_table; | ||||
|     L2TableCache l2_cache;          /* l2 table cache */ | ||||
|     uint32_t table_nelems; | ||||
|     uint32_t l1_shift; | ||||
|     uint32_t l2_shift; | ||||
|     uint32_t l2_mask; | ||||
|  | ||||
|     /* Allocating write request queue */ | ||||
|     QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs; | ||||
| } BDRVQEDState; | ||||
|  | ||||
| enum { | ||||
|     QED_CLUSTER_FOUND,         /* cluster found */ | ||||
|     QED_CLUSTER_L2,            /* cluster missing in L2 */ | ||||
|     QED_CLUSTER_L1,            /* cluster missing in L1 */ | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * qed_find_cluster() completion callback | ||||
|  * | ||||
|  * @opaque:     User data for completion callback | ||||
|  * @ret:        QED_CLUSTER_FOUND   Success | ||||
|  *              QED_CLUSTER_L2      Data cluster unallocated in L2 | ||||
|  *              QED_CLUSTER_L1      L2 unallocated in L1 | ||||
|  *              -errno              POSIX error occurred | ||||
|  * @offset:     Data cluster offset | ||||
|  * @len:        Contiguous bytes starting from cluster offset | ||||
|  * | ||||
|  * This function is invoked when qed_find_cluster() completes. | ||||
|  * | ||||
|  * On success ret is QED_CLUSTER_FOUND and offset/len are a contiguous range | ||||
|  * in the image file. | ||||
|  * | ||||
|  * On failure ret is QED_CLUSTER_L2 or QED_CLUSTER_L1 for missing L2 or L1 | ||||
|  * table offset, respectively.  len is number of contiguous unallocated bytes. | ||||
|  */ | ||||
| typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len); | ||||
|  | ||||
| /** | ||||
|  * Generic callback for chaining async callbacks | ||||
|  */ | ||||
| typedef struct { | ||||
|     BlockDriverCompletionFunc *cb; | ||||
|     void *opaque; | ||||
| } GenericCB; | ||||
|  | ||||
| void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque); | ||||
| void gencb_complete(void *opaque, int ret); | ||||
|  | ||||
| /** | ||||
|  * L2 cache functions | ||||
|  */ | ||||
| void qed_init_l2_cache(L2TableCache *l2_cache); | ||||
| void qed_free_l2_cache(L2TableCache *l2_cache); | ||||
| CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache); | ||||
| void qed_unref_l2_cache_entry(CachedL2Table *entry); | ||||
| CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset); | ||||
| void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table); | ||||
|  | ||||
| /** | ||||
|  * Table I/O functions | ||||
|  */ | ||||
| int qed_read_l1_table_sync(BDRVQEDState *s); | ||||
| void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, | ||||
|                         BlockDriverCompletionFunc *cb, void *opaque); | ||||
| int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, | ||||
|                             unsigned int n); | ||||
| int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, | ||||
|                            uint64_t offset); | ||||
| void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, | ||||
|                        BlockDriverCompletionFunc *cb, void *opaque); | ||||
| void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, | ||||
|                         unsigned int index, unsigned int n, bool flush, | ||||
|                         BlockDriverCompletionFunc *cb, void *opaque); | ||||
| int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, | ||||
|                             unsigned int index, unsigned int n, bool flush); | ||||
|  | ||||
| /** | ||||
|  * Cluster functions | ||||
|  */ | ||||
| void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, | ||||
|                       size_t len, QEDFindClusterFunc *cb, void *opaque); | ||||
|  | ||||
| /** | ||||
|  * Consistency check | ||||
|  */ | ||||
| int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix); | ||||
|  | ||||
| QEDTable *qed_alloc_table(BDRVQEDState *s); | ||||
|  | ||||
| /** | ||||
|  * Round down to the start of a cluster | ||||
|  */ | ||||
| static inline uint64_t qed_start_of_cluster(BDRVQEDState *s, uint64_t offset) | ||||
| { | ||||
|     return offset & ~(uint64_t)(s->header.cluster_size - 1); | ||||
| } | ||||
|  | ||||
| static inline uint64_t qed_offset_into_cluster(BDRVQEDState *s, uint64_t offset) | ||||
| { | ||||
|     return offset & (s->header.cluster_size - 1); | ||||
| } | ||||
|  | ||||
| static inline uint64_t qed_bytes_to_clusters(BDRVQEDState *s, uint64_t bytes) | ||||
| { | ||||
|     return qed_start_of_cluster(s, bytes + (s->header.cluster_size - 1)) / | ||||
|            (s->header.cluster_size - 1); | ||||
| } | ||||
|  | ||||
| static inline unsigned int qed_l1_index(BDRVQEDState *s, uint64_t pos) | ||||
| { | ||||
|     return pos >> s->l1_shift; | ||||
| } | ||||
|  | ||||
| static inline unsigned int qed_l2_index(BDRVQEDState *s, uint64_t pos) | ||||
| { | ||||
|     return (pos >> s->l2_shift) & s->l2_mask; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Test if a cluster offset is valid | ||||
|  */ | ||||
| static inline bool qed_check_cluster_offset(BDRVQEDState *s, uint64_t offset) | ||||
| { | ||||
|     uint64_t header_size = (uint64_t)s->header.header_size * | ||||
|                            s->header.cluster_size; | ||||
|  | ||||
|     if (offset & (s->header.cluster_size - 1)) { | ||||
|         return false; | ||||
|     } | ||||
|     return offset >= header_size && offset < s->file_size; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Test if a table offset is valid | ||||
|  */ | ||||
| static inline bool qed_check_table_offset(BDRVQEDState *s, uint64_t offset) | ||||
| { | ||||
|     uint64_t end_offset = offset + (s->header.table_size - 1) * | ||||
|                           s->header.cluster_size; | ||||
|  | ||||
|     /* Overflow check */ | ||||
|     if (end_offset <= offset) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     return qed_check_cluster_offset(s, offset) && | ||||
|            qed_check_cluster_offset(s, end_offset); | ||||
| } | ||||
|  | ||||
| #endif /* BLOCK_QED_H */ | ||||
| @@ -27,6 +27,8 @@ | ||||
| #include "qemu-log.h" | ||||
| #include "block_int.h" | ||||
| #include "module.h" | ||||
| #include "compatfd.h" | ||||
| #include <assert.h> | ||||
| #include "block/raw-posix-aio.h" | ||||
|  | ||||
| #ifdef CONFIG_COCOA | ||||
| @@ -48,7 +50,6 @@ | ||||
| #endif | ||||
| #ifdef __linux__ | ||||
| #include <sys/ioctl.h> | ||||
| #include <sys/param.h> | ||||
| #include <linux/cdrom.h> | ||||
| #include <linux/fd.h> | ||||
| #endif | ||||
| @@ -69,10 +70,6 @@ | ||||
| #include <sys/diskslice.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef CONFIG_XFS | ||||
| #include <xfs/xfs.h> | ||||
| #endif | ||||
|  | ||||
| //#define DEBUG_FLOPPY | ||||
|  | ||||
| //#define DEBUG_BLOCK | ||||
| @@ -101,15 +98,16 @@ | ||||
| #define FTYPE_CD     1 | ||||
| #define FTYPE_FD     2 | ||||
|  | ||||
| /* if the FD is not accessed during that time (in ns), we try to | ||||
|    reopen it to see if the disk has been changed */ | ||||
| #define FD_OPEN_TIMEOUT (1000000000) | ||||
| #define ALIGNED_BUFFER_SIZE (32 * 512) | ||||
|  | ||||
| #define MAX_BLOCKSIZE	4096 | ||||
| /* if the FD is not accessed during that time (in ms), we try to | ||||
|    reopen it to see if the disk has been changed */ | ||||
| #define FD_OPEN_TIMEOUT 1000 | ||||
|  | ||||
| typedef struct BDRVRawState { | ||||
|     int fd; | ||||
|     int type; | ||||
|     unsigned int lseek_err_cnt; | ||||
|     int open_flags; | ||||
| #if defined(__linux__) | ||||
|     /* linux floppy specific */ | ||||
| @@ -122,11 +120,7 @@ typedef struct BDRVRawState { | ||||
|     int use_aio; | ||||
|     void *aio_ctx; | ||||
| #endif | ||||
|     uint8_t *aligned_buf; | ||||
|     unsigned aligned_buf_size; | ||||
| #ifdef CONFIG_XFS | ||||
|     bool is_xfs : 1; | ||||
| #endif | ||||
|     uint8_t* aligned_buf; | ||||
| } BDRVRawState; | ||||
|  | ||||
| static int fd_open(BlockDriverState *bs); | ||||
| @@ -142,12 +136,15 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int fd, ret; | ||||
|  | ||||
|     s->lseek_err_cnt = 0; | ||||
|  | ||||
|     s->open_flags = open_flags | O_BINARY; | ||||
|     s->open_flags &= ~O_ACCMODE; | ||||
|     if (bdrv_flags & BDRV_O_RDWR) { | ||||
|     if ((bdrv_flags & BDRV_O_ACCESS) == BDRV_O_RDWR) { | ||||
|         s->open_flags |= O_RDWR; | ||||
|     } else { | ||||
|         s->open_flags |= O_RDONLY; | ||||
|         bs->read_only = 1; | ||||
|     } | ||||
|  | ||||
|     /* Use O_DSYNC for write-through caching, no flags for write-back caching, | ||||
| @@ -169,12 +166,7 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, | ||||
|     s->aligned_buf = NULL; | ||||
|  | ||||
|     if ((bdrv_flags & BDRV_O_NOCACHE)) { | ||||
|         /* | ||||
|          * Allocate a buffer for read/modify/write cycles.  Chose the size | ||||
|          * pessimistically as we don't know the block size yet. | ||||
|          */ | ||||
|         s->aligned_buf_size = 32 * MAX_BLOCKSIZE; | ||||
|         s->aligned_buf = qemu_memalign(MAX_BLOCKSIZE, s->aligned_buf_size); | ||||
|         s->aligned_buf = qemu_blockalign(bs, ALIGNED_BUFFER_SIZE); | ||||
|         if (s->aligned_buf == NULL) { | ||||
|             goto out_close; | ||||
|         } | ||||
| @@ -203,12 +195,6 @@ static int raw_open_common(BlockDriverState *bs, const char *filename, | ||||
| #endif | ||||
|     } | ||||
|  | ||||
| #ifdef CONFIG_XFS | ||||
|     if (platform_test_xfs_fd(s->fd)) { | ||||
|         s->is_xfs = 1; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| out_free_buf: | ||||
| @@ -221,9 +207,13 @@ out_close: | ||||
| static int raw_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int open_flags = 0; | ||||
|  | ||||
|     s->type = FTYPE_FILE; | ||||
|     return raw_open_common(bs, filename, flags, 0); | ||||
|     if (flags & BDRV_O_CREAT) | ||||
|         open_flags = O_CREAT | O_TRUNC; | ||||
|  | ||||
|     return raw_open_common(bs, filename, flags, open_flags); | ||||
| } | ||||
|  | ||||
| /* XXX: use host sector size if necessary with: | ||||
| @@ -236,7 +226,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|         } | ||||
| #endif | ||||
| #ifdef CONFIG_COCOA | ||||
|         uint32_t blockSize = 512; | ||||
|         u_int32_t   blockSize = 512; | ||||
|         if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) { | ||||
|             bufsize = blockSize; | ||||
|         } | ||||
| @@ -260,16 +250,29 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset, | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|  | ||||
|     ret = pread(s->fd, buf, count, offset); | ||||
|     if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) { | ||||
|         ++(s->lseek_err_cnt); | ||||
|         if(s->lseek_err_cnt <= 10) { | ||||
|             DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 | ||||
|                               "] lseek failed : %d = %s\n", | ||||
|                               s->fd, bs->filename, offset, buf, count, | ||||
|                               bs->total_sectors, errno, strerror(errno)); | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
|     s->lseek_err_cnt=0; | ||||
|  | ||||
|     ret = read(s->fd, buf, count); | ||||
|     if (ret == count) | ||||
|         return ret; | ||||
|         goto label__raw_read__success; | ||||
|  | ||||
|     /* Allow reads beyond the end (needed for pwrite) */ | ||||
|     if ((ret == 0) && bs->growable) { | ||||
|         int64_t size = raw_getlength(bs); | ||||
|         if (offset >= size) { | ||||
|             memset(buf, 0, count); | ||||
|             return count; | ||||
|             ret = count; | ||||
|             goto label__raw_read__success; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -279,13 +282,15 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset, | ||||
|                       bs->total_sectors, ret, errno, strerror(errno)); | ||||
|  | ||||
|     /* Try harder for CDrom. */ | ||||
|     if (s->type != FTYPE_FILE) { | ||||
|         ret = pread(s->fd, buf, count, offset); | ||||
|     if (bs->type == BDRV_TYPE_CDROM) { | ||||
|         lseek(s->fd, offset, SEEK_SET); | ||||
|         ret = read(s->fd, buf, count); | ||||
|         if (ret == count) | ||||
|             return ret; | ||||
|         ret = pread(s->fd, buf, count, offset); | ||||
|             goto label__raw_read__success; | ||||
|         lseek(s->fd, offset, SEEK_SET); | ||||
|         ret = read(s->fd, buf, count); | ||||
|         if (ret == count) | ||||
|             return ret; | ||||
|             goto label__raw_read__success; | ||||
|  | ||||
|         DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 | ||||
|                           "] retry read failed %d : %d = %s\n", | ||||
| @@ -293,13 +298,14 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset, | ||||
|                           bs->total_sectors, ret, errno, strerror(errno)); | ||||
|     } | ||||
|  | ||||
| label__raw_read__success: | ||||
|  | ||||
|     return  (ret < 0) ? -errno : ret; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * offset and count are in bytes, but must be multiples of the sector size | ||||
|  * for files opened with O_DIRECT. buf must be aligned to sector size bytes | ||||
|  * then. | ||||
|  * offset and count are in bytes, but must be multiples of 512 for files | ||||
|  * opened with O_DIRECT. buf must be aligned to 512 bytes then. | ||||
|  * | ||||
|  * This function may be called without alignment if the caller ensures | ||||
|  * that O_DIRECT is not in effect. | ||||
| @@ -314,15 +320,29 @@ static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset, | ||||
|     if (ret < 0) | ||||
|         return -errno; | ||||
|  | ||||
|     ret = pwrite(s->fd, buf, count, offset); | ||||
|     if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) { | ||||
|         ++(s->lseek_err_cnt); | ||||
|         if(s->lseek_err_cnt) { | ||||
|             DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" | ||||
|                               PRId64 "] lseek failed : %d = %s\n", | ||||
|                               s->fd, bs->filename, offset, buf, count, | ||||
|                               bs->total_sectors, errno, strerror(errno)); | ||||
|         } | ||||
|         return -EIO; | ||||
|     } | ||||
|     s->lseek_err_cnt = 0; | ||||
|  | ||||
|     ret = write(s->fd, buf, count); | ||||
|     if (ret == count) | ||||
|         return ret; | ||||
|         goto label__raw_write__success; | ||||
|  | ||||
|     DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 | ||||
|                       "] write failed %d : %d = %s\n", | ||||
|                       s->fd, bs->filename, offset, buf, count, | ||||
|                       bs->total_sectors, ret, errno, strerror(errno)); | ||||
|  | ||||
| label__raw_write__success: | ||||
|  | ||||
|     return  (ret < 0) ? -errno : ret; | ||||
| } | ||||
|  | ||||
| @@ -336,25 +356,24 @@ static int raw_pread(BlockDriverState *bs, int64_t offset, | ||||
|                      uint8_t *buf, int count) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     unsigned sector_mask = bs->buffer_alignment - 1; | ||||
|     int size, ret, shift, sum; | ||||
|  | ||||
|     sum = 0; | ||||
|  | ||||
|     if (s->aligned_buf != NULL)  { | ||||
|  | ||||
|         if (offset & sector_mask) { | ||||
|             /* align offset on a sector size bytes boundary */ | ||||
|         if (offset & 0x1ff) { | ||||
|             /* align offset on a 512 bytes boundary */ | ||||
|  | ||||
|             shift = offset & sector_mask; | ||||
|             size = (shift + count + sector_mask) & ~sector_mask; | ||||
|             if (size > s->aligned_buf_size) | ||||
|                 size = s->aligned_buf_size; | ||||
|             shift = offset & 0x1ff; | ||||
|             size = (shift + count + 0x1ff) & ~0x1ff; | ||||
|             if (size > ALIGNED_BUFFER_SIZE) | ||||
|                 size = ALIGNED_BUFFER_SIZE; | ||||
|             ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size); | ||||
|             if (ret < 0) | ||||
|                 return ret; | ||||
|  | ||||
|             size = bs->buffer_alignment - shift; | ||||
|             size = 512 - shift; | ||||
|             if (size > count) | ||||
|                 size = count; | ||||
|             memcpy(buf, s->aligned_buf + shift, size); | ||||
| @@ -367,23 +386,19 @@ static int raw_pread(BlockDriverState *bs, int64_t offset, | ||||
|             if (count == 0) | ||||
|                 return sum; | ||||
|         } | ||||
|         if (count & sector_mask || (uintptr_t) buf & sector_mask) { | ||||
|         if (count & 0x1ff || (uintptr_t) buf & 0x1ff) { | ||||
|  | ||||
|             /* read on aligned buffer */ | ||||
|  | ||||
|             while (count) { | ||||
|  | ||||
|                 size = (count + sector_mask) & ~sector_mask; | ||||
|                 if (size > s->aligned_buf_size) | ||||
|                     size = s->aligned_buf_size; | ||||
|                 size = (count + 0x1ff) & ~0x1ff; | ||||
|                 if (size > ALIGNED_BUFFER_SIZE) | ||||
|                     size = ALIGNED_BUFFER_SIZE; | ||||
|  | ||||
|                 ret = raw_pread_aligned(bs, offset, s->aligned_buf, size); | ||||
|                 if (ret < 0) { | ||||
|                 if (ret < 0) | ||||
|                     return ret; | ||||
|                 } else if (ret == 0) { | ||||
|                     fprintf(stderr, "raw_pread: read beyond end of file\n"); | ||||
|                     abort(); | ||||
|                 } | ||||
|  | ||||
|                 size = ret; | ||||
|                 if (size > count) | ||||
| @@ -409,9 +424,8 @@ static int raw_read(BlockDriverState *bs, int64_t sector_num, | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     ret = raw_pread(bs, sector_num * BDRV_SECTOR_SIZE, buf, | ||||
|                     nb_sectors * BDRV_SECTOR_SIZE); | ||||
|     if (ret == (nb_sectors * BDRV_SECTOR_SIZE)) | ||||
|     ret = raw_pread(bs, sector_num * 512, buf, nb_sectors * 512); | ||||
|     if (ret == (nb_sectors * 512)) | ||||
|         ret = 0; | ||||
|     return ret; | ||||
| } | ||||
| @@ -425,28 +439,25 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, | ||||
|                       const uint8_t *buf, int count) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     unsigned sector_mask = bs->buffer_alignment - 1; | ||||
|     int size, ret, shift, sum; | ||||
|  | ||||
|     sum = 0; | ||||
|  | ||||
|     if (s->aligned_buf != NULL) { | ||||
|  | ||||
|         if (offset & sector_mask) { | ||||
|             /* align offset on a sector size bytes boundary */ | ||||
|             shift = offset & sector_mask; | ||||
|             ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, | ||||
|                                     bs->buffer_alignment); | ||||
|         if (offset & 0x1ff) { | ||||
|             /* align offset on a 512 bytes boundary */ | ||||
|             shift = offset & 0x1ff; | ||||
|             ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, 512); | ||||
|             if (ret < 0) | ||||
|                 return ret; | ||||
|  | ||||
|             size = bs->buffer_alignment - shift; | ||||
|             size = 512 - shift; | ||||
|             if (size > count) | ||||
|                 size = count; | ||||
|             memcpy(s->aligned_buf + shift, buf, size); | ||||
|  | ||||
|             ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, | ||||
|                                      bs->buffer_alignment); | ||||
|             ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, 512); | ||||
|             if (ret < 0) | ||||
|                 return ret; | ||||
|  | ||||
| @@ -458,12 +469,12 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, | ||||
|             if (count == 0) | ||||
|                 return sum; | ||||
|         } | ||||
|         if (count & sector_mask || (uintptr_t) buf & sector_mask) { | ||||
|         if (count & 0x1ff || (uintptr_t) buf & 0x1ff) { | ||||
|  | ||||
|             while ((size = (count & ~sector_mask)) != 0) { | ||||
|             while ((size = (count & ~0x1ff)) != 0) { | ||||
|  | ||||
|                 if (size > s->aligned_buf_size) | ||||
|                     size = s->aligned_buf_size; | ||||
|                 if (size > ALIGNED_BUFFER_SIZE) | ||||
|                     size = ALIGNED_BUFFER_SIZE; | ||||
|  | ||||
|                 memcpy(s->aligned_buf, buf, size); | ||||
|  | ||||
| @@ -476,16 +487,14 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset, | ||||
|                 count -= ret; | ||||
|                 sum += ret; | ||||
|             } | ||||
|             /* here, count < sector_size because (count & ~sector_mask) == 0 */ | ||||
|             /* here, count < 512 because (count & ~0x1ff) == 0 */ | ||||
|             if (count) { | ||||
|                 ret = raw_pread_aligned(bs, offset, s->aligned_buf, | ||||
|                                      bs->buffer_alignment); | ||||
|                 ret = raw_pread_aligned(bs, offset, s->aligned_buf, 512); | ||||
|                 if (ret < 0) | ||||
|                     return ret; | ||||
|                  memcpy(s->aligned_buf, buf, count); | ||||
|  | ||||
|                  ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, | ||||
|                                      bs->buffer_alignment); | ||||
|                  ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, 512); | ||||
|                  if (ret < 0) | ||||
|                      return ret; | ||||
|                  if (count < ret) | ||||
| @@ -503,9 +512,8 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num, | ||||
|                      const uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     int ret; | ||||
|     ret = raw_pwrite(bs, sector_num * BDRV_SECTOR_SIZE, buf, | ||||
|                      nb_sectors * BDRV_SECTOR_SIZE); | ||||
|     if (ret == (nb_sectors * BDRV_SECTOR_SIZE)) | ||||
|     ret = raw_pwrite(bs, sector_num * 512, buf, nb_sectors * 512); | ||||
|     if (ret == (nb_sectors * 512)) | ||||
|         ret = 0; | ||||
|     return ret; | ||||
| } | ||||
| @@ -513,12 +521,12 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num, | ||||
| /* | ||||
|  * Check if all memory in this vector is sector aligned. | ||||
|  */ | ||||
| static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) | ||||
| static int qiov_is_aligned(QEMUIOVector *qiov) | ||||
| { | ||||
|     int i; | ||||
|  | ||||
|     for (i = 0; i < qiov->niov; i++) { | ||||
|         if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) { | ||||
|         if ((uintptr_t) qiov->iov[i].iov_base % 512) { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| @@ -541,7 +549,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs, | ||||
|      * driver that it needs to copy the buffer. | ||||
|      */ | ||||
|     if (s->aligned_buf) { | ||||
|         if (!qiov_is_aligned(bs, qiov)) { | ||||
|         if (!qiov_is_aligned(qiov)) { | ||||
|             type |= QEMU_AIO_MISALIGNED; | ||||
| #ifdef CONFIG_LINUX_AIO | ||||
|         } else if (s->use_aio) { | ||||
| @@ -622,41 +630,21 @@ static int64_t raw_getlength(BlockDriverState *bs) | ||||
|     } else | ||||
|         return st.st_size; | ||||
| } | ||||
| #elif defined(__sun__) | ||||
| static int64_t raw_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     struct dk_minfo minfo; | ||||
|     int ret; | ||||
|  | ||||
|     ret = fd_open(bs); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Use the DKIOCGMEDIAINFO ioctl to read the size. | ||||
|      */ | ||||
|     ret = ioctl(s->fd, DKIOCGMEDIAINFO, &minfo); | ||||
|     if (ret != -1) { | ||||
|         return minfo.dki_lbsize * minfo.dki_capacity; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * There are reports that lseek on some devices fails, but | ||||
|      * irc discussion said that contingency on contingency was overkill. | ||||
|      */ | ||||
|     return lseek(s->fd, 0, SEEK_END); | ||||
| } | ||||
| #elif defined(CONFIG_BSD) | ||||
| static int64_t raw_getlength(BlockDriverState *bs) | ||||
| #else /* !__OpenBSD__ */ | ||||
| static int64_t  raw_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int fd = s->fd; | ||||
|     int64_t size; | ||||
| #ifdef CONFIG_BSD | ||||
|     struct stat sb; | ||||
| #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) | ||||
|     int reopened = 0; | ||||
| #endif | ||||
| #endif | ||||
| #ifdef __sun__ | ||||
|     struct dk_minfo minfo; | ||||
|     int rv; | ||||
| #endif | ||||
|     int ret; | ||||
|  | ||||
| @@ -664,6 +652,7 @@ static int64_t raw_getlength(BlockDriverState *bs) | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|  | ||||
| #ifdef CONFIG_BSD | ||||
| #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) | ||||
| again: | ||||
| #endif | ||||
| @@ -698,24 +687,24 @@ again: | ||||
|             } | ||||
|         } | ||||
| #endif | ||||
|     } else { | ||||
|     } else | ||||
| #endif | ||||
| #ifdef __sun__ | ||||
|     /* | ||||
|      * use the DKIOCGMEDIAINFO ioctl to read the size. | ||||
|      */ | ||||
|     rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo ); | ||||
|     if ( rv != -1 ) { | ||||
|         size = minfo.dki_lbsize * minfo.dki_capacity; | ||||
|     } else /* there are reports that lseek on some devices | ||||
|               fails, but irc discussion said that contingency | ||||
|               on contingency was overkill */ | ||||
| #endif | ||||
|     { | ||||
|         size = lseek(fd, 0, SEEK_END); | ||||
|     } | ||||
|     return size; | ||||
| } | ||||
| #else | ||||
| static int64_t raw_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     ret = fd_open(bs); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     return lseek(s->fd, 0, SEEK_END); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int raw_create(const char *filename, QEMUOptionParameter *options) | ||||
| @@ -727,7 +716,7 @@ static int raw_create(const char *filename, QEMUOptionParameter *options) | ||||
|     /* Read out options */ | ||||
|     while (options && options->name) { | ||||
|         if (!strcmp(options->name, BLOCK_OPT_SIZE)) { | ||||
|             total_size = options->value.n / BDRV_SECTOR_SIZE; | ||||
|             total_size = options->value.n / 512; | ||||
|         } | ||||
|         options++; | ||||
|     } | ||||
| @@ -737,7 +726,7 @@ static int raw_create(const char *filename, QEMUOptionParameter *options) | ||||
|     if (fd < 0) { | ||||
|         result = -errno; | ||||
|     } else { | ||||
|         if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) { | ||||
|         if (ftruncate(fd, total_size * 512) != 0) { | ||||
|             result = -errno; | ||||
|         } | ||||
|         if (close(fd) != 0) { | ||||
| @@ -747,43 +736,12 @@ static int raw_create(const char *filename, QEMUOptionParameter *options) | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| static int raw_flush(BlockDriverState *bs) | ||||
| static void raw_flush(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     return qemu_fdatasync(s->fd); | ||||
|     qemu_fdatasync(s->fd); | ||||
| } | ||||
|  | ||||
| #ifdef CONFIG_XFS | ||||
| static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors) | ||||
| { | ||||
|     struct xfs_flock64 fl; | ||||
|  | ||||
|     memset(&fl, 0, sizeof(fl)); | ||||
|     fl.l_whence = SEEK_SET; | ||||
|     fl.l_start = sector_num << 9; | ||||
|     fl.l_len = (int64_t)nb_sectors << 9; | ||||
|  | ||||
|     if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { | ||||
|         DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno)); | ||||
|         return -errno; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) | ||||
| { | ||||
| #ifdef CONFIG_XFS | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|  | ||||
|     if (s->is_xfs) { | ||||
|         return xfs_discard(s, sector_num, nb_sectors); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static QEMUOptionParameter raw_create_options[] = { | ||||
|     { | ||||
| @@ -794,18 +752,16 @@ static QEMUOptionParameter raw_create_options[] = { | ||||
|     { NULL } | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_file = { | ||||
|     .format_name = "file", | ||||
|     .protocol_name = "file", | ||||
| static BlockDriver bdrv_raw = { | ||||
|     .format_name = "raw", | ||||
|     .instance_size = sizeof(BDRVRawState), | ||||
|     .bdrv_probe = NULL, /* no probe for protocols */ | ||||
|     .bdrv_file_open = raw_open, | ||||
|     .bdrv_open = raw_open, | ||||
|     .bdrv_read = raw_read, | ||||
|     .bdrv_write = raw_write, | ||||
|     .bdrv_close = raw_close, | ||||
|     .bdrv_create = raw_create, | ||||
|     .bdrv_flush = raw_flush, | ||||
|     .bdrv_discard = raw_discard, | ||||
|  | ||||
|     .bdrv_aio_readv = raw_aio_readv, | ||||
|     .bdrv_aio_writev = raw_aio_writev, | ||||
| @@ -927,13 +883,8 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|  | ||||
|     s->type = FTYPE_FILE; | ||||
| #if defined(__linux__) | ||||
|     { | ||||
|         char resolved_path[ MAXPATHLEN ], *temp; | ||||
|  | ||||
|         temp = realpath(filename, resolved_path); | ||||
|         if (temp && strstart(temp, "/dev/sg", NULL)) { | ||||
|             bs->sg = 1; | ||||
|         } | ||||
|     if (strstart(filename, "/dev/sg", NULL)) { | ||||
|         bs->sg = 1; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| @@ -953,7 +904,7 @@ static int fd_open(BlockDriverState *bs) | ||||
|         return 0; | ||||
|     last_media_present = (s->fd >= 0); | ||||
|     if (s->fd >= 0 && | ||||
|         (get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) { | ||||
|         (qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { | ||||
|         close(s->fd); | ||||
|         s->fd = -1; | ||||
| #ifdef DEBUG_FLOPPY | ||||
| @@ -962,7 +913,7 @@ static int fd_open(BlockDriverState *bs) | ||||
|     } | ||||
|     if (s->fd < 0) { | ||||
|         if (s->fd_got_error && | ||||
|             (get_clock() - s->fd_error_time) < FD_OPEN_TIMEOUT) { | ||||
|             (qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) { | ||||
| #ifdef DEBUG_FLOPPY | ||||
|             printf("No floppy (open delayed)\n"); | ||||
| #endif | ||||
| @@ -970,7 +921,7 @@ static int fd_open(BlockDriverState *bs) | ||||
|         } | ||||
|         s->fd = open(bs->filename, s->open_flags & ~O_NONBLOCK); | ||||
|         if (s->fd < 0) { | ||||
|             s->fd_error_time = get_clock(); | ||||
|             s->fd_error_time = qemu_get_clock(rt_clock); | ||||
|             s->fd_got_error = 1; | ||||
|             if (last_media_present) | ||||
|                 s->fd_media_changed = 1; | ||||
| @@ -985,7 +936,7 @@ static int fd_open(BlockDriverState *bs) | ||||
|     } | ||||
|     if (!last_media_present) | ||||
|         s->fd_media_changed = 1; | ||||
|     s->fd_open_time = get_clock(); | ||||
|     s->fd_open_time = qemu_get_clock(rt_clock); | ||||
|     s->fd_got_error = 0; | ||||
|     return 0; | ||||
| } | ||||
| @@ -1037,41 +988,35 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options) | ||||
|     /* Read out options */ | ||||
|     while (options && options->name) { | ||||
|         if (!strcmp(options->name, "size")) { | ||||
|             total_size = options->value.n / BDRV_SECTOR_SIZE; | ||||
|             total_size = options->value.n / 512; | ||||
|         } | ||||
|         options++; | ||||
|     } | ||||
|  | ||||
|     fd = open(filename, O_WRONLY | O_BINARY); | ||||
|     if (fd < 0) | ||||
|         return -errno; | ||||
|         return -EIO; | ||||
|  | ||||
|     if (fstat(fd, &stat_buf) < 0) | ||||
|         ret = -errno; | ||||
|         ret = -EIO; | ||||
|     else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) | ||||
|         ret = -ENODEV; | ||||
|     else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE) | ||||
|         ret = -EIO; | ||||
|     else if (lseek(fd, 0, SEEK_END) < total_size * 512) | ||||
|         ret = -ENOSPC; | ||||
|  | ||||
|     close(fd); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int hdev_has_zero_init(BlockDriverState *bs) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_host_device = { | ||||
|     .format_name        = "host_device", | ||||
|     .protocol_name        = "host_device", | ||||
|     .instance_size      = sizeof(BDRVRawState), | ||||
|     .bdrv_probe_device  = hdev_probe_device, | ||||
|     .bdrv_file_open     = hdev_open, | ||||
|     .bdrv_open          = hdev_open, | ||||
|     .bdrv_close         = raw_close, | ||||
|     .bdrv_create        = hdev_create, | ||||
|     .create_options     = raw_create_options, | ||||
|     .bdrv_has_zero_init = hdev_has_zero_init, | ||||
|     .no_zero_init       = 1, | ||||
|     .bdrv_flush         = raw_flush, | ||||
|  | ||||
|     .bdrv_aio_readv	= raw_aio_readv, | ||||
| @@ -1112,26 +1057,9 @@ static int floppy_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|  | ||||
| static int floppy_probe_device(const char *filename) | ||||
| { | ||||
|     int fd, ret; | ||||
|     int prio = 0; | ||||
|     struct floppy_struct fdparam; | ||||
|  | ||||
|     if (strstart(filename, "/dev/fd", NULL)) | ||||
|         prio = 50; | ||||
|  | ||||
|     fd = open(filename, O_RDONLY | O_NONBLOCK); | ||||
|     if (fd < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     /* Attempt to detect via a floppy specific ioctl */ | ||||
|     ret = ioctl(fd, FDGETPRM, &fdparam); | ||||
|     if (ret >= 0) | ||||
|         prio = 100; | ||||
|  | ||||
|     close(fd); | ||||
| out: | ||||
|     return prio; | ||||
|         return 100; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -1179,14 +1107,13 @@ static int floppy_eject(BlockDriverState *bs, int eject_flag) | ||||
|  | ||||
| static BlockDriver bdrv_host_floppy = { | ||||
|     .format_name        = "host_floppy", | ||||
|     .protocol_name      = "host_floppy", | ||||
|     .instance_size      = sizeof(BDRVRawState), | ||||
|     .bdrv_probe_device	= floppy_probe_device, | ||||
|     .bdrv_file_open     = floppy_open, | ||||
|     .bdrv_open          = floppy_open, | ||||
|     .bdrv_close         = raw_close, | ||||
|     .bdrv_create        = hdev_create, | ||||
|     .create_options     = raw_create_options, | ||||
|     .bdrv_has_zero_init = hdev_has_zero_init, | ||||
|     .no_zero_init       = 1, | ||||
|     .bdrv_flush         = raw_flush, | ||||
|  | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
| @@ -1215,22 +1142,9 @@ static int cdrom_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|  | ||||
| static int cdrom_probe_device(const char *filename) | ||||
| { | ||||
|     int fd, ret; | ||||
|     int prio = 0; | ||||
|  | ||||
|     fd = open(filename, O_RDONLY | O_NONBLOCK); | ||||
|     if (fd < 0) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     /* Attempt to detect via a CDROM specific ioctl */ | ||||
|     ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); | ||||
|     if (ret >= 0) | ||||
|         prio = 100; | ||||
|  | ||||
|     close(fd); | ||||
| out: | ||||
|     return prio; | ||||
|     if (strstart(filename, "/dev/cd", NULL)) | ||||
|         return 100; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int cdrom_is_inserted(BlockDriverState *bs) | ||||
| @@ -1276,14 +1190,13 @@ static int cdrom_set_locked(BlockDriverState *bs, int locked) | ||||
|  | ||||
| static BlockDriver bdrv_host_cdrom = { | ||||
|     .format_name        = "host_cdrom", | ||||
|     .protocol_name      = "host_cdrom", | ||||
|     .instance_size      = sizeof(BDRVRawState), | ||||
|     .bdrv_probe_device	= cdrom_probe_device, | ||||
|     .bdrv_file_open     = cdrom_open, | ||||
|     .bdrv_open          = cdrom_open, | ||||
|     .bdrv_close         = raw_close, | ||||
|     .bdrv_create        = hdev_create, | ||||
|     .create_options     = raw_create_options, | ||||
|     .bdrv_has_zero_init = hdev_has_zero_init, | ||||
|     .no_zero_init       = 1, | ||||
|     .bdrv_flush         = raw_flush, | ||||
|  | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
| @@ -1399,14 +1312,13 @@ static int cdrom_set_locked(BlockDriverState *bs, int locked) | ||||
|  | ||||
| static BlockDriver bdrv_host_cdrom = { | ||||
|     .format_name        = "host_cdrom", | ||||
|     .protocol_name      = "host_cdrom", | ||||
|     .instance_size      = sizeof(BDRVRawState), | ||||
|     .bdrv_probe_device	= cdrom_probe_device, | ||||
|     .bdrv_file_open     = cdrom_open, | ||||
|     .bdrv_open          = cdrom_open, | ||||
|     .bdrv_close         = raw_close, | ||||
|     .bdrv_create        = hdev_create, | ||||
|     .create_options     = raw_create_options, | ||||
|     .bdrv_has_zero_init = hdev_has_zero_init, | ||||
|     .no_zero_init       = 1, | ||||
|     .bdrv_flush         = raw_flush, | ||||
|  | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
| @@ -1424,13 +1336,13 @@ static BlockDriver bdrv_host_cdrom = { | ||||
| }; | ||||
| #endif /* __FreeBSD__ */ | ||||
|  | ||||
| static void bdrv_file_init(void) | ||||
| static void bdrv_raw_init(void) | ||||
| { | ||||
|     /* | ||||
|      * Register all the drivers.  Note that order is important, the driver | ||||
|      * registered last will get probed first. | ||||
|      */ | ||||
|     bdrv_register(&bdrv_file); | ||||
|     bdrv_register(&bdrv_raw); | ||||
|     bdrv_register(&bdrv_host_device); | ||||
| #ifdef __linux__ | ||||
|     bdrv_register(&bdrv_host_floppy); | ||||
| @@ -1441,4 +1353,4 @@ static void bdrv_file_init(void) | ||||
| #endif | ||||
| } | ||||
|  | ||||
| block_init(bdrv_file_init); | ||||
| block_init(bdrv_raw_init); | ||||
|   | ||||
| @@ -76,17 +76,21 @@ static int set_sparse(int fd) | ||||
| static int raw_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int access_flags; | ||||
|     int access_flags, create_flags; | ||||
|     DWORD overlapped; | ||||
|  | ||||
|     s->type = FTYPE_FILE; | ||||
|  | ||||
|     if (flags & BDRV_O_RDWR) { | ||||
|     if ((flags & BDRV_O_ACCESS) == O_RDWR) { | ||||
|         access_flags = GENERIC_READ | GENERIC_WRITE; | ||||
|     } else { | ||||
|         access_flags = GENERIC_READ; | ||||
|     } | ||||
|  | ||||
|     if (flags & BDRV_O_CREAT) { | ||||
|         create_flags = CREATE_ALWAYS; | ||||
|     } else { | ||||
|         create_flags = OPEN_EXISTING; | ||||
|     } | ||||
|     overlapped = FILE_ATTRIBUTE_NORMAL; | ||||
|     if ((flags & BDRV_O_NOCACHE)) | ||||
|         overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; | ||||
| @@ -94,7 +98,7 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|         overlapped |= FILE_FLAG_WRITE_THROUGH; | ||||
|     s->hfile = CreateFile(filename, access_flags, | ||||
|                           FILE_SHARE_READ, NULL, | ||||
|                           OPEN_EXISTING, overlapped, NULL); | ||||
|                           create_flags, overlapped, NULL); | ||||
|     if (s->hfile == INVALID_HANDLE_VALUE) { | ||||
|         int err = GetLastError(); | ||||
|  | ||||
| @@ -147,17 +151,10 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num, | ||||
|     return ret_count; | ||||
| } | ||||
|  | ||||
| static int raw_flush(BlockDriverState *bs) | ||||
| static void raw_flush(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVRawState *s = bs->opaque; | ||||
|     int ret; | ||||
|  | ||||
|     ret = FlushFileBuffers(s->hfile); | ||||
|     if (ret == 0) { | ||||
|         return -EIO; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
|     FlushFileBuffers(s->hfile); | ||||
| } | ||||
|  | ||||
| static void raw_close(BlockDriverState *bs) | ||||
| @@ -245,11 +242,10 @@ static QEMUOptionParameter raw_create_options[] = { | ||||
|     { NULL } | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_file = { | ||||
|     .format_name	= "file", | ||||
|     .protocol_name	= "file", | ||||
| static BlockDriver bdrv_raw = { | ||||
|     .format_name	= "raw", | ||||
|     .instance_size	= sizeof(BDRVRawState), | ||||
|     .bdrv_file_open	= raw_open, | ||||
|     .bdrv_open		= raw_open, | ||||
|     .bdrv_close		= raw_close, | ||||
|     .bdrv_create	= raw_create, | ||||
|     .bdrv_flush		= raw_flush, | ||||
| @@ -341,7 +337,7 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags) | ||||
|     } | ||||
|     s->type = find_device_type(bs, filename); | ||||
|  | ||||
|     if (flags & BDRV_O_RDWR) { | ||||
|     if ((flags & BDRV_O_ACCESS) == O_RDWR) { | ||||
|         access_flags = GENERIC_READ | GENERIC_WRITE; | ||||
|     } else { | ||||
|         access_flags = GENERIC_READ; | ||||
| @@ -401,30 +397,23 @@ static int raw_set_locked(BlockDriverState *bs, int locked) | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int hdev_has_zero_init(BlockDriverState *bs) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_host_device = { | ||||
|     .format_name	= "host_device", | ||||
|     .protocol_name	= "host_device", | ||||
|     .instance_size	= sizeof(BDRVRawState), | ||||
|     .bdrv_probe_device	= hdev_probe_device, | ||||
|     .bdrv_file_open	= hdev_open, | ||||
|     .bdrv_open		= hdev_open, | ||||
|     .bdrv_close		= raw_close, | ||||
|     .bdrv_flush		= raw_flush, | ||||
|     .bdrv_has_zero_init = hdev_has_zero_init, | ||||
|  | ||||
|     .bdrv_read		= raw_read, | ||||
|     .bdrv_write	        = raw_write, | ||||
|     .bdrv_getlength	= raw_getlength, | ||||
| }; | ||||
|  | ||||
| static void bdrv_file_init(void) | ||||
| static void bdrv_raw_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_file); | ||||
|     bdrv_register(&bdrv_raw); | ||||
|     bdrv_register(&bdrv_host_device); | ||||
| } | ||||
|  | ||||
| block_init(bdrv_file_init); | ||||
| block_init(bdrv_raw_init); | ||||
|   | ||||
							
								
								
									
										156
									
								
								block/raw.c
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								block/raw.c
									
									
									
									
									
								
							| @@ -1,156 +0,0 @@ | ||||
|  | ||||
| #include "qemu-common.h" | ||||
| #include "block_int.h" | ||||
| #include "module.h" | ||||
|  | ||||
| static int raw_open(BlockDriverState *bs, int flags) | ||||
| { | ||||
|     bs->sg = bs->file->sg; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int raw_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                     uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     return bdrv_read(bs->file, sector_num, buf, nb_sectors); | ||||
| } | ||||
|  | ||||
| static int raw_write(BlockDriverState *bs, int64_t sector_num, | ||||
|                      const uint8_t *buf, int nb_sectors) | ||||
| { | ||||
|     return bdrv_write(bs->file, sector_num, buf, nb_sectors); | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs, | ||||
|     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|     BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs, | ||||
|     int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, | ||||
|     BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); | ||||
| } | ||||
|  | ||||
| static void raw_close(BlockDriverState *bs) | ||||
| { | ||||
| } | ||||
|  | ||||
| static int raw_flush(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_flush(bs->file); | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs, | ||||
|     BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|     return bdrv_aio_flush(bs->file, cb, opaque); | ||||
| } | ||||
|  | ||||
| static int64_t raw_getlength(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_getlength(bs->file); | ||||
| } | ||||
|  | ||||
| static int raw_truncate(BlockDriverState *bs, int64_t offset) | ||||
| { | ||||
|     return bdrv_truncate(bs->file, offset); | ||||
| } | ||||
|  | ||||
| static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
| { | ||||
|    return 1; /* everything can be opened as raw image */ | ||||
| } | ||||
|  | ||||
| static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) | ||||
| { | ||||
|     return bdrv_discard(bs->file, sector_num, nb_sectors); | ||||
| } | ||||
|  | ||||
| static int raw_is_inserted(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_is_inserted(bs->file); | ||||
| } | ||||
|  | ||||
| static int raw_eject(BlockDriverState *bs, int eject_flag) | ||||
| { | ||||
|     return bdrv_eject(bs->file, eject_flag); | ||||
| } | ||||
|  | ||||
| static int raw_set_locked(BlockDriverState *bs, int locked) | ||||
| { | ||||
|     bdrv_set_locked(bs->file, locked); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) | ||||
| { | ||||
|    return bdrv_ioctl(bs->file, req, buf); | ||||
| } | ||||
|  | ||||
| static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs, | ||||
|         unsigned long int req, void *buf, | ||||
|         BlockDriverCompletionFunc *cb, void *opaque) | ||||
| { | ||||
|    return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque); | ||||
| } | ||||
|  | ||||
| static int raw_create(const char *filename, QEMUOptionParameter *options) | ||||
| { | ||||
|     return bdrv_create_file(filename, options); | ||||
| } | ||||
|  | ||||
| static QEMUOptionParameter raw_create_options[] = { | ||||
|     { | ||||
|         .name = BLOCK_OPT_SIZE, | ||||
|         .type = OPT_SIZE, | ||||
|         .help = "Virtual disk size" | ||||
|     }, | ||||
|     { NULL } | ||||
| }; | ||||
|  | ||||
| static int raw_has_zero_init(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_has_zero_init(bs->file); | ||||
| } | ||||
|  | ||||
| static BlockDriver bdrv_raw = { | ||||
|     .format_name        = "raw", | ||||
|  | ||||
|     /* It's really 0, but we need to make qemu_malloc() happy */ | ||||
|     .instance_size      = 1, | ||||
|  | ||||
|     .bdrv_open          = raw_open, | ||||
|     .bdrv_close         = raw_close, | ||||
|     .bdrv_read          = raw_read, | ||||
|     .bdrv_write         = raw_write, | ||||
|     .bdrv_flush         = raw_flush, | ||||
|     .bdrv_probe         = raw_probe, | ||||
|     .bdrv_getlength     = raw_getlength, | ||||
|     .bdrv_truncate      = raw_truncate, | ||||
|  | ||||
|     .bdrv_aio_readv     = raw_aio_readv, | ||||
|     .bdrv_aio_writev    = raw_aio_writev, | ||||
|     .bdrv_aio_flush     = raw_aio_flush, | ||||
|     .bdrv_discard       = raw_discard, | ||||
|  | ||||
|     .bdrv_is_inserted   = raw_is_inserted, | ||||
|     .bdrv_eject         = raw_eject, | ||||
|     .bdrv_set_locked    = raw_set_locked, | ||||
|     .bdrv_ioctl         = raw_ioctl, | ||||
|     .bdrv_aio_ioctl     = raw_aio_ioctl, | ||||
|  | ||||
|     .bdrv_create        = raw_create, | ||||
|     .create_options     = raw_create_options, | ||||
|     .bdrv_has_zero_init = raw_has_zero_init, | ||||
| }; | ||||
|  | ||||
| static void bdrv_raw_init(void) | ||||
| { | ||||
|     bdrv_register(&bdrv_raw); | ||||
| } | ||||
|  | ||||
| block_init(bdrv_raw_init); | ||||
							
								
								
									
										1059
									
								
								block/rbd.c
									
									
									
									
									
								
							
							
						
						
									
										1059
									
								
								block/rbd.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,71 +0,0 @@ | ||||
| /* | ||||
|  * Ceph - scalable distributed file system | ||||
|  * | ||||
|  * Copyright (C) 2004-2010 Sage Weil <sage@newdream.net> | ||||
|  * | ||||
|  * This is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License version 2.1, as published by the Free Software | ||||
|  * Foundation.  See file COPYING.LIB. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef CEPH_RBD_TYPES_H | ||||
| #define CEPH_RBD_TYPES_H | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * rbd image 'foo' consists of objects | ||||
|  *   foo.rbd      - image metadata | ||||
|  *   foo.00000000 | ||||
|  *   foo.00000001 | ||||
|  *   ...          - data | ||||
|  */ | ||||
|  | ||||
| #define RBD_SUFFIX              ".rbd" | ||||
| #define RBD_DIRECTORY           "rbd_directory" | ||||
| #define RBD_INFO                "rbd_info" | ||||
|  | ||||
| #define RBD_DEFAULT_OBJ_ORDER   22   /* 4MB */ | ||||
|  | ||||
| #define RBD_MAX_OBJ_NAME_SIZE   96 | ||||
| #define RBD_MAX_BLOCK_NAME_SIZE 24 | ||||
| #define RBD_MAX_SEG_NAME_SIZE   128 | ||||
|  | ||||
| #define RBD_COMP_NONE           0 | ||||
| #define RBD_CRYPT_NONE          0 | ||||
|  | ||||
| #define RBD_HEADER_TEXT         "<<< Rados Block Device Image >>>\n" | ||||
| #define RBD_HEADER_SIGNATURE    "RBD" | ||||
| #define RBD_HEADER_VERSION      "001.005" | ||||
|  | ||||
| struct rbd_info { | ||||
|     uint64_t max_id; | ||||
| } __attribute__ ((packed)); | ||||
|  | ||||
| struct rbd_obj_snap_ondisk { | ||||
|     uint64_t id; | ||||
|     uint64_t image_size; | ||||
| } __attribute__((packed)); | ||||
|  | ||||
| struct rbd_obj_header_ondisk { | ||||
|     char text[40]; | ||||
|     char block_name[RBD_MAX_BLOCK_NAME_SIZE]; | ||||
|     char signature[4]; | ||||
|     char version[8]; | ||||
|     struct { | ||||
|         uint8_t order; | ||||
|         uint8_t crypt_type; | ||||
|         uint8_t comp_type; | ||||
|         uint8_t unused; | ||||
|     } __attribute__((packed)) options; | ||||
|     uint64_t image_size; | ||||
|     uint64_t snap_seq; | ||||
|     uint32_t snap_count; | ||||
|     uint32_t reserved; | ||||
|     uint64_t snap_names_len; | ||||
|     struct rbd_obj_snap_ondisk snaps[0]; | ||||
| } __attribute__((packed)); | ||||
|  | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										2037
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
							
						
						
									
										2037
									
								
								block/sheepdog.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										57
									
								
								block/vdi.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								block/vdi.c
									
									
									
									
									
								
							| @@ -119,13 +119,13 @@ void uuid_unparse(const uuid_t uu, char *out); | ||||
| #if !defined(CONFIG_UUID) | ||||
| void uuid_generate(uuid_t out) | ||||
| { | ||||
|     memset(out, 0, sizeof(uuid_t)); | ||||
|     memset(out, 0, sizeof(out)); | ||||
| } | ||||
|  | ||||
| int uuid_is_null(const uuid_t uu) | ||||
| { | ||||
|     uuid_t null_uuid = { 0 }; | ||||
|     return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0; | ||||
|     return memcmp(uu, null_uuid, sizeof(uu)) == 0; | ||||
| } | ||||
|  | ||||
| void uuid_unparse(const uuid_t uu, char *out) | ||||
| @@ -186,6 +186,7 @@ typedef struct { | ||||
| } VdiHeader; | ||||
|  | ||||
| typedef struct { | ||||
|     BlockDriverState *hd; | ||||
|     /* The block map entries are little endian (even in memory). */ | ||||
|     uint32_t *bmap; | ||||
|     /* Size of block (bytes). */ | ||||
| @@ -290,10 +291,11 @@ static void vdi_header_print(VdiHeader *header) | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res) | ||||
| static int vdi_check(BlockDriverState *bs) | ||||
| { | ||||
|     /* TODO: additional checks possible. */ | ||||
|     BDRVVdiState *s = (BDRVVdiState *)bs->opaque; | ||||
|     int n_errors = 0; | ||||
|     uint32_t blocks_allocated = 0; | ||||
|     uint32_t block; | ||||
|     uint32_t *bmap; | ||||
| @@ -313,12 +315,11 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res) | ||||
|                 } else { | ||||
|                     fprintf(stderr, "ERROR: block index %" PRIu32 | ||||
|                             " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry); | ||||
|                     res->corruptions++; | ||||
|                 } | ||||
|             } else { | ||||
|                 fprintf(stderr, "ERROR: block index %" PRIu32 | ||||
|                         " too large, is %" PRIu32 "\n", block, bmap_entry); | ||||
|                 res->corruptions++; | ||||
|                 n_errors++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -326,12 +327,12 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res) | ||||
|         fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32 | ||||
|                ", should be %" PRIu32 "\n", | ||||
|                blocks_allocated, s->header.blocks_allocated); | ||||
|         res->corruptions++; | ||||
|         n_errors++; | ||||
|     } | ||||
|  | ||||
|     qemu_free(bmap); | ||||
|  | ||||
|     return 0; | ||||
|     return n_errors; | ||||
| } | ||||
|  | ||||
| static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | ||||
| @@ -375,15 +376,21 @@ static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| static int vdi_open(BlockDriverState *bs, int flags) | ||||
| static int vdi_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVVdiState *s = bs->opaque; | ||||
|     VdiHeader header; | ||||
|     size_t bmap_size; | ||||
|     int ret; | ||||
|  | ||||
|     logout("\n"); | ||||
|  | ||||
|     if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) { | ||||
|     ret = bdrv_file_open(&s->hd, filename, flags); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_read(s->hd, 0, (uint8_t *)&header, 1) < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
| @@ -440,10 +447,8 @@ static int vdi_open(BlockDriverState *bs, int flags) | ||||
|  | ||||
|     bmap_size = header.blocks_in_image * sizeof(uint32_t); | ||||
|     bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE; | ||||
|     if (bmap_size > 0) { | ||||
|         s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE); | ||||
|     } | ||||
|     if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) { | ||||
|     s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE); | ||||
|     if (bdrv_read(s->hd, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) { | ||||
|         goto fail_free_bmap; | ||||
|     } | ||||
|  | ||||
| @@ -453,6 +458,7 @@ static int vdi_open(BlockDriverState *bs, int flags) | ||||
|     qemu_free(s->bmap); | ||||
|  | ||||
|  fail: | ||||
|     bdrv_delete(s->hd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| @@ -476,7 +482,7 @@ static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num, | ||||
| static void vdi_aio_cancel(BlockDriverAIOCB *blockacb) | ||||
| { | ||||
|     /* TODO: This code is untested. How can I get it executed? */ | ||||
|     VdiAIOCB *acb = container_of(blockacb, VdiAIOCB, common); | ||||
|     VdiAIOCB *acb = (VdiAIOCB *)blockacb; | ||||
|     logout("\n"); | ||||
|     if (acb->hd_aiocb) { | ||||
|         bdrv_aio_cancel(acb->hd_aiocb); | ||||
| @@ -607,7 +613,7 @@ static void vdi_aio_read_cb(void *opaque, int ret) | ||||
|         acb->hd_iov.iov_base = (void *)acb->buf; | ||||
|         acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE; | ||||
|         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||
|         acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov, | ||||
|         acb->hd_aiocb = bdrv_aio_readv(s->hd, offset, &acb->hd_qiov, | ||||
|                                        n_sectors, vdi_aio_read_cb, acb); | ||||
|         if (acb->hd_aiocb == NULL) { | ||||
|             goto done; | ||||
| @@ -670,7 +676,7 @@ static void vdi_aio_write_cb(void *opaque, int ret) | ||||
|             acb->hd_iov.iov_base = acb->block_buffer; | ||||
|             acb->hd_iov.iov_len = SECTOR_SIZE; | ||||
|             qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||
|             acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1, | ||||
|             acb->hd_aiocb = bdrv_aio_writev(s->hd, 0, &acb->hd_qiov, 1, | ||||
|                                             vdi_aio_write_cb, acb); | ||||
|             if (acb->hd_aiocb == NULL) { | ||||
|                 goto done; | ||||
| @@ -699,7 +705,7 @@ static void vdi_aio_write_cb(void *opaque, int ret) | ||||
|             qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||
|             logout("will write %u block map sectors starting from entry %u\n", | ||||
|                    n_sectors, bmap_first); | ||||
|             acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, | ||||
|             acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov, | ||||
|                                             n_sectors, vdi_aio_write_cb, acb); | ||||
|             if (acb->hd_aiocb == NULL) { | ||||
|                 goto done; | ||||
| @@ -748,7 +754,7 @@ static void vdi_aio_write_cb(void *opaque, int ret) | ||||
|         acb->hd_iov.iov_base = (void *)block; | ||||
|         acb->hd_iov.iov_len = s->block_size; | ||||
|         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||
|         acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, | ||||
|         acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, | ||||
|                                         &acb->hd_qiov, s->block_sectors, | ||||
|                                         vdi_aio_write_cb, acb); | ||||
|         if (acb->hd_aiocb == NULL) { | ||||
| @@ -761,7 +767,7 @@ static void vdi_aio_write_cb(void *opaque, int ret) | ||||
|         acb->hd_iov.iov_base = (void *)acb->buf; | ||||
|         acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE; | ||||
|         qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); | ||||
|         acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, | ||||
|         acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov, | ||||
|                                         n_sectors, vdi_aio_write_cb, acb); | ||||
|         if (acb->hd_aiocb == NULL) { | ||||
|             goto done; | ||||
| @@ -867,10 +873,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options) | ||||
|         result = -errno; | ||||
|     } | ||||
|  | ||||
|     bmap = NULL; | ||||
|     if (bmap_size > 0) { | ||||
|         bmap = (uint32_t *)qemu_mallocz(bmap_size); | ||||
|     } | ||||
|     bmap = (uint32_t *)qemu_mallocz(bmap_size); | ||||
|     for (i = 0; i < blocks; i++) { | ||||
|         if (image_type == VDI_TYPE_STATIC) { | ||||
|             bmap[i] = i; | ||||
| @@ -897,12 +900,16 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options) | ||||
|  | ||||
| static void vdi_close(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVVdiState *s = bs->opaque; | ||||
|     logout("\n"); | ||||
|     bdrv_delete(s->hd); | ||||
| } | ||||
|  | ||||
| static int vdi_flush(BlockDriverState *bs) | ||||
| static void vdi_flush(BlockDriverState *bs) | ||||
| { | ||||
|     BDRVVdiState *s = bs->opaque; | ||||
|     logout("\n"); | ||||
|     return bdrv_flush(bs->file); | ||||
|     bdrv_flush(s->hd); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										251
									
								
								block/vmdk.c
									
									
									
									
									
								
							
							
						
						
									
										251
									
								
								block/vmdk.c
									
									
									
									
									
								
							| @@ -61,6 +61,7 @@ typedef struct { | ||||
| #define L2_CACHE_SIZE 16 | ||||
|  | ||||
| typedef struct BDRVVmdkState { | ||||
|     BlockDriverState *hd; | ||||
|     int64_t l1_table_offset; | ||||
|     int64_t l1_backup_table_offset; | ||||
|     uint32_t *l1_table; | ||||
| @@ -75,6 +76,7 @@ typedef struct BDRVVmdkState { | ||||
|  | ||||
|     unsigned int cluster_sectors; | ||||
|     uint32_t parent_cid; | ||||
|     int is_parent; | ||||
| } BDRVVmdkState; | ||||
|  | ||||
| typedef struct VmdkMetaData { | ||||
| @@ -107,13 +109,14 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|  | ||||
| static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) | ||||
| { | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|     char desc[DESC_SIZE]; | ||||
|     uint32_t cid; | ||||
|     const char *p_name, *cid_str; | ||||
|     size_t cid_str_size; | ||||
|  | ||||
|     /* the descriptor offset = 0x200 */ | ||||
|     if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
|     if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
|         return 0; | ||||
|  | ||||
|     if (parent) { | ||||
| @@ -134,11 +137,12 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) | ||||
|  | ||||
| static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) | ||||
| { | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|     char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; | ||||
|     char *p_name, *tmp_str; | ||||
|  | ||||
|     /* the descriptor offset = 0x200 */ | ||||
|     if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
|     if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
|         return -1; | ||||
|  | ||||
|     tmp_str = strstr(desc,"parentCID"); | ||||
| @@ -149,7 +153,7 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) | ||||
|         pstrcat(desc, sizeof(desc), tmp_desc); | ||||
|     } | ||||
|  | ||||
|     if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0) | ||||
|     if (bdrv_pwrite_sync(s->hd, 0x200, desc, DESC_SIZE) < 0) | ||||
|         return -1; | ||||
|     return 0; | ||||
| } | ||||
| @@ -175,7 +179,6 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) | ||||
| static int vmdk_snapshot_create(const char *filename, const char *backing_file) | ||||
| { | ||||
|     int snp_fd, p_fd; | ||||
|     int ret; | ||||
|     uint32_t p_cid; | ||||
|     char *p_name, *gd_buf, *rgd_buf; | ||||
|     const char *real_filename, *temp_str; | ||||
| @@ -200,49 +203,34 @@ static int vmdk_snapshot_create(const char *filename, const char *backing_file) | ||||
|  | ||||
|     snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); | ||||
|     if (snp_fd < 0) | ||||
|         return -errno; | ||||
|         return -1; | ||||
|     p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); | ||||
|     if (p_fd < 0) { | ||||
|         close(snp_fd); | ||||
|         return -errno; | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* read the header */ | ||||
|     if (lseek(p_fd, 0x0, SEEK_SET) == -1) { | ||||
|         ret = -errno; | ||||
|     if (lseek(p_fd, 0x0, SEEK_SET) == -1) | ||||
|         goto fail; | ||||
|     } | ||||
|     if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) { | ||||
|         ret = -errno; | ||||
|     if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     /* write the header */ | ||||
|     if (lseek(snp_fd, 0x0, SEEK_SET) == -1) { | ||||
|         ret = -errno; | ||||
|     if (lseek(snp_fd, 0x0, SEEK_SET) == -1) | ||||
|         goto fail; | ||||
|     } | ||||
|     if (write(snp_fd, hdr, HEADER_SIZE) == -1) { | ||||
|         ret = -errno; | ||||
|     if (write(snp_fd, hdr, HEADER_SIZE) == -1) | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     memset(&header, 0, sizeof(header)); | ||||
|     memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC | ||||
|  | ||||
|     if (ftruncate(snp_fd, header.grain_offset << 9)) { | ||||
|         ret = -errno; | ||||
|         goto fail; | ||||
|     } | ||||
|     ftruncate(snp_fd, header.grain_offset << 9); | ||||
|     /* the descriptor offset = 0x200 */ | ||||
|     if (lseek(p_fd, 0x200, SEEK_SET) == -1) { | ||||
|         ret = -errno; | ||||
|     if (lseek(p_fd, 0x200, SEEK_SET) == -1) | ||||
|         goto fail; | ||||
|     } | ||||
|     if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) { | ||||
|         ret = -errno; | ||||
|     if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     if ((p_name = strstr(p_desc,"CID")) != NULL) { | ||||
|         p_name += sizeof("CID"); | ||||
| @@ -261,14 +249,10 @@ static int vmdk_snapshot_create(const char *filename, const char *backing_file) | ||||
|              (uint32_t)header.capacity, real_filename); | ||||
|  | ||||
|     /* write the descriptor */ | ||||
|     if (lseek(snp_fd, 0x200, SEEK_SET) == -1) { | ||||
|         ret = -errno; | ||||
|     if (lseek(snp_fd, 0x200, SEEK_SET) == -1) | ||||
|         goto fail; | ||||
|     } | ||||
|     if (write(snp_fd, s_desc, strlen(s_desc)) == -1) { | ||||
|         ret = -errno; | ||||
|     if (write(snp_fd, s_desc, strlen(s_desc)) == -1) | ||||
|         goto fail; | ||||
|     } | ||||
|  | ||||
|     gd_offset = header.gd_offset * SECTOR_SIZE;     // offset of GD table | ||||
|     rgd_offset = header.rgd_offset * SECTOR_SIZE;   // offset of RGD table | ||||
| @@ -278,73 +262,70 @@ static int vmdk_snapshot_create(const char *filename, const char *backing_file) | ||||
|      * 512 GTE per GT, each GTE points to grain | ||||
|      */ | ||||
|     gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; | ||||
|     if (!gt_size) { | ||||
|         ret = -EINVAL; | ||||
|     if (!gt_size) | ||||
|         goto fail; | ||||
|     } | ||||
|     gde_entries = (uint32_t)(capacity / gt_size);  // number of gde/rgde | ||||
|     gd_size = gde_entries * sizeof(uint32_t); | ||||
|  | ||||
|     /* write RGD */ | ||||
|     rgd_buf = qemu_malloc(gd_size); | ||||
|     if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) { | ||||
|         ret = -errno; | ||||
|     if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) | ||||
|         goto fail_rgd; | ||||
|     } | ||||
|     if (read(p_fd, rgd_buf, gd_size) != gd_size) { | ||||
|         ret = -errno; | ||||
|     if (read(p_fd, rgd_buf, gd_size) != gd_size) | ||||
|         goto fail_rgd; | ||||
|     } | ||||
|     if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) { | ||||
|         ret = -errno; | ||||
|     if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) | ||||
|         goto fail_rgd; | ||||
|     } | ||||
|     if (write(snp_fd, rgd_buf, gd_size) == -1) { | ||||
|         ret = -errno; | ||||
|     if (write(snp_fd, rgd_buf, gd_size) == -1) | ||||
|         goto fail_rgd; | ||||
|     } | ||||
|  | ||||
|     /* write GD */ | ||||
|     gd_buf = qemu_malloc(gd_size); | ||||
|     if (lseek(p_fd, gd_offset, SEEK_SET) == -1) { | ||||
|         ret = -errno; | ||||
|     if (lseek(p_fd, gd_offset, SEEK_SET) == -1) | ||||
|         goto fail_gd; | ||||
|     } | ||||
|     if (read(p_fd, gd_buf, gd_size) != gd_size) { | ||||
|         ret = -errno; | ||||
|     if (read(p_fd, gd_buf, gd_size) != gd_size) | ||||
|         goto fail_gd; | ||||
|     } | ||||
|     if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) { | ||||
|         ret = -errno; | ||||
|     if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) | ||||
|         goto fail_gd; | ||||
|     } | ||||
|     if (write(snp_fd, gd_buf, gd_size) == -1) { | ||||
|         ret = -errno; | ||||
|     if (write(snp_fd, gd_buf, gd_size) == -1) | ||||
|         goto fail_gd; | ||||
|     } | ||||
|     ret = 0; | ||||
|  | ||||
| fail_gd: | ||||
|     qemu_free(gd_buf); | ||||
| fail_rgd: | ||||
|     qemu_free(rgd_buf); | ||||
| fail: | ||||
|  | ||||
|     close(p_fd); | ||||
|     close(snp_fd); | ||||
|     return ret; | ||||
|     return 0; | ||||
|  | ||||
|     fail_gd: | ||||
|     qemu_free(gd_buf); | ||||
|     fail_rgd: | ||||
|     qemu_free(rgd_buf); | ||||
|     fail: | ||||
|     close(p_fd); | ||||
|     close(snp_fd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int vmdk_parent_open(BlockDriverState *bs) | ||||
| static void vmdk_parent_close(BlockDriverState *bs) | ||||
| { | ||||
|     if (bs->backing_hd) | ||||
|         bdrv_close(bs->backing_hd); | ||||
| } | ||||
|  | ||||
| static int parent_open = 0; | ||||
| static int vmdk_parent_open(BlockDriverState *bs, const char * filename) | ||||
| { | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|     char *p_name; | ||||
|     char desc[DESC_SIZE]; | ||||
|     char parent_img_name[1024]; | ||||
|  | ||||
|     /* the descriptor offset = 0x200 */ | ||||
|     if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
|     if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) | ||||
|         return -1; | ||||
|  | ||||
|     if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { | ||||
|         char *end_name; | ||||
|         struct stat file_buf; | ||||
|  | ||||
|         p_name += sizeof("parentFileNameHint") + 1; | ||||
|         if ((end_name = strchr(p_name,'\"')) == NULL) | ||||
| @@ -353,25 +334,50 @@ static int vmdk_parent_open(BlockDriverState *bs) | ||||
|             return -1; | ||||
|  | ||||
|         pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); | ||||
|         if (stat(bs->backing_file, &file_buf) != 0) { | ||||
|             path_combine(parent_img_name, sizeof(parent_img_name), | ||||
|                          filename, bs->backing_file); | ||||
|         } else { | ||||
|             pstrcpy(parent_img_name, sizeof(parent_img_name), | ||||
|                     bs->backing_file); | ||||
|         } | ||||
|  | ||||
|         bs->backing_hd = bdrv_new(""); | ||||
|         if (!bs->backing_hd) { | ||||
|             failure: | ||||
|             bdrv_close(s->hd); | ||||
|             return -1; | ||||
|         } | ||||
|         parent_open = 1; | ||||
|         if (bdrv_open(bs->backing_hd, parent_img_name, BDRV_O_RDONLY) < 0) | ||||
|             goto failure; | ||||
|         parent_open = 0; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int vmdk_open(BlockDriverState *bs, int flags) | ||||
| static int vmdk_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|     uint32_t magic; | ||||
|     int l1_size, i; | ||||
|     int l1_size, i, ret; | ||||
|  | ||||
|     if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) | ||||
|     if (parent_open) | ||||
|         // Parent must be opened as RO. | ||||
|         flags = BDRV_O_RDONLY; | ||||
|  | ||||
|     ret = bdrv_file_open(&s->hd, filename, flags); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|     if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic)) | ||||
|         goto fail; | ||||
|  | ||||
|     magic = be32_to_cpu(magic); | ||||
|     if (magic == VMDK3_MAGIC) { | ||||
|         VMDK3Header header; | ||||
|  | ||||
|         if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) | ||||
|         if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) | ||||
|             goto fail; | ||||
|         s->cluster_sectors = le32_to_cpu(header.granularity); | ||||
|         s->l2_size = 1 << 9; | ||||
| @@ -383,7 +389,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) | ||||
|     } else if (magic == VMDK4_MAGIC) { | ||||
|         VMDK4Header header; | ||||
|  | ||||
|         if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) | ||||
|         if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) | ||||
|             goto fail; | ||||
|         bs->total_sectors = le64_to_cpu(header.capacity); | ||||
|         s->cluster_sectors = le64_to_cpu(header.granularity); | ||||
| @@ -396,8 +402,13 @@ static int vmdk_open(BlockDriverState *bs, int flags) | ||||
|         s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; | ||||
|         s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; | ||||
|  | ||||
|         if (parent_open) | ||||
|             s->is_parent = 1; | ||||
|         else | ||||
|             s->is_parent = 0; | ||||
|  | ||||
|         // try to open parent images, if exist | ||||
|         if (vmdk_parent_open(bs) != 0) | ||||
|         if (vmdk_parent_open(bs, filename) != 0) | ||||
|             goto fail; | ||||
|         // write the CID once after the image creation | ||||
|         s->parent_cid = vmdk_read_cid(bs,1); | ||||
| @@ -408,7 +419,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) | ||||
|     /* read the L1 table */ | ||||
|     l1_size = s->l1_size * sizeof(uint32_t); | ||||
|     s->l1_table = qemu_malloc(l1_size); | ||||
|     if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size) | ||||
|     if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, l1_size) != l1_size) | ||||
|         goto fail; | ||||
|     for(i = 0; i < s->l1_size; i++) { | ||||
|         le32_to_cpus(&s->l1_table[i]); | ||||
| @@ -416,7 +427,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) | ||||
|  | ||||
|     if (s->l1_backup_table_offset) { | ||||
|         s->l1_backup_table = qemu_malloc(l1_size); | ||||
|         if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) | ||||
|         if (bdrv_pread(s->hd, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) | ||||
|             goto fail; | ||||
|         for(i = 0; i < s->l1_size; i++) { | ||||
|             le32_to_cpus(&s->l1_backup_table[i]); | ||||
| @@ -429,6 +440,7 @@ static int vmdk_open(BlockDriverState *bs, int flags) | ||||
|     qemu_free(s->l1_backup_table); | ||||
|     qemu_free(s->l1_table); | ||||
|     qemu_free(s->l2_cache); | ||||
|     bdrv_delete(s->hd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| @@ -456,7 +468,7 @@ static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset, | ||||
|         } | ||||
|  | ||||
|         //Write grain only into the active image | ||||
|         ret = bdrv_write(bs->file, cluster_offset, whole_grain, | ||||
|         ret = bdrv_write(s->hd, cluster_offset, whole_grain, | ||||
|             s->cluster_sectors); | ||||
|         if (ret < 0) { | ||||
|             return -1; | ||||
| @@ -470,13 +482,13 @@ static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data) | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|  | ||||
|     /* update L2 table */ | ||||
|     if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), | ||||
|     if (bdrv_pwrite_sync(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), | ||||
|                     &(m_data->offset), sizeof(m_data->offset)) < 0) | ||||
|         return -1; | ||||
|     /* update backup L2 table */ | ||||
|     if (s->l1_backup_table_offset != 0) { | ||||
|         m_data->l2_offset = s->l1_backup_table[m_data->l1_index]; | ||||
|         if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), | ||||
|         if (bdrv_pwrite_sync(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), | ||||
|                         &(m_data->offset), sizeof(m_data->offset)) < 0) | ||||
|             return -1; | ||||
|     } | ||||
| @@ -524,7 +536,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, | ||||
|         } | ||||
|     } | ||||
|     l2_table = s->l2_cache + (min_index * s->l2_size); | ||||
|     if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != | ||||
|     if (bdrv_pread(s->hd, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != | ||||
|                                                                         s->l2_size * sizeof(uint32_t)) | ||||
|         return 0; | ||||
|  | ||||
| @@ -537,15 +549,15 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data, | ||||
|     if (!cluster_offset) { | ||||
|         if (!allocate) | ||||
|             return 0; | ||||
|  | ||||
|         // Avoid the L2 tables update for the images that have snapshots. | ||||
|         cluster_offset = bdrv_getlength(bs->file); | ||||
|         bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9)); | ||||
|  | ||||
|         cluster_offset >>= 9; | ||||
|         tmp = cpu_to_le32(cluster_offset); | ||||
|         l2_table[l2_index] = tmp; | ||||
|         if (!s->is_parent) { | ||||
|             cluster_offset = bdrv_getlength(s->hd); | ||||
|             bdrv_truncate(s->hd, cluster_offset + (s->cluster_sectors << 9)); | ||||
|  | ||||
|             cluster_offset >>= 9; | ||||
|             tmp = cpu_to_le32(cluster_offset); | ||||
|             l2_table[l2_index] = tmp; | ||||
|         } | ||||
|         /* First of all we write grain itself, to avoid race condition | ||||
|          * that may to corrupt the image. | ||||
|          * This problem may occur because of insufficient space on host disk | ||||
| @@ -607,7 +619,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, | ||||
|                 memset(buf, 0, 512 * n); | ||||
|             } | ||||
|         } else { | ||||
|             if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) | ||||
|             if(bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) | ||||
|                 return -1; | ||||
|         } | ||||
|         nb_sectors -= n; | ||||
| @@ -643,7 +655,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, | ||||
|         if (!cluster_offset) | ||||
|             return -1; | ||||
|  | ||||
|         if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) | ||||
|         if (bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) | ||||
|             return -1; | ||||
|         if (m_data.valid) { | ||||
|             /* update L2 tables */ | ||||
| @@ -691,7 +703,6 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) | ||||
|     int64_t total_size = 0; | ||||
|     const char *backing_file = NULL; | ||||
|     int flags = 0; | ||||
|     int ret; | ||||
|  | ||||
|     // Read out options | ||||
|     while (options && options->name) { | ||||
| @@ -713,7 +724,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) | ||||
|     fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, | ||||
|               0644); | ||||
|     if (fd < 0) | ||||
|         return -errno; | ||||
|         return -1; | ||||
|     magic = cpu_to_be32(VMDK4_MAGIC); | ||||
|     memset(&header, 0, sizeof(header)); | ||||
|     header.version = cpu_to_le32(1); | ||||
| @@ -748,44 +759,22 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) | ||||
|     header.check_bytes[3] = 0xa; | ||||
|  | ||||
|     /* write all the data */ | ||||
|     ret = qemu_write_full(fd, &magic, sizeof(magic)); | ||||
|     if (ret != sizeof(magic)) { | ||||
|         ret = -errno; | ||||
|         goto exit; | ||||
|     } | ||||
|     ret = qemu_write_full(fd, &header, sizeof(header)); | ||||
|     if (ret != sizeof(header)) { | ||||
|         ret = -errno; | ||||
|         goto exit; | ||||
|     } | ||||
|     write(fd, &magic, sizeof(magic)); | ||||
|     write(fd, &header, sizeof(header)); | ||||
|  | ||||
|     ret = ftruncate(fd, header.grain_offset << 9); | ||||
|     if (ret < 0) { | ||||
|         ret = -errno; | ||||
|         goto exit; | ||||
|     } | ||||
|     ftruncate(fd, header.grain_offset << 9); | ||||
|  | ||||
|     /* write grain directory */ | ||||
|     lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); | ||||
|     for (i = 0, tmp = header.rgd_offset + gd_size; | ||||
|          i < gt_count; i++, tmp += gt_size) { | ||||
|         ret = qemu_write_full(fd, &tmp, sizeof(tmp)); | ||||
|         if (ret != sizeof(tmp)) { | ||||
|             ret = -errno; | ||||
|             goto exit; | ||||
|         } | ||||
|     } | ||||
|          i < gt_count; i++, tmp += gt_size) | ||||
|         write(fd, &tmp, sizeof(tmp)); | ||||
|  | ||||
|     /* write backup grain directory */ | ||||
|     lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); | ||||
|     for (i = 0, tmp = header.gd_offset + gd_size; | ||||
|          i < gt_count; i++, tmp += gt_size) { | ||||
|         ret = qemu_write_full(fd, &tmp, sizeof(tmp)); | ||||
|         if (ret != sizeof(tmp)) { | ||||
|             ret = -errno; | ||||
|             goto exit; | ||||
|         } | ||||
|     } | ||||
|          i < gt_count; i++, tmp += gt_size) | ||||
|         write(fd, &tmp, sizeof(tmp)); | ||||
|  | ||||
|     /* compose the descriptor */ | ||||
|     real_filename = filename; | ||||
| @@ -802,16 +791,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options) | ||||
|  | ||||
|     /* write the descriptor */ | ||||
|     lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); | ||||
|     ret = qemu_write_full(fd, desc, strlen(desc)); | ||||
|     if (ret != strlen(desc)) { | ||||
|         ret = -errno; | ||||
|         goto exit; | ||||
|     } | ||||
|     write(fd, desc, strlen(desc)); | ||||
|  | ||||
|     ret = 0; | ||||
| exit: | ||||
|     close(fd); | ||||
|     return ret; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void vmdk_close(BlockDriverState *bs) | ||||
| @@ -820,11 +803,15 @@ static void vmdk_close(BlockDriverState *bs) | ||||
|  | ||||
|     qemu_free(s->l1_table); | ||||
|     qemu_free(s->l2_cache); | ||||
|     // try to close parent image, if exist | ||||
|     vmdk_parent_close(s->hd); | ||||
|     bdrv_delete(s->hd); | ||||
| } | ||||
|  | ||||
| static int vmdk_flush(BlockDriverState *bs) | ||||
| static void vmdk_flush(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_flush(bs->file); | ||||
|     BDRVVmdkState *s = bs->opaque; | ||||
|     bdrv_flush(s->hd); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -851,7 +838,7 @@ static BlockDriver bdrv_vmdk = { | ||||
|     .format_name	= "vmdk", | ||||
|     .instance_size	= sizeof(BDRVVmdkState), | ||||
|     .bdrv_probe		= vmdk_probe, | ||||
|     .bdrv_open      = vmdk_open, | ||||
|     .bdrv_open		= vmdk_open, | ||||
|     .bdrv_read		= vmdk_read, | ||||
|     .bdrv_write		= vmdk_write, | ||||
|     .bdrv_close		= vmdk_close, | ||||
|   | ||||
							
								
								
									
										135
									
								
								block/vpc.c
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								block/vpc.c
									
									
									
									
									
								
							| @@ -110,6 +110,8 @@ struct vhd_dyndisk_header { | ||||
| }; | ||||
|  | ||||
| typedef struct BDRVVPCState { | ||||
|     BlockDriverState *hd; | ||||
|  | ||||
|     uint8_t footer_buf[HEADER_SIZE]; | ||||
|     uint64_t free_data_block_offset; | ||||
|     int max_table_entries; | ||||
| @@ -148,16 +150,20 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int vpc_open(BlockDriverState *bs, int flags) | ||||
| static int vpc_open(BlockDriverState *bs, const char *filename, int flags) | ||||
| { | ||||
|     BDRVVPCState *s = bs->opaque; | ||||
|     int i; | ||||
|     int ret, i; | ||||
|     struct vhd_footer* footer; | ||||
|     struct vhd_dyndisk_header* dyndisk_header; | ||||
|     uint8_t buf[HEADER_SIZE]; | ||||
|     uint32_t checksum; | ||||
|  | ||||
|     if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) | ||||
|     ret = bdrv_file_open(&s->hd, filename, flags); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|  | ||||
|     if (bdrv_pread(s->hd, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) | ||||
|         goto fail; | ||||
|  | ||||
|     footer = (struct vhd_footer*) s->footer_buf; | ||||
| @@ -168,7 +174,7 @@ static int vpc_open(BlockDriverState *bs, int flags) | ||||
|     footer->checksum = 0; | ||||
|     if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) | ||||
|         fprintf(stderr, "block-vpc: The header checksum of '%s' is " | ||||
|             "incorrect.\n", bs->filename); | ||||
|             "incorrect.\n", filename); | ||||
|  | ||||
|     // The visible size of a image in Virtual PC depends on the geometry | ||||
|     // rather than on the size stored in the footer (the size in the footer | ||||
| @@ -176,7 +182,7 @@ static int vpc_open(BlockDriverState *bs, int flags) | ||||
|     bs->total_sectors = (int64_t) | ||||
|         be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; | ||||
|  | ||||
|     if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) | ||||
|     if (bdrv_pread(s->hd, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) | ||||
|             != HEADER_SIZE) | ||||
|         goto fail; | ||||
|  | ||||
| @@ -193,7 +199,7 @@ static int vpc_open(BlockDriverState *bs, int flags) | ||||
|     s->pagetable = qemu_malloc(s->max_table_entries * 4); | ||||
|  | ||||
|     s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); | ||||
|     if (bdrv_pread(bs->file, s->bat_offset, s->pagetable, | ||||
|     if (bdrv_pread(s->hd, s->bat_offset, s->pagetable, | ||||
|             s->max_table_entries * 4) != s->max_table_entries * 4) | ||||
| 	    goto fail; | ||||
|  | ||||
| @@ -222,6 +228,7 @@ static int vpc_open(BlockDriverState *bs, int flags) | ||||
|  | ||||
|     return 0; | ||||
|  fail: | ||||
|     bdrv_delete(s->hd); | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| @@ -259,7 +266,7 @@ static inline int64_t get_sector_offset(BlockDriverState *bs, | ||||
|  | ||||
|         s->last_bitmap_offset = bitmap_offset; | ||||
|         memset(bitmap, 0xff, s->bitmap_size); | ||||
|         bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); | ||||
|         bdrv_pwrite_sync(s->hd, bitmap_offset, bitmap, s->bitmap_size); | ||||
|     } | ||||
|  | ||||
| //    printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", | ||||
| @@ -309,7 +316,7 @@ static int rewrite_footer(BlockDriverState* bs) | ||||
|     BDRVVPCState *s = bs->opaque; | ||||
|     int64_t offset = s->free_data_block_offset; | ||||
|  | ||||
|     ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); | ||||
|     ret = bdrv_pwrite_sync(s->hd, offset, s->footer_buf, HEADER_SIZE); | ||||
|     if (ret < 0) | ||||
|         return ret; | ||||
|  | ||||
| @@ -344,7 +351,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) | ||||
|  | ||||
|     // Initialize the block's bitmap | ||||
|     memset(bitmap, 0xff, s->bitmap_size); | ||||
|     bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, | ||||
|     bdrv_pwrite_sync(s->hd, s->free_data_block_offset, bitmap, | ||||
|         s->bitmap_size); | ||||
|  | ||||
|     // Write new footer (the old one will be overwritten) | ||||
| @@ -356,7 +363,7 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) | ||||
|     // Write BAT entry to disk | ||||
|     bat_offset = s->bat_offset + (4 * index); | ||||
|     bat_value = be32_to_cpu(s->pagetable[index]); | ||||
|     ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); | ||||
|     ret = bdrv_pwrite_sync(s->hd, bat_offset, &bat_value, 4); | ||||
|     if (ret < 0) | ||||
|         goto fail; | ||||
|  | ||||
| @@ -373,30 +380,21 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, | ||||
|     BDRVVPCState *s = bs->opaque; | ||||
|     int ret; | ||||
|     int64_t offset; | ||||
|     int64_t sectors, sectors_per_block; | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
|         offset = get_sector_offset(bs, sector_num, 0); | ||||
|  | ||||
|         sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; | ||||
|         sectors = sectors_per_block - (sector_num % sectors_per_block); | ||||
|         if (sectors > nb_sectors) { | ||||
|             sectors = nb_sectors; | ||||
|         } | ||||
|  | ||||
|         if (offset == -1) { | ||||
|             memset(buf, 0, sectors * BDRV_SECTOR_SIZE); | ||||
|             memset(buf, 0, 512); | ||||
|         } else { | ||||
|             ret = bdrv_pread(bs->file, offset, buf, | ||||
|                 sectors * BDRV_SECTOR_SIZE); | ||||
|             if (ret != sectors * BDRV_SECTOR_SIZE) { | ||||
|             ret = bdrv_pread(s->hd, offset, buf, 512); | ||||
|             if (ret != 512) | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         nb_sectors -= sectors; | ||||
|         sector_num += sectors; | ||||
|         buf += sectors * BDRV_SECTOR_SIZE; | ||||
|         nb_sectors--; | ||||
|         sector_num++; | ||||
|         buf += 512; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| @@ -406,41 +404,29 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num, | ||||
| { | ||||
|     BDRVVPCState *s = bs->opaque; | ||||
|     int64_t offset; | ||||
|     int64_t sectors, sectors_per_block; | ||||
|     int ret; | ||||
|  | ||||
|     while (nb_sectors > 0) { | ||||
|         offset = get_sector_offset(bs, sector_num, 1); | ||||
|  | ||||
|         sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; | ||||
|         sectors = sectors_per_block - (sector_num % sectors_per_block); | ||||
|         if (sectors > nb_sectors) { | ||||
|             sectors = nb_sectors; | ||||
|         } | ||||
|  | ||||
|         if (offset == -1) { | ||||
|             offset = alloc_block(bs, sector_num); | ||||
|             if (offset < 0) | ||||
|                 return -1; | ||||
|         } | ||||
|  | ||||
|         ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE); | ||||
|         if (ret != sectors * BDRV_SECTOR_SIZE) { | ||||
|         ret = bdrv_pwrite(s->hd, offset, buf, 512); | ||||
|         if (ret != 512) | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         nb_sectors -= sectors; | ||||
|         sector_num += sectors; | ||||
|         buf += sectors * BDRV_SECTOR_SIZE; | ||||
|         nb_sectors--; | ||||
|         sector_num++; | ||||
|         buf += 512; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int vpc_flush(BlockDriverState *bs) | ||||
| { | ||||
|     return bdrv_flush(bs->file); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Calculates the number of cylinders, heads and sectors per cylinder | ||||
| @@ -502,7 +488,6 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) | ||||
|     uint8_t secs_per_cyl = 0; | ||||
|     size_t block_size, num_bat_entries; | ||||
|     int64_t total_sectors = 0; | ||||
|     int ret = -EIO; | ||||
|  | ||||
|     // Read out options | ||||
|     while (options && options->name) { | ||||
| @@ -522,8 +507,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) | ||||
|     for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { | ||||
|         if (calculate_geometry(total_sectors + i, | ||||
|                                &cyls, &heads, &secs_per_cyl)) { | ||||
|             ret = -EFBIG; | ||||
|             goto fail; | ||||
|             return -EFBIG; | ||||
|         } | ||||
|     } | ||||
|     total_sectors = (int64_t) cyls * heads * secs_per_cyl; | ||||
| @@ -562,28 +546,22 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) | ||||
|     block_size = 0x200000; | ||||
|     num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); | ||||
|  | ||||
|     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { | ||||
|         goto fail; | ||||
|     } | ||||
|     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) | ||||
|         return -EIO; | ||||
|  | ||||
|     if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { | ||||
|         goto fail; | ||||
|     } | ||||
|     if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) | ||||
|         return -EIO; | ||||
|     if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) | ||||
|         return -EIO; | ||||
|  | ||||
|     // Write the initial BAT | ||||
|     if (lseek(fd, 3 * 512, SEEK_SET) < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     if (lseek(fd, 3 * 512, SEEK_SET) < 0) | ||||
|         return -EIO; | ||||
|  | ||||
|     memset(buf, 0xFF, 512); | ||||
|     for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) { | ||||
|         if (write(fd, buf, 512) != 512) { | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
|     for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) | ||||
|         if (write(fd, buf, 512) != 512) | ||||
|             return -EIO; | ||||
|  | ||||
|  | ||||
|     // Prepare the Dynamic Disk Header | ||||
| @@ -600,18 +578,13 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) | ||||
|     dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); | ||||
|  | ||||
|     // Write the header | ||||
|     if (lseek(fd, 512, SEEK_SET) < 0) { | ||||
|         goto fail; | ||||
|     } | ||||
|     if (lseek(fd, 512, SEEK_SET) < 0) | ||||
|         return -EIO; | ||||
|     if (write(fd, buf, 1024) != 1024) | ||||
|         return -EIO; | ||||
|  | ||||
|     if (write(fd, buf, 1024) != 1024) { | ||||
|         goto fail; | ||||
|     } | ||||
|     ret = 0; | ||||
|  | ||||
|  fail: | ||||
|     close(fd); | ||||
|     return ret; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void vpc_close(BlockDriverState *bs) | ||||
| @@ -621,6 +594,7 @@ static void vpc_close(BlockDriverState *bs) | ||||
| #ifdef CACHE | ||||
|     qemu_free(s->pageentry_u8); | ||||
| #endif | ||||
|     bdrv_delete(s->hd); | ||||
| } | ||||
|  | ||||
| static QEMUOptionParameter vpc_create_options[] = { | ||||
| @@ -633,15 +607,14 @@ static QEMUOptionParameter vpc_create_options[] = { | ||||
| }; | ||||
|  | ||||
| static BlockDriver bdrv_vpc = { | ||||
|     .format_name    = "vpc", | ||||
|     .instance_size  = sizeof(BDRVVPCState), | ||||
|     .bdrv_probe     = vpc_probe, | ||||
|     .bdrv_open      = vpc_open, | ||||
|     .bdrv_read      = vpc_read, | ||||
|     .bdrv_write     = vpc_write, | ||||
|     .bdrv_flush     = vpc_flush, | ||||
|     .bdrv_close     = vpc_close, | ||||
|     .bdrv_create    = vpc_create, | ||||
|     .format_name	= "vpc", | ||||
|     .instance_size	= sizeof(BDRVVPCState), | ||||
|     .bdrv_probe		= vpc_probe, | ||||
|     .bdrv_open		= vpc_open, | ||||
|     .bdrv_read		= vpc_read, | ||||
|     .bdrv_write		= vpc_write, | ||||
|     .bdrv_close		= vpc_close, | ||||
|     .bdrv_create	= vpc_create, | ||||
|  | ||||
|     .create_options = vpc_create_options, | ||||
| }; | ||||
|   | ||||
| @@ -512,7 +512,7 @@ static inline uint8_t fat_chksum(const direntry_t* entry) | ||||
|     for(i=0;i<11;i++) { | ||||
|         unsigned char c; | ||||
|  | ||||
|         c = (i < 8) ? entry->name[i] : entry->extension[i-8]; | ||||
|         c = (i <= 8) ? entry->name[i] : entry->extension[i-8]; | ||||
|         chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c; | ||||
|     } | ||||
|  | ||||
| @@ -756,7 +756,6 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) | ||||
|         if (st.st_size > 0x7fffffff) { | ||||
| 	    fprintf(stderr, "File %s is larger than 2GB\n", buffer); | ||||
| 	    free(buffer); | ||||
|             closedir(dir); | ||||
| 	    return -2; | ||||
|         } | ||||
| 	direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size); | ||||
| @@ -1100,8 +1099,8 @@ static inline void vvfat_close_current_file(BDRVVVFATState *s) | ||||
|  */ | ||||
| static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) | ||||
| { | ||||
|     int index3=index1+1; | ||||
|     while(1) { | ||||
|         int index3; | ||||
| 	mapping_t* mapping; | ||||
| 	index3=(index1+index2)/2; | ||||
| 	mapping=array_get(&(s->mapping),index3); | ||||
| @@ -1245,7 +1244,7 @@ static void print_direntry(const direntry_t* direntry) | ||||
|     int j = 0; | ||||
|     char buffer[1024]; | ||||
|  | ||||
|     fprintf(stderr, "direntry %p: ", direntry); | ||||
|     fprintf(stderr, "direntry 0x%x: ", (int)direntry); | ||||
|     if(!direntry) | ||||
| 	return; | ||||
|     if(is_long_name(direntry)) { | ||||
| @@ -1274,11 +1273,7 @@ static void print_direntry(const direntry_t* direntry) | ||||
|  | ||||
| static void print_mapping(const mapping_t* mapping) | ||||
| { | ||||
|     fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, " | ||||
|         "first_mapping_index = %d, name = %s, mode = 0x%x, " , | ||||
|         mapping, mapping->begin, mapping->end, mapping->dir_index, | ||||
|         mapping->first_mapping_index, mapping->path, mapping->mode); | ||||
|  | ||||
|     fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode); | ||||
|     if (mapping->mode & MODE_DIRECTORY) | ||||
| 	fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); | ||||
|     else | ||||
| @@ -1643,7 +1638,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, | ||||
| 	    /* new file */ | ||||
| 	    schedule_new_file(s, qemu_strdup(path), cluster_num); | ||||
| 	else { | ||||
|             abort(); | ||||
| 	    assert(0); | ||||
| 	    return 0; | ||||
| 	} | ||||
|     } | ||||
| @@ -1664,7 +1659,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, | ||||
| 		    if (offset != mapping->info.file.offset + s->cluster_size | ||||
| 			    * (cluster_num - mapping->begin)) { | ||||
| 			/* offset of this cluster in file chain has changed */ | ||||
|                         abort(); | ||||
| 			assert(0); | ||||
| 			copy_it = 1; | ||||
| 		    } else if (offset == 0) { | ||||
| 			const char* basename = get_basename(mapping->path); | ||||
| @@ -1676,7 +1671,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, | ||||
|  | ||||
| 		    if (mapping->first_mapping_index != first_mapping_index | ||||
| 			    && mapping->info.file.offset > 0) { | ||||
|                         abort(); | ||||
| 			assert(0); | ||||
| 			copy_it = 1; | ||||
| 		    } | ||||
|  | ||||
| @@ -1842,7 +1837,7 @@ DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i) | ||||
| 		    goto fail; | ||||
| 		} | ||||
| 	    } else | ||||
|                 abort(); /* cluster_count = 0; */ | ||||
| 		assert(0); /* cluster_count = 0; */ | ||||
|  | ||||
| 	    ret += cluster_count; | ||||
| 	} | ||||
| @@ -2283,6 +2278,7 @@ static void check1(BDRVVVFATState* s) | ||||
| 	    fprintf(stderr, "deleted\n"); | ||||
| 	    continue; | ||||
| 	} | ||||
| 	assert(mapping->dir_index >= 0); | ||||
| 	assert(mapping->dir_index < s->directory.next); | ||||
| 	direntry_t* direntry = array_get(&(s->directory), mapping->dir_index); | ||||
| 	assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0); | ||||
| @@ -2462,17 +2458,14 @@ static int handle_commits(BDRVVVFATState* s) | ||||
| 	commit_t* commit = array_get(&(s->commits), i); | ||||
| 	switch(commit->action) { | ||||
| 	case ACTION_RENAME: case ACTION_MKDIR: | ||||
|             abort(); | ||||
| 	    assert(0); | ||||
| 	    fail = -2; | ||||
| 	    break; | ||||
| 	case ACTION_WRITEOUT: { | ||||
| #ifndef NDEBUG | ||||
|             /* these variables are only used by assert() below */ | ||||
| 	    direntry_t* entry = array_get(&(s->directory), | ||||
| 		    commit->param.writeout.dir_index); | ||||
| 	    uint32_t begin = begin_of_direntry(entry); | ||||
| 	    mapping_t* mapping = find_mapping_for_cluster(s, begin); | ||||
| #endif | ||||
|  | ||||
| 	    assert(mapping); | ||||
| 	    assert(mapping->begin == begin); | ||||
| @@ -2523,7 +2516,7 @@ static int handle_commits(BDRVVVFATState* s) | ||||
| 	    break; | ||||
| 	} | ||||
| 	default: | ||||
|             abort(); | ||||
| 	    assert(0); | ||||
| 	} | ||||
|     } | ||||
|     if (i > 0 && array_remove_slice(&(s->commits), 0, i)) | ||||
| @@ -2611,7 +2604,7 @@ static int do_commit(BDRVVVFATState* s) | ||||
|     ret = handle_renames_and_mkdirs(s); | ||||
|     if (ret) { | ||||
| 	fprintf(stderr, "Error handling renames (%d)\n", ret); | ||||
|         abort(); | ||||
| 	assert(0); | ||||
| 	return ret; | ||||
|     } | ||||
|  | ||||
| @@ -2622,21 +2615,21 @@ static int do_commit(BDRVVVFATState* s) | ||||
|     ret = commit_direntries(s, 0, -1); | ||||
|     if (ret) { | ||||
| 	fprintf(stderr, "Fatal: error while committing (%d)\n", ret); | ||||
|         abort(); | ||||
| 	assert(0); | ||||
| 	return ret; | ||||
|     } | ||||
|  | ||||
|     ret = handle_commits(s); | ||||
|     if (ret) { | ||||
| 	fprintf(stderr, "Error handling commits (%d)\n", ret); | ||||
|         abort(); | ||||
| 	assert(0); | ||||
| 	return ret; | ||||
|     } | ||||
|  | ||||
|     ret = handle_deletes(s); | ||||
|     if (ret) { | ||||
| 	fprintf(stderr, "Error deleting\n"); | ||||
|         abort(); | ||||
|         assert(0); | ||||
| 	return ret; | ||||
|     } | ||||
|  | ||||
| @@ -2665,11 +2658,6 @@ static int vvfat_write(BlockDriverState *bs, int64_t sector_num, | ||||
|  | ||||
| DLOG(checkpoint()); | ||||
|  | ||||
|     /* Check if we're operating in read-only mode */ | ||||
|     if (s->qcow == NULL) { | ||||
|         return -EACCES; | ||||
|     } | ||||
|  | ||||
|     vvfat_close_current_file(s); | ||||
|  | ||||
|     /* | ||||
| @@ -2768,12 +2756,12 @@ static int vvfat_is_allocated(BlockDriverState *bs, | ||||
|  | ||||
| static int write_target_commit(BlockDriverState *bs, int64_t sector_num, | ||||
| 	const uint8_t* buffer, int nb_sectors) { | ||||
|     BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); | ||||
|     BDRVVVFATState* s = bs->opaque; | ||||
|     return try_commit(s); | ||||
| } | ||||
|  | ||||
| static void write_target_close(BlockDriverState *bs) { | ||||
|     BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); | ||||
|     BDRVVVFATState* s = bs->opaque; | ||||
|     bdrv_delete(s->qcow); | ||||
|     free(s->qcow_filename); | ||||
| } | ||||
| @@ -2788,7 +2776,6 @@ static int enable_write_target(BDRVVVFATState *s) | ||||
| { | ||||
|     BlockDriver *bdrv_qcow; | ||||
|     QEMUOptionParameter *options; | ||||
|     int ret; | ||||
|     int size = sector2cluster(s, s->sector_count); | ||||
|     s->used_clusters = calloc(size, 1); | ||||
|  | ||||
| @@ -2804,17 +2791,9 @@ static int enable_write_target(BDRVVVFATState *s) | ||||
|  | ||||
|     if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0) | ||||
| 	return -1; | ||||
|  | ||||
|     s->qcow = bdrv_new(""); | ||||
|     if (s->qcow == NULL) { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_open(s->qcow, s->qcow_filename, | ||||
|             BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow); | ||||
|     if (ret < 0) { | ||||
| 	return ret; | ||||
|     } | ||||
|     if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0) | ||||
| 	return -1; | ||||
|  | ||||
| #ifndef _WIN32 | ||||
|     unlink(s->qcow_filename); | ||||
| @@ -2822,8 +2801,7 @@ static int enable_write_target(BDRVVVFATState *s) | ||||
|  | ||||
|     s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1); | ||||
|     s->bs->backing_hd->drv = &vvfat_write_target; | ||||
|     s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*)); | ||||
|     *(void**)s->bs->backing_hd->opaque = s; | ||||
|     s->bs->backing_hd->opaque = s; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -2843,7 +2821,7 @@ static void vvfat_close(BlockDriverState *bs) | ||||
| static BlockDriver bdrv_vvfat = { | ||||
|     .format_name	= "vvfat", | ||||
|     .instance_size	= sizeof(BDRVVVFATState), | ||||
|     .bdrv_file_open	= vvfat_open, | ||||
|     .bdrv_open		= vvfat_open, | ||||
|     .bdrv_read		= vvfat_read, | ||||
|     .bdrv_write		= vvfat_write, | ||||
|     .bdrv_close		= vvfat_close, | ||||
| @@ -2881,7 +2859,7 @@ static void checkpoint(void) { | ||||
|     return; | ||||
|     /* avoid compiler warnings: */ | ||||
|     hexdump(NULL, 100); | ||||
|     remove_mapping(vvv, 0); | ||||
|     remove_mapping(vvv, NULL); | ||||
|     print_mapping(NULL); | ||||
|     print_direntry(NULL); | ||||
| } | ||||
|   | ||||
							
								
								
									
										87
									
								
								block_int.h
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								block_int.h
									
									
									
									
									
								
							| @@ -26,9 +26,9 @@ | ||||
|  | ||||
| #include "block.h" | ||||
| #include "qemu-option.h" | ||||
| #include "qemu-queue.h" | ||||
|  | ||||
| #define BLOCK_FLAG_ENCRYPT	1 | ||||
| #define BLOCK_FLAG_COMPRESS	2 | ||||
| #define BLOCK_FLAG_COMPAT6	4 | ||||
|  | ||||
| #define BLOCK_OPT_SIZE          "size" | ||||
| @@ -37,7 +37,6 @@ | ||||
| #define BLOCK_OPT_BACKING_FILE  "backing_file" | ||||
| #define BLOCK_OPT_BACKING_FMT   "backing_fmt" | ||||
| #define BLOCK_OPT_CLUSTER_SIZE  "cluster_size" | ||||
| #define BLOCK_OPT_TABLE_SIZE    "table_size" | ||||
| #define BLOCK_OPT_PREALLOC      "preallocation" | ||||
|  | ||||
| typedef struct AIOPool { | ||||
| @@ -51,15 +50,14 @@ struct BlockDriver { | ||||
|     int instance_size; | ||||
|     int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); | ||||
|     int (*bdrv_probe_device)(const char *filename); | ||||
|     int (*bdrv_open)(BlockDriverState *bs, int flags); | ||||
|     int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags); | ||||
|     int (*bdrv_open)(BlockDriverState *bs, const char *filename, int flags); | ||||
|     int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, | ||||
|                      uint8_t *buf, int nb_sectors); | ||||
|     int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, | ||||
|                       const uint8_t *buf, int nb_sectors); | ||||
|     void (*bdrv_close)(BlockDriverState *bs); | ||||
|     int (*bdrv_create)(const char *filename, QEMUOptionParameter *options); | ||||
|     int (*bdrv_flush)(BlockDriverState *bs); | ||||
|     void (*bdrv_flush)(BlockDriverState *bs); | ||||
|     int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num, | ||||
|                              int nb_sectors, int *pnum); | ||||
|     int (*bdrv_set_key)(BlockDriverState *bs, const char *key); | ||||
| @@ -73,8 +71,6 @@ struct BlockDriver { | ||||
|         BlockDriverCompletionFunc *cb, void *opaque); | ||||
|     BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, | ||||
|         BlockDriverCompletionFunc *cb, void *opaque); | ||||
|     int (*bdrv_discard)(BlockDriverState *bs, int64_t sector_num, | ||||
|                         int nb_sectors); | ||||
|  | ||||
|     int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs, | ||||
|         int num_reqs); | ||||
| @@ -95,8 +91,6 @@ struct BlockDriver { | ||||
|     int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id); | ||||
|     int (*bdrv_snapshot_list)(BlockDriverState *bs, | ||||
|                               QEMUSnapshotInfo **psn_info); | ||||
|     int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, | ||||
|                                   const char *snapshot_name); | ||||
|     int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); | ||||
|  | ||||
|     int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf, | ||||
| @@ -104,9 +98,6 @@ struct BlockDriver { | ||||
|     int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf, | ||||
|                              int64_t pos, int size); | ||||
|  | ||||
|     int (*bdrv_change_backing_file)(BlockDriverState *bs, | ||||
|         const char *backing_file, const char *backing_fmt); | ||||
|  | ||||
|     /* removable device specific */ | ||||
|     int (*bdrv_is_inserted)(BlockDriverState *bs); | ||||
|     int (*bdrv_media_changed)(BlockDriverState *bs); | ||||
| @@ -123,44 +114,32 @@ struct BlockDriver { | ||||
|     QEMUOptionParameter *create_options; | ||||
|  | ||||
|  | ||||
|     /* | ||||
|      * Returns 0 for completed check, -errno for internal errors. | ||||
|      * The check results are stored in result. | ||||
|      */ | ||||
|     int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result); | ||||
|     /* Returns number of errors in image, -errno for internal errors */ | ||||
|     int (*bdrv_check)(BlockDriverState* bs); | ||||
|  | ||||
|     void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); | ||||
|     /* Set if newly created images are not guaranteed to contain only zeros */ | ||||
|     int no_zero_init; | ||||
|  | ||||
|     /* | ||||
|      * Returns 1 if newly created images are guaranteed to contain only | ||||
|      * zeros, 0 otherwise. | ||||
|      */ | ||||
|     int (*bdrv_has_zero_init)(BlockDriverState *bs); | ||||
|  | ||||
|     QLIST_ENTRY(BlockDriver) list; | ||||
|     struct BlockDriver *next; | ||||
| }; | ||||
|  | ||||
| struct BlockDriverState { | ||||
|     int64_t total_sectors; /* if we are reading a disk image, give its | ||||
|                               size in sectors */ | ||||
|     int read_only; /* if true, the media is read only */ | ||||
|     int keep_read_only; /* if true, the media was requested to stay read only */ | ||||
|     int open_flags; /* flags used to open the file, re-used for re-open */ | ||||
|     int removable; /* if true, the media can be removed */ | ||||
|     int locked;    /* if true, the media cannot temporarily be ejected */ | ||||
|     int tray_open; /* if true, the virtual tray is open */ | ||||
|     int encrypted; /* if true, the media is encrypted */ | ||||
|     int valid_key; /* if true, a valid encryption key has been set */ | ||||
|     int sg;        /* if true, the device is a /dev/sg* */ | ||||
|     /* event callback when inserting/removing */ | ||||
|     void (*change_cb)(void *opaque, int reason); | ||||
|     void (*change_cb)(void *opaque); | ||||
|     void *change_opaque; | ||||
|  | ||||
|     BlockDriver *drv; /* NULL means no media */ | ||||
|     void *opaque; | ||||
|  | ||||
|     DeviceState *peer; | ||||
|  | ||||
|     char filename[1024]; | ||||
|     char backing_file[1024]; /* if non zero, the image is a diff of | ||||
|                                 this file image */ | ||||
| @@ -169,8 +148,6 @@ struct BlockDriverState { | ||||
|     int media_changed; | ||||
|  | ||||
|     BlockDriverState *backing_hd; | ||||
|     BlockDriverState *file; | ||||
|  | ||||
|     /* async read/write emulation */ | ||||
|  | ||||
|     void *sync_aiocb; | ||||
| @@ -180,7 +157,6 @@ struct BlockDriverState { | ||||
|     uint64_t wr_bytes; | ||||
|     uint64_t rd_ops; | ||||
|     uint64_t wr_ops; | ||||
|     uint64_t wr_highest_sector; | ||||
|  | ||||
|     /* Whether the disk can expand beyond total_sectors */ | ||||
|     int growable; | ||||
| @@ -195,18 +171,12 @@ struct BlockDriverState { | ||||
|        drivers. They are not used by the block driver */ | ||||
|     int cyls, heads, secs, translation; | ||||
|     int type; | ||||
|     BlockErrorAction on_read_error, on_write_error; | ||||
|     char device_name[32]; | ||||
|     unsigned long *dirty_bitmap; | ||||
|     int64_t dirty_count; | ||||
|     int in_use; /* users other than guest access, eg. block migration */ | ||||
|     QTAILQ_ENTRY(BlockDriverState) list; | ||||
|     BlockDriverState *next; | ||||
|     void *private; | ||||
| }; | ||||
|  | ||||
| #define CHANGE_MEDIA	0x01 | ||||
| #define CHANGE_SIZE	0x02 | ||||
|  | ||||
| struct BlockDriverAIOCB { | ||||
|     AIOPool *pool; | ||||
|     BlockDriverState *bs; | ||||
| @@ -223,43 +193,10 @@ void qemu_aio_release(void *p); | ||||
|  | ||||
| void *qemu_blockalign(BlockDriverState *bs, size_t size); | ||||
|  | ||||
| extern BlockDriverState *bdrv_first; | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| int is_windows_drive(const char *filename); | ||||
| #endif | ||||
|  | ||||
| typedef struct BlockConf { | ||||
|     BlockDriverState *bs; | ||||
|     uint16_t physical_block_size; | ||||
|     uint16_t logical_block_size; | ||||
|     uint16_t min_io_size; | ||||
|     uint32_t opt_io_size; | ||||
|     int32_t bootindex; | ||||
|     uint32_t discard_granularity; | ||||
| } BlockConf; | ||||
|  | ||||
| static inline unsigned int get_physical_block_exp(BlockConf *conf) | ||||
| { | ||||
|     unsigned int exp = 0, size; | ||||
|  | ||||
|     for (size = conf->physical_block_size; | ||||
|         size > conf->logical_block_size; | ||||
|         size >>= 1) { | ||||
|         exp++; | ||||
|     } | ||||
|  | ||||
|     return exp; | ||||
| } | ||||
|  | ||||
| #define DEFINE_BLOCK_PROPERTIES(_state, _conf)                          \ | ||||
|     DEFINE_PROP_DRIVE("drive", _state, _conf.bs),                       \ | ||||
|     DEFINE_PROP_UINT16("logical_block_size", _state,                    \ | ||||
|                        _conf.logical_block_size, 512),                  \ | ||||
|     DEFINE_PROP_UINT16("physical_block_size", _state,                   \ | ||||
|                        _conf.physical_block_size, 512),                 \ | ||||
|     DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0),  \ | ||||
|     DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0),    \ | ||||
|     DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1),        \ | ||||
|     DEFINE_PROP_UINT32("discard_granularity", _state, \ | ||||
|                        _conf.discard_granularity, 0) | ||||
|  | ||||
| #endif /* BLOCK_INT_H */ | ||||
|   | ||||
							
								
								
									
										787
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										787
									
								
								blockdev.c
									
									
									
									
									
								
							| @@ -1,787 +0,0 @@ | ||||
| /* | ||||
|  * QEMU host block devices | ||||
|  * | ||||
|  * Copyright (c) 2003-2008 Fabrice Bellard | ||||
|  * | ||||
|  * 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 "block.h" | ||||
| #include "blockdev.h" | ||||
| #include "monitor.h" | ||||
| #include "qerror.h" | ||||
| #include "qemu-option.h" | ||||
| #include "qemu-config.h" | ||||
| #include "sysemu.h" | ||||
| #include "hw/qdev.h" | ||||
| #include "block_int.h" | ||||
|  | ||||
| static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); | ||||
|  | ||||
| static const char *const if_name[IF_COUNT] = { | ||||
|     [IF_NONE] = "none", | ||||
|     [IF_IDE] = "ide", | ||||
|     [IF_SCSI] = "scsi", | ||||
|     [IF_FLOPPY] = "floppy", | ||||
|     [IF_PFLASH] = "pflash", | ||||
|     [IF_MTD] = "mtd", | ||||
|     [IF_SD] = "sd", | ||||
|     [IF_VIRTIO] = "virtio", | ||||
|     [IF_XEN] = "xen", | ||||
| }; | ||||
|  | ||||
| static const int if_max_devs[IF_COUNT] = { | ||||
|     /* | ||||
|      * Do not change these numbers!  They govern how drive option | ||||
|      * index maps to unit and bus.  That mapping is ABI. | ||||
|      * | ||||
|      * All controllers used to imlement if=T drives need to support | ||||
|      * if_max_devs[T] units, for any T with if_max_devs[T] != 0. | ||||
|      * Otherwise, some index values map to "impossible" bus, unit | ||||
|      * values. | ||||
|      * | ||||
|      * For instance, if you change [IF_SCSI] to 255, -drive | ||||
|      * if=scsi,index=12 no longer means bus=1,unit=5, but | ||||
|      * bus=0,unit=12.  With an lsi53c895a controller (7 units max), | ||||
|      * the drive can't be set up.  Regression. | ||||
|      */ | ||||
|     [IF_IDE] = 2, | ||||
|     [IF_SCSI] = 7, | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * We automatically delete the drive when a device using it gets | ||||
|  * unplugged.  Questionable feature, but we can't just drop it. | ||||
|  * Device models call blockdev_mark_auto_del() to schedule the | ||||
|  * automatic deletion, and generic qdev code calls blockdev_auto_del() | ||||
|  * when deletion is actually safe. | ||||
|  */ | ||||
| void blockdev_mark_auto_del(BlockDriverState *bs) | ||||
| { | ||||
|     DriveInfo *dinfo = drive_get_by_blockdev(bs); | ||||
|  | ||||
|     if (dinfo) { | ||||
|         dinfo->auto_del = 1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void blockdev_auto_del(BlockDriverState *bs) | ||||
| { | ||||
|     DriveInfo *dinfo = drive_get_by_blockdev(bs); | ||||
|  | ||||
|     if (dinfo && dinfo->auto_del) { | ||||
|         drive_put_ref(dinfo); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int drive_index_to_bus_id(BlockInterfaceType type, int index) | ||||
| { | ||||
|     int max_devs = if_max_devs[type]; | ||||
|     return max_devs ? index / max_devs : 0; | ||||
| } | ||||
|  | ||||
| static int drive_index_to_unit_id(BlockInterfaceType type, int index) | ||||
| { | ||||
|     int max_devs = if_max_devs[type]; | ||||
|     return max_devs ? index % max_devs : index; | ||||
| } | ||||
|  | ||||
| QemuOpts *drive_def(const char *optstr) | ||||
| { | ||||
|     return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0); | ||||
| } | ||||
|  | ||||
| QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, | ||||
|                     const char *optstr) | ||||
| { | ||||
|     QemuOpts *opts; | ||||
|     char buf[32]; | ||||
|  | ||||
|     opts = drive_def(optstr); | ||||
|     if (!opts) { | ||||
|         return NULL; | ||||
|     } | ||||
|     if (type != IF_DEFAULT) { | ||||
|         qemu_opt_set(opts, "if", if_name[type]); | ||||
|     } | ||||
|     if (index >= 0) { | ||||
|         snprintf(buf, sizeof(buf), "%d", index); | ||||
|         qemu_opt_set(opts, "index", buf); | ||||
|     } | ||||
|     if (file) | ||||
|         qemu_opt_set(opts, "file", file); | ||||
|     return opts; | ||||
| } | ||||
|  | ||||
| DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) | ||||
| { | ||||
|     DriveInfo *dinfo; | ||||
|  | ||||
|     /* seek interface, bus and unit */ | ||||
|  | ||||
|     QTAILQ_FOREACH(dinfo, &drives, next) { | ||||
|         if (dinfo->type == type && | ||||
| 	    dinfo->bus == bus && | ||||
| 	    dinfo->unit == unit) | ||||
|             return dinfo; | ||||
|     } | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) | ||||
| { | ||||
|     return drive_get(type, | ||||
|                      drive_index_to_bus_id(type, index), | ||||
|                      drive_index_to_unit_id(type, index)); | ||||
| } | ||||
|  | ||||
| int drive_get_max_bus(BlockInterfaceType type) | ||||
| { | ||||
|     int max_bus; | ||||
|     DriveInfo *dinfo; | ||||
|  | ||||
|     max_bus = -1; | ||||
|     QTAILQ_FOREACH(dinfo, &drives, next) { | ||||
|         if(dinfo->type == type && | ||||
|            dinfo->bus > max_bus) | ||||
|             max_bus = dinfo->bus; | ||||
|     } | ||||
|     return max_bus; | ||||
| } | ||||
|  | ||||
| /* Get a block device.  This should only be used for single-drive devices | ||||
|    (e.g. SD/Floppy/MTD).  Multi-disk devices (scsi/ide) should use the | ||||
|    appropriate bus.  */ | ||||
| DriveInfo *drive_get_next(BlockInterfaceType type) | ||||
| { | ||||
|     static int next_block_unit[IF_COUNT]; | ||||
|  | ||||
|     return drive_get(type, 0, next_block_unit[type]++); | ||||
| } | ||||
|  | ||||
| DriveInfo *drive_get_by_blockdev(BlockDriverState *bs) | ||||
| { | ||||
|     DriveInfo *dinfo; | ||||
|  | ||||
|     QTAILQ_FOREACH(dinfo, &drives, next) { | ||||
|         if (dinfo->bdrv == bs) { | ||||
|             return dinfo; | ||||
|         } | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static void bdrv_format_print(void *opaque, const char *name) | ||||
| { | ||||
|     error_printf(" %s", name); | ||||
| } | ||||
|  | ||||
| static void drive_uninit(DriveInfo *dinfo) | ||||
| { | ||||
|     qemu_opts_del(dinfo->opts); | ||||
|     bdrv_delete(dinfo->bdrv); | ||||
|     qemu_free(dinfo->id); | ||||
|     QTAILQ_REMOVE(&drives, dinfo, next); | ||||
|     qemu_free(dinfo); | ||||
| } | ||||
|  | ||||
| void drive_put_ref(DriveInfo *dinfo) | ||||
| { | ||||
|     assert(dinfo->refcount); | ||||
|     if (--dinfo->refcount == 0) { | ||||
|         drive_uninit(dinfo); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void drive_get_ref(DriveInfo *dinfo) | ||||
| { | ||||
|     dinfo->refcount++; | ||||
| } | ||||
|  | ||||
| static int parse_block_error_action(const char *buf, int is_read) | ||||
| { | ||||
|     if (!strcmp(buf, "ignore")) { | ||||
|         return BLOCK_ERR_IGNORE; | ||||
|     } else if (!is_read && !strcmp(buf, "enospc")) { | ||||
|         return BLOCK_ERR_STOP_ENOSPC; | ||||
|     } else if (!strcmp(buf, "stop")) { | ||||
|         return BLOCK_ERR_STOP_ANY; | ||||
|     } else if (!strcmp(buf, "report")) { | ||||
|         return BLOCK_ERR_REPORT; | ||||
|     } else { | ||||
|         error_report("'%s' invalid %s error action", | ||||
|                      buf, is_read ? "read" : "write"); | ||||
|         return -1; | ||||
|     } | ||||
| } | ||||
|  | ||||
| DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) | ||||
| { | ||||
|     const char *buf; | ||||
|     const char *file = NULL; | ||||
|     char devname[128]; | ||||
|     const char *serial; | ||||
|     const char *mediastr = ""; | ||||
|     BlockInterfaceType type; | ||||
|     enum { MEDIA_DISK, MEDIA_CDROM } media; | ||||
|     int bus_id, unit_id; | ||||
|     int cyls, heads, secs, translation; | ||||
|     BlockDriver *drv = NULL; | ||||
|     int max_devs; | ||||
|     int index; | ||||
|     int ro = 0; | ||||
|     int bdrv_flags = 0; | ||||
|     int on_read_error, on_write_error; | ||||
|     const char *devaddr; | ||||
|     DriveInfo *dinfo; | ||||
|     int snapshot = 0; | ||||
|     int ret; | ||||
|  | ||||
|     translation = BIOS_ATA_TRANSLATION_AUTO; | ||||
|  | ||||
|     if (default_to_scsi) { | ||||
|         type = IF_SCSI; | ||||
|         pstrcpy(devname, sizeof(devname), "scsi"); | ||||
|     } else { | ||||
|         type = IF_IDE; | ||||
|         pstrcpy(devname, sizeof(devname), "ide"); | ||||
|     } | ||||
|     media = MEDIA_DISK; | ||||
|  | ||||
|     /* extract parameters */ | ||||
|     bus_id  = qemu_opt_get_number(opts, "bus", 0); | ||||
|     unit_id = qemu_opt_get_number(opts, "unit", -1); | ||||
|     index   = qemu_opt_get_number(opts, "index", -1); | ||||
|  | ||||
|     cyls  = qemu_opt_get_number(opts, "cyls", 0); | ||||
|     heads = qemu_opt_get_number(opts, "heads", 0); | ||||
|     secs  = qemu_opt_get_number(opts, "secs", 0); | ||||
|  | ||||
|     snapshot = qemu_opt_get_bool(opts, "snapshot", 0); | ||||
|     ro = qemu_opt_get_bool(opts, "readonly", 0); | ||||
|  | ||||
|     file = qemu_opt_get(opts, "file"); | ||||
|     serial = qemu_opt_get(opts, "serial"); | ||||
|  | ||||
|     if ((buf = qemu_opt_get(opts, "if")) != NULL) { | ||||
|         pstrcpy(devname, sizeof(devname), buf); | ||||
|         for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++) | ||||
|             ; | ||||
|         if (type == IF_COUNT) { | ||||
|             error_report("unsupported bus type '%s'", buf); | ||||
|             return NULL; | ||||
| 	} | ||||
|     } | ||||
|     max_devs = if_max_devs[type]; | ||||
|  | ||||
|     if (cyls || heads || secs) { | ||||
|         if (cyls < 1 || (type == IF_IDE && cyls > 16383)) { | ||||
|             error_report("invalid physical cyls number"); | ||||
| 	    return NULL; | ||||
| 	} | ||||
|         if (heads < 1 || (type == IF_IDE && heads > 16)) { | ||||
|             error_report("invalid physical heads number"); | ||||
| 	    return NULL; | ||||
| 	} | ||||
|         if (secs < 1 || (type == IF_IDE && secs > 63)) { | ||||
|             error_report("invalid physical secs number"); | ||||
| 	    return NULL; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     if ((buf = qemu_opt_get(opts, "trans")) != NULL) { | ||||
|         if (!cyls) { | ||||
|             error_report("'%s' trans must be used with cyls,heads and secs", | ||||
|                          buf); | ||||
|             return NULL; | ||||
|         } | ||||
|         if (!strcmp(buf, "none")) | ||||
|             translation = BIOS_ATA_TRANSLATION_NONE; | ||||
|         else if (!strcmp(buf, "lba")) | ||||
|             translation = BIOS_ATA_TRANSLATION_LBA; | ||||
|         else if (!strcmp(buf, "auto")) | ||||
|             translation = BIOS_ATA_TRANSLATION_AUTO; | ||||
| 	else { | ||||
|             error_report("'%s' invalid translation type", buf); | ||||
| 	    return NULL; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     if ((buf = qemu_opt_get(opts, "media")) != NULL) { | ||||
|         if (!strcmp(buf, "disk")) { | ||||
| 	    media = MEDIA_DISK; | ||||
| 	} else if (!strcmp(buf, "cdrom")) { | ||||
|             if (cyls || secs || heads) { | ||||
|                 error_report("'%s' invalid physical CHS format", buf); | ||||
| 	        return NULL; | ||||
|             } | ||||
| 	    media = MEDIA_CDROM; | ||||
| 	} else { | ||||
| 	    error_report("'%s' invalid media", buf); | ||||
| 	    return NULL; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     if ((buf = qemu_opt_get(opts, "cache")) != NULL) { | ||||
|         if (!strcmp(buf, "off") || !strcmp(buf, "none")) { | ||||
|             bdrv_flags |= BDRV_O_NOCACHE; | ||||
|         } else if (!strcmp(buf, "writeback")) { | ||||
|             bdrv_flags |= BDRV_O_CACHE_WB; | ||||
|         } else if (!strcmp(buf, "unsafe")) { | ||||
|             bdrv_flags |= BDRV_O_CACHE_WB; | ||||
|             bdrv_flags |= BDRV_O_NO_FLUSH; | ||||
|         } else if (!strcmp(buf, "writethrough")) { | ||||
|             /* this is the default */ | ||||
|         } else { | ||||
|            error_report("invalid cache option"); | ||||
|            return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #ifdef CONFIG_LINUX_AIO | ||||
|     if ((buf = qemu_opt_get(opts, "aio")) != NULL) { | ||||
|         if (!strcmp(buf, "native")) { | ||||
|             bdrv_flags |= BDRV_O_NATIVE_AIO; | ||||
|         } else if (!strcmp(buf, "threads")) { | ||||
|             /* this is the default */ | ||||
|         } else { | ||||
|            error_report("invalid aio option"); | ||||
|            return NULL; | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     if ((buf = qemu_opt_get(opts, "format")) != NULL) { | ||||
|        if (strcmp(buf, "?") == 0) { | ||||
|            error_printf("Supported formats:"); | ||||
|            bdrv_iterate_format(bdrv_format_print, NULL); | ||||
|            error_printf("\n"); | ||||
|            return NULL; | ||||
|         } | ||||
|         drv = bdrv_find_whitelisted_format(buf); | ||||
|         if (!drv) { | ||||
|             error_report("'%s' invalid format", buf); | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     on_write_error = BLOCK_ERR_STOP_ENOSPC; | ||||
|     if ((buf = qemu_opt_get(opts, "werror")) != NULL) { | ||||
|         if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { | ||||
|             error_report("werror is not supported by this bus type"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         on_write_error = parse_block_error_action(buf, 0); | ||||
|         if (on_write_error < 0) { | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     on_read_error = BLOCK_ERR_REPORT; | ||||
|     if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { | ||||
|         if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { | ||||
|             error_report("rerror is not supported by this bus type"); | ||||
|             return NULL; | ||||
|         } | ||||
|  | ||||
|         on_read_error = parse_block_error_action(buf, 1); | ||||
|         if (on_read_error < 0) { | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) { | ||||
|         if (type != IF_VIRTIO) { | ||||
|             error_report("addr is not supported by this bus type"); | ||||
|             return NULL; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* compute bus and unit according index */ | ||||
|  | ||||
|     if (index != -1) { | ||||
|         if (bus_id != 0 || unit_id != -1) { | ||||
|             error_report("index cannot be used with bus and unit"); | ||||
|             return NULL; | ||||
|         } | ||||
|         bus_id = drive_index_to_bus_id(type, index); | ||||
|         unit_id = drive_index_to_unit_id(type, index); | ||||
|     } | ||||
|  | ||||
|     /* if user doesn't specify a unit_id, | ||||
|      * try to find the first free | ||||
|      */ | ||||
|  | ||||
|     if (unit_id == -1) { | ||||
|        unit_id = 0; | ||||
|        while (drive_get(type, bus_id, unit_id) != NULL) { | ||||
|            unit_id++; | ||||
|            if (max_devs && unit_id >= max_devs) { | ||||
|                unit_id -= max_devs; | ||||
|                bus_id++; | ||||
|            } | ||||
|        } | ||||
|     } | ||||
|  | ||||
|     /* check unit id */ | ||||
|  | ||||
|     if (max_devs && unit_id >= max_devs) { | ||||
|         error_report("unit %d too big (max is %d)", | ||||
|                      unit_id, max_devs - 1); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * catch multiple definitions | ||||
|      */ | ||||
|  | ||||
|     if (drive_get(type, bus_id, unit_id) != NULL) { | ||||
|         error_report("drive with bus=%d, unit=%d (index=%d) exists", | ||||
|                      bus_id, unit_id, index); | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     /* init */ | ||||
|  | ||||
|     dinfo = qemu_mallocz(sizeof(*dinfo)); | ||||
|     if ((buf = qemu_opts_id(opts)) != NULL) { | ||||
|         dinfo->id = qemu_strdup(buf); | ||||
|     } else { | ||||
|         /* no id supplied -> create one */ | ||||
|         dinfo->id = qemu_mallocz(32); | ||||
|         if (type == IF_IDE || type == IF_SCSI) | ||||
|             mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd"; | ||||
|         if (max_devs) | ||||
|             snprintf(dinfo->id, 32, "%s%i%s%i", | ||||
|                      devname, bus_id, mediastr, unit_id); | ||||
|         else | ||||
|             snprintf(dinfo->id, 32, "%s%s%i", | ||||
|                      devname, mediastr, unit_id); | ||||
|     } | ||||
|     dinfo->bdrv = bdrv_new(dinfo->id); | ||||
|     dinfo->devaddr = devaddr; | ||||
|     dinfo->type = type; | ||||
|     dinfo->bus = bus_id; | ||||
|     dinfo->unit = unit_id; | ||||
|     dinfo->opts = opts; | ||||
|     dinfo->refcount = 1; | ||||
|     if (serial) | ||||
|         strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1); | ||||
|     QTAILQ_INSERT_TAIL(&drives, dinfo, next); | ||||
|  | ||||
|     bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); | ||||
|  | ||||
|     switch(type) { | ||||
|     case IF_IDE: | ||||
|     case IF_SCSI: | ||||
|     case IF_XEN: | ||||
|     case IF_NONE: | ||||
|         switch(media) { | ||||
| 	case MEDIA_DISK: | ||||
|             if (cyls != 0) { | ||||
|                 bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs); | ||||
|                 bdrv_set_translation_hint(dinfo->bdrv, translation); | ||||
|             } | ||||
| 	    break; | ||||
| 	case MEDIA_CDROM: | ||||
|             bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM); | ||||
| 	    break; | ||||
| 	} | ||||
|         break; | ||||
|     case IF_SD: | ||||
|         /* FIXME: This isn't really a floppy, but it's a reasonable | ||||
|            approximation.  */ | ||||
|     case IF_FLOPPY: | ||||
|         bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_FLOPPY); | ||||
|         break; | ||||
|     case IF_PFLASH: | ||||
|     case IF_MTD: | ||||
|         break; | ||||
|     case IF_VIRTIO: | ||||
|         /* add virtio block device */ | ||||
|         opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); | ||||
|         qemu_opt_set(opts, "driver", "virtio-blk-pci"); | ||||
|         qemu_opt_set(opts, "drive", dinfo->id); | ||||
|         if (devaddr) | ||||
|             qemu_opt_set(opts, "addr", devaddr); | ||||
|         break; | ||||
|     default: | ||||
|         abort(); | ||||
|     } | ||||
|     if (!file || !*file) { | ||||
|         return dinfo; | ||||
|     } | ||||
|     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); | ||||
|     } | ||||
|  | ||||
|     if (media == MEDIA_CDROM) { | ||||
|         /* CDROM is fine for any interface, don't check.  */ | ||||
|         ro = 1; | ||||
|     } else if (ro == 1) { | ||||
|         if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) { | ||||
|             error_report("readonly not supported by this bus type"); | ||||
|             goto err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bdrv_flags |= ro ? 0 : BDRV_O_RDWR; | ||||
|  | ||||
|     ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv); | ||||
|     if (ret < 0) { | ||||
|         error_report("could not open disk image %s: %s", | ||||
|                      file, strerror(-ret)); | ||||
|         goto err; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_key_required(dinfo->bdrv)) | ||||
|         autostart = 0; | ||||
|     return dinfo; | ||||
|  | ||||
| err: | ||||
|     bdrv_delete(dinfo->bdrv); | ||||
|     qemu_free(dinfo->id); | ||||
|     QTAILQ_REMOVE(&drives, dinfo, next); | ||||
|     qemu_free(dinfo); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void do_commit(Monitor *mon, const QDict *qdict) | ||||
| { | ||||
|     const char *device = qdict_get_str(qdict, "device"); | ||||
|     BlockDriverState *bs; | ||||
|  | ||||
|     if (!strcmp(device, "all")) { | ||||
|         bdrv_commit_all(); | ||||
|     } else { | ||||
|         bs = bdrv_find(device); | ||||
|         if (!bs) { | ||||
|             qerror_report(QERR_DEVICE_NOT_FOUND, device); | ||||
|             return; | ||||
|         } | ||||
|         bdrv_commit(bs); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) | ||||
| { | ||||
|     const char *device = qdict_get_str(qdict, "device"); | ||||
|     const char *filename = qdict_get_try_str(qdict, "snapshot_file"); | ||||
|     const char *format = qdict_get_try_str(qdict, "format"); | ||||
|     BlockDriverState *bs; | ||||
|     BlockDriver *drv, *proto_drv; | ||||
|     int ret = 0; | ||||
|     int flags; | ||||
|  | ||||
|     if (!filename) { | ||||
|         qerror_report(QERR_MISSING_PARAMETER, "snapshot_file"); | ||||
|         ret = -1; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     bs = bdrv_find(device); | ||||
|     if (!bs) { | ||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, device); | ||||
|         ret = -1; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     if (!format) { | ||||
|         format = "qcow2"; | ||||
|     } | ||||
|  | ||||
|     drv = bdrv_find_format(format); | ||||
|     if (!drv) { | ||||
|         qerror_report(QERR_INVALID_BLOCK_FORMAT, format); | ||||
|         ret = -1; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     proto_drv = bdrv_find_protocol(filename); | ||||
|     if (!proto_drv) { | ||||
|         qerror_report(QERR_INVALID_BLOCK_FORMAT, format); | ||||
|         ret = -1; | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     ret = bdrv_img_create(filename, format, bs->filename, | ||||
|                           bs->drv->format_name, NULL, -1, bs->open_flags); | ||||
|     if (ret) { | ||||
|         goto out; | ||||
|     } | ||||
|  | ||||
|     qemu_aio_flush(); | ||||
|     bdrv_flush(bs); | ||||
|  | ||||
|     flags = bs->open_flags; | ||||
|     bdrv_close(bs); | ||||
|     ret = bdrv_open(bs, filename, flags, drv); | ||||
|     /* | ||||
|      * If reopening the image file we just created fails, we really | ||||
|      * are in trouble :( | ||||
|      */ | ||||
|     if (ret != 0) { | ||||
|         abort(); | ||||
|     } | ||||
| out: | ||||
|     if (ret) { | ||||
|         ret = -1; | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| static int eject_device(Monitor *mon, BlockDriverState *bs, int force) | ||||
| { | ||||
|     if (!force) { | ||||
|         if (!bdrv_is_removable(bs)) { | ||||
|             qerror_report(QERR_DEVICE_NOT_REMOVABLE, | ||||
|                            bdrv_get_device_name(bs)); | ||||
|             return -1; | ||||
|         } | ||||
|         if (bdrv_is_locked(bs)) { | ||||
|             qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs)); | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
|     bdrv_close(bs); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
|     int force = qdict_get_try_bool(qdict, "force", 0); | ||||
|     const char *filename = qdict_get_str(qdict, "device"); | ||||
|  | ||||
|     bs = bdrv_find(filename); | ||||
|     if (!bs) { | ||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, filename); | ||||
|         return -1; | ||||
|     } | ||||
|     return eject_device(mon, bs, force); | ||||
| } | ||||
|  | ||||
| int do_block_set_passwd(Monitor *mon, const QDict *qdict, | ||||
|                         QObject **ret_data) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
|     int err; | ||||
|  | ||||
|     bs = bdrv_find(qdict_get_str(qdict, "device")); | ||||
|     if (!bs) { | ||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device")); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     err = bdrv_set_key(bs, qdict_get_str(qdict, "password")); | ||||
|     if (err == -EINVAL) { | ||||
|         qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs)); | ||||
|         return -1; | ||||
|     } else if (err < 0) { | ||||
|         qerror_report(QERR_INVALID_PASSWORD); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int do_change_block(Monitor *mon, const char *device, | ||||
|                     const char *filename, const char *fmt) | ||||
| { | ||||
|     BlockDriverState *bs; | ||||
|     BlockDriver *drv = NULL; | ||||
|     int bdrv_flags; | ||||
|  | ||||
|     bs = bdrv_find(device); | ||||
|     if (!bs) { | ||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, device); | ||||
|         return -1; | ||||
|     } | ||||
|     if (fmt) { | ||||
|         drv = bdrv_find_whitelisted_format(fmt); | ||||
|         if (!drv) { | ||||
|             qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt); | ||||
|             return -1; | ||||
|         } | ||||
|     } | ||||
|     if (eject_device(mon, bs, 0) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
|     bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; | ||||
|     bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; | ||||
|     if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) { | ||||
|         qerror_report(QERR_OPEN_FILE_FAILED, filename); | ||||
|         return -1; | ||||
|     } | ||||
|     return monitor_read_bdrv_key_start(mon, bs, NULL, NULL); | ||||
| } | ||||
|  | ||||
| int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) | ||||
| { | ||||
|     const char *id = qdict_get_str(qdict, "id"); | ||||
|     BlockDriverState *bs; | ||||
|  | ||||
|     bs = bdrv_find(id); | ||||
|     if (!bs) { | ||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, id); | ||||
|         return -1; | ||||
|     } | ||||
|     if (bdrv_in_use(bs)) { | ||||
|         qerror_report(QERR_DEVICE_IN_USE, id); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     /* quiesce block driver; prevent further io */ | ||||
|     qemu_aio_flush(); | ||||
|     bdrv_flush(bs); | ||||
|     bdrv_close(bs); | ||||
|  | ||||
|     /* if we have a device associated with this BlockDriverState (bs->peer) | ||||
|      * then we need to make the drive anonymous until the device | ||||
|      * can be removed.  If this is a drive with no device backing | ||||
|      * then we can just get rid of the block driver state right here. | ||||
|      */ | ||||
|     if (bs->peer) { | ||||
|         bdrv_make_anon(bs); | ||||
|     } else { | ||||
|         drive_uninit(drive_get_by_blockdev(bs)); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the | ||||
|  * existing QERR_ macro mess is cleaned up.  A good example for better | ||||
|  * error reports can be found in the qemu-img resize code. | ||||
|  */ | ||||
| int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data) | ||||
| { | ||||
|     const char *device = qdict_get_str(qdict, "device"); | ||||
|     int64_t size = qdict_get_int(qdict, "size"); | ||||
|     BlockDriverState *bs; | ||||
|  | ||||
|     bs = bdrv_find(device); | ||||
|     if (!bs) { | ||||
|         qerror_report(QERR_DEVICE_NOT_FOUND, device); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (size < 0) { | ||||
|         qerror_report(QERR_UNDEFINED_ERROR); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     if (bdrv_truncate(bs, size)) { | ||||
|         qerror_report(QERR_UNDEFINED_ERROR); | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										68
									
								
								blockdev.h
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								blockdev.h
									
									
									
									
									
								
							| @@ -1,68 +0,0 @@ | ||||
| /* | ||||
|  * QEMU host block devices | ||||
|  * | ||||
|  * Copyright (c) 2003-2008 Fabrice Bellard | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or | ||||
|  * later.  See the COPYING file in the top-level directory. | ||||
|  */ | ||||
|  | ||||
| #ifndef BLOCKDEV_H | ||||
| #define BLOCKDEV_H | ||||
|  | ||||
| #include "block.h" | ||||
| #include "qemu-queue.h" | ||||
|  | ||||
| void blockdev_mark_auto_del(BlockDriverState *bs); | ||||
| void blockdev_auto_del(BlockDriverState *bs); | ||||
|  | ||||
| #define BLOCK_SERIAL_STRLEN 20 | ||||
|  | ||||
| typedef enum { | ||||
|     IF_DEFAULT = -1,            /* for use with drive_add() only */ | ||||
|     IF_NONE, | ||||
|     IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, | ||||
|     IF_COUNT | ||||
| } BlockInterfaceType; | ||||
|  | ||||
| struct DriveInfo { | ||||
|     BlockDriverState *bdrv; | ||||
|     char *id; | ||||
|     const char *devaddr; | ||||
|     BlockInterfaceType type; | ||||
|     int bus; | ||||
|     int unit; | ||||
|     int auto_del;               /* see blockdev_mark_auto_del() */ | ||||
|     QemuOpts *opts; | ||||
|     char serial[BLOCK_SERIAL_STRLEN + 1]; | ||||
|     QTAILQ_ENTRY(DriveInfo) next; | ||||
|     int refcount; | ||||
| }; | ||||
|  | ||||
| DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); | ||||
| DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); | ||||
| int drive_get_max_bus(BlockInterfaceType type); | ||||
| DriveInfo *drive_get_next(BlockInterfaceType type); | ||||
| void drive_get_ref(DriveInfo *dinfo); | ||||
| void drive_put_ref(DriveInfo *dinfo); | ||||
| DriveInfo *drive_get_by_blockdev(BlockDriverState *bs); | ||||
|  | ||||
| QemuOpts *drive_def(const char *optstr); | ||||
| QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, | ||||
|                     const char *optstr); | ||||
| DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi); | ||||
|  | ||||
| /* device-hotplug */ | ||||
|  | ||||
| DriveInfo *add_init_drive(const char *opts); | ||||
|  | ||||
| void do_commit(Monitor *mon, const QDict *qdict); | ||||
| int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data); | ||||
| int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data); | ||||
| int do_change_block(Monitor *mon, const char *device, | ||||
|                     const char *filename, const char *fmt); | ||||
| int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); | ||||
| int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data); | ||||
| int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data); | ||||
|  | ||||
| #endif | ||||
| @@ -176,6 +176,8 @@ int loader_exec(const char * filename, char ** argv, char ** envp, | ||||
|  | ||||
|     retval = prepare_binprm(&bprm); | ||||
|  | ||||
|     infop->host_argv = argv; | ||||
|  | ||||
|     if(retval>=0) { | ||||
|         if (bprm.buf[0] == 0x7f | ||||
|                 && bprm.buf[1] == 'E' | ||||
|   | ||||
| @@ -1044,7 +1044,7 @@ static void load_symbols(struct elfhdr *hdr, int fd) | ||||
|     struct elf_shdr sechdr, symtab, strtab; | ||||
|     char *strings; | ||||
|     struct syminfo *s; | ||||
|     struct elf_sym *syms, *new_syms; | ||||
|     struct elf_sym *syms; | ||||
|  | ||||
|     lseek(fd, hdr->e_shoff, SEEK_SET); | ||||
|     for (i = 0; i < hdr->e_shnum; i++) { | ||||
| @@ -1072,24 +1072,15 @@ static void load_symbols(struct elfhdr *hdr, int fd) | ||||
|     /* Now know where the strtab and symtab are.  Snarf them. */ | ||||
|     s = malloc(sizeof(*s)); | ||||
|     syms = malloc(symtab.sh_size); | ||||
|     if (!syms) { | ||||
|         free(s); | ||||
|     if (!syms) | ||||
|         return; | ||||
|     } | ||||
|     s->disas_strtab = strings = malloc(strtab.sh_size); | ||||
|     if (!s->disas_strtab) { | ||||
|         free(s); | ||||
|         free(syms); | ||||
|     if (!s->disas_strtab) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     lseek(fd, symtab.sh_offset, SEEK_SET); | ||||
|     if (read(fd, syms, symtab.sh_size) != symtab.sh_size) { | ||||
|         free(s); | ||||
|         free(syms); | ||||
|         free(strings); | ||||
|     if (read(fd, syms, symtab.sh_size) != symtab.sh_size) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     nsyms = symtab.sh_size / sizeof(struct elf_sym); | ||||
|  | ||||
| @@ -1114,29 +1105,13 @@ static void load_symbols(struct elfhdr *hdr, int fd) | ||||
| #endif | ||||
|         i++; | ||||
|     } | ||||
|  | ||||
|      /* Attempt to free the storage associated with the local symbols | ||||
|         that we threw away.  Whether or not this has any effect on the | ||||
|         memory allocation depends on the malloc implementation and how | ||||
|         many symbols we managed to discard. */ | ||||
|     new_syms = realloc(syms, nsyms * sizeof(*syms)); | ||||
|     if (new_syms == NULL) { | ||||
|         free(s); | ||||
|         free(syms); | ||||
|         free(strings); | ||||
|         return; | ||||
|     } | ||||
|     syms = new_syms; | ||||
|     syms = realloc(syms, nsyms * sizeof(*syms)); | ||||
|  | ||||
|     qsort(syms, nsyms, sizeof(*syms), symcmp); | ||||
|  | ||||
|     lseek(fd, strtab.sh_offset, SEEK_SET); | ||||
|     if (read(fd, strings, strtab.sh_size) != strtab.sh_size) { | ||||
|         free(s); | ||||
|         free(syms); | ||||
|         free(strings); | ||||
|     if (read(fd, strings, strtab.sh_size) != strtab.sh_size) | ||||
|         return; | ||||
|     } | ||||
|     s->disas_num_syms = nsyms; | ||||
| #if ELF_CLASS == ELFCLASS32 | ||||
|     s->disas_symtab.elf32 = syms; | ||||
|   | ||||
| @@ -30,8 +30,8 @@ | ||||
| #include "qemu-common.h" | ||||
| /* For tb_lock */ | ||||
| #include "exec-all.h" | ||||
| #include "tcg.h" | ||||
| #include "qemu-timer.h" | ||||
|  | ||||
|  | ||||
| #include "envlist.h" | ||||
|  | ||||
| #define DEBUG_LOGFILE "/tmp/qemu.log" | ||||
| @@ -43,7 +43,7 @@ unsigned long guest_base; | ||||
| int have_guest_base; | ||||
| #endif | ||||
|  | ||||
| static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; | ||||
| static const char *interp_prefix = CONFIG_QEMU_PREFIX; | ||||
| const char *qemu_uname_release = CONFIG_UNAME_RELEASE; | ||||
| extern char **environ; | ||||
| enum BSDType bsd_type; | ||||
| @@ -759,10 +759,6 @@ int main(int argc, char **argv) | ||||
|     } | ||||
|  | ||||
|     cpu_model = NULL; | ||||
| #if defined(cpudef_setup) | ||||
|     cpudef_setup(); /* parse cpu definitions in target config file (TBD) */ | ||||
| #endif | ||||
|  | ||||
|     optind = 1; | ||||
|     for(;;) { | ||||
|         if (optind >= argc) | ||||
| @@ -795,12 +791,6 @@ int main(int argc, char **argv) | ||||
|             r = argv[optind++]; | ||||
|             if (envlist_setenv(envlist, r) != 0) | ||||
|                 usage(); | ||||
|         } else if (!strcmp(r, "ignore-environment")) { | ||||
|             envlist_free(envlist); | ||||
|             if ((envlist = envlist_create()) == NULL) { | ||||
|                 (void) fprintf(stderr, "Unable to allocate envlist\n"); | ||||
|                 exit(1); | ||||
|             } | ||||
|         } else if (!strcmp(r, "U")) { | ||||
|             r = argv[optind++]; | ||||
|             if (envlist_unsetenv(envlist, r) != 0) | ||||
| @@ -976,13 +966,6 @@ int main(int argc, char **argv) | ||||
|     syscall_init(); | ||||
|     signal_init(); | ||||
|  | ||||
| #if defined(CONFIG_USE_GUEST_BASE) | ||||
|     /* Now that we've loaded the binary, GUEST_BASE is fixed.  Delay | ||||
|        generating the prologue until now so that the prologue can take | ||||
|        the real value of GUEST_BASE into account.  */ | ||||
|     tcg_prologue_init(&tcg_ctx); | ||||
| #endif | ||||
|  | ||||
|     /* build Task State */ | ||||
|     memset(ts, 0, sizeof(TaskState)); | ||||
|     init_task_state(ts); | ||||
|   | ||||
| @@ -77,15 +77,16 @@ void mmap_unlock(void) | ||||
| void *qemu_vmalloc(size_t size) | ||||
| { | ||||
|     void *p; | ||||
|     unsigned long addr; | ||||
|     mmap_lock(); | ||||
|     /* Use map and mark the pages as used.  */ | ||||
|     p = mmap(NULL, size, PROT_READ | PROT_WRITE, | ||||
|              MAP_PRIVATE | MAP_ANON, -1, 0); | ||||
|  | ||||
|     if (h2g_valid(p)) { | ||||
|     addr = (unsigned long)p; | ||||
|     if (addr == (target_ulong) addr) { | ||||
|         /* Allocated region overlaps guest address space. | ||||
|            This may recurse.  */ | ||||
|         abi_ulong addr = h2g(p); | ||||
|         page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size), | ||||
|                        PAGE_RESERVED); | ||||
|     } | ||||
| @@ -239,7 +240,7 @@ static int mmap_frag(abi_ulong real_start, | ||||
|            possible while it is a shared mapping */ | ||||
|         if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED && | ||||
|             (prot & PROT_WRITE)) | ||||
|             return -1; | ||||
|             return -EINVAL; | ||||
|  | ||||
|         /* adjust protection to be able to read */ | ||||
|         if (!(prot1 & PROT_WRITE)) | ||||
|   | ||||
| @@ -50,6 +50,7 @@ struct image_info { | ||||
|     abi_ulong entry; | ||||
|     abi_ulong code_offset; | ||||
|     abi_ulong data_offset; | ||||
|     char      **host_argv; | ||||
|     int       personality; | ||||
| }; | ||||
|  | ||||
| @@ -138,7 +139,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, | ||||
| abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, | ||||
|                             abi_long arg2, abi_long arg3, abi_long arg4, | ||||
|                             abi_long arg5, abi_long arg6); | ||||
| void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); | ||||
| void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); | ||||
| extern THREAD CPUState *thread_env; | ||||
| void cpu_loop(CPUState *env); | ||||
| char *target_strerror(int err); | ||||
|   | ||||
							
								
								
									
										23
									
								
								bswap.h
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								bswap.h
									
									
									
									
									
								
							| @@ -144,7 +144,6 @@ CPU_CONVERT(le, 64, uint64_t) | ||||
|  | ||||
| #define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) | ||||
| #define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) | ||||
| #define cpu_to_be64wu(p, v) cpu_to_be64w(p, v) | ||||
|  | ||||
| #else | ||||
|  | ||||
| @@ -202,28 +201,12 @@ static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) | ||||
|     p1[3] = v & 0xff; | ||||
| } | ||||
|  | ||||
| static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) | ||||
| { | ||||
|     uint8_t *p1 = (uint8_t *)p; | ||||
|  | ||||
|     p1[0] = v >> 56; | ||||
|     p1[1] = v >> 48; | ||||
|     p1[2] = v >> 40; | ||||
|     p1[3] = v >> 32; | ||||
|     p1[4] = v >> 24; | ||||
|     p1[5] = v >> 16; | ||||
|     p1[6] = v >> 8; | ||||
|     p1[7] = v & 0xff; | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #ifdef HOST_WORDS_BIGENDIAN | ||||
| #define cpu_to_32wu cpu_to_be32wu | ||||
| #define leul_to_cpu(v) glue(glue(le,HOST_LONG_BITS),_to_cpu)(v) | ||||
| #else | ||||
| #define cpu_to_32wu cpu_to_le32wu | ||||
| #define leul_to_cpu(v) (v) | ||||
| #endif | ||||
|  | ||||
| #undef le_bswap | ||||
| @@ -231,10 +214,4 @@ static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) | ||||
| #undef le_bswaps | ||||
| #undef be_bswaps | ||||
|  | ||||
| /* len must be one of 1, 2, 4 */ | ||||
| static inline uint32_t qemu_bswap_len(uint32_t value, int len) | ||||
| { | ||||
|     return bswap32(value) >> (32 - 8 * len); | ||||
| } | ||||
|  | ||||
| #endif /* BSWAP_H */ | ||||
|   | ||||
							
								
								
									
										13
									
								
								bt-host.c
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								bt-host.c
									
									
									
									
									
								
							| @@ -50,19 +50,19 @@ static void bt_host_send(struct HCIInfo *hci, | ||||
|     struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci; | ||||
|     uint8_t pkt = type; | ||||
|     struct iovec iv[2]; | ||||
|     int ret; | ||||
|  | ||||
|     iv[0].iov_base = (void *)&pkt; | ||||
|     iv[0].iov_len  = 1; | ||||
|     iv[1].iov_base = (void *) data; | ||||
|     iv[1].iov_len  = len; | ||||
|  | ||||
|     while (writev(s->fd, iv, 2) < 0) { | ||||
|     while ((ret = writev(s->fd, iv, 2)) < 0) | ||||
|         if (errno != EAGAIN && errno != EINTR) { | ||||
|             fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", | ||||
|                             errno); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len) | ||||
| @@ -80,6 +80,13 @@ static void bt_host_sco(struct HCIInfo *hci, const uint8_t *data, int len) | ||||
|     bt_host_send(hci, HCI_SCODATA_PKT, data, len); | ||||
| } | ||||
|  | ||||
| static int bt_host_read_poll(void *opaque) | ||||
| { | ||||
|     struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; | ||||
|  | ||||
|     return !!s->hci.evt_recv; | ||||
| } | ||||
|  | ||||
| static void bt_host_read(void *opaque) | ||||
| { | ||||
|     struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; | ||||
| @@ -185,7 +192,7 @@ struct HCIInfo *bt_host_hci(const char *id) | ||||
|     s->hci.acl_send = bt_host_acl; | ||||
|     s->hci.bdaddr_set = bt_host_bdaddr_set; | ||||
|  | ||||
|     qemu_set_fd_handler(s->fd, bt_host_read, NULL, s); | ||||
|     qemu_set_fd_handler2(s->fd, bt_host_read_poll, bt_host_read, NULL, s); | ||||
|  | ||||
|     return &s->hci; | ||||
| } | ||||
|   | ||||
| @@ -39,10 +39,10 @@ typedef struct QEMUFileBuffered | ||||
| } QEMUFileBuffered; | ||||
|  | ||||
| #ifdef DEBUG_BUFFERED_FILE | ||||
| #define DPRINTF(fmt, ...) \ | ||||
| #define dprintf(fmt, ...) \ | ||||
|     do { printf("buffered-file: " fmt, ## __VA_ARGS__); } while (0) | ||||
| #else | ||||
| #define DPRINTF(fmt, ...) \ | ||||
| #define dprintf(fmt, ...) \ | ||||
|     do { } while (0) | ||||
| #endif | ||||
|  | ||||
| @@ -52,7 +52,7 @@ static void buffered_append(QEMUFileBuffered *s, | ||||
|     if (size > (s->buffer_capacity - s->buffer_size)) { | ||||
|         void *tmp; | ||||
|  | ||||
|         DPRINTF("increasing buffer capacity from %zu by %zu\n", | ||||
|         dprintf("increasing buffer capacity from %zu by %zu\n", | ||||
|                 s->buffer_capacity, size + 1024); | ||||
|  | ||||
|         s->buffer_capacity += size + 1024; | ||||
| @@ -75,11 +75,11 @@ static void buffered_flush(QEMUFileBuffered *s) | ||||
|     size_t offset = 0; | ||||
|  | ||||
|     if (s->has_error) { | ||||
|         DPRINTF("flush when error, bailing\n"); | ||||
|         dprintf("flush when error, bailing\n"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size); | ||||
|     dprintf("flushing %zu byte(s) of data\n", s->buffer_size); | ||||
|  | ||||
|     while (offset < s->buffer_size) { | ||||
|         ssize_t ret; | ||||
| @@ -87,22 +87,22 @@ static void buffered_flush(QEMUFileBuffered *s) | ||||
|         ret = s->put_buffer(s->opaque, s->buffer + offset, | ||||
|                             s->buffer_size - offset); | ||||
|         if (ret == -EAGAIN) { | ||||
|             DPRINTF("backend not ready, freezing\n"); | ||||
|             dprintf("backend not ready, freezing\n"); | ||||
|             s->freeze_output = 1; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if (ret <= 0) { | ||||
|             DPRINTF("error flushing data, %zd\n", ret); | ||||
|             dprintf("error flushing data, %zd\n", ret); | ||||
|             s->has_error = 1; | ||||
|             break; | ||||
|         } else { | ||||
|             DPRINTF("flushed %zd byte(s)\n", ret); | ||||
|             dprintf("flushed %zd byte(s)\n", ret); | ||||
|             offset += ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size); | ||||
|     dprintf("flushed %zu of %zu byte(s)\n", offset, s->buffer_size); | ||||
|     memmove(s->buffer, s->buffer + offset, s->buffer_size - offset); | ||||
|     s->buffer_size -= offset; | ||||
| } | ||||
| @@ -113,57 +113,49 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in | ||||
|     int offset = 0; | ||||
|     ssize_t ret; | ||||
|  | ||||
|     DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos); | ||||
|     dprintf("putting %d bytes at %" PRId64 "\n", size, pos); | ||||
|  | ||||
|     if (s->has_error) { | ||||
|         DPRINTF("flush when error, bailing\n"); | ||||
|         dprintf("flush when error, bailing\n"); | ||||
|         return -EINVAL; | ||||
|     } | ||||
|  | ||||
|     DPRINTF("unfreezing output\n"); | ||||
|     dprintf("unfreezing output\n"); | ||||
|     s->freeze_output = 0; | ||||
|  | ||||
|     buffered_flush(s); | ||||
|  | ||||
|     while (!s->freeze_output && offset < size) { | ||||
|         if (s->bytes_xfer > s->xfer_limit) { | ||||
|             DPRINTF("transfer limit exceeded when putting\n"); | ||||
|             dprintf("transfer limit exceeded when putting\n"); | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         ret = s->put_buffer(s->opaque, buf + offset, size - offset); | ||||
|         if (ret == -EAGAIN) { | ||||
|             DPRINTF("backend not ready, freezing\n"); | ||||
|             dprintf("backend not ready, freezing\n"); | ||||
|             s->freeze_output = 1; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if (ret <= 0) { | ||||
|             DPRINTF("error putting\n"); | ||||
|             dprintf("error putting\n"); | ||||
|             s->has_error = 1; | ||||
|             offset = -EINVAL; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         DPRINTF("put %zd byte(s)\n", ret); | ||||
|         dprintf("put %zd byte(s)\n", ret); | ||||
|         offset += ret; | ||||
|         s->bytes_xfer += ret; | ||||
|     } | ||||
|  | ||||
|     if (offset >= 0) { | ||||
|         DPRINTF("buffering %d bytes\n", size - offset); | ||||
|         dprintf("buffering %d bytes\n", size - offset); | ||||
|         buffered_append(s, buf + offset, size - offset); | ||||
|         offset = size; | ||||
|     } | ||||
|  | ||||
|     if (pos == 0 && size == 0) { | ||||
|         DPRINTF("file is ready\n"); | ||||
|         if (s->bytes_xfer <= s->xfer_limit) { | ||||
|             DPRINTF("notifying client\n"); | ||||
|             s->put_ready(s->opaque); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return offset; | ||||
| } | ||||
|  | ||||
| @@ -172,7 +164,7 @@ static int buffered_close(void *opaque) | ||||
|     QEMUFileBuffered *s = opaque; | ||||
|     int ret; | ||||
|  | ||||
|     DPRINTF("closing\n"); | ||||
|     dprintf("closing\n"); | ||||
|  | ||||
|     while (!s->has_error && s->buffer_size) { | ||||
|         buffered_flush(s); | ||||
| @@ -206,23 +198,20 @@ static int buffered_rate_limit(void *opaque) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate) | ||||
| static size_t buffered_set_rate_limit(void *opaque, size_t new_rate) | ||||
| { | ||||
|     QEMUFileBuffered *s = opaque; | ||||
|  | ||||
|     if (s->has_error) | ||||
|         goto out; | ||||
|  | ||||
|     if (new_rate > SIZE_MAX) { | ||||
|         new_rate = SIZE_MAX; | ||||
|     } | ||||
|  | ||||
|     s->xfer_limit = new_rate / 10; | ||||
|      | ||||
| out: | ||||
|     return s->xfer_limit; | ||||
| } | ||||
|  | ||||
| static int64_t buffered_get_rate_limit(void *opaque) | ||||
| static size_t buffered_get_rate_limit(void *opaque) | ||||
| { | ||||
|     QEMUFileBuffered *s = opaque; | ||||
|    | ||||
| @@ -233,10 +222,8 @@ static void buffered_rate_tick(void *opaque) | ||||
| { | ||||
|     QEMUFileBuffered *s = opaque; | ||||
|  | ||||
|     if (s->has_error) { | ||||
|         buffered_close(s); | ||||
|     if (s->has_error) | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); | ||||
|  | ||||
|   | ||||
| @@ -57,30 +57,6 @@ static void ppc_init_cacheline_sizes(void) | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) | ||||
| #include <errno.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/sysctl.h> | ||||
|  | ||||
| static void ppc_init_cacheline_sizes(void) | ||||
| { | ||||
|     size_t len = 4; | ||||
|     unsigned cacheline; | ||||
|  | ||||
|     if (sysctlbyname ("machdep.cacheline_size", &cacheline, &len, NULL, 0)) { | ||||
|         fprintf(stderr, "sysctlbyname machdep.cacheline_size failed: %s\n", | ||||
|                 strerror(errno)); | ||||
|         exit(1); | ||||
|     } | ||||
|  | ||||
|     qemu_cache_conf.dcache_bsize = cacheline; | ||||
|     qemu_cache_conf.icache_bsize = cacheline; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef __linux__ | ||||
| void qemu_cache_utils_init(char **envp) | ||||
| { | ||||
|   | ||||
| @@ -9,7 +9,7 @@ struct qemu_cache_conf { | ||||
|  | ||||
| extern struct qemu_cache_conf qemu_cache_conf; | ||||
|  | ||||
| void qemu_cache_utils_init(char **envp); | ||||
| extern void qemu_cache_utils_init(char **envp); | ||||
|  | ||||
| /* mildly adjusted code from tcg-dyngen.c */ | ||||
| static inline void flush_icache_range(unsigned long start, unsigned long stop) | ||||
| @@ -34,7 +34,28 @@ static inline void flush_icache_range(unsigned long start, unsigned long stop) | ||||
|     asm volatile ("isync" : : : "memory"); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Is this correct for PPC? | ||||
|  */ | ||||
| static inline void dma_flush_range(unsigned long start, unsigned long stop) | ||||
| { | ||||
| } | ||||
|  | ||||
| #elif defined(__ia64__) | ||||
| static inline void flush_icache_range(unsigned long start, unsigned long stop) | ||||
| { | ||||
|     while (start < stop) { | ||||
| 	asm volatile ("fc %0" :: "r"(start)); | ||||
| 	start += 32; | ||||
|     } | ||||
|     asm volatile (";;sync.i;;srlz.i;;"); | ||||
| } | ||||
| #define dma_flush_range(start, end) flush_icache_range(start, end) | ||||
| #define qemu_cache_utils_init(envp) do { (void) (envp); } while (0) | ||||
| #else | ||||
| static inline void dma_flush_range(unsigned long start, unsigned long stop) | ||||
| { | ||||
| } | ||||
| #define qemu_cache_utils_init(envp) do { (void) (envp); } while (0) | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -5,9 +5,6 @@ | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Luiz Capitulino <lcapitulino@redhat.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  */ | ||||
| #include <check.h> | ||||
|  | ||||
| @@ -50,7 +47,7 @@ START_TEST(qdict_put_obj_test) | ||||
|     qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num))); | ||||
|  | ||||
|     fail_unless(qdict_size(qdict) == 1); | ||||
|     ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]); | ||||
|     ent = QLIST_FIRST(&qdict->table[12345 % QDICT_HASH_SIZE]); | ||||
|     qi = qobject_to_qint(ent->value); | ||||
|     fail_unless(qint_get_int(qi) == num); | ||||
|  | ||||
| @@ -194,36 +191,6 @@ START_TEST(qobject_to_qdict_test) | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(qdict_iterapi_test) | ||||
| { | ||||
|     int count; | ||||
|     const QDictEntry *ent; | ||||
|  | ||||
|     fail_unless(qdict_first(tests_dict) == NULL); | ||||
|  | ||||
|     qdict_put(tests_dict, "key1", qint_from_int(1)); | ||||
|     qdict_put(tests_dict, "key2", qint_from_int(2)); | ||||
|     qdict_put(tests_dict, "key3", qint_from_int(3)); | ||||
|  | ||||
|     count = 0; | ||||
|     for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ | ||||
|         fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); | ||||
|         count++; | ||||
|     } | ||||
|  | ||||
|     fail_unless(count == qdict_size(tests_dict)); | ||||
|  | ||||
|     /* Do it again to test restarting */ | ||||
|     count = 0; | ||||
|     for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ | ||||
|         fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); | ||||
|         count++; | ||||
|     } | ||||
|  | ||||
|     fail_unless(count == qdict_size(tests_dict)); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| /* | ||||
|  * Errors test-cases | ||||
|  */ | ||||
| @@ -368,7 +335,6 @@ static Suite *qdict_suite(void) | ||||
|     tcase_add_test(qdict_public2_tcase, qdict_haskey_test); | ||||
|     tcase_add_test(qdict_public2_tcase, qdict_del_test); | ||||
|     tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test); | ||||
|     tcase_add_test(qdict_public2_tcase, qdict_iterapi_test); | ||||
|  | ||||
|     qdict_errors_tcase = tcase_create("Errors"); | ||||
|     suite_add_tcase(s, qdict_errors_tcase); | ||||
|   | ||||
| @@ -1,6 +1,11 @@ | ||||
| /* | ||||
|  * QFloat unit-tests. | ||||
|  * | ||||
|  * Copyright (C) 2009 Red Hat Inc. | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Luiz Capitulino <lcapitulino@redhat.com> | ||||
|  * | ||||
|  * Copyright IBM, Corp. 2009 | ||||
|  * | ||||
|  * Authors: | ||||
|   | ||||
| @@ -5,9 +5,6 @@ | ||||
|  * | ||||
|  * Authors: | ||||
|  *  Luiz Capitulino <lcapitulino@redhat.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  */ | ||||
| #include <check.h> | ||||
|  | ||||
|   | ||||
							
								
								
									
										115
									
								
								check-qjson.c
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								check-qjson.c
									
									
									
									
									
								
							| @@ -9,6 +9,7 @@ | ||||
|  * | ||||
|  */ | ||||
| #include <check.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
| #include "qstring.h" | ||||
| #include "qint.h" | ||||
| @@ -28,13 +29,6 @@ START_TEST(escaped_string) | ||||
|         const char *decoded; | ||||
|         int skip; | ||||
|     } test_cases[] = { | ||||
|         { "\"\\b\"", "\b" }, | ||||
|         { "\"\\f\"", "\f" }, | ||||
|         { "\"\\n\"", "\n" }, | ||||
|         { "\"\\r\"", "\r" }, | ||||
|         { "\"\\t\"", "\t" }, | ||||
|         { "\"\\/\"", "\\/" }, | ||||
|         { "\"\\\\\"", "\\" }, | ||||
|         { "\"\\\"\"", "\"" }, | ||||
|         { "\"hello world \\\"embedded string\\\"\"", | ||||
|           "hello world \"embedded string\"" }, | ||||
| @@ -55,14 +49,11 @@ START_TEST(escaped_string) | ||||
|         fail_unless(qobject_type(obj) == QTYPE_QSTRING); | ||||
|          | ||||
|         str = qobject_to_qstring(obj); | ||||
|         fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0, | ||||
|                     "%s != %s\n", qstring_get_str(str), test_cases[i].decoded); | ||||
|         fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); | ||||
|  | ||||
|         if (test_cases[i].skip == 0) { | ||||
|             str = qobject_to_json(obj); | ||||
|             fail_unless(strcmp(qstring_get_str(str),test_cases[i].encoded) == 0, | ||||
|                         "%s != %s\n", qstring_get_str(str), | ||||
|                                       test_cases[i].encoded); | ||||
|             fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); | ||||
|  | ||||
|             qobject_decref(obj); | ||||
|         } | ||||
| @@ -637,92 +628,11 @@ START_TEST(simple_varargs) | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(empty_input) | ||||
| { | ||||
|     const char *empty = ""; | ||||
|  | ||||
|     QObject *obj = qobject_from_json(empty); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(unterminated_string) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("\"abc"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(unterminated_sq_string) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("'abc"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(unterminated_escape) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("\"abc\\\""); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(unterminated_array) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("[32"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(unterminated_array_comma) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("[32,"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(invalid_array_comma) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("[32,}"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(unterminated_dict) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("{'abc':32"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(unterminated_dict_comma) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("{'abc':32,"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| #if 0 | ||||
| START_TEST(invalid_dict_comma) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("{'abc':32,}"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
|  | ||||
| START_TEST(unterminated_literal) | ||||
| { | ||||
|     QObject *obj = qobject_from_json("nul"); | ||||
|     fail_unless(obj == NULL); | ||||
| } | ||||
| END_TEST | ||||
| #endif | ||||
|  | ||||
| static Suite *qjson_suite(void) | ||||
| { | ||||
|     Suite *suite; | ||||
|     TCase *string_literals, *number_literals, *keyword_literals; | ||||
|     TCase *dicts, *lists, *whitespace, *varargs, *errors; | ||||
|     TCase *dicts, *lists, *whitespace, *varargs; | ||||
|  | ||||
|     string_literals = tcase_create("String Literals"); | ||||
|     tcase_add_test(string_literals, simple_string); | ||||
| @@ -748,22 +658,6 @@ static Suite *qjson_suite(void) | ||||
|     varargs = tcase_create("Varargs"); | ||||
|     tcase_add_test(varargs, simple_varargs); | ||||
|  | ||||
|     errors = tcase_create("Invalid JSON"); | ||||
|     tcase_add_test(errors, empty_input); | ||||
|     tcase_add_test(errors, unterminated_string); | ||||
|     tcase_add_test(errors, unterminated_escape); | ||||
|     tcase_add_test(errors, unterminated_sq_string); | ||||
|     tcase_add_test(errors, unterminated_array); | ||||
|     tcase_add_test(errors, unterminated_array_comma); | ||||
|     tcase_add_test(errors, invalid_array_comma); | ||||
|     tcase_add_test(errors, unterminated_dict); | ||||
|     tcase_add_test(errors, unterminated_dict_comma); | ||||
| #if 0 | ||||
|     /* FIXME: this print parse error messages on stderr.  */ | ||||
|     tcase_add_test(errors, invalid_dict_comma); | ||||
|     tcase_add_test(errors, unterminated_literal); | ||||
| #endif | ||||
|  | ||||
|     suite = suite_create("QJSON test-suite"); | ||||
|     suite_add_tcase(suite, string_literals); | ||||
|     suite_add_tcase(suite, number_literals); | ||||
| @@ -772,7 +666,6 @@ static Suite *qjson_suite(void) | ||||
|     suite_add_tcase(suite, lists); | ||||
|     suite_add_tcase(suite, whitespace); | ||||
|     suite_add_tcase(suite, varargs); | ||||
|     suite_add_tcase(suite, errors); | ||||
|  | ||||
|     return suite; | ||||
| } | ||||
|   | ||||
| @@ -6,8 +6,8 @@ | ||||
|  * Authors: | ||||
|  *  Luiz Capitulino <lcapitulino@redhat.com> | ||||
|  * | ||||
|  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | ||||
|  * See the COPYING.LIB file in the top-level directory. | ||||
|  * This work is licensed under the terms of the GNU GPL, version 2. See | ||||
|  * the COPYING file in the top-level directory. | ||||
|  */ | ||||
| #include <check.h> | ||||
|  | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user