Compare commits
	
		
			95 Commits
		
	
	
		
			pull-input
			...
			pull-input
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8977bd111f | ||
|  | f85d28316a | ||
|  | ee8c0b622c | ||
|  | 6f5943cf45 | ||
|  | 8b84286f4c | ||
|  | 1ff5eedd1d | ||
|  | 86846bfe64 | ||
|  | 2386a90730 | ||
|  | 11c7fa7fa6 | ||
|  | f5c0ab1312 | ||
|  | 178ac111bc | ||
|  | 6054d883d6 | ||
|  | 5118dc5975 | ||
|  | 45e66b7beb | ||
|  | 65903a8b08 | ||
|  | fc13d93726 | ||
|  | 3478881130 | ||
|  | d2e064a73e | ||
|  | 3894c78764 | ||
|  | 0aff637e92 | ||
|  | 5f758366c0 | ||
|  | 3f9286b721 | ||
|  | 3b69595068 | ||
|  | 5a7733b0b7 | ||
|  | 770a63792b | ||
|  | b30f4dfbda | ||
|  | 8e4e86afa5 | ||
|  | 76eb98d51c | ||
|  | d426d9fba8 | ||
|  | 7bca3892cb | ||
|  | 03cf077ac9 | ||
|  | 7b35d0c44c | ||
|  | 61c7bbd236 | ||
|  | ca8c0fab95 | ||
|  | f2564d88fe | ||
|  | b1fe60cd35 | ||
|  | a2554a334a | ||
|  | c5fa6c86d0 | ||
|  | 5bc8f026dd | ||
|  | 465bee1da8 | ||
|  | 6a23082b4e | ||
|  | 82a402e99f | ||
|  | 43f35cb5e0 | ||
|  | d66e5cee00 | ||
|  | 46485de0cb | ||
|  | 42eb58179b | ||
|  | 7159a45b2b | ||
|  | ea54feff58 | ||
|  | 0a86cb7317 | ||
|  | 97a3ea5719 | ||
|  | e3542c67af | ||
|  | 9aedd5a5d6 | ||
|  | b5e51dd714 | ||
|  | d530e34232 | ||
|  | 4ad303369c | ||
|  | 4993f7ea7e | ||
|  | 8a5eb36a1c | ||
|  | 9c52681277 | ||
|  | 26e2da7279 | ||
|  | 6906046169 | ||
|  | 395071a763 | ||
|  | e88ae2264d | ||
|  | 91e7fcca47 | ||
|  | c4ce4c4b1f | ||
|  | 24fd848950 | ||
|  | 11b389f21e | ||
|  | b162b49adc | ||
|  | 40d19394b7 | ||
|  | 13e315dada | ||
|  | 6297d9a279 | ||
|  | 29136cd8a4 | ||
|  | 87a560c455 | ||
|  | 297a3646c2 | ||
|  | cdaec3808e | ||
|  | 2ddb16a95f | ||
|  | f9f3a5ecde | ||
|  | be3c771796 | ||
|  | 192cca60ae | ||
|  | 4fa953f20d | ||
|  | 468866b816 | ||
|  | e2cd0f4fb4 | ||
|  | cbc95538ed | ||
|  | f9bee751be | ||
|  | 6e2bb3ec70 | ||
|  | 6a86dec619 | ||
|  | 3d2acaa308 | ||
|  | 5917af812e | ||
|  | b03c38057b | ||
|  | dbe5c58f2a | ||
|  | d383c625e2 | ||
|  | 4522b69c6c | ||
|  | 23335f6273 | ||
|  | 34bb4d02e0 | ||
|  | 6ee143a0a4 | ||
|  | 4bbeb8b173 | 
| @@ -659,6 +659,12 @@ S: Supported | |||||||
| F: hw/block/nvme* | F: hw/block/nvme* | ||||||
| F: tests/nvme-test.c | F: tests/nvme-test.c | ||||||
|  |  | ||||||
|  | megasas | ||||||
|  | M: Hannes Reinecke <hare@suse.de> | ||||||
|  | S: Supported | ||||||
|  | F: hw/scsi/megasas.c | ||||||
|  | F: hw/scsi/mfi.h | ||||||
|  |  | ||||||
| Xilinx EDK | Xilinx EDK | ||||||
| M: Peter Crosthwaite <peter.crosthwaite@xilinx.com> | M: Peter Crosthwaite <peter.crosthwaite@xilinx.com> | ||||||
| M: Edgar E. Iglesias <edgar.iglesias@gmail.com> | M: Edgar E. Iglesias <edgar.iglesias@gmail.com> | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								block.c
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								block.c
									
									
									
									
									
								
							| @@ -1274,6 +1274,33 @@ out: | |||||||
|     g_free(tmp_filename); |     g_free(tmp_filename); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static QDict *parse_json_filename(const char *filename, Error **errp) | ||||||
|  | { | ||||||
|  |     QObject *options_obj; | ||||||
|  |     QDict *options; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     ret = strstart(filename, "json:", &filename); | ||||||
|  |     assert(ret); | ||||||
|  |  | ||||||
|  |     options_obj = qobject_from_json(filename); | ||||||
|  |     if (!options_obj) { | ||||||
|  |         error_setg(errp, "Could not parse the JSON options"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (qobject_type(options_obj) != QTYPE_QDICT) { | ||||||
|  |         qobject_decref(options_obj); | ||||||
|  |         error_setg(errp, "Invalid JSON object given"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     options = qobject_to_qdict(options_obj); | ||||||
|  |     qdict_flatten(options); | ||||||
|  |  | ||||||
|  |     return options; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Opens a disk image (raw, qcow2, vmdk, ...) |  * Opens a disk image (raw, qcow2, vmdk, ...) | ||||||
|  * |  * | ||||||
| @@ -1337,6 +1364,20 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, | |||||||
|         options = qdict_new(); |         options = qdict_new(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (filename && g_str_has_prefix(filename, "json:")) { | ||||||
|  |         QDict *json_options = parse_json_filename(filename, &local_err); | ||||||
|  |         if (local_err) { | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             goto fail; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /* Options given in the filename have lower priority than options | ||||||
|  |          * specified directly */ | ||||||
|  |         qdict_join(options, json_options, false); | ||||||
|  |         QDECREF(json_options); | ||||||
|  |         filename = NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bs->options = options; |     bs->options = options; | ||||||
|     options = qdict_clone_shallow(options); |     options = qdict_clone_shallow(options); | ||||||
|  |  | ||||||
| @@ -3248,6 +3289,15 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, | |||||||
|  |  | ||||||
|     ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); |     ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); | ||||||
|  |  | ||||||
|  |     if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF && | ||||||
|  |         !(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_write_zeroes && | ||||||
|  |         qemu_iovec_is_zero(qiov)) { | ||||||
|  |         flags |= BDRV_REQ_ZERO_WRITE; | ||||||
|  |         if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) { | ||||||
|  |             flags |= BDRV_REQ_MAY_UNMAP; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         /* Do nothing, write notifier decided to fail this request */ |         /* Do nothing, write notifier decided to fail this request */ | ||||||
|     } else if (flags & BDRV_REQ_ZERO_WRITE) { |     } else if (flags & BDRV_REQ_ZERO_WRITE) { | ||||||
| @@ -3864,7 +3914,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, | |||||||
|  |  | ||||||
|     if (!bs->drv->bdrv_co_get_block_status) { |     if (!bs->drv->bdrv_co_get_block_status) { | ||||||
|         *pnum = nb_sectors; |         *pnum = nb_sectors; | ||||||
|         ret = BDRV_BLOCK_DATA; |         ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; | ||||||
|         if (bs->drv->protocol_name) { |         if (bs->drv->protocol_name) { | ||||||
|             ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE); |             ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE); | ||||||
|         } |         } | ||||||
| @@ -3883,6 +3933,10 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs, | |||||||
|                                      *pnum, pnum); |                                      *pnum, pnum); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { | ||||||
|  |         ret |= BDRV_BLOCK_ALLOCATED; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO)) { |     if (!(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO)) { | ||||||
|         if (bdrv_unallocated_blocks_are_zero(bs)) { |         if (bdrv_unallocated_blocks_are_zero(bs)) { | ||||||
|             ret |= BDRV_BLOCK_ZERO; |             ret |= BDRV_BLOCK_ZERO; | ||||||
| @@ -3959,9 +4013,7 @@ int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, | |||||||
|     if (ret < 0) { |     if (ret < 0) { | ||||||
|         return ret; |         return ret; | ||||||
|     } |     } | ||||||
|     return |     return (ret & BDRV_BLOCK_ALLOCATED); | ||||||
|         (ret & BDRV_BLOCK_DATA) || |  | ||||||
|         ((ret & BDRV_BLOCK_ZERO) && !bdrv_has_zero_init(bs)); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|   | |||||||
							
								
								
									
										79
									
								
								block/curl.c
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								block/curl.c
									
									
									
									
									
								
							| @@ -23,6 +23,7 @@ | |||||||
|  */ |  */ | ||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
|  | #include "qapi/qmp/qbool.h" | ||||||
| #include <curl/curl.h> | #include <curl/curl.h> | ||||||
|  |  | ||||||
| // #define DEBUG | // #define DEBUG | ||||||
| @@ -37,6 +38,21 @@ | |||||||
| #if LIBCURL_VERSION_NUM >= 0x071000 | #if LIBCURL_VERSION_NUM >= 0x071000 | ||||||
| /* The multi interface timer callback was introduced in 7.16.0 */ | /* The multi interface timer callback was introduced in 7.16.0 */ | ||||||
| #define NEED_CURL_TIMER_CALLBACK | #define NEED_CURL_TIMER_CALLBACK | ||||||
|  | #define HAVE_SOCKET_ACTION | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #ifndef HAVE_SOCKET_ACTION | ||||||
|  | /* If curl_multi_socket_action isn't available, define it statically here in | ||||||
|  |  * terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is | ||||||
|  |  * less efficient but still safe. */ | ||||||
|  | static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, | ||||||
|  |                                             curl_socket_t sockfd, | ||||||
|  |                                             int ev_bitmask, | ||||||
|  |                                             int *running_handles) | ||||||
|  | { | ||||||
|  |     return curl_multi_socket(multi_handle, sockfd, running_handles); | ||||||
|  | } | ||||||
|  | #define curl_multi_socket_action __curl_multi_socket_action | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ | #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ | ||||||
| @@ -46,12 +62,16 @@ | |||||||
| #define CURL_NUM_STATES 8 | #define CURL_NUM_STATES 8 | ||||||
| #define CURL_NUM_ACB    8 | #define CURL_NUM_ACB    8 | ||||||
| #define SECTOR_SIZE     512 | #define SECTOR_SIZE     512 | ||||||
| #define READ_AHEAD_SIZE (256 * 1024) | #define READ_AHEAD_DEFAULT (256 * 1024) | ||||||
|  |  | ||||||
| #define FIND_RET_NONE   0 | #define FIND_RET_NONE   0 | ||||||
| #define FIND_RET_OK     1 | #define FIND_RET_OK     1 | ||||||
| #define FIND_RET_WAIT   2 | #define FIND_RET_WAIT   2 | ||||||
|  |  | ||||||
|  | #define CURL_BLOCK_OPT_URL       "url" | ||||||
|  | #define CURL_BLOCK_OPT_READAHEAD "readahead" | ||||||
|  | #define CURL_BLOCK_OPT_SSLVERIFY "sslverify" | ||||||
|  |  | ||||||
| struct BDRVCURLState; | struct BDRVCURLState; | ||||||
|  |  | ||||||
| typedef struct CURLAIOCB { | typedef struct CURLAIOCB { | ||||||
| @@ -88,6 +108,7 @@ typedef struct BDRVCURLState { | |||||||
|     CURLState states[CURL_NUM_STATES]; |     CURLState states[CURL_NUM_STATES]; | ||||||
|     char *url; |     char *url; | ||||||
|     size_t readahead_size; |     size_t readahead_size; | ||||||
|  |     bool sslverify; | ||||||
|     bool accept_range; |     bool accept_range; | ||||||
| } BDRVCURLState; | } BDRVCURLState; | ||||||
|  |  | ||||||
| @@ -354,6 +375,8 @@ static CURLState *curl_init_state(BDRVCURLState *s) | |||||||
|             return NULL; |             return NULL; | ||||||
|         } |         } | ||||||
|         curl_easy_setopt(state->curl, CURLOPT_URL, s->url); |         curl_easy_setopt(state->curl, CURLOPT_URL, s->url); | ||||||
|  |         curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, | ||||||
|  |                          (long) s->sslverify); | ||||||
|         curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5); |         curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5); | ||||||
|         curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, |         curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, | ||||||
|                          (void *)curl_read_cb); |                          (void *)curl_read_cb); | ||||||
| @@ -396,43 +419,7 @@ static void curl_clean_state(CURLState *s) | |||||||
| static void curl_parse_filename(const char *filename, QDict *options, | static void curl_parse_filename(const char *filename, QDict *options, | ||||||
|                                 Error **errp) |                                 Error **errp) | ||||||
| { | { | ||||||
|  |     qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename)); | ||||||
|     #define RA_OPTSTR ":readahead=" |  | ||||||
|     char *file; |  | ||||||
|     char *ra; |  | ||||||
|     const char *ra_val; |  | ||||||
|     int parse_state = 0; |  | ||||||
|  |  | ||||||
|     file = g_strdup(filename); |  | ||||||
|  |  | ||||||
|     /* Parse a trailing ":readahead=#:" param, if present. */ |  | ||||||
|     ra = file + strlen(file) - 1; |  | ||||||
|     while (ra >= file) { |  | ||||||
|         if (parse_state == 0) { |  | ||||||
|             if (*ra == ':') { |  | ||||||
|                 parse_state++; |  | ||||||
|             } else { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } else if (parse_state == 1) { |  | ||||||
|             if (*ra > '9' || *ra < '0') { |  | ||||||
|                 char *opt_start = ra - strlen(RA_OPTSTR) + 1; |  | ||||||
|                 if (opt_start > file && |  | ||||||
|                     strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) { |  | ||||||
|                     ra_val = ra + 1; |  | ||||||
|                     ra -= strlen(RA_OPTSTR) - 1; |  | ||||||
|                     *ra = '\0'; |  | ||||||
|                     qdict_put(options, "readahead", qstring_from_str(ra_val)); |  | ||||||
|                 } |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         ra--; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     qdict_put(options, "url", qstring_from_str(file)); |  | ||||||
|  |  | ||||||
|     g_free(file); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static QemuOptsList runtime_opts = { | static QemuOptsList runtime_opts = { | ||||||
| @@ -440,15 +427,20 @@ static QemuOptsList runtime_opts = { | |||||||
|     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), |     .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), | ||||||
|     .desc = { |     .desc = { | ||||||
|         { |         { | ||||||
|             .name = "url", |             .name = CURL_BLOCK_OPT_URL, | ||||||
|             .type = QEMU_OPT_STRING, |             .type = QEMU_OPT_STRING, | ||||||
|             .help = "URL to open", |             .help = "URL to open", | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             .name = "readahead", |             .name = CURL_BLOCK_OPT_READAHEAD, | ||||||
|             .type = QEMU_OPT_SIZE, |             .type = QEMU_OPT_SIZE, | ||||||
|             .help = "Readahead size", |             .help = "Readahead size", | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |             .name = CURL_BLOCK_OPT_SSLVERIFY, | ||||||
|  |             .type = QEMU_OPT_BOOL, | ||||||
|  |             .help = "Verify SSL certificate" | ||||||
|  |         }, | ||||||
|         { /* end of list */ } |         { /* end of list */ } | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| @@ -477,14 +469,17 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto out_noclean; |         goto out_noclean; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE); |     s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD, | ||||||
|  |                                           READ_AHEAD_DEFAULT); | ||||||
|     if ((s->readahead_size & 0x1ff) != 0) { |     if ((s->readahead_size & 0x1ff) != 0) { | ||||||
|         error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", |         error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", | ||||||
|                    s->readahead_size); |                    s->readahead_size); | ||||||
|         goto out_noclean; |         goto out_noclean; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     file = qemu_opt_get(opts, "url"); |     s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true); | ||||||
|  |  | ||||||
|  |     file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL); | ||||||
|     if (file == NULL) { |     if (file == NULL) { | ||||||
|         error_setg(errp, "curl block driver requires an 'url' option"); |         error_setg(errp, "curl block driver requires an 'url' option"); | ||||||
|         goto out_noclean; |         goto out_noclean; | ||||||
|   | |||||||
							
								
								
									
										317
									
								
								block/iscsi.c
									
									
									
									
									
								
							
							
						
						
									
										317
									
								
								block/iscsi.c
									
									
									
									
									
								
							| @@ -30,6 +30,8 @@ | |||||||
| #include "qemu-common.h" | #include "qemu-common.h" | ||||||
| #include "qemu/config-file.h" | #include "qemu/config-file.h" | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
|  | #include "qemu/bitops.h" | ||||||
|  | #include "qemu/bitmap.h" | ||||||
| #include "block/block_int.h" | #include "block/block_int.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
| #include "block/scsi.h" | #include "block/scsi.h" | ||||||
| @@ -59,6 +61,8 @@ typedef struct IscsiLun { | |||||||
|     struct scsi_inquiry_logical_block_provisioning lbp; |     struct scsi_inquiry_logical_block_provisioning lbp; | ||||||
|     struct scsi_inquiry_block_limits bl; |     struct scsi_inquiry_block_limits bl; | ||||||
|     unsigned char *zeroblock; |     unsigned char *zeroblock; | ||||||
|  |     unsigned long *allocationmap; | ||||||
|  |     int cluster_sectors; | ||||||
| } IscsiLun; | } IscsiLun; | ||||||
|  |  | ||||||
| typedef struct IscsiTask { | typedef struct IscsiTask { | ||||||
| @@ -92,6 +96,15 @@ typedef struct IscsiAIOCB { | |||||||
| #define MAX_NOP_FAILURES 3 | #define MAX_NOP_FAILURES 3 | ||||||
| #define ISCSI_CMD_RETRIES 5 | #define ISCSI_CMD_RETRIES 5 | ||||||
|  |  | ||||||
|  | /* this threshhold is a trade-off knob to choose between | ||||||
|  |  * the potential additional overhead of an extra GET_LBA_STATUS request | ||||||
|  |  * vs. unnecessarily reading a lot of zero sectors over the wire. | ||||||
|  |  * If a read request is greater or equal than ISCSI_CHECKALLOC_THRES | ||||||
|  |  * sectors we check the allocation status of the area covered by the | ||||||
|  |  * request first if the allocationmap indicates that the area might be | ||||||
|  |  * unallocated. */ | ||||||
|  | #define ISCSI_CHECKALLOC_THRES 64 | ||||||
|  |  | ||||||
| static void | static void | ||||||
| iscsi_bh_cb(void *p) | iscsi_bh_cb(void *p) | ||||||
| { | { | ||||||
| @@ -273,6 +286,32 @@ static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors, | |||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void iscsi_allocationmap_set(IscsiLun *iscsilun, int64_t sector_num, | ||||||
|  |                                     int nb_sectors) | ||||||
|  | { | ||||||
|  |     if (iscsilun->allocationmap == NULL) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     bitmap_set(iscsilun->allocationmap, | ||||||
|  |                sector_num / iscsilun->cluster_sectors, | ||||||
|  |                DIV_ROUND_UP(nb_sectors, iscsilun->cluster_sectors)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void iscsi_allocationmap_clear(IscsiLun *iscsilun, int64_t sector_num, | ||||||
|  |                                       int nb_sectors) | ||||||
|  | { | ||||||
|  |     int64_t cluster_num, nb_clusters; | ||||||
|  |     if (iscsilun->allocationmap == NULL) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     cluster_num = DIV_ROUND_UP(sector_num, iscsilun->cluster_sectors); | ||||||
|  |     nb_clusters = (sector_num + nb_sectors) / iscsilun->cluster_sectors | ||||||
|  |                   - cluster_num; | ||||||
|  |     if (nb_clusters > 0) { | ||||||
|  |         bitmap_clear(iscsilun->allocationmap, cluster_num, nb_clusters); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, | static int coroutine_fn iscsi_co_writev(BlockDriverState *bs, | ||||||
|                                         int64_t sector_num, int nb_sectors, |                                         int64_t sector_num, int nb_sectors, | ||||||
|                                         QEMUIOVector *iov) |                                         QEMUIOVector *iov) | ||||||
| @@ -336,9 +375,125 @@ retry: | |||||||
|         return -EIO; |         return -EIO; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #if defined(LIBISCSI_FEATURE_IOVECTOR) | ||||||
|  | static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun, | ||||||
|  |                                              int64_t sector_num, int nb_sectors) | ||||||
|  | { | ||||||
|  |     unsigned long size; | ||||||
|  |     if (iscsilun->allocationmap == NULL) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors); | ||||||
|  |     return !(find_next_bit(iscsilun->allocationmap, size, | ||||||
|  |                            sector_num / iscsilun->cluster_sectors) == size); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, | ||||||
|  |                                                   int64_t sector_num, | ||||||
|  |                                                   int nb_sectors, int *pnum) | ||||||
|  | { | ||||||
|  |     IscsiLun *iscsilun = bs->opaque; | ||||||
|  |     struct scsi_get_lba_status *lbas = NULL; | ||||||
|  |     struct scsi_lba_status_descriptor *lbasd = NULL; | ||||||
|  |     struct IscsiTask iTask; | ||||||
|  |     int64_t ret; | ||||||
|  |  | ||||||
|  |     iscsi_co_init_iscsitask(iscsilun, &iTask); | ||||||
|  |  | ||||||
|  |     if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { | ||||||
|  |         ret = -EINVAL; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* default to all sectors allocated */ | ||||||
|  |     ret = BDRV_BLOCK_DATA; | ||||||
|  |     ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID; | ||||||
|  |     *pnum = nb_sectors; | ||||||
|  |  | ||||||
|  |     /* LUN does not support logical block provisioning */ | ||||||
|  |     if (iscsilun->lbpme == 0) { | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | retry: | ||||||
|  |     if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun, | ||||||
|  |                                   sector_qemu2lun(sector_num, iscsilun), | ||||||
|  |                                   8 + 16, iscsi_co_generic_cb, | ||||||
|  |                                   &iTask) == NULL) { | ||||||
|  |         ret = -ENOMEM; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     while (!iTask.complete) { | ||||||
|  |         iscsi_set_events(iscsilun); | ||||||
|  |         qemu_coroutine_yield(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (iTask.do_retry) { | ||||||
|  |         if (iTask.task != NULL) { | ||||||
|  |             scsi_free_scsi_task(iTask.task); | ||||||
|  |             iTask.task = NULL; | ||||||
|  |         } | ||||||
|  |         iTask.complete = 0; | ||||||
|  |         goto retry; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (iTask.status != SCSI_STATUS_GOOD) { | ||||||
|  |         /* in case the get_lba_status_callout fails (i.e. | ||||||
|  |          * because the device is busy or the cmd is not | ||||||
|  |          * supported) we pretend all blocks are allocated | ||||||
|  |          * for backwards compatibility */ | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lbas = scsi_datain_unmarshall(iTask.task); | ||||||
|  |     if (lbas == NULL) { | ||||||
|  |         ret = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lbasd = &lbas->descriptors[0]; | ||||||
|  |  | ||||||
|  |     if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) { | ||||||
|  |         ret = -EIO; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun); | ||||||
|  |  | ||||||
|  |     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || | ||||||
|  |         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { | ||||||
|  |         ret &= ~BDRV_BLOCK_DATA; | ||||||
|  |         if (iscsilun->lbprz) { | ||||||
|  |             ret |= BDRV_BLOCK_ZERO; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ret & BDRV_BLOCK_ZERO) { | ||||||
|  |         iscsi_allocationmap_clear(iscsilun, sector_num, *pnum); | ||||||
|  |     } else { | ||||||
|  |         iscsi_allocationmap_set(iscsilun, sector_num, *pnum); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (*pnum > nb_sectors) { | ||||||
|  |         *pnum = nb_sectors; | ||||||
|  |     } | ||||||
|  | out: | ||||||
|  |     if (iTask.task != NULL) { | ||||||
|  |         scsi_free_scsi_task(iTask.task); | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif /* LIBISCSI_FEATURE_IOVECTOR */ | ||||||
|  |  | ||||||
|  |  | ||||||
| static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, | static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, | ||||||
|                                        int64_t sector_num, int nb_sectors, |                                        int64_t sector_num, int nb_sectors, | ||||||
|                                        QEMUIOVector *iov) |                                        QEMUIOVector *iov) | ||||||
| @@ -355,6 +510,22 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, | |||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #if defined(LIBISCSI_FEATURE_IOVECTOR) | ||||||
|  |     if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES && | ||||||
|  |         !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) { | ||||||
|  |         int64_t ret; | ||||||
|  |         int pnum; | ||||||
|  |         ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum); | ||||||
|  |         if (ret < 0) { | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |         if (ret & BDRV_BLOCK_ZERO && pnum >= nb_sectors) { | ||||||
|  |             qemu_iovec_memset(iov, 0, 0x00, iov->size); | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     lba = sector_qemu2lun(sector_num, iscsilun); |     lba = sector_qemu2lun(sector_num, iscsilun); | ||||||
|     num_sectors = sector_qemu2lun(nb_sectors, iscsilun); |     num_sectors = sector_qemu2lun(nb_sectors, iscsilun); | ||||||
|  |  | ||||||
| @@ -643,101 +814,6 @@ iscsi_getlength(BlockDriverState *bs) | |||||||
|     return len; |     return len; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if defined(LIBISCSI_FEATURE_IOVECTOR) |  | ||||||
|  |  | ||||||
| static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs, |  | ||||||
|                                                   int64_t sector_num, |  | ||||||
|                                                   int nb_sectors, int *pnum) |  | ||||||
| { |  | ||||||
|     IscsiLun *iscsilun = bs->opaque; |  | ||||||
|     struct scsi_get_lba_status *lbas = NULL; |  | ||||||
|     struct scsi_lba_status_descriptor *lbasd = NULL; |  | ||||||
|     struct IscsiTask iTask; |  | ||||||
|     int64_t ret; |  | ||||||
|  |  | ||||||
|     iscsi_co_init_iscsitask(iscsilun, &iTask); |  | ||||||
|  |  | ||||||
|     if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { |  | ||||||
|         ret = -EINVAL; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /* default to all sectors allocated */ |  | ||||||
|     ret = BDRV_BLOCK_DATA; |  | ||||||
|     ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID; |  | ||||||
|     *pnum = nb_sectors; |  | ||||||
|  |  | ||||||
|     /* LUN does not support logical block provisioning */ |  | ||||||
|     if (iscsilun->lbpme == 0) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| retry: |  | ||||||
|     if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun, |  | ||||||
|                                   sector_qemu2lun(sector_num, iscsilun), |  | ||||||
|                                   8 + 16, iscsi_co_generic_cb, |  | ||||||
|                                   &iTask) == NULL) { |  | ||||||
|         ret = -ENOMEM; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     while (!iTask.complete) { |  | ||||||
|         iscsi_set_events(iscsilun); |  | ||||||
|         qemu_coroutine_yield(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (iTask.do_retry) { |  | ||||||
|         if (iTask.task != NULL) { |  | ||||||
|             scsi_free_scsi_task(iTask.task); |  | ||||||
|             iTask.task = NULL; |  | ||||||
|         } |  | ||||||
|         iTask.complete = 0; |  | ||||||
|         goto retry; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (iTask.status != SCSI_STATUS_GOOD) { |  | ||||||
|         /* in case the get_lba_status_callout fails (i.e. |  | ||||||
|          * because the device is busy or the cmd is not |  | ||||||
|          * supported) we pretend all blocks are allocated |  | ||||||
|          * for backwards compatibility */ |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lbas = scsi_datain_unmarshall(iTask.task); |  | ||||||
|     if (lbas == NULL) { |  | ||||||
|         ret = -EIO; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     lbasd = &lbas->descriptors[0]; |  | ||||||
|  |  | ||||||
|     if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) { |  | ||||||
|         ret = -EIO; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun); |  | ||||||
|     if (*pnum > nb_sectors) { |  | ||||||
|         *pnum = nb_sectors; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || |  | ||||||
|         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { |  | ||||||
|         ret &= ~BDRV_BLOCK_DATA; |  | ||||||
|         if (iscsilun->lbprz) { |  | ||||||
|             ret |= BDRV_BLOCK_ZERO; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| out: |  | ||||||
|     if (iTask.task != NULL) { |  | ||||||
|         scsi_free_scsi_task(iTask.task); |  | ||||||
|     } |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif /* LIBISCSI_FEATURE_IOVECTOR */ |  | ||||||
|  |  | ||||||
| static int | static int | ||||||
| coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num, | coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num, | ||||||
|                                    int nb_sectors) |                                    int nb_sectors) | ||||||
| @@ -791,6 +867,8 @@ retry: | |||||||
|         return -EIO; |         return -EIO; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors); | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -809,13 +887,14 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, | |||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) { |     if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) { | ||||||
|         /* WRITE SAME without UNMAP is not supported by the target */ |         /* WRITE SAME with UNMAP is not supported by the target, | ||||||
|         return -ENOTSUP; |          * fall back and try WRITE SAME without UNMAP */ | ||||||
|  |         flags &= ~BDRV_REQ_MAY_UNMAP; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) { |     if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) { | ||||||
|         /* WRITE SAME with UNMAP is not supported by the target */ |         /* WRITE SAME without UNMAP is not supported by the target */ | ||||||
|         return -ENOTSUP; |         return -ENOTSUP; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -864,6 +943,12 @@ retry: | |||||||
|         return -EIO; |         return -EIO; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (flags & BDRV_REQ_MAY_UNMAP) { | ||||||
|  |         iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors); | ||||||
|  |     } else { | ||||||
|  |         iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1295,6 +1380,22 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); |     timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  |     /* Guess the internal cluster (page) size of the iscsi target by the means | ||||||
|  |      * of opt_unmap_gran. Transfer the unmap granularity only if it has a | ||||||
|  |      * reasonable size */ | ||||||
|  |     if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 4 * 1024 && | ||||||
|  |         iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) { | ||||||
|  |         iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran * | ||||||
|  |                                      iscsilun->block_size) >> BDRV_SECTOR_BITS; | ||||||
|  | #if defined(LIBISCSI_FEATURE_IOVECTOR) | ||||||
|  |         if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) { | ||||||
|  |             iscsilun->allocationmap = | ||||||
|  |                 bitmap_new(DIV_ROUND_UP(bs->total_sectors, | ||||||
|  |                                         iscsilun->cluster_sectors)); | ||||||
|  |         } | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |  | ||||||
| out: | out: | ||||||
|     qemu_opts_del(opts); |     qemu_opts_del(opts); | ||||||
|     if (initiator_name != NULL) { |     if (initiator_name != NULL) { | ||||||
| @@ -1328,6 +1429,7 @@ static void iscsi_close(BlockDriverState *bs) | |||||||
|     qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL); |     qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL); | ||||||
|     iscsi_destroy_context(iscsi); |     iscsi_destroy_context(iscsi); | ||||||
|     g_free(iscsilun->zeroblock); |     g_free(iscsilun->zeroblock); | ||||||
|  |     g_free(iscsilun->allocationmap); | ||||||
|     memset(iscsilun, 0, sizeof(IscsiLun)); |     memset(iscsilun, 0, sizeof(IscsiLun)); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1388,6 +1490,13 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset) | |||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (iscsilun->allocationmap != NULL) { | ||||||
|  |         g_free(iscsilun->allocationmap); | ||||||
|  |         iscsilun->allocationmap = | ||||||
|  |             bitmap_new(DIV_ROUND_UP(bs->total_sectors, | ||||||
|  |                                     iscsilun->cluster_sectors)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1450,13 +1559,7 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) | |||||||
|     IscsiLun *iscsilun = bs->opaque; |     IscsiLun *iscsilun = bs->opaque; | ||||||
|     bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz; |     bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz; | ||||||
|     bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws; |     bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws; | ||||||
|     /* Guess the internal cluster (page) size of the iscsi target by the means |     bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE; | ||||||
|      * of opt_unmap_gran. Transfer the unmap granularity only if it has a |  | ||||||
|      * reasonable size for bdi->cluster_size */ |  | ||||||
|     if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 64 * 1024 && |  | ||||||
|         iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) { |  | ||||||
|         bdi->cluster_size = iscsilun->bl.opt_unmap_gran * iscsilun->block_size; |  | ||||||
|     } |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -50,6 +50,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     info->backing_file_depth = bdrv_get_backing_file_depth(bs); |     info->backing_file_depth = bdrv_get_backing_file_depth(bs); | ||||||
|  |     info->detect_zeroes = bs->detect_zeroes; | ||||||
|  |  | ||||||
|     if (bs->io_limits_enabled) { |     if (bs->io_limits_enabled) { | ||||||
|         ThrottleConfig cfg; |         ThrottleConfig cfg; | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								block/qcow.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								block/qcow.c
									
									
									
									
									
								
							| @@ -48,9 +48,10 @@ typedef struct QCowHeader { | |||||||
|     uint64_t size; /* in bytes */ |     uint64_t size; /* in bytes */ | ||||||
|     uint8_t cluster_bits; |     uint8_t cluster_bits; | ||||||
|     uint8_t l2_bits; |     uint8_t l2_bits; | ||||||
|  |     uint16_t padding; | ||||||
|     uint32_t crypt_method; |     uint32_t crypt_method; | ||||||
|     uint64_t l1_table_offset; |     uint64_t l1_table_offset; | ||||||
| } QCowHeader; | } QEMU_PACKED QCowHeader; | ||||||
|  |  | ||||||
| #define L2_CACHE_SIZE 16 | #define L2_CACHE_SIZE 16 | ||||||
|  |  | ||||||
| @@ -60,7 +61,7 @@ typedef struct BDRVQcowState { | |||||||
|     int cluster_sectors; |     int cluster_sectors; | ||||||
|     int l2_bits; |     int l2_bits; | ||||||
|     int l2_size; |     int l2_size; | ||||||
|     int l1_size; |     unsigned int l1_size; | ||||||
|     uint64_t cluster_offset_mask; |     uint64_t cluster_offset_mask; | ||||||
|     uint64_t l1_table_offset; |     uint64_t l1_table_offset; | ||||||
|     uint64_t *l1_table; |     uint64_t *l1_table; | ||||||
| @@ -96,7 +97,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|                      Error **errp) |                      Error **errp) | ||||||
| { | { | ||||||
|     BDRVQcowState *s = bs->opaque; |     BDRVQcowState *s = bs->opaque; | ||||||
|     int len, i, shift, ret; |     unsigned int len, i, shift; | ||||||
|  |     int ret; | ||||||
|     QCowHeader header; |     QCowHeader header; | ||||||
|  |  | ||||||
|     ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); |     ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); | ||||||
| @@ -127,11 +129,25 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (header.size <= 1 || header.cluster_bits < 9) { |     if (header.size <= 1) { | ||||||
|         error_setg(errp, "invalid value in qcow header"); |         error_setg(errp, "Image size is too small (must be at least 2 bytes)"); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
|         goto fail; |         goto fail; | ||||||
|     } |     } | ||||||
|  |     if (header.cluster_bits < 9 || header.cluster_bits > 16) { | ||||||
|  |         error_setg(errp, "Cluster size must be between 512 and 64k"); | ||||||
|  |         ret = -EINVAL; | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /* l2_bits specifies number of entries; storing a uint64_t in each entry, | ||||||
|  |      * so bytes = num_entries << 3. */ | ||||||
|  |     if (header.l2_bits < 9 - 3 || header.l2_bits > 16 - 3) { | ||||||
|  |         error_setg(errp, "L2 table size must be between 512 and 64k"); | ||||||
|  |         ret = -EINVAL; | ||||||
|  |         goto fail; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (header.crypt_method > QCOW_CRYPT_AES) { |     if (header.crypt_method > QCOW_CRYPT_AES) { | ||||||
|         error_setg(errp, "invalid encryption method in qcow header"); |         error_setg(errp, "invalid encryption method in qcow header"); | ||||||
|         ret = -EINVAL; |         ret = -EINVAL; | ||||||
| @@ -151,7 +167,19 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|  |  | ||||||
|     /* read the level 1 table */ |     /* read the level 1 table */ | ||||||
|     shift = s->cluster_bits + s->l2_bits; |     shift = s->cluster_bits + s->l2_bits; | ||||||
|     s->l1_size = (header.size + (1LL << shift) - 1) >> shift; |     if (header.size > UINT64_MAX - (1LL << shift)) { | ||||||
|  |         error_setg(errp, "Image too large"); | ||||||
|  |         ret = -EINVAL; | ||||||
|  |         goto fail; | ||||||
|  |     } else { | ||||||
|  |         uint64_t l1_size = (header.size + (1LL << shift) - 1) >> shift; | ||||||
|  |         if (l1_size > INT_MAX / sizeof(uint64_t)) { | ||||||
|  |             error_setg(errp, "Image too large"); | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             goto fail; | ||||||
|  |         } | ||||||
|  |         s->l1_size = l1_size; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     s->l1_table_offset = header.l1_table_offset; |     s->l1_table_offset = header.l1_table_offset; | ||||||
|     s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t)); |     s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t)); | ||||||
| @@ -175,7 +203,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, | |||||||
|     if (header.backing_file_offset != 0) { |     if (header.backing_file_offset != 0) { | ||||||
|         len = header.backing_file_size; |         len = header.backing_file_size; | ||||||
|         if (len > 1023) { |         if (len > 1023) { | ||||||
|             len = 1023; |             error_setg(errp, "Backing file name too long"); | ||||||
|  |             ret = -EINVAL; | ||||||
|  |             goto fail; | ||||||
|         } |         } | ||||||
|         ret = bdrv_pread(bs->file, header.backing_file_offset, |         ret = bdrv_pread(bs->file, header.backing_file_offset, | ||||||
|                    bs->backing_file, len); |                    bs->backing_file, len); | ||||||
|   | |||||||
| @@ -472,10 +472,17 @@ static void vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s, | |||||||
|             s->curr_header = 0; |             s->curr_header = 0; | ||||||
|         } else if (h2_seq > h1_seq) { |         } else if (h2_seq > h1_seq) { | ||||||
|             s->curr_header = 1; |             s->curr_header = 1; | ||||||
|  |         } else { | ||||||
|  |             /* The Microsoft Disk2VHD tool will create 2 identical | ||||||
|  |              * headers, with identical sequence numbers.  If the headers are | ||||||
|  |              * identical, don't consider the file corrupt */ | ||||||
|  |             if (!memcmp(header1, header2, sizeof(VHDXHeader))) { | ||||||
|  |                 s->curr_header = 0; | ||||||
|             } else { |             } else { | ||||||
|                 goto fail; |                 goto fail; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     vhdx_region_register(s, s->headers[s->curr_header]->log_offset, |     vhdx_region_register(s, s->headers[s->curr_header]->log_offset, | ||||||
|                             s->headers[s->curr_header]->log_length); |                             s->headers[s->curr_header]->log_length); | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								blockdev.c
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								blockdev.c
									
									
									
									
									
								
							| @@ -288,6 +288,25 @@ static int parse_block_error_action(const char *buf, bool is_read, Error **errp) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static inline int parse_enum_option(const char *lookup[], const char *buf, | ||||||
|  |                                     int max, int def, Error **errp) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     if (!buf) { | ||||||
|  |         return def; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (i = 0; i < max; i++) { | ||||||
|  |         if (!strcmp(buf, lookup[i])) { | ||||||
|  |             return i; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     error_setg(errp, "invalid parameter value: %s", buf); | ||||||
|  |     return def; | ||||||
|  | } | ||||||
|  |  | ||||||
| static bool check_throttle_config(ThrottleConfig *cfg, Error **errp) | static bool check_throttle_config(ThrottleConfig *cfg, Error **errp) | ||||||
| { | { | ||||||
|     if (throttle_conflicting(cfg)) { |     if (throttle_conflicting(cfg)) { | ||||||
| @@ -324,6 +343,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, | |||||||
|     QemuOpts *opts; |     QemuOpts *opts; | ||||||
|     const char *id; |     const char *id; | ||||||
|     bool has_driver_specific_opts; |     bool has_driver_specific_opts; | ||||||
|  |     BlockdevDetectZeroesOptions detect_zeroes; | ||||||
|     BlockDriver *drv = NULL; |     BlockDriver *drv = NULL; | ||||||
|  |  | ||||||
|     /* Check common options by copying from bs_opts to opts, all other options |     /* Check common options by copying from bs_opts to opts, all other options | ||||||
| @@ -452,6 +472,24 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     detect_zeroes = | ||||||
|  |         parse_enum_option(BlockdevDetectZeroesOptions_lookup, | ||||||
|  |                           qemu_opt_get(opts, "detect-zeroes"), | ||||||
|  |                           BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX, | ||||||
|  |                           BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, | ||||||
|  |                           &error); | ||||||
|  |     if (error) { | ||||||
|  |         error_propagate(errp, error); | ||||||
|  |         goto early_err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && | ||||||
|  |         !(bdrv_flags & BDRV_O_UNMAP)) { | ||||||
|  |         error_setg(errp, "setting detect-zeroes to unmap is not allowed " | ||||||
|  |                          "without setting discard operation to unmap"); | ||||||
|  |         goto early_err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /* init */ |     /* init */ | ||||||
|     dinfo = g_malloc0(sizeof(*dinfo)); |     dinfo = g_malloc0(sizeof(*dinfo)); | ||||||
|     dinfo->id = g_strdup(qemu_opts_id(opts)); |     dinfo->id = g_strdup(qemu_opts_id(opts)); | ||||||
| @@ -462,6 +500,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts, | |||||||
|     } |     } | ||||||
|     dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; |     dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0; | ||||||
|     dinfo->bdrv->read_only = ro; |     dinfo->bdrv->read_only = ro; | ||||||
|  |     dinfo->bdrv->detect_zeroes = detect_zeroes; | ||||||
|     dinfo->refcount = 1; |     dinfo->refcount = 1; | ||||||
|     if (serial != NULL) { |     if (serial != NULL) { | ||||||
|         dinfo->serial = g_strdup(serial); |         dinfo->serial = g_strdup(serial); | ||||||
| @@ -2455,6 +2494,10 @@ QemuOptsList qemu_common_drive_opts = { | |||||||
|             .name = "copy-on-read", |             .name = "copy-on-read", | ||||||
|             .type = QEMU_OPT_BOOL, |             .type = QEMU_OPT_BOOL, | ||||||
|             .help = "copy read data from backing file into image file", |             .help = "copy read data from backing file into image file", | ||||||
|  |         },{ | ||||||
|  |             .name = "detect-zeroes", | ||||||
|  |             .type = QEMU_OPT_STRING, | ||||||
|  |             .help = "try to optimize zero writes (off, on, unmap)", | ||||||
|         }, |         }, | ||||||
|         { /* end of list */ } |         { /* end of list */ } | ||||||
|     }, |     }, | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| CONFIG_VIRTIO=y | CONFIG_VIRTIO=y | ||||||
| CONFIG_SCLPCONSOLE=y | CONFIG_SCLPCONSOLE=y | ||||||
| CONFIG_S390_FLIC=$(CONFIG_KVM) | CONFIG_S390_FLIC=y | ||||||
|  | CONFIG_S390_FLIC_KVM=$(CONFIG_KVM) | ||||||
|   | |||||||
							
								
								
									
										76
									
								
								docs/multiseat.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								docs/multiseat.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | |||||||
|  |  | ||||||
|  | multiseat howto (with some multihead coverage) | ||||||
|  | ============================================== | ||||||
|  |  | ||||||
|  | host side | ||||||
|  | --------- | ||||||
|  |  | ||||||
|  | First you must compile qemu with a user interface supporting | ||||||
|  | multihead/multiseat and input event routing.  Right now this list is | ||||||
|  | pretty short: sdl2. | ||||||
|  |  | ||||||
|  |   ./configure --enable-sdl --with-sdlabi=2.0 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Next put together the qemu command line: | ||||||
|  |  | ||||||
|  | qemu	-enable-kvm -usb $memory $disk $whatever \ | ||||||
|  | 	-display sdl \ | ||||||
|  | 	-vga std \ | ||||||
|  | 	-device usb-tablet | ||||||
|  |  | ||||||
|  | That is it for the first head, which will use the standard vga, the | ||||||
|  | standard ps/2 keyboard (implicitly there) and the usb-tablet.  Now the | ||||||
|  | additional switches for the second head: | ||||||
|  |  | ||||||
|  | 	-device pci-bridge,addr=12.0,chassis_nr=2,id=head.2 \ | ||||||
|  | 	-device secondary-vga,bus=head.2,addr=02.0,id=video.2 \ | ||||||
|  | 	-device nec-usb-xhci,bus=head.2,addr=0f.0,id=usb.2 \ | ||||||
|  | 	-device usb-kbd,bus=usb.2.0,port=1,display=video.2 \ | ||||||
|  | 	-device usb-tablet,bus=usb.2.0,port=2,display=video.2 | ||||||
|  |  | ||||||
|  | This places a pci bridge in slot 12, connects a display adapter and | ||||||
|  | xhci (usb) controller to the bridge.  Then it adds a usb keyboard and | ||||||
|  | usb mouse, both connected to the xhci and linked to the display. | ||||||
|  |  | ||||||
|  | The "display=video2" sets up the input routing.  Any input coming from | ||||||
|  | the window which belongs to the video.2 display adapter will be routed | ||||||
|  | to these input devices. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | guest side | ||||||
|  | ---------- | ||||||
|  |  | ||||||
|  | You need a pretty recent linux guest.  systemd with loginctl.  kernel | ||||||
|  | 3.14+ with CONFIG_DRM_BOCHS enabled.  Fedora 20 will do.  Must be | ||||||
|  | fully updated for the new kernel though, i.e. the live iso doesn't cut | ||||||
|  | it. | ||||||
|  |  | ||||||
|  | Now we'll have to configure the guest.  Boot and login.  By default | ||||||
|  | all devices belong to seat0.  You can use "loginctl seat-status seat0" | ||||||
|  | to list them all (and to get the sysfs paths for cut+paste).  Now | ||||||
|  | we'll go assign all pci devices connected the pci bridge in slot 12 to | ||||||
|  | a new head: | ||||||
|  |  | ||||||
|  | loginctl attach seat-qemu \ | ||||||
|  | 	 /sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/drm/card1 | ||||||
|  | loginctl attach seat-qemu \ | ||||||
|  | 	 /sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/graphics/fb1 | ||||||
|  | loginctl attach seat-qemu \ | ||||||
|  | 	 /sys/devices/pci0000:00/0000:00:12.0/0000:01:0f.0/usb2 | ||||||
|  |  | ||||||
|  | Use "loginctl seat-status seat-qemu" to check the result.  It isn't | ||||||
|  | needed to assign the usb devices to the head individually, assigning a | ||||||
|  | usb (root) hub will automatically assign all usb devices connected to | ||||||
|  | it too. | ||||||
|  |  | ||||||
|  | BTW: loginctl writes udev rules to /etc/udev/rules.d to make these | ||||||
|  | device assignments permanent, so you need to do this only once. | ||||||
|  |  | ||||||
|  | Now simply restart gdm (rebooting will do too), and a login screen | ||||||
|  | should show up on the second head. | ||||||
|  |  | ||||||
|  | Enjoy! | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | Gerd Hoffmann <kraxel@redhat.com> | ||||||
| @@ -48,7 +48,7 @@ The QAPI schema definitions can be modularized using the 'include' directive: | |||||||
|  { 'include': 'path/to/file.json'} |  { 'include': 'path/to/file.json'} | ||||||
|  |  | ||||||
| The directive is evaluated recursively, and include paths are relative to the | The directive is evaluated recursively, and include paths are relative to the | ||||||
| file using the directive. | file using the directive. Multiple includes of the same file are safe. | ||||||
|  |  | ||||||
|  |  | ||||||
| === Complex types === | === Complex types === | ||||||
| @@ -230,14 +230,13 @@ node structure that can be used to chain together a list of such types in | |||||||
| case we want to accept/return a list of this type with a command), and a | case we want to accept/return a list of this type with a command), and a | ||||||
| command which takes that type as a parameter and returns the same type: | command which takes that type as a parameter and returns the same type: | ||||||
|  |  | ||||||
|     mdroth@illuin:~/w/qemu2.git$ cat example-schema.json |     $ cat example-schema.json | ||||||
|     { 'type': 'UserDefOne', |     { 'type': 'UserDefOne', | ||||||
|       'data': { 'integer': 'int', 'string': 'str' } } |       'data': { 'integer': 'int', 'string': 'str' } } | ||||||
|  |  | ||||||
|     { 'command': 'my-command', |     { 'command': 'my-command', | ||||||
|       'data':    {'arg1': 'UserDefOne'}, |       'data':    {'arg1': 'UserDefOne'}, | ||||||
|       'returns': 'UserDefOne' } |       'returns': 'UserDefOne' } | ||||||
|     mdroth@illuin:~/w/qemu2.git$ |  | ||||||
|  |  | ||||||
| === scripts/qapi-types.py === | === scripts/qapi-types.py === | ||||||
|  |  | ||||||
| @@ -255,14 +254,25 @@ created code. | |||||||
|  |  | ||||||
| Example: | Example: | ||||||
|  |  | ||||||
|     mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \ |     $ python scripts/qapi-types.py --output-dir="qapi-generated" \ | ||||||
|       --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json |     --prefix="example-" --input-file=example-schema.json | ||||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c |     $ cat qapi-generated/example-qapi-types.c | ||||||
|     /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ | [Uninteresting stuff omitted...] | ||||||
|  |  | ||||||
|     #include "qapi/qapi-dealloc-visitor.h" |     void qapi_free_UserDefOneList(UserDefOneList * obj) | ||||||
|     #include "example-qapi-types.h" |     { | ||||||
|     #include "example-qapi-visit.h" |         QapiDeallocVisitor *md; | ||||||
|  |         Visitor *v; | ||||||
|  |  | ||||||
|  |         if (!obj) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         md = qapi_dealloc_visitor_new(); | ||||||
|  |         v = qapi_dealloc_get_visitor(md); | ||||||
|  |         visit_type_UserDefOneList(v, &obj, NULL, NULL); | ||||||
|  |         qapi_dealloc_visitor_cleanup(md); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void qapi_free_UserDefOne(UserDefOne * obj) |     void qapi_free_UserDefOne(UserDefOne * obj) | ||||||
|     { |     { | ||||||
| @@ -279,32 +289,38 @@ Example: | |||||||
|         qapi_dealloc_visitor_cleanup(md); |         qapi_dealloc_visitor_cleanup(md); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h |     $ cat qapi-generated/example-qapi-types.h | ||||||
|     /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ | [Uninteresting stuff omitted...] | ||||||
|     #ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES |  | ||||||
|     #define QAPI_GENERATED_EXAMPLE_QAPI_TYPES |  | ||||||
|  |  | ||||||
|     #include "qapi/qapi-types-core.h" |     #ifndef EXAMPLE_QAPI_TYPES_H | ||||||
|  |     #define EXAMPLE_QAPI_TYPES_H | ||||||
|  |  | ||||||
|  | [Builtin types omitted...] | ||||||
|  |  | ||||||
|     typedef struct UserDefOne UserDefOne; |     typedef struct UserDefOne UserDefOne; | ||||||
|  |  | ||||||
|     typedef struct UserDefOneList |     typedef struct UserDefOneList | ||||||
|     { |     { | ||||||
|  |         union { | ||||||
|             UserDefOne *value; |             UserDefOne *value; | ||||||
|  |             uint64_t padding; | ||||||
|  |         }; | ||||||
|         struct UserDefOneList *next; |         struct UserDefOneList *next; | ||||||
|     } UserDefOneList; |     } UserDefOneList; | ||||||
|  |  | ||||||
|  | [Functions on builtin types omitted...] | ||||||
|  |  | ||||||
|     struct UserDefOne |     struct UserDefOne | ||||||
|     { |     { | ||||||
|         int64_t integer; |         int64_t integer; | ||||||
|         char * string; |         char * string; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     void qapi_free_UserDefOneList(UserDefOneList * obj); | ||||||
|     void qapi_free_UserDefOne(UserDefOne * obj); |     void qapi_free_UserDefOne(UserDefOne * obj); | ||||||
|  |  | ||||||
|     #endif |     #endif | ||||||
|  |  | ||||||
|  |  | ||||||
| === scripts/qapi-visit.py === | === scripts/qapi-visit.py === | ||||||
|  |  | ||||||
| Used to generate the visitor functions used to walk through and convert | Used to generate the visitor functions used to walk through and convert | ||||||
| @@ -325,51 +341,78 @@ $(prefix)qapi-visit.h: declarations for previously mentioned visitor | |||||||
|  |  | ||||||
| Example: | Example: | ||||||
|  |  | ||||||
|     mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \ |     $ python scripts/qapi-visit.py --output-dir="qapi-generated" | ||||||
|         --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json |     --prefix="example-" --input-file=example-schema.json | ||||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c |     $ cat qapi-generated/example-qapi-visit.c | ||||||
|     /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | [Uninteresting stuff omitted...] | ||||||
|  |  | ||||||
|     #include "example-qapi-visit.h" |     static void visit_type_UserDefOne_fields(Visitor *m, UserDefOne ** obj, Error **errp) | ||||||
|  |     { | ||||||
|  |         Error *err = NULL; | ||||||
|  |         visit_type_int(m, &(*obj)->integer, "integer", &err); | ||||||
|  |         if (err) { | ||||||
|  |             goto out; | ||||||
|  |         } | ||||||
|  |         visit_type_str(m, &(*obj)->string, "string", &err); | ||||||
|  |         if (err) { | ||||||
|  |             goto out; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     out: | ||||||
|  |         error_propagate(errp, err); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp) |     void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp) | ||||||
|     { |     { | ||||||
|         visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp); |         Error *err = NULL; | ||||||
|         visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp); |  | ||||||
|         visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp); |         visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err); | ||||||
|         visit_end_struct(m, errp); |         if (!err) { | ||||||
|  |             if (*obj) { | ||||||
|  |                 visit_type_UserDefOne_fields(m, obj, errp); | ||||||
|  |             } | ||||||
|  |             visit_end_struct(m, &err); | ||||||
|  |         } | ||||||
|  |         error_propagate(errp, err); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp) |     void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp) | ||||||
|     { |     { | ||||||
|         GenericList *i, **prev = (GenericList **)obj; |         Error *err = NULL; | ||||||
|  |         GenericList *i, **prev; | ||||||
|  |  | ||||||
|         visit_start_list(m, name, errp); |         visit_start_list(m, name, &err); | ||||||
|  |         if (err) { | ||||||
|  |             goto out; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) { |         for (prev = (GenericList **)obj; | ||||||
|  |              !err && (i = visit_next_list(m, prev, &err)) != NULL; | ||||||
|  |              prev = &i) { | ||||||
|             UserDefOneList *native_i = (UserDefOneList *)i; |             UserDefOneList *native_i = (UserDefOneList *)i; | ||||||
|             visit_type_UserDefOne(m, &native_i->value, NULL, errp); |             visit_type_UserDefOne(m, &native_i->value, NULL, &err); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         visit_end_list(m, errp); |         error_propagate(errp, err); | ||||||
|  |         err = NULL; | ||||||
|  |         visit_end_list(m, &err); | ||||||
|  |     out: | ||||||
|  |         error_propagate(errp, err); | ||||||
|     } |     } | ||||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h |     $ python scripts/qapi-commands.py --output-dir="qapi-generated" \ | ||||||
|     /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ |     --prefix="example-" --input-file=example-schema.json | ||||||
|  |     $ cat qapi-generated/example-qapi-visit.h | ||||||
|  | [Uninteresting stuff omitted...] | ||||||
|  |  | ||||||
|     #ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT |     #ifndef EXAMPLE_QAPI_VISIT_H | ||||||
|     #define QAPI_GENERATED_EXAMPLE_QAPI_VISIT |     #define EXAMPLE_QAPI_VISIT_H | ||||||
|  |  | ||||||
|     #include "qapi/qapi-visit-core.h" | [Visitors for builtin types omitted...] | ||||||
|     #include "example-qapi-types.h" |  | ||||||
|  |  | ||||||
|     void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp); |     void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp); | ||||||
|     void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp); |     void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp); | ||||||
|  |  | ||||||
|     #endif |     #endif | ||||||
|     mdroth@illuin:~/w/qemu2.git$ |  | ||||||
|  |  | ||||||
| (The actual structure of the visit_type_* functions is a bit more complex |  | ||||||
| in order to propagate errors correctly and avoid leaking memory). |  | ||||||
|  |  | ||||||
| === scripts/qapi-commands.py === | === scripts/qapi-commands.py === | ||||||
|  |  | ||||||
| @@ -390,77 +433,80 @@ $(prefix)qmp-commands.h: Function prototypes for the QMP commands | |||||||
|  |  | ||||||
| Example: | Example: | ||||||
|  |  | ||||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c |     $ cat qapi-generated/example-qmp-marshal.c | ||||||
|     /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | [Uninteresting stuff omitted...] | ||||||
|  |  | ||||||
|     #include "qemu-objects.h" |  | ||||||
|     #include "qapi/qmp-core.h" |  | ||||||
|     #include "qapi/qapi-visit-core.h" |  | ||||||
|     #include "qapi/qmp-output-visitor.h" |  | ||||||
|     #include "qapi/qmp-input-visitor.h" |  | ||||||
|     #include "qapi/qapi-dealloc-visitor.h" |  | ||||||
|     #include "example-qapi-types.h" |  | ||||||
|     #include "example-qapi-visit.h" |  | ||||||
|  |  | ||||||
|     #include "example-qmp-commands.h" |  | ||||||
|     static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp) |     static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp) | ||||||
|     { |     { | ||||||
|         QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); |         Error *local_err = NULL; | ||||||
|         QmpOutputVisitor *mo = qmp_output_visitor_new(); |         QmpOutputVisitor *mo = qmp_output_visitor_new(); | ||||||
|  |         QapiDeallocVisitor *md; | ||||||
|         Visitor *v; |         Visitor *v; | ||||||
|  |  | ||||||
|         v = qmp_output_get_visitor(mo); |         v = qmp_output_get_visitor(mo); | ||||||
|         visit_type_UserDefOne(v, &ret_in, "unused", errp); |         visit_type_UserDefOne(v, &ret_in, "unused", &local_err); | ||||||
|         v = qapi_dealloc_get_visitor(md); |         if (local_err) { | ||||||
|         visit_type_UserDefOne(v, &ret_in, "unused", errp); |             goto out; | ||||||
|         qapi_dealloc_visitor_cleanup(md); |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         *ret_out = qmp_output_get_qobject(mo); |         *ret_out = qmp_output_get_qobject(mo); | ||||||
|  |  | ||||||
|  |     out: | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|  |         qmp_output_visitor_cleanup(mo); | ||||||
|  |         md = qapi_dealloc_visitor_new(); | ||||||
|  |         v = qapi_dealloc_get_visitor(md); | ||||||
|  |         visit_type_UserDefOne(v, &ret_in, "unused", NULL); | ||||||
|  |         qapi_dealloc_visitor_cleanup(md); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     static void qmp_marshal_input_my_command(QmpState *qmp__sess, QDict *args, QObject **ret, Error **errp) |     static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp) | ||||||
|     { |     { | ||||||
|  |         Error *local_err = NULL; | ||||||
|         UserDefOne * retval = NULL; |         UserDefOne * retval = NULL; | ||||||
|         QmpInputVisitor *mi; |         QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); | ||||||
|         QapiDeallocVisitor *md; |         QapiDeallocVisitor *md; | ||||||
|         Visitor *v; |         Visitor *v; | ||||||
|         UserDefOne * arg1 = NULL; |         UserDefOne * arg1 = NULL; | ||||||
|  |  | ||||||
|         mi = qmp_input_visitor_new(QOBJECT(args)); |  | ||||||
|         v = qmp_input_get_visitor(mi); |         v = qmp_input_get_visitor(mi); | ||||||
|         visit_type_UserDefOne(v, &arg1, "arg1", errp); |         visit_type_UserDefOne(v, &arg1, "arg1", &local_err); | ||||||
|  |         if (local_err) { | ||||||
|         if (error_is_set(errp)) { |  | ||||||
|             goto out; |             goto out; | ||||||
|         } |         } | ||||||
|         retval = qmp_my_command(arg1, errp); |  | ||||||
|         qmp_marshal_output_my_command(retval, ret, errp); |         retval = qmp_my_command(arg1, &local_err); | ||||||
|  |         if (local_err) { | ||||||
|  |             goto out; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         qmp_marshal_output_my_command(retval, ret, &local_err); | ||||||
|  |  | ||||||
|     out: |     out: | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|  |         qmp_input_visitor_cleanup(mi); | ||||||
|         md = qapi_dealloc_visitor_new(); |         md = qapi_dealloc_visitor_new(); | ||||||
|         v = qapi_dealloc_get_visitor(md); |         v = qapi_dealloc_get_visitor(md); | ||||||
|         visit_type_UserDefOne(v, &arg1, "arg1", errp); |         visit_type_UserDefOne(v, &arg1, "arg1", NULL); | ||||||
|         qapi_dealloc_visitor_cleanup(md); |         qapi_dealloc_visitor_cleanup(md); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     static void qmp_init_marshal(void) |     static void qmp_init_marshal(void) | ||||||
|     { |     { | ||||||
|         qmp_register_command("my-command", qmp_marshal_input_my_command); |         qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qapi_init(qmp_init_marshal); |     qapi_init(qmp_init_marshal); | ||||||
|     mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h |     $ cat qapi-generated/example-qmp-commands.h | ||||||
|     /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | [Uninteresting stuff omitted...] | ||||||
|  |  | ||||||
|     #ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS |     #ifndef EXAMPLE_QMP_COMMANDS_H | ||||||
|     #define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS |     #define EXAMPLE_QMP_COMMANDS_H | ||||||
|  |  | ||||||
|     #include "example-qapi-types.h" |     #include "example-qapi-types.h" | ||||||
|     #include "error.h" |     #include "qapi/qmp/qdict.h" | ||||||
|  |     #include "qapi/error.h" | ||||||
|  |  | ||||||
|     UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp); |     UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp); | ||||||
|  |  | ||||||
|     #endif |     #endif | ||||||
|     mdroth@illuin:~/w/qemu2.git$ |  | ||||||
|   | |||||||
| @@ -556,6 +556,7 @@ ETEXI | |||||||
|         .params     = "keys [hold_ms]", |         .params     = "keys [hold_ms]", | ||||||
|         .help       = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", |         .help       = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", | ||||||
|         .mhandler.cmd = hmp_send_key, |         .mhandler.cmd = hmp_send_key, | ||||||
|  |         .command_completion = sendkey_completion, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| STEXI | STEXI | ||||||
| @@ -1233,9 +1234,10 @@ ETEXI | |||||||
|     { |     { | ||||||
|         .name       = "netdev_add", |         .name       = "netdev_add", | ||||||
|         .args_type  = "netdev:O", |         .args_type  = "netdev:O", | ||||||
|         .params     = "[user|tap|socket|hubport|netmap],id=str[,prop=value][,...]", |         .params     = "[user|tap|socket|vde|bridge|hubport|netmap],id=str[,prop=value][,...]", | ||||||
|         .help       = "add host network device", |         .help       = "add host network device", | ||||||
|         .mhandler.cmd = hmp_netdev_add, |         .mhandler.cmd = hmp_netdev_add, | ||||||
|  |         .command_completion = netdev_add_completion, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| STEXI | STEXI | ||||||
| @@ -1250,6 +1252,7 @@ ETEXI | |||||||
|         .params     = "id", |         .params     = "id", | ||||||
|         .help       = "remove host network device", |         .help       = "remove host network device", | ||||||
|         .mhandler.cmd = hmp_netdev_del, |         .mhandler.cmd = hmp_netdev_del, | ||||||
|  |         .command_completion = netdev_del_completion, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| STEXI | STEXI | ||||||
| @@ -1339,6 +1342,7 @@ ETEXI | |||||||
|         .params     = "name on|off", |         .params     = "name on|off", | ||||||
|         .help       = "change the link status of a network adapter", |         .help       = "change the link status of a network adapter", | ||||||
|         .mhandler.cmd = hmp_set_link, |         .mhandler.cmd = hmp_set_link, | ||||||
|  |         .command_completion = set_link_completion, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| STEXI | STEXI | ||||||
| @@ -1622,6 +1626,7 @@ ETEXI | |||||||
|         .params     = "args", |         .params     = "args", | ||||||
|         .help       = "add chardev", |         .help       = "add chardev", | ||||||
|         .mhandler.cmd = hmp_chardev_add, |         .mhandler.cmd = hmp_chardev_add, | ||||||
|  |         .command_completion = chardev_add_completion, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| STEXI | STEXI | ||||||
| @@ -1638,6 +1643,7 @@ ETEXI | |||||||
|         .params     = "id", |         .params     = "id", | ||||||
|         .help       = "remove chardev", |         .help       = "remove chardev", | ||||||
|         .mhandler.cmd = hmp_chardev_remove, |         .mhandler.cmd = hmp_chardev_remove, | ||||||
|  |         .command_completion = chardev_remove_completion, | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
| STEXI | STEXI | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								hmp.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								hmp.c
									
									
									
									
									
								
							| @@ -341,6 +341,11 @@ void hmp_info_block(Monitor *mon, const QDict *qdict) | |||||||
|                            info->value->inserted->backing_file_depth); |                            info->value->inserted->backing_file_depth); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if (info->value->inserted->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF) { | ||||||
|  |             monitor_printf(mon, "    Detect zeroes:    %s\n", | ||||||
|  |                            BlockdevDetectZeroesOptions_lookup[info->value->inserted->detect_zeroes]); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (info->value->inserted->bps |         if (info->value->inserted->bps | ||||||
|             || info->value->inserted->bps_rd |             || info->value->inserted->bps_rd | ||||||
|             || info->value->inserted->bps_wr |             || info->value->inserted->bps_wr | ||||||
| @@ -1388,6 +1393,7 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict) | |||||||
| void hmp_object_add(Monitor *mon, const QDict *qdict) | void hmp_object_add(Monitor *mon, const QDict *qdict) | ||||||
| { | { | ||||||
|     Error *err = NULL; |     Error *err = NULL; | ||||||
|  |     Error *err_end = NULL; | ||||||
|     QemuOpts *opts; |     QemuOpts *opts; | ||||||
|     char *type = NULL; |     char *type = NULL; | ||||||
|     char *id = NULL; |     char *id = NULL; | ||||||
| @@ -1411,24 +1417,23 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) | |||||||
|     qdict_del(pdict, "qom-type"); |     qdict_del(pdict, "qom-type"); | ||||||
|     visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err); |     visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err); | ||||||
|     if (err) { |     if (err) { | ||||||
|         goto out_clean; |         goto out_end; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     qdict_del(pdict, "id"); |     qdict_del(pdict, "id"); | ||||||
|     visit_type_str(opts_get_visitor(ov), &id, "id", &err); |     visit_type_str(opts_get_visitor(ov), &id, "id", &err); | ||||||
|     if (err) { |     if (err) { | ||||||
|         goto out_clean; |         goto out_end; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     object_add(type, id, pdict, opts_get_visitor(ov), &err); |     object_add(type, id, pdict, opts_get_visitor(ov), &err); | ||||||
|     if (err) { |  | ||||||
|         goto out_clean; | out_end: | ||||||
|     } |     visit_end_struct(opts_get_visitor(ov), &err_end); | ||||||
|     visit_end_struct(opts_get_visitor(ov), &err); |     if (!err && err_end) { | ||||||
|     if (err) { |  | ||||||
|         qmp_object_del(id, NULL); |         qmp_object_del(id, NULL); | ||||||
|     } |     } | ||||||
|  |     error_propagate(&err, err_end); | ||||||
| out_clean: | out_clean: | ||||||
|     opts_visitor_cleanup(ov); |     opts_visitor_cleanup(ov); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								hmp.h
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								hmp.h
									
									
									
									
									
								
							| @@ -97,5 +97,11 @@ void object_add_completion(ReadLineState *rs, int nb_args, const char *str); | |||||||
| void object_del_completion(ReadLineState *rs, int nb_args, const char *str); | void object_del_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
| void device_add_completion(ReadLineState *rs, int nb_args, const char *str); | void device_add_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
| void device_del_completion(ReadLineState *rs, int nb_args, const char *str); | void device_del_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
|  | void sendkey_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
|  | void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
|  | void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
|  | void set_link_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
|  | void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
|  | void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -245,7 +245,7 @@ static void intel_hda_update_int_sts(IntelHDAState *d) | |||||||
|  |  | ||||||
|     /* update global status */ |     /* update global status */ | ||||||
|     if (sts & d->int_ctl) { |     if (sts & d->int_ctl) { | ||||||
|         sts |= (1 << 31); |         sts |= (1U << 31); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     d->int_sts = sts; |     d->int_sts = sts; | ||||||
| @@ -257,7 +257,7 @@ static void intel_hda_update_irq(IntelHDAState *d) | |||||||
|     int level; |     int level; | ||||||
|  |  | ||||||
|     intel_hda_update_int_sts(d); |     intel_hda_update_int_sts(d); | ||||||
|     if (d->int_sts & (1 << 31) && d->int_ctl & (1 << 31)) { |     if (d->int_sts & (1U << 31) && d->int_ctl & (1U << 31)) { | ||||||
|         level = 1; |         level = 1; | ||||||
|     } else { |     } else { | ||||||
|         level = 0; |         level = 0; | ||||||
| @@ -574,7 +574,7 @@ static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint3 | |||||||
|     if (st->ctl & 0x01) { |     if (st->ctl & 0x01) { | ||||||
|         /* reset */ |         /* reset */ | ||||||
|         dprint(d, 1, "st #%d: reset\n", reg->stream); |         dprint(d, 1, "st #%d: reset\n", reg->stream); | ||||||
|         st->ctl = 0; |         st->ctl = SD_STS_FIFO_READY << 24; | ||||||
|     } |     } | ||||||
|     if ((st->ctl & 0x02) != (old & 0x02)) { |     if ((st->ctl & 0x02) != (old & 0x02)) { | ||||||
|         uint32_t stnr = (st->ctl >> 20) & 0x0f; |         uint32_t stnr = (st->ctl >> 20) & 0x0f; | ||||||
| @@ -829,6 +829,7 @@ static const struct IntelHDAReg regtab[] = { | |||||||
|         .wclear   = 0x1c000000,                                       \ |         .wclear   = 0x1c000000,                                       \ | ||||||
|         .offset   = offsetof(IntelHDAState, st[_i].ctl),              \ |         .offset   = offsetof(IntelHDAState, st[_i].ctl),              \ | ||||||
|         .whandler = intel_hda_set_st_ctl,                             \ |         .whandler = intel_hda_set_st_ctl,                             \ | ||||||
|  |         .reset    = SD_STS_FIFO_READY << 24                           \ | ||||||
|     },                                                                \ |     },                                                                \ | ||||||
|     [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = {                              \ |     [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = {                              \ | ||||||
|         .stream   = _i,                                               \ |         .stream   = _i,                                               \ | ||||||
|   | |||||||
							
								
								
									
										214
									
								
								hw/input/hid.c
									
									
									
									
									
								
							
							
						
						
									
										214
									
								
								hw/input/hid.c
									
									
									
									
									
								
							| @@ -105,70 +105,135 @@ void hid_set_next_idle(HIDState *hs) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons) | static void hid_pointer_event(DeviceState *dev, QemuConsole *src, | ||||||
|  |                               InputEvent *evt) | ||||||
| { | { | ||||||
|     e->xdx = e->ydy = e->dz = 0; |     static const int bmap[INPUT_BUTTON_MAX] = { | ||||||
|     e->buttons_state = buttons; |         [INPUT_BUTTON_LEFT]   = 0x01, | ||||||
| } |         [INPUT_BUTTON_RIGHT]  = 0x02, | ||||||
|  |         [INPUT_BUTTON_MIDDLE] = 0x04, | ||||||
|  |     }; | ||||||
|  |     HIDState *hs = (HIDState *)dev; | ||||||
|  |     HIDPointerEvent *e; | ||||||
|  |  | ||||||
| static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel, |     assert(hs->n < QUEUE_LENGTH); | ||||||
|                                       int x1, int y1, int z1) { |     e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK]; | ||||||
|     if (xyrel) { |  | ||||||
|         e->xdx += x1; |     switch (evt->kind) { | ||||||
|         e->ydy += y1; |     case INPUT_EVENT_KIND_REL: | ||||||
|  |         if (evt->rel->axis == INPUT_AXIS_X) { | ||||||
|  |             e->xdx += evt->rel->value; | ||||||
|  |         } else if (evt->rel->axis == INPUT_AXIS_Y) { | ||||||
|  |             e->ydy -= evt->rel->value; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case INPUT_EVENT_KIND_ABS: | ||||||
|  |         if (evt->rel->axis == INPUT_AXIS_X) { | ||||||
|  |             e->xdx = evt->rel->value; | ||||||
|  |         } else if (evt->rel->axis == INPUT_AXIS_Y) { | ||||||
|  |             e->ydy = evt->rel->value; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     case INPUT_EVENT_KIND_BTN: | ||||||
|  |         if (evt->btn->down) { | ||||||
|  |             e->buttons_state |= bmap[evt->btn->button]; | ||||||
|  |             if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) { | ||||||
|  |                 e->dz--; | ||||||
|  |             } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) { | ||||||
|  |                 e->dz++; | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|         e->xdx = x1; |             e->buttons_state &= ~bmap[evt->btn->button]; | ||||||
|         e->ydy = y1; |  | ||||||
|         /* Windows drivers do not like the 0/0 position and ignore such |  | ||||||
|          * events. */ |  | ||||||
|         if (!(x1 | y1)) { |  | ||||||
|             e->xdx = 1; |  | ||||||
|         } |         } | ||||||
|  |         break; | ||||||
|  |  | ||||||
|  |     default: | ||||||
|  |         /* keep gcc happy */ | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|     e->dz += z1; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void hid_pointer_event(void *opaque, | static void hid_pointer_sync(DeviceState *dev) | ||||||
|                               int x1, int y1, int z1, int buttons_state) |  | ||||||
| { | { | ||||||
|     HIDState *hs = opaque; |     HIDState *hs = (HIDState *)dev; | ||||||
|     unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; |     HIDPointerEvent *prev, *curr, *next; | ||||||
|     unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; |     bool event_compression = false; | ||||||
|  |  | ||||||
|     /* We combine events where feasible to keep the queue small.  We shouldn't |     if (hs->n == QUEUE_LENGTH-1) { | ||||||
|      * combine anything with the first event of a particular button state, as |         /* | ||||||
|      * that would change the location of the button state change.  When the |          * Queue full.  We are loosing information, but we at least | ||||||
|      * queue is empty, a second event is needed because we don't know if |          * keep track of most recent button state. | ||||||
|      * the first event changed the button state.  */ |          */ | ||||||
|     if (hs->n == QUEUE_LENGTH) { |         return; | ||||||
|         /* Queue full.  Discard old button state, combine motion normally.  */ |     } | ||||||
|         hs->ptr.queue[use_slot].buttons_state = buttons_state; |  | ||||||
|     } else if (hs->n < 2 || |     prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK]; | ||||||
|                hs->ptr.queue[use_slot].buttons_state != buttons_state || |     curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK]; | ||||||
|                hs->ptr.queue[previous_slot].buttons_state != |     next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK]; | ||||||
|                hs->ptr.queue[use_slot].buttons_state) { |  | ||||||
|         /* Cannot or should not combine, so add an empty item to the queue.  */ |     if (hs->n > 0) { | ||||||
|         QUEUE_INCR(use_slot); |         /* | ||||||
|  |          * No button state change between previous and current event | ||||||
|  |          * (and previous wasn't seen by the guest yet), so there is | ||||||
|  |          * motion information only and we can combine the two event | ||||||
|  |          * into one. | ||||||
|  |          */ | ||||||
|  |         if (curr->buttons_state == prev->buttons_state) { | ||||||
|  |             event_compression = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (event_compression) { | ||||||
|  |         /* add current motion to previous, clear current */ | ||||||
|  |         if (hs->kind == HID_MOUSE) { | ||||||
|  |             prev->xdx += curr->xdx; | ||||||
|  |             curr->xdx = 0; | ||||||
|  |             prev->ydy -= curr->ydy; | ||||||
|  |             curr->ydy = 0; | ||||||
|  |         } else { | ||||||
|  |             prev->xdx = curr->xdx; | ||||||
|  |             prev->ydy = curr->ydy; | ||||||
|  |         } | ||||||
|  |         prev->dz += curr->dz; | ||||||
|  |         curr->dz = 0; | ||||||
|  |     } else { | ||||||
|  |         /* prepate next (clear rel, copy abs + btns) */ | ||||||
|  |         if (hs->kind == HID_MOUSE) { | ||||||
|  |             next->xdx = 0; | ||||||
|  |             next->ydy = 0; | ||||||
|  |         } else { | ||||||
|  |             next->xdx = curr->xdx; | ||||||
|  |             next->ydy = curr->ydy; | ||||||
|  |         } | ||||||
|  |         next->dz = 0; | ||||||
|  |         next->buttons_state = curr->buttons_state; | ||||||
|  |         /* make current guest visible, notify guest */ | ||||||
|         hs->n++; |         hs->n++; | ||||||
|         hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state); |  | ||||||
|     } |  | ||||||
|     hid_pointer_event_combine(&hs->ptr.queue[use_slot], |  | ||||||
|                               hs->kind == HID_MOUSE, |  | ||||||
|                               x1, y1, z1); |  | ||||||
|         hs->event(hs); |         hs->event(hs); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void hid_keyboard_event(void *opaque, int keycode) | static void hid_keyboard_event(DeviceState *dev, QemuConsole *src, | ||||||
|  |                                InputEvent *evt) | ||||||
| { | { | ||||||
|     HIDState *hs = opaque; |     HIDState *hs = (HIDState *)dev; | ||||||
|  |     int scancodes[3], i, count; | ||||||
|     int slot; |     int slot; | ||||||
|  |  | ||||||
|     if (hs->n == QUEUE_LENGTH) { |     count = qemu_input_key_value_to_scancode(evt->key->key, | ||||||
|  |                                              evt->key->down, | ||||||
|  |                                              scancodes); | ||||||
|  |     if (hs->n + count > QUEUE_LENGTH) { | ||||||
|         fprintf(stderr, "usb-kbd: warning: key event queue full\n"); |         fprintf(stderr, "usb-kbd: warning: key event queue full\n"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |     for (i = 0; i < count; i++) { | ||||||
|         slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; |         slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; | ||||||
|     hs->kbd.keycodes[slot] = keycode; |         hs->kbd.keycodes[slot] = scancodes[i]; | ||||||
|  |     } | ||||||
|     hs->event(hs); |     hs->event(hs); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -247,14 +312,14 @@ static inline int int_clamp(int val, int vmin, int vmax) | |||||||
| void hid_pointer_activate(HIDState *hs) | void hid_pointer_activate(HIDState *hs) | ||||||
| { | { | ||||||
|     if (!hs->ptr.mouse_grabbed) { |     if (!hs->ptr.mouse_grabbed) { | ||||||
|         qemu_activate_mouse_event_handler(hs->ptr.eh_entry); |         qemu_input_handler_activate(hs->s); | ||||||
|         hs->ptr.mouse_grabbed = 1; |         hs->ptr.mouse_grabbed = 1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) | int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) | ||||||
| { | { | ||||||
|     int dx, dy, dz, b, l; |     int dx, dy, dz, l; | ||||||
|     int index; |     int index; | ||||||
|     HIDPointerEvent *e; |     HIDPointerEvent *e; | ||||||
|  |  | ||||||
| @@ -279,17 +344,6 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) | |||||||
|     dz = int_clamp(e->dz, -127, 127); |     dz = int_clamp(e->dz, -127, 127); | ||||||
|     e->dz -= dz; |     e->dz -= dz; | ||||||
|  |  | ||||||
|     b = 0; |  | ||||||
|     if (e->buttons_state & MOUSE_EVENT_LBUTTON) { |  | ||||||
|         b |= 0x01; |  | ||||||
|     } |  | ||||||
|     if (e->buttons_state & MOUSE_EVENT_RBUTTON) { |  | ||||||
|         b |= 0x02; |  | ||||||
|     } |  | ||||||
|     if (e->buttons_state & MOUSE_EVENT_MBUTTON) { |  | ||||||
|         b |= 0x04; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (hs->n && |     if (hs->n && | ||||||
|         !e->dz && |         !e->dz && | ||||||
|         (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { |         (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) { | ||||||
| @@ -304,7 +358,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) | |||||||
|     switch (hs->kind) { |     switch (hs->kind) { | ||||||
|     case HID_MOUSE: |     case HID_MOUSE: | ||||||
|         if (len > l) { |         if (len > l) { | ||||||
|             buf[l++] = b; |             buf[l++] = e->buttons_state; | ||||||
|         } |         } | ||||||
|         if (len > l) { |         if (len > l) { | ||||||
|             buf[l++] = dx; |             buf[l++] = dx; | ||||||
| @@ -319,7 +373,7 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) | |||||||
|  |  | ||||||
|     case HID_TABLET: |     case HID_TABLET: | ||||||
|         if (len > l) { |         if (len > l) { | ||||||
|             buf[l++] = b; |             buf[l++] = e->buttons_state; | ||||||
|         } |         } | ||||||
|         if (len > l) { |         if (len > l) { | ||||||
|             buf[l++] = dx & 0xff; |             buf[l++] = dx & 0xff; | ||||||
| @@ -413,31 +467,45 @@ void hid_reset(HIDState *hs) | |||||||
|  |  | ||||||
| void hid_free(HIDState *hs) | void hid_free(HIDState *hs) | ||||||
| { | { | ||||||
|     switch (hs->kind) { |     qemu_input_handler_unregister(hs->s); | ||||||
|     case HID_KEYBOARD: |  | ||||||
|         qemu_remove_kbd_event_handler(hs->kbd.eh_entry); |  | ||||||
|         break; |  | ||||||
|     case HID_MOUSE: |  | ||||||
|     case HID_TABLET: |  | ||||||
|         qemu_remove_mouse_event_handler(hs->ptr.eh_entry); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     hid_del_idle_timer(hs); |     hid_del_idle_timer(hs); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static QemuInputHandler hid_keyboard_handler = { | ||||||
|  |     .name  = "QEMU HID Keyboard", | ||||||
|  |     .mask  = INPUT_EVENT_MASK_KEY, | ||||||
|  |     .event = hid_keyboard_event, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static QemuInputHandler hid_mouse_handler = { | ||||||
|  |     .name  = "QEMU HID Mouse", | ||||||
|  |     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, | ||||||
|  |     .event = hid_pointer_event, | ||||||
|  |     .sync  = hid_pointer_sync, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static QemuInputHandler hid_tablet_handler = { | ||||||
|  |     .name  = "QEMU HID Tablet", | ||||||
|  |     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, | ||||||
|  |     .event = hid_pointer_event, | ||||||
|  |     .sync  = hid_pointer_sync, | ||||||
|  | }; | ||||||
|  |  | ||||||
| void hid_init(HIDState *hs, int kind, HIDEventFunc event) | void hid_init(HIDState *hs, int kind, HIDEventFunc event) | ||||||
| { | { | ||||||
|     hs->kind = kind; |     hs->kind = kind; | ||||||
|     hs->event = event; |     hs->event = event; | ||||||
|  |  | ||||||
|     if (hs->kind == HID_KEYBOARD) { |     if (hs->kind == HID_KEYBOARD) { | ||||||
|         hs->kbd.eh_entry = qemu_add_kbd_event_handler(hid_keyboard_event, hs); |         hs->s = qemu_input_handler_register((DeviceState *)hs, | ||||||
|  |                                             &hid_keyboard_handler); | ||||||
|  |         qemu_input_handler_activate(hs->s); | ||||||
|     } else if (hs->kind == HID_MOUSE) { |     } else if (hs->kind == HID_MOUSE) { | ||||||
|         hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, |         hs->s = qemu_input_handler_register((DeviceState *)hs, | ||||||
|                                                         0, "QEMU HID Mouse"); |                                             &hid_mouse_handler); | ||||||
|     } else if (hs->kind == HID_TABLET) { |     } else if (hs->kind == HID_TABLET) { | ||||||
|         hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs, |         hs->s = qemu_input_handler_register((DeviceState *)hs, | ||||||
|                                                         1, "QEMU HID Tablet"); |                                             &hid_tablet_handler); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,3 +26,4 @@ obj-$(CONFIG_XICS) += xics.o | |||||||
| obj-$(CONFIG_XICS_KVM) += xics_kvm.o | obj-$(CONFIG_XICS_KVM) += xics_kvm.o | ||||||
| obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o | obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o | ||||||
| obj-$(CONFIG_S390_FLIC) += s390_flic.o | obj-$(CONFIG_S390_FLIC) += s390_flic.o | ||||||
|  | obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o | ||||||
|   | |||||||
| @@ -1,322 +1,103 @@ | |||||||
| /* | /* | ||||||
|  * QEMU S390x KVM floating interrupt controller (flic) |  * QEMU S390x floating interrupt controller (flic) | ||||||
|  * |  * | ||||||
|  * Copyright 2014 IBM Corp. |  * Copyright 2014 IBM Corp. | ||||||
|  * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com> |  * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com> | ||||||
|  |  *            Cornelia Huck <cornelia.huck@de.ibm.com> | ||||||
|  * |  * | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or (at |  * This work is licensed under the terms of the GNU GPL, version 2 or (at | ||||||
|  * your option) any later version. See the COPYING file in the top-level |  * your option) any later version. See the COPYING file in the top-level | ||||||
|  * directory. |  * directory. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #include <sys/ioctl.h> |  | ||||||
| #include "qemu/error-report.h" | #include "qemu/error-report.h" | ||||||
| #include "hw/sysbus.h" | #include "hw/sysbus.h" | ||||||
| #include "sysemu/kvm.h" |  | ||||||
| #include "migration/qemu-file.h" | #include "migration/qemu-file.h" | ||||||
| #include "hw/s390x/s390_flic.h" | #include "hw/s390x/s390_flic.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
|  |  | ||||||
| #define FLIC_SAVE_INITIAL_SIZE getpagesize() | S390FLICState *s390_get_flic(void) | ||||||
| #define FLIC_FAILED (-1UL) | { | ||||||
| #define FLIC_SAVEVM_VERSION 1 |     S390FLICState *fs; | ||||||
|  |  | ||||||
|  |     fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL)); | ||||||
|  |     if (!fs) { | ||||||
|  |         fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL)); | ||||||
|  |     } | ||||||
|  |     return fs; | ||||||
|  | } | ||||||
|  |  | ||||||
| void s390_flic_init(void) | void s390_flic_init(void) | ||||||
| { | { | ||||||
|     DeviceState *dev; |     DeviceState *dev; | ||||||
|     int r; |     int r; | ||||||
|  |  | ||||||
|     if (kvm_enabled()) { |     dev = s390_flic_kvm_create(); | ||||||
|         dev = qdev_create(NULL, "s390-flic"); |     if (!dev) { | ||||||
|         object_property_add_child(qdev_get_machine(), "s390-flic", |         dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC); | ||||||
|  |         object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC, | ||||||
|                                   OBJECT(dev), NULL); |                                   OBJECT(dev), NULL); | ||||||
|  |     } | ||||||
|     r = qdev_init(dev); |     r = qdev_init(dev); | ||||||
|     if (r) { |     if (r) { | ||||||
|         error_report("flic: couldn't create qdev"); |         error_report("flic: couldn't create qdev"); | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, | ||||||
|  * flic_get_all_irqs - store all pending irqs in buffer |                                          uint8_t isc, bool swap, | ||||||
|  * @buf: pointer to buffer which is passed to kernel |                                          bool is_maskable) | ||||||
|  * @len: length of buffer |  | ||||||
|  * @flic: pointer to flic device state |  | ||||||
|  * |  | ||||||
|  * Returns: -ENOMEM if buffer is too small, |  | ||||||
|  * -EINVAL if attr.group is invalid, |  | ||||||
|  * -EFAULT if copying to userspace failed, |  | ||||||
|  * on success return number of stored interrupts |  | ||||||
|  */ |  | ||||||
| static int flic_get_all_irqs(KVMS390FLICState *flic, |  | ||||||
|                              void *buf, int len) |  | ||||||
| { | { | ||||||
|     struct kvm_device_attr attr = { |     /* nothing to do */ | ||||||
|         .group = KVM_DEV_FLIC_GET_ALL_IRQS, |     return 0; | ||||||
|         .addr = (uint64_t) buf, |  | ||||||
|         .attr = len, |  | ||||||
|     }; |  | ||||||
|     int rc; |  | ||||||
|  |  | ||||||
|     rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr); |  | ||||||
|  |  | ||||||
|     return rc == -1 ? -errno : rc; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void flic_enable_pfault(KVMS390FLICState *flic) | static int qemu_s390_io_adapter_map(S390FLICState *fs, uint32_t id, | ||||||
|  |                                     uint64_t map_addr, bool do_map) | ||||||
| { | { | ||||||
|     struct kvm_device_attr attr = { |     /* nothing to do */ | ||||||
|         .group = KVM_DEV_FLIC_APF_ENABLE, |     return 0; | ||||||
|     }; |  | ||||||
|     int rc; |  | ||||||
|  |  | ||||||
|     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); |  | ||||||
|  |  | ||||||
|     if (rc) { |  | ||||||
|         fprintf(stderr, "flic: couldn't enable pfault\n"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void flic_disable_wait_pfault(KVMS390FLICState *flic) | static int qemu_s390_add_adapter_routes(S390FLICState *fs, | ||||||
|  |                                         AdapterRoutes *routes) | ||||||
| { | { | ||||||
|     struct kvm_device_attr attr = { |     return -ENOSYS; | ||||||
|         .group = KVM_DEV_FLIC_APF_DISABLE_WAIT, |  | ||||||
|     }; |  | ||||||
|     int rc; |  | ||||||
|  |  | ||||||
|     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); |  | ||||||
|  |  | ||||||
|     if (rc) { |  | ||||||
|         fprintf(stderr, "flic: couldn't disable pfault\n"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** flic_enqueue_irqs - returns 0 on success | static void qemu_s390_release_adapter_routes(S390FLICState *fs, | ||||||
|  * @buf: pointer to buffer which is passed to kernel |                                              AdapterRoutes *routes) | ||||||
|  * @len: length of buffer |  | ||||||
|  * @flic: pointer to flic device state |  | ||||||
|  * |  | ||||||
|  * Returns: -EINVAL if attr.group is unknown |  | ||||||
|  */ |  | ||||||
| static int flic_enqueue_irqs(void *buf, uint64_t len, |  | ||||||
|                             KVMS390FLICState *flic) |  | ||||||
| { | { | ||||||
|     int rc; |  | ||||||
|     struct kvm_device_attr attr = { |  | ||||||
|         .group = KVM_DEV_FLIC_ENQUEUE, |  | ||||||
|         .addr = (uint64_t) buf, |  | ||||||
|         .attr = len, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); |  | ||||||
|  |  | ||||||
|     return rc ? -errno : 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) | ||||||
|  * __get_all_irqs - store all pending irqs in buffer |  | ||||||
|  * @flic: pointer to flic device state |  | ||||||
|  * @buf: pointer to pointer to a buffer |  | ||||||
|  * @len: length of buffer |  | ||||||
|  * |  | ||||||
|  * Returns: return value of flic_get_all_irqs |  | ||||||
|  * Note: Retry and increase buffer size until flic_get_all_irqs |  | ||||||
|  * either returns a value >= 0 or a negative error code. |  | ||||||
|  * -ENOMEM is an exception, which means the buffer is too small |  | ||||||
|  * and we should try again. Other negative error codes can be |  | ||||||
|  * -EFAULT and -EINVAL which we ignore at this point |  | ||||||
|  */ |  | ||||||
| static int __get_all_irqs(KVMS390FLICState *flic, |  | ||||||
|                           void **buf, int len) |  | ||||||
| { | { | ||||||
|     int r; |     S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); | ||||||
|  |  | ||||||
|     do { |     fsc->register_io_adapter = qemu_s390_register_io_adapter; | ||||||
|         /* returns -ENOMEM if buffer is too small and number |     fsc->io_adapter_map = qemu_s390_io_adapter_map; | ||||||
|          * of queued interrupts on success */ |     fsc->add_adapter_routes = qemu_s390_add_adapter_routes; | ||||||
|         r = flic_get_all_irqs(flic, *buf, len); |     fsc->release_adapter_routes = qemu_s390_release_adapter_routes; | ||||||
|         if (r >= 0) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         len *= 2; |  | ||||||
|         *buf = g_try_realloc(*buf, len); |  | ||||||
|         if (!buf) { |  | ||||||
|             return -ENOMEM; |  | ||||||
|         } |  | ||||||
|     } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER); |  | ||||||
|  |  | ||||||
|     return r; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | static const TypeInfo qemu_s390_flic_info = { | ||||||
|  * kvm_flic_save - Save pending floating interrupts |     .name          = TYPE_QEMU_S390_FLIC, | ||||||
|  * @f: QEMUFile containing migration state |     .parent        = TYPE_S390_FLIC_COMMON, | ||||||
|  * @opaque: pointer to flic device state |     .instance_size = sizeof(QEMUS390FLICState), | ||||||
|  * |     .class_init    = qemu_s390_flic_class_init, | ||||||
|  * Note: Pass buf and len to kernel. Start with one page and |  | ||||||
|  * increase until buffer is sufficient or maxium size is |  | ||||||
|  * reached |  | ||||||
|  */ |  | ||||||
| static void kvm_flic_save(QEMUFile *f, void *opaque) |  | ||||||
| { |  | ||||||
|     KVMS390FLICState *flic = opaque; |  | ||||||
|     int len = FLIC_SAVE_INITIAL_SIZE; |  | ||||||
|     void *buf; |  | ||||||
|     int count; |  | ||||||
|  |  | ||||||
|     flic_disable_wait_pfault((struct KVMS390FLICState *) opaque); |  | ||||||
|  |  | ||||||
|     buf = g_try_malloc0(len); |  | ||||||
|     if (!buf) { |  | ||||||
|         /* Storing FLIC_FAILED into the count field here will cause the |  | ||||||
|          * target system to fail when attempting to load irqs from the |  | ||||||
|          * migration state */ |  | ||||||
|         error_report("flic: couldn't allocate memory"); |  | ||||||
|         qemu_put_be64(f, FLIC_FAILED); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     count = __get_all_irqs(flic, &buf, len); |  | ||||||
|     if (count < 0) { |  | ||||||
|         error_report("flic: couldn't retrieve irqs from kernel, rc %d", |  | ||||||
|                      count); |  | ||||||
|         /* Storing FLIC_FAILED into the count field here will cause the |  | ||||||
|          * target system to fail when attempting to load irqs from the |  | ||||||
|          * migration state */ |  | ||||||
|         qemu_put_be64(f, FLIC_FAILED); |  | ||||||
|     } else { |  | ||||||
|         qemu_put_be64(f, count); |  | ||||||
|         qemu_put_buffer(f, (uint8_t *) buf, |  | ||||||
|                         count * sizeof(struct kvm_s390_irq)); |  | ||||||
|     } |  | ||||||
|     g_free(buf); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * kvm_flic_load - Load pending floating interrupts |  | ||||||
|  * @f: QEMUFile containing migration state |  | ||||||
|  * @opaque: pointer to flic device state |  | ||||||
|  * @version_id: version id for migration |  | ||||||
|  * |  | ||||||
|  * Returns: value of flic_enqueue_irqs, -EINVAL on error |  | ||||||
|  * Note: Do nothing when no interrupts where stored |  | ||||||
|  * in QEMUFile |  | ||||||
|  */ |  | ||||||
| static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id) |  | ||||||
| { |  | ||||||
|     uint64_t len = 0; |  | ||||||
|     uint64_t count = 0; |  | ||||||
|     void *buf = NULL; |  | ||||||
|     int r = 0; |  | ||||||
|  |  | ||||||
|     if (version_id != FLIC_SAVEVM_VERSION) { |  | ||||||
|         r = -EINVAL; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     flic_enable_pfault((struct KVMS390FLICState *) opaque); |  | ||||||
|  |  | ||||||
|     count = qemu_get_be64(f); |  | ||||||
|     len = count * sizeof(struct kvm_s390_irq); |  | ||||||
|     if (count == FLIC_FAILED) { |  | ||||||
|         r = -EINVAL; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     if (count == 0) { |  | ||||||
|         r = 0; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|     buf = g_try_malloc0(len); |  | ||||||
|     if (!buf) { |  | ||||||
|         r = -ENOMEM; |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { |  | ||||||
|         r = -EINVAL; |  | ||||||
|         goto out_free; |  | ||||||
|     } |  | ||||||
|     r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque); |  | ||||||
|  |  | ||||||
| out_free: |  | ||||||
|     g_free(buf); |  | ||||||
| out: |  | ||||||
|     return r; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) |  | ||||||
| { |  | ||||||
|     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); |  | ||||||
|     struct kvm_create_device cd = {0}; |  | ||||||
|     int ret; |  | ||||||
|  |  | ||||||
|     flic_state->fd = -1; |  | ||||||
|     if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { |  | ||||||
|         trace_flic_no_device_api(errno); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cd.type = KVM_DEV_TYPE_FLIC; |  | ||||||
|     ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); |  | ||||||
|     if (ret < 0) { |  | ||||||
|         trace_flic_create_device(errno); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     flic_state->fd = cd.fd; |  | ||||||
|  |  | ||||||
|     /* Register savevm handler for floating interrupts */ |  | ||||||
|     register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save, |  | ||||||
|                     kvm_flic_load, (void *) flic_state); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp) |  | ||||||
| { |  | ||||||
|     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); |  | ||||||
|  |  | ||||||
|     unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void kvm_s390_flic_reset(DeviceState *dev) |  | ||||||
| { |  | ||||||
|     KVMS390FLICState *flic = KVM_S390_FLIC(dev); |  | ||||||
|     struct kvm_device_attr attr = { |  | ||||||
|         .group = KVM_DEV_FLIC_CLEAR_IRQS, |  | ||||||
|     }; |  | ||||||
|     int rc = 0; |  | ||||||
|  |  | ||||||
|     if (flic->fd == -1) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     flic_disable_wait_pfault(flic); |  | ||||||
|  |  | ||||||
|     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); |  | ||||||
|     if (rc) { |  | ||||||
|         trace_flic_reset_failed(errno); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     flic_enable_pfault(flic); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) |  | ||||||
| { |  | ||||||
|     DeviceClass *dc = DEVICE_CLASS(oc); |  | ||||||
|  |  | ||||||
|     dc->realize = kvm_s390_flic_realize; |  | ||||||
|     dc->unrealize = kvm_s390_flic_unrealize; |  | ||||||
|     dc->reset = kvm_s390_flic_reset; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static const TypeInfo kvm_s390_flic_info = { |  | ||||||
|     .name          = TYPE_KVM_S390_FLIC, |  | ||||||
|     .parent        = TYPE_SYS_BUS_DEVICE, |  | ||||||
|     .instance_size = sizeof(KVMS390FLICState), |  | ||||||
|     .class_init    = kvm_s390_flic_class_init, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void kvm_s390_flic_register_types(void) | static const TypeInfo s390_flic_common_info = { | ||||||
|  |     .name          = TYPE_S390_FLIC_COMMON, | ||||||
|  |     .parent        = TYPE_SYS_BUS_DEVICE, | ||||||
|  |     .instance_size = sizeof(S390FLICState), | ||||||
|  |     .class_size    = sizeof(S390FLICStateClass), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void qemu_s390_flic_register_types(void) | ||||||
| { | { | ||||||
|     type_register_static(&kvm_s390_flic_info); |     type_register_static(&s390_flic_common_info); | ||||||
|  |     type_register_static(&qemu_s390_flic_info); | ||||||
| } | } | ||||||
|  |  | ||||||
| type_init(kvm_s390_flic_register_types) | type_init(qemu_s390_flic_register_types) | ||||||
|   | |||||||
							
								
								
									
										420
									
								
								hw/intc/s390_flic_kvm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										420
									
								
								hw/intc/s390_flic_kvm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,420 @@ | |||||||
|  | /* | ||||||
|  |  * QEMU S390x KVM floating interrupt controller (flic) | ||||||
|  |  * | ||||||
|  |  * Copyright 2014 IBM Corp. | ||||||
|  |  * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com> | ||||||
|  |  *            Cornelia Huck <cornelia.huck@de.ibm.com> | ||||||
|  |  * | ||||||
|  |  * This work is licensed under the terms of the GNU GPL, version 2 or (at | ||||||
|  |  * your option) any later version. See the COPYING file in the top-level | ||||||
|  |  * directory. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <sys/ioctl.h> | ||||||
|  | #include "qemu/error-report.h" | ||||||
|  | #include "hw/sysbus.h" | ||||||
|  | #include "sysemu/kvm.h" | ||||||
|  | #include "migration/qemu-file.h" | ||||||
|  | #include "hw/s390x/s390_flic.h" | ||||||
|  | #include "hw/s390x/adapter.h" | ||||||
|  | #include "trace.h" | ||||||
|  |  | ||||||
|  | #define FLIC_SAVE_INITIAL_SIZE getpagesize() | ||||||
|  | #define FLIC_FAILED (-1UL) | ||||||
|  | #define FLIC_SAVEVM_VERSION 1 | ||||||
|  |  | ||||||
|  | typedef struct KVMS390FLICState { | ||||||
|  |     S390FLICState parent_obj; | ||||||
|  |  | ||||||
|  |     uint32_t fd; | ||||||
|  | } KVMS390FLICState; | ||||||
|  |  | ||||||
|  | DeviceState *s390_flic_kvm_create(void) | ||||||
|  | { | ||||||
|  |     DeviceState *dev = NULL; | ||||||
|  |  | ||||||
|  |     if (kvm_enabled()) { | ||||||
|  |         dev = qdev_create(NULL, TYPE_KVM_S390_FLIC); | ||||||
|  |         object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC, | ||||||
|  |                                   OBJECT(dev), NULL); | ||||||
|  |     } | ||||||
|  |     return dev; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * flic_get_all_irqs - store all pending irqs in buffer | ||||||
|  |  * @buf: pointer to buffer which is passed to kernel | ||||||
|  |  * @len: length of buffer | ||||||
|  |  * @flic: pointer to flic device state | ||||||
|  |  * | ||||||
|  |  * Returns: -ENOMEM if buffer is too small, | ||||||
|  |  * -EINVAL if attr.group is invalid, | ||||||
|  |  * -EFAULT if copying to userspace failed, | ||||||
|  |  * on success return number of stored interrupts | ||||||
|  |  */ | ||||||
|  | static int flic_get_all_irqs(KVMS390FLICState *flic, | ||||||
|  |                              void *buf, int len) | ||||||
|  | { | ||||||
|  |     struct kvm_device_attr attr = { | ||||||
|  |         .group = KVM_DEV_FLIC_GET_ALL_IRQS, | ||||||
|  |         .addr = (uint64_t) buf, | ||||||
|  |         .attr = len, | ||||||
|  |     }; | ||||||
|  |     int rc; | ||||||
|  |  | ||||||
|  |     rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr); | ||||||
|  |  | ||||||
|  |     return rc == -1 ? -errno : rc; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void flic_enable_pfault(KVMS390FLICState *flic) | ||||||
|  | { | ||||||
|  |     struct kvm_device_attr attr = { | ||||||
|  |         .group = KVM_DEV_FLIC_APF_ENABLE, | ||||||
|  |     }; | ||||||
|  |     int rc; | ||||||
|  |  | ||||||
|  |     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); | ||||||
|  |  | ||||||
|  |     if (rc) { | ||||||
|  |         fprintf(stderr, "flic: couldn't enable pfault\n"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void flic_disable_wait_pfault(KVMS390FLICState *flic) | ||||||
|  | { | ||||||
|  |     struct kvm_device_attr attr = { | ||||||
|  |         .group = KVM_DEV_FLIC_APF_DISABLE_WAIT, | ||||||
|  |     }; | ||||||
|  |     int rc; | ||||||
|  |  | ||||||
|  |     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); | ||||||
|  |  | ||||||
|  |     if (rc) { | ||||||
|  |         fprintf(stderr, "flic: couldn't disable pfault\n"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** flic_enqueue_irqs - returns 0 on success | ||||||
|  |  * @buf: pointer to buffer which is passed to kernel | ||||||
|  |  * @len: length of buffer | ||||||
|  |  * @flic: pointer to flic device state | ||||||
|  |  * | ||||||
|  |  * Returns: -EINVAL if attr.group is unknown | ||||||
|  |  */ | ||||||
|  | static int flic_enqueue_irqs(void *buf, uint64_t len, | ||||||
|  |                             KVMS390FLICState *flic) | ||||||
|  | { | ||||||
|  |     int rc; | ||||||
|  |     struct kvm_device_attr attr = { | ||||||
|  |         .group = KVM_DEV_FLIC_ENQUEUE, | ||||||
|  |         .addr = (uint64_t) buf, | ||||||
|  |         .attr = len, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); | ||||||
|  |  | ||||||
|  |     return rc ? -errno : 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * __get_all_irqs - store all pending irqs in buffer | ||||||
|  |  * @flic: pointer to flic device state | ||||||
|  |  * @buf: pointer to pointer to a buffer | ||||||
|  |  * @len: length of buffer | ||||||
|  |  * | ||||||
|  |  * Returns: return value of flic_get_all_irqs | ||||||
|  |  * Note: Retry and increase buffer size until flic_get_all_irqs | ||||||
|  |  * either returns a value >= 0 or a negative error code. | ||||||
|  |  * -ENOMEM is an exception, which means the buffer is too small | ||||||
|  |  * and we should try again. Other negative error codes can be | ||||||
|  |  * -EFAULT and -EINVAL which we ignore at this point | ||||||
|  |  */ | ||||||
|  | static int __get_all_irqs(KVMS390FLICState *flic, | ||||||
|  |                           void **buf, int len) | ||||||
|  | { | ||||||
|  |     int r; | ||||||
|  |  | ||||||
|  |     do { | ||||||
|  |         /* returns -ENOMEM if buffer is too small and number | ||||||
|  |          * of queued interrupts on success */ | ||||||
|  |         r = flic_get_all_irqs(flic, *buf, len); | ||||||
|  |         if (r >= 0) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         len *= 2; | ||||||
|  |         *buf = g_try_realloc(*buf, len); | ||||||
|  |         if (!buf) { | ||||||
|  |             return -ENOMEM; | ||||||
|  |         } | ||||||
|  |     } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER); | ||||||
|  |  | ||||||
|  |     return r; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, | ||||||
|  |                                         uint8_t isc, bool swap, | ||||||
|  |                                         bool is_maskable) | ||||||
|  | { | ||||||
|  |     struct kvm_s390_io_adapter adapter = { | ||||||
|  |         .id = id, | ||||||
|  |         .isc = isc, | ||||||
|  |         .maskable = is_maskable, | ||||||
|  |         .swap = swap, | ||||||
|  |     }; | ||||||
|  |     KVMS390FLICState *flic = KVM_S390_FLIC(fs); | ||||||
|  |     int r, ret; | ||||||
|  |     struct kvm_device_attr attr = { | ||||||
|  |         .group = KVM_DEV_FLIC_ADAPTER_REGISTER, | ||||||
|  |         .addr = (uint64_t)&adapter, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) { | ||||||
|  |         return -ENOSYS; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); | ||||||
|  |  | ||||||
|  |     ret = r ? -errno : 0; | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id, | ||||||
|  |                                    uint64_t map_addr, bool do_map) | ||||||
|  | { | ||||||
|  |     struct kvm_s390_io_adapter_req req = { | ||||||
|  |         .id = id, | ||||||
|  |         .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP, | ||||||
|  |         .addr = map_addr, | ||||||
|  |     }; | ||||||
|  |     struct kvm_device_attr attr = { | ||||||
|  |         .group = KVM_DEV_FLIC_ADAPTER_MODIFY, | ||||||
|  |         .addr = (uint64_t)&req, | ||||||
|  |     }; | ||||||
|  |     KVMS390FLICState *flic = KVM_S390_FLIC(fs); | ||||||
|  |     int r; | ||||||
|  |  | ||||||
|  |     if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) { | ||||||
|  |         return -ENOSYS; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); | ||||||
|  |     return r ? -errno : 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int kvm_s390_add_adapter_routes(S390FLICState *fs, | ||||||
|  |                                        AdapterRoutes *routes) | ||||||
|  | { | ||||||
|  |     int ret, i; | ||||||
|  |     uint64_t ind_offset = routes->adapter.ind_offset; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < routes->num_routes; i++) { | ||||||
|  |         ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter); | ||||||
|  |         if (ret < 0) { | ||||||
|  |             goto out_undo; | ||||||
|  |         } | ||||||
|  |         routes->gsi[i] = ret; | ||||||
|  |         routes->adapter.ind_offset++; | ||||||
|  |     } | ||||||
|  |     /* Restore passed-in structure to original state. */ | ||||||
|  |     routes->adapter.ind_offset = ind_offset; | ||||||
|  |     return 0; | ||||||
|  | out_undo: | ||||||
|  |     while (--i >= 0) { | ||||||
|  |         kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); | ||||||
|  |         routes->gsi[i] = -1; | ||||||
|  |     } | ||||||
|  |     routes->adapter.ind_offset = ind_offset; | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void kvm_s390_release_adapter_routes(S390FLICState *fs, | ||||||
|  |                                             AdapterRoutes *routes) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     for (i = 0; i < routes->num_routes; i++) { | ||||||
|  |         if (routes->gsi[i] >= 0) { | ||||||
|  |             kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); | ||||||
|  |             routes->gsi[i] = -1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * kvm_flic_save - Save pending floating interrupts | ||||||
|  |  * @f: QEMUFile containing migration state | ||||||
|  |  * @opaque: pointer to flic device state | ||||||
|  |  * | ||||||
|  |  * Note: Pass buf and len to kernel. Start with one page and | ||||||
|  |  * increase until buffer is sufficient or maxium size is | ||||||
|  |  * reached | ||||||
|  |  */ | ||||||
|  | static void kvm_flic_save(QEMUFile *f, void *opaque) | ||||||
|  | { | ||||||
|  |     KVMS390FLICState *flic = opaque; | ||||||
|  |     int len = FLIC_SAVE_INITIAL_SIZE; | ||||||
|  |     void *buf; | ||||||
|  |     int count; | ||||||
|  |  | ||||||
|  |     flic_disable_wait_pfault((struct KVMS390FLICState *) opaque); | ||||||
|  |  | ||||||
|  |     buf = g_try_malloc0(len); | ||||||
|  |     if (!buf) { | ||||||
|  |         /* Storing FLIC_FAILED into the count field here will cause the | ||||||
|  |          * target system to fail when attempting to load irqs from the | ||||||
|  |          * migration state */ | ||||||
|  |         error_report("flic: couldn't allocate memory"); | ||||||
|  |         qemu_put_be64(f, FLIC_FAILED); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     count = __get_all_irqs(flic, &buf, len); | ||||||
|  |     if (count < 0) { | ||||||
|  |         error_report("flic: couldn't retrieve irqs from kernel, rc %d", | ||||||
|  |                      count); | ||||||
|  |         /* Storing FLIC_FAILED into the count field here will cause the | ||||||
|  |          * target system to fail when attempting to load irqs from the | ||||||
|  |          * migration state */ | ||||||
|  |         qemu_put_be64(f, FLIC_FAILED); | ||||||
|  |     } else { | ||||||
|  |         qemu_put_be64(f, count); | ||||||
|  |         qemu_put_buffer(f, (uint8_t *) buf, | ||||||
|  |                         count * sizeof(struct kvm_s390_irq)); | ||||||
|  |     } | ||||||
|  |     g_free(buf); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * kvm_flic_load - Load pending floating interrupts | ||||||
|  |  * @f: QEMUFile containing migration state | ||||||
|  |  * @opaque: pointer to flic device state | ||||||
|  |  * @version_id: version id for migration | ||||||
|  |  * | ||||||
|  |  * Returns: value of flic_enqueue_irqs, -EINVAL on error | ||||||
|  |  * Note: Do nothing when no interrupts where stored | ||||||
|  |  * in QEMUFile | ||||||
|  |  */ | ||||||
|  | static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id) | ||||||
|  | { | ||||||
|  |     uint64_t len = 0; | ||||||
|  |     uint64_t count = 0; | ||||||
|  |     void *buf = NULL; | ||||||
|  |     int r = 0; | ||||||
|  |  | ||||||
|  |     if (version_id != FLIC_SAVEVM_VERSION) { | ||||||
|  |         r = -EINVAL; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     flic_enable_pfault((struct KVMS390FLICState *) opaque); | ||||||
|  |  | ||||||
|  |     count = qemu_get_be64(f); | ||||||
|  |     len = count * sizeof(struct kvm_s390_irq); | ||||||
|  |     if (count == FLIC_FAILED) { | ||||||
|  |         r = -EINVAL; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     if (count == 0) { | ||||||
|  |         r = 0; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     buf = g_try_malloc0(len); | ||||||
|  |     if (!buf) { | ||||||
|  |         r = -ENOMEM; | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { | ||||||
|  |         r = -EINVAL; | ||||||
|  |         goto out_free; | ||||||
|  |     } | ||||||
|  |     r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque); | ||||||
|  |  | ||||||
|  | out_free: | ||||||
|  |     g_free(buf); | ||||||
|  | out: | ||||||
|  |     return r; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) | ||||||
|  | { | ||||||
|  |     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); | ||||||
|  |     struct kvm_create_device cd = {0}; | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     flic_state->fd = -1; | ||||||
|  |     if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { | ||||||
|  |         trace_flic_no_device_api(errno); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     cd.type = KVM_DEV_TYPE_FLIC; | ||||||
|  |     ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); | ||||||
|  |     if (ret < 0) { | ||||||
|  |         trace_flic_create_device(errno); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     flic_state->fd = cd.fd; | ||||||
|  |  | ||||||
|  |     /* Register savevm handler for floating interrupts */ | ||||||
|  |     register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save, | ||||||
|  |                     kvm_flic_load, (void *) flic_state); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp) | ||||||
|  | { | ||||||
|  |     KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); | ||||||
|  |  | ||||||
|  |     unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void kvm_s390_flic_reset(DeviceState *dev) | ||||||
|  | { | ||||||
|  |     KVMS390FLICState *flic = KVM_S390_FLIC(dev); | ||||||
|  |     struct kvm_device_attr attr = { | ||||||
|  |         .group = KVM_DEV_FLIC_CLEAR_IRQS, | ||||||
|  |     }; | ||||||
|  |     int rc = 0; | ||||||
|  |  | ||||||
|  |     if (flic->fd == -1) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     flic_disable_wait_pfault(flic); | ||||||
|  |  | ||||||
|  |     rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); | ||||||
|  |     if (rc) { | ||||||
|  |         trace_flic_reset_failed(errno); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     flic_enable_pfault(flic); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) | ||||||
|  | { | ||||||
|  |     DeviceClass *dc = DEVICE_CLASS(oc); | ||||||
|  |     S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); | ||||||
|  |  | ||||||
|  |     dc->realize = kvm_s390_flic_realize; | ||||||
|  |     dc->unrealize = kvm_s390_flic_unrealize; | ||||||
|  |     dc->reset = kvm_s390_flic_reset; | ||||||
|  |     fsc->register_io_adapter = kvm_s390_register_io_adapter; | ||||||
|  |     fsc->io_adapter_map = kvm_s390_io_adapter_map; | ||||||
|  |     fsc->add_adapter_routes = kvm_s390_add_adapter_routes; | ||||||
|  |     fsc->release_adapter_routes = kvm_s390_release_adapter_routes; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const TypeInfo kvm_s390_flic_info = { | ||||||
|  |     .name          = TYPE_KVM_S390_FLIC, | ||||||
|  |     .parent        = TYPE_S390_FLIC_COMMON, | ||||||
|  |     .instance_size = sizeof(KVMS390FLICState), | ||||||
|  |     .class_init    = kvm_s390_flic_class_init, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void kvm_s390_flic_register_types(void) | ||||||
|  | { | ||||||
|  |     type_register_static(&kvm_s390_flic_info); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type_init(kvm_s390_flic_register_types) | ||||||
| @@ -16,6 +16,7 @@ | |||||||
| #include "ioinst.h" | #include "ioinst.h" | ||||||
| #include "css.h" | #include "css.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
|  | #include "hw/s390x/s390_flic.h" | ||||||
|  |  | ||||||
| typedef struct CrwContainer { | typedef struct CrwContainer { | ||||||
|     CRW crw; |     CRW crw; | ||||||
| @@ -39,6 +40,13 @@ typedef struct CssImage { | |||||||
|     ChpInfo chpids[MAX_CHPID + 1]; |     ChpInfo chpids[MAX_CHPID + 1]; | ||||||
| } CssImage; | } CssImage; | ||||||
|  |  | ||||||
|  | typedef struct IoAdapter { | ||||||
|  |     uint32_t id; | ||||||
|  |     uint8_t type; | ||||||
|  |     uint8_t isc; | ||||||
|  |     QTAILQ_ENTRY(IoAdapter) sibling; | ||||||
|  | } IoAdapter; | ||||||
|  |  | ||||||
| typedef struct ChannelSubSys { | typedef struct ChannelSubSys { | ||||||
|     QTAILQ_HEAD(, CrwContainer) pending_crws; |     QTAILQ_HEAD(, CrwContainer) pending_crws; | ||||||
|     bool do_crw_mchk; |     bool do_crw_mchk; | ||||||
| @@ -49,6 +57,7 @@ typedef struct ChannelSubSys { | |||||||
|     uint64_t chnmon_area; |     uint64_t chnmon_area; | ||||||
|     CssImage *css[MAX_CSSID + 1]; |     CssImage *css[MAX_CSSID + 1]; | ||||||
|     uint8_t default_cssid; |     uint8_t default_cssid; | ||||||
|  |     QTAILQ_HEAD(, IoAdapter) io_adapters; | ||||||
| } ChannelSubSys; | } ChannelSubSys; | ||||||
|  |  | ||||||
| static ChannelSubSys *channel_subsys; | static ChannelSubSys *channel_subsys; | ||||||
| @@ -69,6 +78,46 @@ int css_create_css_image(uint8_t cssid, bool default_image) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, | ||||||
|  |                             bool maskable, uint32_t *id) | ||||||
|  | { | ||||||
|  |     IoAdapter *adapter; | ||||||
|  |     bool found = false; | ||||||
|  |     int ret; | ||||||
|  |     S390FLICState *fs = s390_get_flic(); | ||||||
|  |     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); | ||||||
|  |  | ||||||
|  |     *id = 0; | ||||||
|  |     QTAILQ_FOREACH(adapter, &channel_subsys->io_adapters, sibling) { | ||||||
|  |         if ((adapter->type == type) && (adapter->isc == isc)) { | ||||||
|  |             *id = adapter->id; | ||||||
|  |             found = true; | ||||||
|  |             ret = 0; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (adapter->id >= *id) { | ||||||
|  |             *id = adapter->id + 1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (found) { | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |     adapter = g_new0(IoAdapter, 1); | ||||||
|  |     ret = fsc->register_io_adapter(fs, *id, isc, swap, maskable); | ||||||
|  |     if (ret == 0) { | ||||||
|  |         adapter->id = *id; | ||||||
|  |         adapter->isc = isc; | ||||||
|  |         adapter->type = type; | ||||||
|  |         QTAILQ_INSERT_TAIL(&channel_subsys->io_adapters, adapter, sibling); | ||||||
|  |     } else { | ||||||
|  |         g_free(adapter); | ||||||
|  |         fprintf(stderr, "Unexpected error %d when registering adapter %d\n", | ||||||
|  |                 ret, *id); | ||||||
|  |     } | ||||||
|  | out: | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
| uint16_t css_build_subchannel_id(SubchDev *sch) | uint16_t css_build_subchannel_id(SubchDev *sch) | ||||||
| { | { | ||||||
|     if (channel_subsys->max_cssid > 0) { |     if (channel_subsys->max_cssid > 0) { | ||||||
| @@ -1235,6 +1284,7 @@ static void css_init(void) | |||||||
|     channel_subsys->do_crw_mchk = true; |     channel_subsys->do_crw_mchk = true; | ||||||
|     channel_subsys->crws_lost = false; |     channel_subsys->crws_lost = false; | ||||||
|     channel_subsys->chnmon_active = false; |     channel_subsys->chnmon_active = false; | ||||||
|  |     QTAILQ_INIT(&channel_subsys->io_adapters); | ||||||
| } | } | ||||||
| machine_init(css_init); | machine_init(css_init); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -98,4 +98,8 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, | |||||||
|                            int hotplugged, int add); |                            int hotplugged, int add); | ||||||
| void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); | void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); | ||||||
| void css_adapter_interrupt(uint8_t isc); | void css_adapter_interrupt(uint8_t isc); | ||||||
|  |  | ||||||
|  | #define CSS_IO_ADAPTER_VIRTIO 1 | ||||||
|  | int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, | ||||||
|  |                             bool maskable, uint32_t *id); | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -21,12 +21,77 @@ | |||||||
| #include "hw/sysbus.h" | #include "hw/sysbus.h" | ||||||
| #include "qemu/bitops.h" | #include "qemu/bitops.h" | ||||||
| #include "hw/virtio/virtio-bus.h" | #include "hw/virtio/virtio-bus.h" | ||||||
|  | #include "hw/s390x/adapter.h" | ||||||
|  | #include "hw/s390x/s390_flic.h" | ||||||
|  |  | ||||||
| #include "ioinst.h" | #include "ioinst.h" | ||||||
| #include "css.h" | #include "css.h" | ||||||
| #include "virtio-ccw.h" | #include "virtio-ccw.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
|  |  | ||||||
|  | static QTAILQ_HEAD(, IndAddr) indicator_addresses = | ||||||
|  |     QTAILQ_HEAD_INITIALIZER(indicator_addresses); | ||||||
|  |  | ||||||
|  | static IndAddr *get_indicator(hwaddr ind_addr, int len) | ||||||
|  | { | ||||||
|  |     IndAddr *indicator; | ||||||
|  |  | ||||||
|  |     QTAILQ_FOREACH(indicator, &indicator_addresses, sibling) { | ||||||
|  |         if (indicator->addr == ind_addr) { | ||||||
|  |             indicator->refcnt++; | ||||||
|  |             return indicator; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     indicator = g_new0(IndAddr, 1); | ||||||
|  |     indicator->addr = ind_addr; | ||||||
|  |     indicator->len = len; | ||||||
|  |     indicator->refcnt = 1; | ||||||
|  |     QTAILQ_INSERT_TAIL(&indicator_addresses, indicator, sibling); | ||||||
|  |     return indicator; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr, | ||||||
|  |                                bool do_map) | ||||||
|  | { | ||||||
|  |     S390FLICState *fs = s390_get_flic(); | ||||||
|  |     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); | ||||||
|  |  | ||||||
|  |     return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void release_indicator(AdapterInfo *adapter, IndAddr *indicator) | ||||||
|  | { | ||||||
|  |     assert(indicator->refcnt > 0); | ||||||
|  |     indicator->refcnt--; | ||||||
|  |     if (indicator->refcnt > 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     QTAILQ_REMOVE(&indicator_addresses, indicator, sibling); | ||||||
|  |     if (indicator->map) { | ||||||
|  |         s390_io_adapter_map(adapter, indicator->map, false); | ||||||
|  |     } | ||||||
|  |     g_free(indicator); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int map_indicator(AdapterInfo *adapter, IndAddr *indicator) | ||||||
|  | { | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     if (indicator->map) { | ||||||
|  |         return 0; /* already mapped is not an error */ | ||||||
|  |     } | ||||||
|  |     indicator->map = indicator->addr; | ||||||
|  |     ret = s390_io_adapter_map(adapter, indicator->map, true); | ||||||
|  |     if ((ret != 0) && (ret != -ENOSYS)) { | ||||||
|  |         goto out_err; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  | out_err: | ||||||
|  |     indicator->map = 0; | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
| static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, | static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, | ||||||
|                                VirtioCcwDevice *dev); |                                VirtioCcwDevice *dev); | ||||||
|  |  | ||||||
| @@ -445,7 +510,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) | |||||||
|             ret = -EFAULT; |             ret = -EFAULT; | ||||||
|         } else { |         } else { | ||||||
|             indicators = ldq_phys(&address_space_memory, ccw.cda); |             indicators = ldq_phys(&address_space_memory, ccw.cda); | ||||||
|             dev->indicators = indicators; |             dev->indicators = get_indicator(indicators, sizeof(uint64_t)); | ||||||
|             sch->curr_status.scsw.count = ccw.count - sizeof(indicators); |             sch->curr_status.scsw.count = ccw.count - sizeof(indicators); | ||||||
|             ret = 0; |             ret = 0; | ||||||
|         } |         } | ||||||
| @@ -465,7 +530,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) | |||||||
|             ret = -EFAULT; |             ret = -EFAULT; | ||||||
|         } else { |         } else { | ||||||
|             indicators = ldq_phys(&address_space_memory, ccw.cda); |             indicators = ldq_phys(&address_space_memory, ccw.cda); | ||||||
|             dev->indicators2 = indicators; |             dev->indicators2 = get_indicator(indicators, sizeof(uint64_t)); | ||||||
|             sch->curr_status.scsw.count = ccw.count - sizeof(indicators); |             sch->curr_status.scsw.count = ccw.count - sizeof(indicators); | ||||||
|             ret = 0; |             ret = 0; | ||||||
|         } |         } | ||||||
| @@ -517,13 +582,20 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) | |||||||
|                 ret = -EFAULT; |                 ret = -EFAULT; | ||||||
|             } else { |             } else { | ||||||
|                 len = hw_len; |                 len = hw_len; | ||||||
|                 dev->summary_indicator = thinint->summary_indicator; |                 dev->summary_indicator = | ||||||
|                 dev->indicators = thinint->device_indicator; |                     get_indicator(thinint->summary_indicator, sizeof(uint8_t)); | ||||||
|  |                 dev->indicators = get_indicator(thinint->device_indicator, | ||||||
|  |                                                 thinint->ind_bit / 8 + 1); | ||||||
|                 dev->thinint_isc = thinint->isc; |                 dev->thinint_isc = thinint->isc; | ||||||
|                 dev->ind_bit = thinint->ind_bit; |                 dev->routes.adapter.ind_offset = thinint->ind_bit; | ||||||
|  |                 dev->routes.adapter.summary_offset = 7; | ||||||
|                 cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); |                 cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); | ||||||
|                 sch->thinint_active = ((dev->indicators != 0) && |                 ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, | ||||||
|                                        (dev->summary_indicator != 0)); |                                               dev->thinint_isc, true, false, | ||||||
|  |                                               &dev->routes.adapter.adapter_id); | ||||||
|  |                 assert(ret == 0); | ||||||
|  |                 sch->thinint_active = ((dev->indicators != NULL) && | ||||||
|  |                                        (dev->summary_indicator != NULL)); | ||||||
|                 sch->curr_status.scsw.count = ccw.count - len; |                 sch->curr_status.scsw.count = ccw.count - len; | ||||||
|                 ret = 0; |                 ret = 0; | ||||||
|             } |             } | ||||||
| @@ -554,7 +626,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) | |||||||
|     sch->driver_data = dev; |     sch->driver_data = dev; | ||||||
|     dev->sch = sch; |     dev->sch = sch; | ||||||
|  |  | ||||||
|     dev->indicators = 0; |     dev->indicators = NULL; | ||||||
|  |  | ||||||
|     /* Initialize subchannel structure. */ |     /* Initialize subchannel structure. */ | ||||||
|     sch->channel_prog = 0x0; |     sch->channel_prog = 0x0; | ||||||
| @@ -693,7 +765,10 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev) | |||||||
|         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); |         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); | ||||||
|         g_free(sch); |         g_free(sch); | ||||||
|     } |     } | ||||||
|     dev->indicators = 0; |     if (dev->indicators) { | ||||||
|  |         release_indicator(&dev->routes.adapter, dev->indicators); | ||||||
|  |         dev->indicators = NULL; | ||||||
|  |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -950,17 +1025,19 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) | |||||||
|              * ind_bit indicates the start of the indicators in a big |              * ind_bit indicates the start of the indicators in a big | ||||||
|              * endian notation. |              * endian notation. | ||||||
|              */ |              */ | ||||||
|             virtio_set_ind_atomic(sch, dev->indicators + |             uint64_t ind_bit = dev->routes.adapter.ind_offset; | ||||||
|                                   (dev->ind_bit + vector) / 8, |  | ||||||
|                                   0x80 >> ((dev->ind_bit + vector) % 8)); |             virtio_set_ind_atomic(sch, dev->indicators->addr + | ||||||
|             if (!virtio_set_ind_atomic(sch, dev->summary_indicator, |                                   (ind_bit + vector) / 8, | ||||||
|  |                                   0x80 >> ((ind_bit + vector) % 8)); | ||||||
|  |             if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr, | ||||||
|                                        0x01)) { |                                        0x01)) { | ||||||
|                 css_adapter_interrupt(dev->thinint_isc); |                 css_adapter_interrupt(dev->thinint_isc); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             indicators = ldq_phys(&address_space_memory, dev->indicators); |             indicators = ldq_phys(&address_space_memory, dev->indicators->addr); | ||||||
|             indicators |= 1ULL << vector; |             indicators |= 1ULL << vector; | ||||||
|             stq_phys(&address_space_memory, dev->indicators, indicators); |             stq_phys(&address_space_memory, dev->indicators->addr, indicators); | ||||||
|             css_conditional_io_interrupt(sch); |             css_conditional_io_interrupt(sch); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
| @@ -968,9 +1045,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         vector = 0; |         vector = 0; | ||||||
|         indicators = ldq_phys(&address_space_memory, dev->indicators2); |         indicators = ldq_phys(&address_space_memory, dev->indicators2->addr); | ||||||
|         indicators |= 1ULL << vector; |         indicators |= 1ULL << vector; | ||||||
|         stq_phys(&address_space_memory, dev->indicators2, indicators); |         stq_phys(&address_space_memory, dev->indicators2->addr, indicators); | ||||||
|         css_conditional_io_interrupt(sch); |         css_conditional_io_interrupt(sch); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -991,9 +1068,18 @@ static void virtio_ccw_reset(DeviceState *d) | |||||||
|     virtio_ccw_stop_ioeventfd(dev); |     virtio_ccw_stop_ioeventfd(dev); | ||||||
|     virtio_reset(vdev); |     virtio_reset(vdev); | ||||||
|     css_reset_sch(dev->sch); |     css_reset_sch(dev->sch); | ||||||
|     dev->indicators = 0; |     if (dev->indicators) { | ||||||
|     dev->indicators2 = 0; |         release_indicator(&dev->routes.adapter, dev->indicators); | ||||||
|     dev->summary_indicator = 0; |         dev->indicators = NULL; | ||||||
|  |     } | ||||||
|  |     if (dev->indicators2) { | ||||||
|  |         release_indicator(&dev->routes.adapter, dev->indicators2); | ||||||
|  |         dev->indicators2 = NULL; | ||||||
|  |     } | ||||||
|  |     if (dev->summary_indicator) { | ||||||
|  |         release_indicator(&dev->routes.adapter, dev->summary_indicator); | ||||||
|  |         dev->summary_indicator = NULL; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void virtio_ccw_vmstate_change(DeviceState *d, bool running) | static void virtio_ccw_vmstate_change(DeviceState *d, bool running) | ||||||
| @@ -1027,6 +1113,79 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign) | |||||||
|     return virtio_ccw_set_guest2host_notifier(dev, n, assign, false); |     return virtio_ccw_set_guest2host_notifier(dev, n, assign, false); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int virtio_ccw_get_mappings(VirtioCcwDevice *dev) | ||||||
|  | { | ||||||
|  |     int r; | ||||||
|  |  | ||||||
|  |     if (!dev->sch->thinint_active) { | ||||||
|  |         return -EINVAL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     r = map_indicator(&dev->routes.adapter, dev->summary_indicator); | ||||||
|  |     if (r) { | ||||||
|  |         return r; | ||||||
|  |     } | ||||||
|  |     r = map_indicator(&dev->routes.adapter, dev->indicators); | ||||||
|  |     if (r) { | ||||||
|  |         return r; | ||||||
|  |     } | ||||||
|  |     dev->routes.adapter.summary_addr = dev->summary_indicator->map; | ||||||
|  |     dev->routes.adapter.ind_addr = dev->indicators->map; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); | ||||||
|  |     int ret; | ||||||
|  |     S390FLICState *fs = s390_get_flic(); | ||||||
|  |     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); | ||||||
|  |  | ||||||
|  |     ret = virtio_ccw_get_mappings(dev); | ||||||
|  |     if (ret) { | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  |     for (i = 0; i < nvqs; i++) { | ||||||
|  |         if (!virtio_queue_get_num(vdev, i)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     dev->routes.num_routes = i; | ||||||
|  |     return fsc->add_adapter_routes(fs, &dev->routes); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs) | ||||||
|  | { | ||||||
|  |     S390FLICState *fs = s390_get_flic(); | ||||||
|  |     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); | ||||||
|  |  | ||||||
|  |     fsc->release_adapter_routes(fs, &dev->routes); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n) | ||||||
|  | { | ||||||
|  |     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); | ||||||
|  |     VirtQueue *vq = virtio_get_queue(vdev, n); | ||||||
|  |     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); | ||||||
|  |  | ||||||
|  |     return kvm_irqchip_add_irqfd_notifier(kvm_state, notifier, NULL, | ||||||
|  |                                           dev->routes.gsi[n]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n) | ||||||
|  | { | ||||||
|  |     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); | ||||||
|  |     VirtQueue *vq = virtio_get_queue(vdev, n); | ||||||
|  |     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); | ||||||
|  |     int ret; | ||||||
|  |  | ||||||
|  |     ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, notifier, | ||||||
|  |                                             dev->routes.gsi[n]); | ||||||
|  |     assert(ret == 0); | ||||||
|  | } | ||||||
|  |  | ||||||
| static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, | static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, | ||||||
|                                          bool assign, bool with_irqfd) |                                          bool assign, bool with_irqfd) | ||||||
| { | { | ||||||
| @@ -1042,11 +1201,17 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, | |||||||
|             return r; |             return r; | ||||||
|         } |         } | ||||||
|         virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); |         virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); | ||||||
|         /* We do not support irqfd for classic I/O interrupts, because the |         if (with_irqfd) { | ||||||
|          * classic interrupts are intermixed with the subchannel status, that |             r = virtio_ccw_add_irqfd(dev, n); | ||||||
|          * is queried with test subchannel. We want to use vhost, though. |             if (r) { | ||||||
|          * Lets make sure to have vhost running and wire up the irq fd to |                 virtio_queue_set_guest_notifier_fd_handler(vq, false, | ||||||
|          * land in qemu (and only the irq fd) in this code. |                                                            with_irqfd); | ||||||
|  |                 return r; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         /* | ||||||
|  |          * We do not support individual masking for channel devices, so we | ||||||
|  |          * need to manually trigger any guest masking callbacks here. | ||||||
|          */ |          */ | ||||||
|         if (k->guest_notifier_mask) { |         if (k->guest_notifier_mask) { | ||||||
|             k->guest_notifier_mask(vdev, n, false); |             k->guest_notifier_mask(vdev, n, false); | ||||||
| @@ -1060,6 +1225,9 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, | |||||||
|         if (k->guest_notifier_mask) { |         if (k->guest_notifier_mask) { | ||||||
|             k->guest_notifier_mask(vdev, n, true); |             k->guest_notifier_mask(vdev, n, true); | ||||||
|         } |         } | ||||||
|  |         if (with_irqfd) { | ||||||
|  |             virtio_ccw_remove_irqfd(dev, n); | ||||||
|  |         } | ||||||
|         virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); |         virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); | ||||||
|         event_notifier_cleanup(notifier); |         event_notifier_cleanup(notifier); | ||||||
|     } |     } | ||||||
| @@ -1071,24 +1239,39 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, | |||||||
| { | { | ||||||
|     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); |     VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); | ||||||
|     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); |     VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); | ||||||
|  |     bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled(); | ||||||
|     int r, n; |     int r, n; | ||||||
|  |  | ||||||
|  |     if (with_irqfd && assigned) { | ||||||
|  |         /* irq routes need to be set up before assigning irqfds */ | ||||||
|  |         r = virtio_ccw_setup_irqroutes(dev, nvqs); | ||||||
|  |         if (r < 0) { | ||||||
|  |             goto irqroute_error; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     for (n = 0; n < nvqs; n++) { |     for (n = 0; n < nvqs; n++) { | ||||||
|         if (!virtio_queue_get_num(vdev, n)) { |         if (!virtio_queue_get_num(vdev, n)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         /* false -> true, as soon as irqfd works */ |         r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd); | ||||||
|         r = virtio_ccw_set_guest_notifier(dev, n, assigned, false); |  | ||||||
|         if (r < 0) { |         if (r < 0) { | ||||||
|             goto assign_error; |             goto assign_error; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     if (with_irqfd && !assigned) { | ||||||
|  |         /* release irq routes after irqfds have been released */ | ||||||
|  |         virtio_ccw_release_irqroutes(dev, nvqs); | ||||||
|  |     } | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
| assign_error: | assign_error: | ||||||
|     while (--n >= 0) { |     while (--n >= 0) { | ||||||
|         virtio_ccw_set_guest_notifier(dev, n, !assigned, false); |         virtio_ccw_set_guest_notifier(dev, n, !assigned, false); | ||||||
|     } |     } | ||||||
|  | irqroute_error: | ||||||
|  |     if (with_irqfd && assigned) { | ||||||
|  |         virtio_ccw_release_irqroutes(dev, nvqs); | ||||||
|  |     } | ||||||
|     return r; |     return r; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ | |||||||
| #include <hw/virtio/virtio-balloon.h> | #include <hw/virtio/virtio-balloon.h> | ||||||
| #include <hw/virtio/virtio-rng.h> | #include <hw/virtio/virtio-rng.h> | ||||||
| #include <hw/virtio/virtio-bus.h> | #include <hw/virtio/virtio-bus.h> | ||||||
|  | #include <hw/s390x/s390_flic.h> | ||||||
|  |  | ||||||
| #define VIRTUAL_CSSID 0xfe | #define VIRTUAL_CSSID 0xfe | ||||||
|  |  | ||||||
| @@ -75,6 +76,14 @@ typedef struct VirtIOCCWDeviceClass { | |||||||
| #define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1 | #define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1 | ||||||
| #define VIRTIO_CCW_FLAG_USE_IOEVENTFD   (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT) | #define VIRTIO_CCW_FLAG_USE_IOEVENTFD   (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT) | ||||||
|  |  | ||||||
|  | typedef struct IndAddr { | ||||||
|  |     hwaddr addr; | ||||||
|  |     uint64_t map; | ||||||
|  |     unsigned long refcnt; | ||||||
|  |     int len; | ||||||
|  |     QTAILQ_ENTRY(IndAddr) sibling; | ||||||
|  | } IndAddr; | ||||||
|  |  | ||||||
| struct VirtioCcwDevice { | struct VirtioCcwDevice { | ||||||
|     DeviceState parent_obj; |     DeviceState parent_obj; | ||||||
|     SubchDev *sch; |     SubchDev *sch; | ||||||
| @@ -85,10 +94,11 @@ struct VirtioCcwDevice { | |||||||
|     bool ioeventfd_disabled; |     bool ioeventfd_disabled; | ||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
|     uint8_t thinint_isc; |     uint8_t thinint_isc; | ||||||
|  |     AdapterRoutes routes; | ||||||
|     /* Guest provided values: */ |     /* Guest provided values: */ | ||||||
|     hwaddr indicators; |     IndAddr *indicators; | ||||||
|     hwaddr indicators2; |     IndAddr *indicators2; | ||||||
|     hwaddr summary_indicator; |     IndAddr *summary_indicator; | ||||||
|     uint64_t ind_bit; |     uint64_t ind_bit; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ | |||||||
| #include "hw/hw.h" | #include "hw/hw.h" | ||||||
| #include "hw/pci/pci.h" | #include "hw/pci/pci.h" | ||||||
| #include "sysemu/dma.h" | #include "sysemu/dma.h" | ||||||
|  | #include "hw/pci/msi.h" | ||||||
| #include "hw/pci/msix.h" | #include "hw/pci/msix.h" | ||||||
| #include "qemu/iov.h" | #include "qemu/iov.h" | ||||||
| #include "hw/scsi/scsi.h" | #include "hw/scsi/scsi.h" | ||||||
| @@ -43,9 +44,11 @@ | |||||||
|  |  | ||||||
| #define MEGASAS_FLAG_USE_JBOD      0 | #define MEGASAS_FLAG_USE_JBOD      0 | ||||||
| #define MEGASAS_MASK_USE_JBOD      (1 << MEGASAS_FLAG_USE_JBOD) | #define MEGASAS_MASK_USE_JBOD      (1 << MEGASAS_FLAG_USE_JBOD) | ||||||
| #define MEGASAS_FLAG_USE_MSIX      1 | #define MEGASAS_FLAG_USE_MSI       1 | ||||||
|  | #define MEGASAS_MASK_USE_MSI       (1 << MEGASAS_FLAG_USE_MSI) | ||||||
|  | #define MEGASAS_FLAG_USE_MSIX      2 | ||||||
| #define MEGASAS_MASK_USE_MSIX      (1 << MEGASAS_FLAG_USE_MSIX) | #define MEGASAS_MASK_USE_MSIX      (1 << MEGASAS_FLAG_USE_MSIX) | ||||||
| #define MEGASAS_FLAG_USE_QUEUE64   2 | #define MEGASAS_FLAG_USE_QUEUE64   3 | ||||||
| #define MEGASAS_MASK_USE_QUEUE64   (1 << MEGASAS_FLAG_USE_QUEUE64) | #define MEGASAS_MASK_USE_QUEUE64   (1 << MEGASAS_FLAG_USE_QUEUE64) | ||||||
|  |  | ||||||
| static const char *mfi_frame_desc[] = { | static const char *mfi_frame_desc[] = { | ||||||
| @@ -132,6 +135,11 @@ static bool megasas_use_queue64(MegasasState *s) | |||||||
|     return s->flags & MEGASAS_MASK_USE_QUEUE64; |     return s->flags & MEGASAS_MASK_USE_QUEUE64; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static bool megasas_use_msi(MegasasState *s) | ||||||
|  | { | ||||||
|  |     return s->flags & MEGASAS_MASK_USE_MSI; | ||||||
|  | } | ||||||
|  |  | ||||||
| static bool megasas_use_msix(MegasasState *s) | static bool megasas_use_msix(MegasasState *s) | ||||||
| { | { | ||||||
|     return s->flags & MEGASAS_MASK_USE_MSIX; |     return s->flags & MEGASAS_MASK_USE_MSIX; | ||||||
| @@ -538,6 +546,9 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) | |||||||
|             if (msix_enabled(pci_dev)) { |             if (msix_enabled(pci_dev)) { | ||||||
|                 trace_megasas_msix_raise(0); |                 trace_megasas_msix_raise(0); | ||||||
|                 msix_notify(pci_dev, 0); |                 msix_notify(pci_dev, 0); | ||||||
|  |             } else if (msi_enabled(pci_dev)) { | ||||||
|  |                 trace_megasas_msi_raise(0); | ||||||
|  |                 msi_notify(pci_dev, 0); | ||||||
|             } else { |             } else { | ||||||
|                 trace_megasas_irq_raise(); |                 trace_megasas_irq_raise(); | ||||||
|                 pci_irq_assert(pci_dev); |                 pci_irq_assert(pci_dev); | ||||||
| @@ -717,8 +728,8 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) | |||||||
|     snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION); |     snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION); | ||||||
|     memcpy(info.image_component[0].name, "APP", 3); |     memcpy(info.image_component[0].name, "APP", 3); | ||||||
|     memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9); |     memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9); | ||||||
|     memcpy(info.image_component[0].build_date, __DATE__, 11); |     memcpy(info.image_component[0].build_date, "Apr  1 2014", 11); | ||||||
|     memcpy(info.image_component[0].build_time, __TIME__, 8); |     memcpy(info.image_component[0].build_time, "12:34:56", 8); | ||||||
|     info.image_component_count = 1; |     info.image_component_count = 1; | ||||||
|     if (pci_dev->has_rom) { |     if (pci_dev->has_rom) { | ||||||
|         uint8_t biosver[32]; |         uint8_t biosver[32]; | ||||||
| @@ -1106,6 +1117,21 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) | |||||||
|     return MFI_STAT_OK; |     return MFI_STAT_OK; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd) | ||||||
|  | { | ||||||
|  |     uint16_t flags; | ||||||
|  |  | ||||||
|  |     /* mbox0 contains flags */ | ||||||
|  |     flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]); | ||||||
|  |     trace_megasas_dcmd_ld_list_query(cmd->index, flags); | ||||||
|  |     if (flags == MR_LD_QUERY_TYPE_ALL || | ||||||
|  |         flags == MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) { | ||||||
|  |         return megasas_dcmd_ld_get_list(s, cmd); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return MFI_STAT_OK; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, | static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, | ||||||
|                                       MegasasCmd *cmd) |                                       MegasasCmd *cmd) | ||||||
| { | { | ||||||
| @@ -1409,6 +1435,8 @@ static const struct dcmd_cmd_tbl_t { | |||||||
|       megasas_dcmd_dummy }, |       megasas_dcmd_dummy }, | ||||||
|     { MFI_DCMD_LD_GET_LIST, "LD_GET_LIST", |     { MFI_DCMD_LD_GET_LIST, "LD_GET_LIST", | ||||||
|       megasas_dcmd_ld_get_list}, |       megasas_dcmd_ld_get_list}, | ||||||
|  |     { MFI_DCMD_LD_LIST_QUERY, "LD_LIST_QUERY", | ||||||
|  |       megasas_dcmd_ld_list_query }, | ||||||
|     { MFI_DCMD_LD_GET_INFO, "LD_GET_INFO", |     { MFI_DCMD_LD_GET_INFO, "LD_GET_INFO", | ||||||
|       megasas_dcmd_ld_get_info }, |       megasas_dcmd_ld_get_info }, | ||||||
|     { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP", |     { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP", | ||||||
| @@ -1939,12 +1967,20 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, | |||||||
|         break; |         break; | ||||||
|     case MFI_OMSK: |     case MFI_OMSK: | ||||||
|         s->intr_mask = val; |         s->intr_mask = val; | ||||||
|         if (!megasas_intr_enabled(s) && !msix_enabled(pci_dev)) { |         if (!megasas_intr_enabled(s) && | ||||||
|  |             !msi_enabled(pci_dev) && | ||||||
|  |             !msix_enabled(pci_dev)) { | ||||||
|             trace_megasas_irq_lower(); |             trace_megasas_irq_lower(); | ||||||
|             pci_irq_deassert(pci_dev); |             pci_irq_deassert(pci_dev); | ||||||
|         } |         } | ||||||
|         if (megasas_intr_enabled(s)) { |         if (megasas_intr_enabled(s)) { | ||||||
|  |             if (msix_enabled(pci_dev)) { | ||||||
|  |                 trace_megasas_msix_enabled(0); | ||||||
|  |             } else if (msi_enabled(pci_dev)) { | ||||||
|  |                 trace_megasas_msi_enabled(0); | ||||||
|  |             } else { | ||||||
|                 trace_megasas_intr_enabled(); |                 trace_megasas_intr_enabled(); | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             trace_megasas_intr_disabled(); |             trace_megasas_intr_disabled(); | ||||||
|         } |         } | ||||||
| @@ -2068,6 +2104,7 @@ static const VMStateDescription vmstate_megasas = { | |||||||
|     .minimum_version_id_old = 0, |     .minimum_version_id_old = 0, | ||||||
|     .fields      = (VMStateField[]) { |     .fields      = (VMStateField[]) { | ||||||
|         VMSTATE_PCI_DEVICE(parent_obj, MegasasState), |         VMSTATE_PCI_DEVICE(parent_obj, MegasasState), | ||||||
|  |         VMSTATE_MSIX(parent_obj, MegasasState), | ||||||
|  |  | ||||||
|         VMSTATE_INT32(fw_state, MegasasState), |         VMSTATE_INT32(fw_state, MegasasState), | ||||||
|         VMSTATE_INT32(intr_mask, MegasasState), |         VMSTATE_INT32(intr_mask, MegasasState), | ||||||
| @@ -2083,9 +2120,12 @@ static void megasas_scsi_uninit(PCIDevice *d) | |||||||
| { | { | ||||||
|     MegasasState *s = MEGASAS(d); |     MegasasState *s = MEGASAS(d); | ||||||
|  |  | ||||||
| #ifdef USE_MSIX |     if (megasas_use_msix(s)) { | ||||||
|     msix_uninit(d, &s->mmio_io); |         msix_uninit(d, &s->mmio_io, &s->mmio_io); | ||||||
| #endif |     } | ||||||
|  |     if (megasas_use_msi(s)) { | ||||||
|  |         msi_uninit(d); | ||||||
|  |     } | ||||||
|     memory_region_destroy(&s->mmio_io); |     memory_region_destroy(&s->mmio_io); | ||||||
|     memory_region_destroy(&s->port_io); |     memory_region_destroy(&s->port_io); | ||||||
|     memory_region_destroy(&s->queue_io); |     memory_region_destroy(&s->queue_io); | ||||||
| @@ -2124,15 +2164,15 @@ static int megasas_scsi_init(PCIDevice *dev) | |||||||
|     memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s, |     memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s, | ||||||
|                           "megasas-queue", 0x40000); |                           "megasas-queue", 0x40000); | ||||||
|  |  | ||||||
| #ifdef USE_MSIX |     if (megasas_use_msi(s) && | ||||||
|     /* MSI-X support is currently broken */ |         msi_init(dev, 0x50, 1, true, false)) { | ||||||
|  |         s->flags &= ~MEGASAS_MASK_USE_MSI; | ||||||
|  |     } | ||||||
|     if (megasas_use_msix(s) && |     if (megasas_use_msix(s) && | ||||||
|         msix_init(dev, 15, &s->mmio_io, 0, 0x2000)) { |         msix_init(dev, 15, &s->mmio_io, 0, 0x2000, | ||||||
|  |                   &s->mmio_io, 0, 0x3800, 0x68)) { | ||||||
|         s->flags &= ~MEGASAS_MASK_USE_MSIX; |         s->flags &= ~MEGASAS_MASK_USE_MSIX; | ||||||
|     } |     } | ||||||
| #else |  | ||||||
|     s->flags &= ~MEGASAS_MASK_USE_MSIX; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64; |     bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64; | ||||||
|     pci_register_bar(dev, 0, bar_type, &s->mmio_io); |     pci_register_bar(dev, 0, bar_type, &s->mmio_io); | ||||||
| @@ -2164,7 +2204,6 @@ static int megasas_scsi_init(PCIDevice *dev) | |||||||
|         s->fw_cmds = MEGASAS_MAX_FRAMES; |         s->fw_cmds = MEGASAS_MAX_FRAMES; | ||||||
|     } |     } | ||||||
|     trace_megasas_init(s->fw_sge, s->fw_cmds, |     trace_megasas_init(s->fw_sge, s->fw_cmds, | ||||||
|                        megasas_use_msix(s) ? "MSI-X" : "INTx", |  | ||||||
|                        megasas_is_jbod(s) ? "jbod" : "raid"); |                        megasas_is_jbod(s) ? "jbod" : "raid"); | ||||||
|     s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ? |     s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ? | ||||||
|         MAX_SCSI_DEVS : MFI_MAX_LD; |         MAX_SCSI_DEVS : MFI_MAX_LD; | ||||||
| @@ -2189,6 +2228,13 @@ static int megasas_scsi_init(PCIDevice *dev) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | megasas_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len) | ||||||
|  | { | ||||||
|  |     pci_default_write_config(pci, addr, val, len); | ||||||
|  |     msi_write_config(pci, addr, val, len); | ||||||
|  | } | ||||||
|  |  | ||||||
| static Property megasas_properties[] = { | static Property megasas_properties[] = { | ||||||
|     DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, |     DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge, | ||||||
|                        MEGASAS_DEFAULT_SGE), |                        MEGASAS_DEFAULT_SGE), | ||||||
| @@ -2196,10 +2242,10 @@ static Property megasas_properties[] = { | |||||||
|                        MEGASAS_DEFAULT_FRAMES), |                        MEGASAS_DEFAULT_FRAMES), | ||||||
|     DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), |     DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial), | ||||||
|     DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), |     DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0), | ||||||
| #ifdef USE_MSIX |     DEFINE_PROP_BIT("use_msi", MegasasState, flags, | ||||||
|  |                     MEGASAS_FLAG_USE_MSI, false), | ||||||
|     DEFINE_PROP_BIT("use_msix", MegasasState, flags, |     DEFINE_PROP_BIT("use_msix", MegasasState, flags, | ||||||
|                     MEGASAS_FLAG_USE_MSIX, false), |                     MEGASAS_FLAG_USE_MSIX, false), | ||||||
| #endif |  | ||||||
|     DEFINE_PROP_BIT("use_jbod", MegasasState, flags, |     DEFINE_PROP_BIT("use_jbod", MegasasState, flags, | ||||||
|                     MEGASAS_FLAG_USE_JBOD, false), |                     MEGASAS_FLAG_USE_JBOD, false), | ||||||
|     DEFINE_PROP_END_OF_LIST(), |     DEFINE_PROP_END_OF_LIST(), | ||||||
| @@ -2222,6 +2268,7 @@ static void megasas_class_init(ObjectClass *oc, void *data) | |||||||
|     dc->vmsd = &vmstate_megasas; |     dc->vmsd = &vmstate_megasas; | ||||||
|     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); | ||||||
|     dc->desc = "LSI MegaRAID SAS 1078"; |     dc->desc = "LSI MegaRAID SAS 1078"; | ||||||
|  |     pc->config_write = megasas_write_config; | ||||||
| } | } | ||||||
|  |  | ||||||
| static const TypeInfo megasas_info = { | static const TypeInfo megasas_info = { | ||||||
|   | |||||||
| @@ -164,6 +164,7 @@ typedef enum { | |||||||
|     MFI_DCMD_PD_BLINK =                 0x02070100, |     MFI_DCMD_PD_BLINK =                 0x02070100, | ||||||
|     MFI_DCMD_PD_UNBLINK =               0x02070200, |     MFI_DCMD_PD_UNBLINK =               0x02070200, | ||||||
|     MFI_DCMD_LD_GET_LIST =              0x03010000, |     MFI_DCMD_LD_GET_LIST =              0x03010000, | ||||||
|  |     MFI_DCMD_LD_LIST_QUERY =            0x03010100, | ||||||
|     MFI_DCMD_LD_GET_INFO =              0x03020000, |     MFI_DCMD_LD_GET_INFO =              0x03020000, | ||||||
|     MFI_DCMD_LD_GET_PROP =              0x03030000, |     MFI_DCMD_LD_GET_PROP =              0x03030000, | ||||||
|     MFI_DCMD_LD_SET_PROP =              0x03040000, |     MFI_DCMD_LD_SET_PROP =              0x03040000, | ||||||
| @@ -411,6 +412,14 @@ typedef enum { | |||||||
|     MR_PD_QUERY_TYPE_EXPOSED_TO_HOST =  5, /*query for system drives */ |     MR_PD_QUERY_TYPE_EXPOSED_TO_HOST =  5, /*query for system drives */ | ||||||
| } mfi_pd_query_type; | } mfi_pd_query_type; | ||||||
|  |  | ||||||
|  | typedef enum { | ||||||
|  |     MR_LD_QUERY_TYPE_ALL =              0, | ||||||
|  |     MR_LD_QUERY_TYPE_EXPOSED_TO_HOST =  1, | ||||||
|  |     MR_LD_QUERY_TYPE_USED_TGT_IDS =     2, | ||||||
|  |     MR_LD_QUERY_TYPE_CLUSTER_ACCESS =   3, | ||||||
|  |     MR_LD_QUERY_TYPE_CLUSTER_LOCALE =   4, | ||||||
|  | } mfi_ld_query_type; | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Other propertities and definitions |  * Other propertities and definitions | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -938,6 +938,7 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) | |||||||
|         if (cmd->xfer == 0) { |         if (cmd->xfer == 0) { | ||||||
|             cmd->xfer = 256; |             cmd->xfer = 256; | ||||||
|         } |         } | ||||||
|  |         /* fall through */ | ||||||
|     case WRITE_10: |     case WRITE_10: | ||||||
|     case WRITE_VERIFY_10: |     case WRITE_VERIFY_10: | ||||||
|     case WRITE_12: |     case WRITE_12: | ||||||
| @@ -952,6 +953,7 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) | |||||||
|         if (cmd->xfer == 0) { |         if (cmd->xfer == 0) { | ||||||
|             cmd->xfer = 256; |             cmd->xfer = 256; | ||||||
|         } |         } | ||||||
|  |         /* fall through */ | ||||||
|     case READ_10: |     case READ_10: | ||||||
|     case RECOVER_BUFFERED_DATA: |     case RECOVER_BUFFERED_DATA: | ||||||
|     case READ_12: |     case READ_12: | ||||||
|   | |||||||
| @@ -2458,21 +2458,27 @@ static int scsi_block_initfn(SCSIDevice *dev) | |||||||
|     int rc; |     int rc; | ||||||
|  |  | ||||||
|     if (!s->qdev.conf.bs) { |     if (!s->qdev.conf.bs) { | ||||||
|         error_report("scsi-block: drive property not set"); |         error_report("drive property not set"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* check we are using a driver managing SG_IO (version 3 and after) */ |     /* check we are using a driver managing SG_IO (version 3 and after) */ | ||||||
|     if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 || |     rc = bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version); | ||||||
|         sg_version < 30000) { |     if (rc < 0) { | ||||||
|         error_report("scsi-block: scsi generic interface too old"); |         error_report("cannot get SG_IO version number: %s.  " | ||||||
|  |                      "Is this a SCSI device?", | ||||||
|  |                      strerror(-rc)); | ||||||
|  |         return -1; | ||||||
|  |     } | ||||||
|  |     if (sg_version < 30000) { | ||||||
|  |         error_report("scsi generic interface too old"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* get device type from INQUIRY data */ |     /* get device type from INQUIRY data */ | ||||||
|     rc = get_device_type(s); |     rc = get_device_type(s); | ||||||
|     if (rc < 0) { |     if (rc < 0) { | ||||||
|         error_report("scsi-block: INQUIRY failed"); |         error_report("INQUIRY failed"); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -394,6 +394,7 @@ static void scsi_destroy(SCSIDevice *s) | |||||||
|  |  | ||||||
| static int scsi_generic_initfn(SCSIDevice *s) | static int scsi_generic_initfn(SCSIDevice *s) | ||||||
| { | { | ||||||
|  |     int rc; | ||||||
|     int sg_version; |     int sg_version; | ||||||
|     struct sg_scsi_id scsiid; |     struct sg_scsi_id scsiid; | ||||||
|  |  | ||||||
| @@ -412,8 +413,11 @@ static int scsi_generic_initfn(SCSIDevice *s) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* check we are using a driver managing SG_IO (version 3 and after */ |     /* check we are using a driver managing SG_IO (version 3 and after */ | ||||||
|     if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0) { |     rc = bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version); | ||||||
|         error_report("scsi generic interface not supported"); |     if (rc < 0) { | ||||||
|  |         error_report("cannot get SG_IO version number: %s.  " | ||||||
|  |                      "Is this a SCSI device?", | ||||||
|  |                      strerror(-rc)); | ||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
|     if (sg_version < 30000) { |     if (sg_version < 30000) { | ||||||
|   | |||||||
| @@ -498,7 +498,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, | |||||||
|                                    uint32_t event, uint32_t reason) |                                    uint32_t event, uint32_t reason) | ||||||
| { | { | ||||||
|     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); |     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); | ||||||
|     VirtIOSCSIReq *req = virtio_scsi_pop_req(s, vs->event_vq); |     VirtIOSCSIReq *req; | ||||||
|     VirtIOSCSIEvent *evt; |     VirtIOSCSIEvent *evt; | ||||||
|     VirtIODevice *vdev = VIRTIO_DEVICE(s); |     VirtIODevice *vdev = VIRTIO_DEVICE(s); | ||||||
|     int in_size; |     int in_size; | ||||||
| @@ -507,6 +507,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     req = virtio_scsi_pop_req(s, vs->event_vq); | ||||||
|     if (!req) { |     if (!req) { | ||||||
|         s->events_dropped = true; |         s->events_dropped = true; | ||||||
|         return; |         return; | ||||||
|   | |||||||
| @@ -793,19 +793,46 @@ static const MemoryRegionOps cmos_ops = { | |||||||
| static void rtc_get_date(Object *obj, Visitor *v, void *opaque, | static void rtc_get_date(Object *obj, Visitor *v, void *opaque, | ||||||
|                          const char *name, Error **errp) |                          const char *name, Error **errp) | ||||||
| { | { | ||||||
|  |     Error *err = NULL; | ||||||
|     RTCState *s = MC146818_RTC(obj); |     RTCState *s = MC146818_RTC(obj); | ||||||
|     struct tm current_tm; |     struct tm current_tm; | ||||||
|  |  | ||||||
|     rtc_update_time(s); |     rtc_update_time(s); | ||||||
|     rtc_get_time(s, ¤t_tm); |     rtc_get_time(s, ¤t_tm); | ||||||
|     visit_start_struct(v, NULL, "struct tm", name, 0, errp); |     visit_start_struct(v, NULL, "struct tm", name, 0, &err); | ||||||
|     visit_type_int32(v, ¤t_tm.tm_year, "tm_year", errp); |     if (err) { | ||||||
|     visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", errp); |         goto out; | ||||||
|     visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", errp); |     } | ||||||
|     visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", errp); |     visit_type_int32(v, ¤t_tm.tm_year, "tm_year", &err); | ||||||
|     visit_type_int32(v, ¤t_tm.tm_min, "tm_min", errp); |     if (err) { | ||||||
|     visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", errp); |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_int32(v, ¤t_tm.tm_min, "tm_min", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  | out_end: | ||||||
|  |     error_propagate(errp, err); | ||||||
|  |     err = NULL; | ||||||
|     visit_end_struct(v, errp); |     visit_end_struct(v, errp); | ||||||
|  | out: | ||||||
|  |     error_propagate(errp, err); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void rtc_realizefn(DeviceState *dev, Error **errp) | static void rtc_realizefn(DeviceState *dev, Error **errp) | ||||||
|   | |||||||
| @@ -47,6 +47,8 @@ typedef struct USBHIDState { | |||||||
|     USBEndpoint *intr; |     USBEndpoint *intr; | ||||||
|     HIDState hid; |     HIDState hid; | ||||||
|     uint32_t usb_version; |     uint32_t usb_version; | ||||||
|  |     char *display; | ||||||
|  |     uint32_t head; | ||||||
| } USBHIDState; | } USBHIDState; | ||||||
|  |  | ||||||
| enum { | enum { | ||||||
| @@ -574,6 +576,9 @@ static int usb_hid_initfn(USBDevice *dev, int kind) | |||||||
|     usb_desc_init(dev); |     usb_desc_init(dev); | ||||||
|     us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); |     us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); | ||||||
|     hid_init(&us->hid, kind, usb_hid_changed); |     hid_init(&us->hid, kind, usb_hid_changed); | ||||||
|  |     if (us->display && us->hid.s) { | ||||||
|  |         qemu_input_handler_bind(us->hid.s, us->display, us->head, NULL); | ||||||
|  |     } | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -653,6 +658,8 @@ static void usb_hid_class_initfn(ObjectClass *klass, void *data) | |||||||
|  |  | ||||||
| static Property usb_tablet_properties[] = { | static Property usb_tablet_properties[] = { | ||||||
|         DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), |         DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), | ||||||
|  |         DEFINE_PROP_STRING("display", USBHIDState, display), | ||||||
|  |         DEFINE_PROP_UINT32("head", USBHIDState, head, 0), | ||||||
|         DEFINE_PROP_END_OF_LIST(), |         DEFINE_PROP_END_OF_LIST(), | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -696,6 +703,11 @@ static const TypeInfo usb_mouse_info = { | |||||||
|     .class_init    = usb_mouse_class_initfn, |     .class_init    = usb_mouse_class_initfn, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static Property usb_keyboard_properties[] = { | ||||||
|  |         DEFINE_PROP_STRING("display", USBHIDState, display), | ||||||
|  |         DEFINE_PROP_END_OF_LIST(), | ||||||
|  | }; | ||||||
|  |  | ||||||
| static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) | static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) | ||||||
| { | { | ||||||
|     DeviceClass *dc = DEVICE_CLASS(klass); |     DeviceClass *dc = DEVICE_CLASS(klass); | ||||||
| @@ -706,6 +718,7 @@ static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) | |||||||
|     uc->product_desc   = "QEMU USB Keyboard"; |     uc->product_desc   = "QEMU USB Keyboard"; | ||||||
|     uc->usb_desc       = &desc_keyboard; |     uc->usb_desc       = &desc_keyboard; | ||||||
|     dc->vmsd = &vmstate_usb_kbd; |     dc->vmsd = &vmstate_usb_kbd; | ||||||
|  |     dc->props = usb_keyboard_properties; | ||||||
|     set_bit(DEVICE_CATEGORY_INPUT, dc->categories); |     set_bit(DEVICE_CATEGORY_INPUT, dc->categories); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -108,6 +108,7 @@ static void balloon_stats_poll_cb(void *opaque) | |||||||
| static void balloon_stats_get_all(Object *obj, struct Visitor *v, | static void balloon_stats_get_all(Object *obj, struct Visitor *v, | ||||||
|                                   void *opaque, const char *name, Error **errp) |                                   void *opaque, const char *name, Error **errp) | ||||||
| { | { | ||||||
|  |     Error *err = NULL; | ||||||
|     VirtIOBalloon *s = opaque; |     VirtIOBalloon *s = opaque; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
| @@ -116,17 +117,33 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v, | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     visit_start_struct(v, NULL, "guest-stats", name, 0, errp); |     visit_start_struct(v, NULL, "guest-stats", name, 0, &err); | ||||||
|     visit_type_int(v, &s->stats_last_update, "last-update", errp); |     if (err) { | ||||||
|  |         goto out; | ||||||
|     visit_start_struct(v, NULL, NULL, "stats", 0, errp); |     } | ||||||
|     for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { |     visit_type_int(v, &s->stats_last_update, "last-update", &err); | ||||||
|         visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], |     if (err) { | ||||||
|                          errp); |         goto out_end; | ||||||
|     } |     } | ||||||
|     visit_end_struct(v, errp); |  | ||||||
|  |  | ||||||
|     visit_end_struct(v, errp); |     visit_start_struct(v, NULL, NULL, "stats", 0, &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) { | ||||||
|  |         visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i], | ||||||
|  |                          &err); | ||||||
|  |     } | ||||||
|  |     error_propagate(errp, err); | ||||||
|  |     err = NULL; | ||||||
|  |     visit_end_struct(v, &err); | ||||||
|  |  | ||||||
|  | out_end: | ||||||
|  |     error_propagate(errp, err); | ||||||
|  |     err = NULL; | ||||||
|  |     visit_end_struct(v, &err); | ||||||
|  | out: | ||||||
|  |     error_propagate(errp, err); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v, | static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v, | ||||||
|   | |||||||
| @@ -120,6 +120,8 @@ typedef enum { | |||||||
| /* BDRV_BLOCK_DATA: data is read from bs->file or another file | /* BDRV_BLOCK_DATA: data is read from bs->file or another file | ||||||
|  * BDRV_BLOCK_ZERO: sectors read as zero |  * BDRV_BLOCK_ZERO: sectors read as zero | ||||||
|  * BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data |  * BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data | ||||||
|  |  * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this | ||||||
|  |  *                       layer (as opposed to the backing file) | ||||||
|  * BDRV_BLOCK_RAW: used internally to indicate that the request |  * BDRV_BLOCK_RAW: used internally to indicate that the request | ||||||
|  *                 was answered by the raw driver and that one |  *                 was answered by the raw driver and that one | ||||||
|  *                 should look in bs->file directly. |  *                 should look in bs->file directly. | ||||||
| @@ -141,10 +143,11 @@ typedef enum { | |||||||
|  *  f    t        f       not allocated or unknown offset, read as zero |  *  f    t        f       not allocated or unknown offset, read as zero | ||||||
|  *  f    f        f       not allocated or unknown offset, read from backing_hd |  *  f    f        f       not allocated or unknown offset, read from backing_hd | ||||||
|  */ |  */ | ||||||
| #define BDRV_BLOCK_DATA         1 | #define BDRV_BLOCK_DATA         0x01 | ||||||
| #define BDRV_BLOCK_ZERO         2 | #define BDRV_BLOCK_ZERO         0x02 | ||||||
| #define BDRV_BLOCK_OFFSET_VALID 4 | #define BDRV_BLOCK_OFFSET_VALID 0x04 | ||||||
| #define BDRV_BLOCK_RAW          8 | #define BDRV_BLOCK_RAW          0x08 | ||||||
|  | #define BDRV_BLOCK_ALLOCATED    0x10 | ||||||
| #define BDRV_BLOCK_OFFSET_MASK  BDRV_SECTOR_MASK | #define BDRV_BLOCK_OFFSET_MASK  BDRV_SECTOR_MASK | ||||||
|  |  | ||||||
| typedef enum { | typedef enum { | ||||||
|   | |||||||
| @@ -364,6 +364,7 @@ struct BlockDriverState { | |||||||
|     BlockJob *job; |     BlockJob *job; | ||||||
|  |  | ||||||
|     QDict *options; |     QDict *options; | ||||||
|  |     BlockdevDetectZeroesOptions detect_zeroes; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| int get_tmp_filename(char *filename, int size); | int get_tmp_filename(char *filename, int size); | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| #define QEMU_HID_H | #define QEMU_HID_H | ||||||
|  |  | ||||||
| #include "migration/vmstate.h" | #include "migration/vmstate.h" | ||||||
|  | #include "ui/input.h" | ||||||
|  |  | ||||||
| #define HID_MOUSE     1 | #define HID_MOUSE     1 | ||||||
| #define HID_TABLET    2 | #define HID_TABLET    2 | ||||||
| @@ -22,7 +23,6 @@ typedef void (*HIDEventFunc)(HIDState *s); | |||||||
| typedef struct HIDMouseState { | typedef struct HIDMouseState { | ||||||
|     HIDPointerEvent queue[QUEUE_LENGTH]; |     HIDPointerEvent queue[QUEUE_LENGTH]; | ||||||
|     int mouse_grabbed; |     int mouse_grabbed; | ||||||
|     QEMUPutMouseEntry *eh_entry; |  | ||||||
| } HIDMouseState; | } HIDMouseState; | ||||||
|  |  | ||||||
| typedef struct HIDKeyboardState { | typedef struct HIDKeyboardState { | ||||||
| @@ -31,7 +31,6 @@ typedef struct HIDKeyboardState { | |||||||
|     uint8_t leds; |     uint8_t leds; | ||||||
|     uint8_t key[16]; |     uint8_t key[16]; | ||||||
|     int32_t keys; |     int32_t keys; | ||||||
|     QEMUPutKbdEntry *eh_entry; |  | ||||||
| } HIDKeyboardState; | } HIDKeyboardState; | ||||||
|  |  | ||||||
| struct HIDState { | struct HIDState { | ||||||
| @@ -47,6 +46,7 @@ struct HIDState { | |||||||
|     bool idle_pending; |     bool idle_pending; | ||||||
|     QEMUTimer *idle_timer; |     QEMUTimer *idle_timer; | ||||||
|     HIDEventFunc event; |     HIDEventFunc event; | ||||||
|  |     QemuInputHandlerState *s; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void hid_init(HIDState *hs, int kind, HIDEventFunc event); | void hid_init(HIDState *hs, int kind, HIDEventFunc event); | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								include/hw/s390x/adapter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								include/hw/s390x/adapter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | /* | ||||||
|  |  * s390 adapter definitions | ||||||
|  |  * | ||||||
|  |  * Copyright 2013,2014 IBM Corp. | ||||||
|  |  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | ||||||
|  |  * | ||||||
|  |  * This work is licensed under the terms of the GNU GPL, version 2 or (at | ||||||
|  |  * your option) any later version. See the COPYING file in the top-level | ||||||
|  |  * directory. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef S390X_ADAPTER_H | ||||||
|  | #define S390X_ADAPTER_H | ||||||
|  |  | ||||||
|  | struct AdapterInfo { | ||||||
|  |     uint64_t ind_addr; | ||||||
|  |     uint64_t summary_addr; | ||||||
|  |     uint64_t ind_offset; | ||||||
|  |     uint32_t summary_offset; | ||||||
|  |     uint32_t adapter_id; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -1,33 +1,76 @@ | |||||||
| /* | /* | ||||||
|  * QEMU S390x KVM floating interrupt controller (flic) |  * QEMU S390x floating interrupt controller (flic) | ||||||
|  * |  * | ||||||
|  * Copyright 2014 IBM Corp. |  * Copyright 2014 IBM Corp. | ||||||
|  * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com> |  * Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com> | ||||||
|  |  *            Cornelia Huck <cornelia.huck@de.ibm.com> | ||||||
|  * |  * | ||||||
|  * This work is licensed under the terms of the GNU GPL, version 2 or (at |  * This work is licensed under the terms of the GNU GPL, version 2 or (at | ||||||
|  * your option) any later version. See the COPYING file in the top-level |  * your option) any later version. See the COPYING file in the top-level | ||||||
|  * directory. |  * directory. | ||||||
|  */ |  */ | ||||||
|  |  | ||||||
| #ifndef __KVM_S390_FLIC_H | #ifndef __HW_S390_FLIC_H | ||||||
| #define __KVM_S390_FLIC_H | #define __HW_S390_FLIC_H | ||||||
|  |  | ||||||
| #include "hw/sysbus.h" | #include "hw/sysbus.h" | ||||||
|  | #include "hw/s390x/adapter.h" | ||||||
|  | #include "hw/virtio/virtio.h" | ||||||
|  |  | ||||||
| #define TYPE_KVM_S390_FLIC "s390-flic" | typedef struct AdapterRoutes { | ||||||
|  |     AdapterInfo adapter; | ||||||
|  |     int num_routes; | ||||||
|  |     int gsi[VIRTIO_PCI_QUEUE_MAX]; | ||||||
|  | } AdapterRoutes; | ||||||
|  |  | ||||||
|  | #define TYPE_S390_FLIC_COMMON "s390-flic" | ||||||
|  | #define S390_FLIC_COMMON(obj) \ | ||||||
|  |     OBJECT_CHECK(S390FLICState, (obj), TYPE_S390_FLIC_COMMON) | ||||||
|  |  | ||||||
|  | typedef struct S390FLICState { | ||||||
|  |     SysBusDevice parent_obj; | ||||||
|  |  | ||||||
|  | } S390FLICState; | ||||||
|  |  | ||||||
|  | #define S390_FLIC_COMMON_CLASS(klass) \ | ||||||
|  |     OBJECT_CLASS_CHECK(S390FLICStateClass, (klass), TYPE_S390_FLIC_COMMON) | ||||||
|  | #define S390_FLIC_COMMON_GET_CLASS(obj) \ | ||||||
|  |     OBJECT_GET_CLASS(S390FLICStateClass, (obj), TYPE_S390_FLIC_COMMON) | ||||||
|  |  | ||||||
|  | typedef struct S390FLICStateClass { | ||||||
|  |     DeviceClass parent_class; | ||||||
|  |  | ||||||
|  |     int (*register_io_adapter)(S390FLICState *fs, uint32_t id, uint8_t isc, | ||||||
|  |                                bool swap, bool maskable); | ||||||
|  |     int (*io_adapter_map)(S390FLICState *fs, uint32_t id, uint64_t map_addr, | ||||||
|  |                           bool do_map); | ||||||
|  |     int (*add_adapter_routes)(S390FLICState *fs, AdapterRoutes *routes); | ||||||
|  |     void (*release_adapter_routes)(S390FLICState *fs, AdapterRoutes *routes); | ||||||
|  | } S390FLICStateClass; | ||||||
|  |  | ||||||
|  | #define TYPE_KVM_S390_FLIC "s390-flic-kvm" | ||||||
| #define KVM_S390_FLIC(obj) \ | #define KVM_S390_FLIC(obj) \ | ||||||
|     OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC) |     OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC) | ||||||
|  |  | ||||||
| typedef struct KVMS390FLICState { | #define TYPE_QEMU_S390_FLIC "s390-flic-qemu" | ||||||
|     SysBusDevice parent_obj; | #define QEMU_S390_FLIC(obj) \ | ||||||
|  |     OBJECT_CHECK(QEMUS390FLICState, (obj), TYPE_QEMU_S390_FLIC) | ||||||
|  |  | ||||||
|     uint32_t fd; | typedef struct QEMUS390FLICState { | ||||||
| } KVMS390FLICState; |     S390FLICState parent_obj; | ||||||
|  | } QEMUS390FLICState; | ||||||
|  |  | ||||||
|  | void s390_flic_init(void); | ||||||
|  |  | ||||||
|  | S390FLICState *s390_get_flic(void); | ||||||
|  |  | ||||||
| #ifdef CONFIG_KVM | #ifdef CONFIG_KVM | ||||||
| void s390_flic_init(void); | DeviceState *s390_flic_kvm_create(void); | ||||||
| #else | #else | ||||||
| static inline void s390_flic_init(void) { } | static inline DeviceState *s390_flic_kvm_create(void) | ||||||
|  | { | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #endif /* __KVM_S390_FLIC_H */ | #endif /* __HW_S390_FLIC_H */ | ||||||
|   | |||||||
| @@ -67,12 +67,6 @@ void error_set_win32(Error **errp, int win32_err, ErrorClass err_class, | |||||||
|  */ |  */ | ||||||
| void error_setg_file_open(Error **errp, int os_errno, const char *filename); | void error_setg_file_open(Error **errp, int os_errno, const char *filename); | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Returns true if an indirect pointer to an error is pointing to a valid |  | ||||||
|  * error object. |  | ||||||
|  */ |  | ||||||
| bool error_is_set(Error **errp); |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Get the error class of an error object. |  * Get the error class of an error object. | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ | |||||||
| #include "qapi/qmp/qobject.h" | #include "qapi/qmp/qobject.h" | ||||||
| #include "qapi/qmp/qlist.h" | #include "qapi/qmp/qlist.h" | ||||||
| #include "qemu/queue.h" | #include "qemu/queue.h" | ||||||
|  | #include <stdbool.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  |  | ||||||
| #define QDICT_BUCKET_MAX 512 | #define QDICT_BUCKET_MAX 512 | ||||||
| @@ -70,4 +71,6 @@ void qdict_flatten(QDict *qdict); | |||||||
| void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); | void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); | ||||||
| void qdict_array_split(QDict *src, QList **dst); | void qdict_array_split(QDict *src, QList **dst); | ||||||
|  |  | ||||||
|  | void qdict_join(QDict *dest, QDict *src, bool overwrite); | ||||||
|  |  | ||||||
| #endif /* QDICT_H */ | #endif /* QDICT_H */ | ||||||
|   | |||||||
| @@ -42,13 +42,9 @@ struct Visitor | |||||||
|                         Error **errp); |                         Error **errp); | ||||||
|  |  | ||||||
|     /* May be NULL */ |     /* May be NULL */ | ||||||
|     void (*start_optional)(Visitor *v, bool *present, const char *name, |     void (*optional)(Visitor *v, bool *present, const char *name, | ||||||
|                      Error **errp); |                      Error **errp); | ||||||
|     void (*end_optional)(Visitor *v, Error **errp); |  | ||||||
|  |  | ||||||
|     void (*start_handle)(Visitor *v, void **obj, const char *kind, |  | ||||||
|                          const char *name, Error **errp); |  | ||||||
|     void (*end_handle)(Visitor *v, Error **errp); |  | ||||||
|     void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp); |     void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp); | ||||||
|     void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp); |     void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp); | ||||||
|     void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp); |     void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp); | ||||||
|   | |||||||
| @@ -39,9 +39,8 @@ void visit_end_implicit_struct(Visitor *v, Error **errp); | |||||||
| void visit_start_list(Visitor *v, const char *name, Error **errp); | void visit_start_list(Visitor *v, const char *name, Error **errp); | ||||||
| GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp); | GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp); | ||||||
| void visit_end_list(Visitor *v, Error **errp); | void visit_end_list(Visitor *v, Error **errp); | ||||||
| void visit_start_optional(Visitor *v, bool *present, const char *name, | void visit_optional(Visitor *v, bool *present, const char *name, | ||||||
|                     Error **errp); |                     Error **errp); | ||||||
| void visit_end_optional(Visitor *v, Error **errp); |  | ||||||
| void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, | void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, | ||||||
|                          const char *name, Error **errp); |                          const char *name, Error **errp); | ||||||
| void visit_type_enum(Visitor *v, int *obj, const char *strings[], | void visit_type_enum(Visitor *v, int *obj, const char *strings[], | ||||||
|   | |||||||
| @@ -318,6 +318,7 @@ void qemu_iovec_concat(QEMUIOVector *dst, | |||||||
| void qemu_iovec_concat_iov(QEMUIOVector *dst, | void qemu_iovec_concat_iov(QEMUIOVector *dst, | ||||||
|                            struct iovec *src_iov, unsigned int src_cnt, |                            struct iovec *src_iov, unsigned int src_cnt, | ||||||
|                            size_t soffset, size_t sbytes); |                            size_t soffset, size_t sbytes); | ||||||
|  | bool qemu_iovec_is_zero(QEMUIOVector *qiov); | ||||||
| void qemu_iovec_destroy(QEMUIOVector *qiov); | void qemu_iovec_destroy(QEMUIOVector *qiov); | ||||||
| void qemu_iovec_reset(QEMUIOVector *qiov); | void qemu_iovec_reset(QEMUIOVector *qiov); | ||||||
| size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, | size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, | ||||||
|   | |||||||
| @@ -74,5 +74,6 @@ typedef struct SHPCDevice SHPCDevice; | |||||||
| typedef struct FWCfgState FWCfgState; | typedef struct FWCfgState FWCfgState; | ||||||
| typedef struct PcGuestInfo PcGuestInfo; | typedef struct PcGuestInfo PcGuestInfo; | ||||||
| typedef struct Range Range; | typedef struct Range Range; | ||||||
|  | typedef struct AdapterInfo AdapterInfo; | ||||||
|  |  | ||||||
| #endif /* QEMU_TYPEDEFS_H */ | #endif /* QEMU_TYPEDEFS_H */ | ||||||
|   | |||||||
| @@ -300,7 +300,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension); | |||||||
|         };                                                           \ |         };                                                           \ | ||||||
|         uint64_t args_tmp[] = { __VA_ARGS__ };                       \ |         uint64_t args_tmp[] = { __VA_ARGS__ };                       \ | ||||||
|         int i;                                                       \ |         int i;                                                       \ | ||||||
|         for (i = 0; i < ARRAY_SIZE(args_tmp) &&                      \ |         for (i = 0; i < (int)ARRAY_SIZE(args_tmp) &&                 \ | ||||||
|                      i < ARRAY_SIZE(cap.args); i++) {                \ |                      i < ARRAY_SIZE(cap.args); i++) {                \ | ||||||
|             cap.args[i] = args_tmp[i];                               \ |             cap.args[i] = args_tmp[i];                               \ | ||||||
|         }                                                            \ |         }                                                            \ | ||||||
| @@ -315,7 +315,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension); | |||||||
|         };                                                           \ |         };                                                           \ | ||||||
|         uint64_t args_tmp[] = { __VA_ARGS__ };                       \ |         uint64_t args_tmp[] = { __VA_ARGS__ };                       \ | ||||||
|         int i;                                                       \ |         int i;                                                       \ | ||||||
|         for (i = 0; i < ARRAY_SIZE(args_tmp) &&                      \ |         for (i = 0; i < (int)ARRAY_SIZE(args_tmp) &&                 \ | ||||||
|                      i < ARRAY_SIZE(cap.args); i++) {                \ |                      i < ARRAY_SIZE(cap.args); i++) {                \ | ||||||
|             cap.args[i] = args_tmp[i];                               \ |             cap.args[i] = args_tmp[i];                               \ | ||||||
|         }                                                            \ |         }                                                            \ | ||||||
| @@ -363,6 +363,8 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg); | |||||||
| int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg); | int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg); | ||||||
| void kvm_irqchip_release_virq(KVMState *s, int virq); | void kvm_irqchip_release_virq(KVMState *s, int virq); | ||||||
|  |  | ||||||
|  | int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter); | ||||||
|  |  | ||||||
| int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, | int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, | ||||||
|                                    EventNotifier *rn, int virq); |                                    EventNotifier *rn, int virq); | ||||||
| int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq); | int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq); | ||||||
|   | |||||||
| @@ -29,6 +29,9 @@ QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev, | |||||||
| void qemu_input_handler_activate(QemuInputHandlerState *s); | void qemu_input_handler_activate(QemuInputHandlerState *s); | ||||||
| void qemu_input_handler_deactivate(QemuInputHandlerState *s); | void qemu_input_handler_deactivate(QemuInputHandlerState *s); | ||||||
| void qemu_input_handler_unregister(QemuInputHandlerState *s); | void qemu_input_handler_unregister(QemuInputHandlerState *s); | ||||||
|  | void qemu_input_handler_bind(QemuInputHandlerState *s, | ||||||
|  |                              const char *device_id, int head, | ||||||
|  |                              Error **errp); | ||||||
| void qemu_input_event_send(QemuConsole *src, InputEvent *evt); | void qemu_input_event_send(QemuConsole *src, InputEvent *evt); | ||||||
| void qemu_input_event_sync(void); | void qemu_input_event_sync(void); | ||||||
|  |  | ||||||
| @@ -36,6 +39,7 @@ InputEvent *qemu_input_event_new_key(KeyValue *key, bool down); | |||||||
| void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); | void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); | ||||||
| void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down); | void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down); | ||||||
| void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down); | void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down); | ||||||
|  | int qemu_input_key_number_to_qcode(uint8_t nr); | ||||||
| int qemu_input_key_value_to_number(const KeyValue *value); | int qemu_input_key_value_to_number(const KeyValue *value); | ||||||
| int qemu_input_key_value_to_qcode(const KeyValue *value); | int qemu_input_key_value_to_qcode(const KeyValue *value); | ||||||
| int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, | int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								kvm-all.c
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								kvm-all.c
									
									
									
									
									
								
							| @@ -27,6 +27,7 @@ | |||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "hw/hw.h" | #include "hw/hw.h" | ||||||
| #include "hw/pci/msi.h" | #include "hw/pci/msi.h" | ||||||
|  | #include "hw/s390x/adapter.h" | ||||||
| #include "exec/gdbstub.h" | #include "exec/gdbstub.h" | ||||||
| #include "sysemu/kvm.h" | #include "sysemu/kvm.h" | ||||||
| #include "qemu/bswap.h" | #include "qemu/bswap.h" | ||||||
| @@ -1236,6 +1237,35 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int rfd, int virq, | |||||||
|     return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd); |     return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) | ||||||
|  | { | ||||||
|  |     struct kvm_irq_routing_entry kroute; | ||||||
|  |     int virq; | ||||||
|  |  | ||||||
|  |     if (!kvm_gsi_routing_enabled()) { | ||||||
|  |         return -ENOSYS; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     virq = kvm_irqchip_get_virq(s); | ||||||
|  |     if (virq < 0) { | ||||||
|  |         return virq; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     kroute.gsi = virq; | ||||||
|  |     kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER; | ||||||
|  |     kroute.flags = 0; | ||||||
|  |     kroute.u.adapter.summary_addr = adapter->summary_addr; | ||||||
|  |     kroute.u.adapter.ind_addr = adapter->ind_addr; | ||||||
|  |     kroute.u.adapter.summary_offset = adapter->summary_offset; | ||||||
|  |     kroute.u.adapter.ind_offset = adapter->ind_offset; | ||||||
|  |     kroute.u.adapter.adapter_id = adapter->adapter_id; | ||||||
|  |  | ||||||
|  |     kvm_add_routing_entry(s, &kroute); | ||||||
|  |     kvm_irqchip_commit_routes(s); | ||||||
|  |  | ||||||
|  |     return virq; | ||||||
|  | } | ||||||
|  |  | ||||||
| #else /* !KVM_CAP_IRQ_ROUTING */ | #else /* !KVM_CAP_IRQ_ROUTING */ | ||||||
|  |  | ||||||
| void kvm_init_irq_routing(KVMState *s) | void kvm_init_irq_routing(KVMState *s) | ||||||
| @@ -1256,6 +1286,11 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) | |||||||
|     return -ENOSYS; |     return -ENOSYS; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) | ||||||
|  | { | ||||||
|  |     return -ENOSYS; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign) | static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign) | ||||||
| { | { | ||||||
|     abort(); |     abort(); | ||||||
| @@ -1285,7 +1320,8 @@ static int kvm_irqchip_create(KVMState *s) | |||||||
|     int ret; |     int ret; | ||||||
|  |  | ||||||
|     if (!qemu_opt_get_bool(qemu_get_machine_opts(), "kernel_irqchip", true) || |     if (!qemu_opt_get_bool(qemu_get_machine_opts(), "kernel_irqchip", true) || | ||||||
|         !kvm_check_extension(s, KVM_CAP_IRQCHIP)) { |         (!kvm_check_extension(s, KVM_CAP_IRQCHIP) && | ||||||
|  |          (kvm_vm_enable_cap(s, KVM_CAP_S390_IRQCHIP, 0) < 0))) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -136,6 +136,11 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) | |||||||
|     return -ENOSYS; |     return -ENOSYS; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) | ||||||
|  | { | ||||||
|  |     return -ENOSYS; | ||||||
|  | } | ||||||
|  |  | ||||||
| int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, | int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, | ||||||
|                                    EventNotifier *rn, int virq) |                                    EventNotifier *rn, int virq) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ | |||||||
| #include <linux/types.h> | #include <linux/types.h> | ||||||
|  |  | ||||||
| #define __KVM_S390 | #define __KVM_S390 | ||||||
|  | #define __KVM_HAVE_GUEST_DEBUG | ||||||
|  |  | ||||||
| /* Device control API: s390-specific devices */ | /* Device control API: s390-specific devices */ | ||||||
| #define KVM_DEV_FLIC_GET_ALL_IRQS	1 | #define KVM_DEV_FLIC_GET_ALL_IRQS	1 | ||||||
| @@ -54,6 +55,13 @@ struct kvm_s390_io_adapter_req { | |||||||
| 	__u64 addr; | 	__u64 addr; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /* kvm attr_group  on vm fd */ | ||||||
|  | #define KVM_S390_VM_MEM_CTRL		0 | ||||||
|  |  | ||||||
|  | /* kvm attributes for mem_ctrl */ | ||||||
|  | #define KVM_S390_VM_MEM_ENABLE_CMMA	0 | ||||||
|  | #define KVM_S390_VM_MEM_CLR_CMMA	1 | ||||||
|  |  | ||||||
| /* for KVM_GET_REGS and KVM_SET_REGS */ | /* for KVM_GET_REGS and KVM_SET_REGS */ | ||||||
| struct kvm_regs { | struct kvm_regs { | ||||||
| 	/* general purpose regs for s390 */ | 	/* general purpose regs for s390 */ | ||||||
| @@ -72,11 +80,31 @@ struct kvm_fpu { | |||||||
| 	__u64 fprs[16]; | 	__u64 fprs[16]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | #define KVM_GUESTDBG_USE_HW_BP		0x00010000 | ||||||
|  |  | ||||||
|  | #define KVM_HW_BP			1 | ||||||
|  | #define KVM_HW_WP_WRITE			2 | ||||||
|  | #define KVM_SINGLESTEP			4 | ||||||
|  |  | ||||||
| struct kvm_debug_exit_arch { | struct kvm_debug_exit_arch { | ||||||
|  | 	__u64 addr; | ||||||
|  | 	__u8 type; | ||||||
|  | 	__u8 pad[7]; /* Should be set to 0 */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct kvm_hw_breakpoint { | ||||||
|  | 	__u64 addr; | ||||||
|  | 	__u64 phys_addr; | ||||||
|  | 	__u64 len; | ||||||
|  | 	__u8 type; | ||||||
|  | 	__u8 pad[7]; /* Should be set to 0 */ | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* for KVM_SET_GUEST_DEBUG */ | /* for KVM_SET_GUEST_DEBUG */ | ||||||
| struct kvm_guest_debug_arch { | struct kvm_guest_debug_arch { | ||||||
|  | 	__u32 nr_hw_bp; | ||||||
|  | 	__u32 pad; /* Should be set to 0 */ | ||||||
|  | 	struct kvm_hw_breakpoint *hw_bp; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #define KVM_SYNC_PREFIX (1UL << 0) | #define KVM_SYNC_PREFIX (1UL << 0) | ||||||
|   | |||||||
| @@ -416,6 +416,8 @@ struct kvm_s390_psw { | |||||||
| #define KVM_S390_INT_PFAULT_INIT	0xfffe0004u | #define KVM_S390_INT_PFAULT_INIT	0xfffe0004u | ||||||
| #define KVM_S390_INT_PFAULT_DONE	0xfffe0005u | #define KVM_S390_INT_PFAULT_DONE	0xfffe0005u | ||||||
| #define KVM_S390_MCHK			0xfffe1000u | #define KVM_S390_MCHK			0xfffe1000u | ||||||
|  | #define KVM_S390_INT_CLOCK_COMP		0xffff1004u | ||||||
|  | #define KVM_S390_INT_CPU_TIMER		0xffff1005u | ||||||
| #define KVM_S390_INT_VIRTIO		0xffff2603u | #define KVM_S390_INT_VIRTIO		0xffff2603u | ||||||
| #define KVM_S390_INT_SERVICE		0xffff2401u | #define KVM_S390_INT_SERVICE		0xffff2401u | ||||||
| #define KVM_S390_INT_EMERGENCY		0xffff1201u | #define KVM_S390_INT_EMERGENCY		0xffff1201u | ||||||
| @@ -515,6 +517,7 @@ enum { | |||||||
| 	kvm_ioeventfd_flag_nr_pio, | 	kvm_ioeventfd_flag_nr_pio, | ||||||
| 	kvm_ioeventfd_flag_nr_deassign, | 	kvm_ioeventfd_flag_nr_deassign, | ||||||
| 	kvm_ioeventfd_flag_nr_virtio_ccw_notify, | 	kvm_ioeventfd_flag_nr_virtio_ccw_notify, | ||||||
|  | 	kvm_ioeventfd_flag_nr_fast_mmio, | ||||||
| 	kvm_ioeventfd_flag_nr_max, | 	kvm_ioeventfd_flag_nr_max, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -529,7 +532,7 @@ enum { | |||||||
| struct kvm_ioeventfd { | struct kvm_ioeventfd { | ||||||
| 	__u64 datamatch; | 	__u64 datamatch; | ||||||
| 	__u64 addr;        /* legal pio/mmio address */ | 	__u64 addr;        /* legal pio/mmio address */ | ||||||
| 	__u32 len;         /* 1, 2, 4, or 8 bytes    */ | 	__u32 len;         /* 1, 2, 4, or 8 bytes; or 0 to ignore length */ | ||||||
| 	__s32 fd; | 	__s32 fd; | ||||||
| 	__u32 flags; | 	__u32 flags; | ||||||
| 	__u8  pad[36]; | 	__u8  pad[36]; | ||||||
| @@ -743,6 +746,8 @@ struct kvm_ppc_smmu_info { | |||||||
| #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97 | #define KVM_CAP_IOAPIC_POLARITY_IGNORED 97 | ||||||
| #define KVM_CAP_ENABLE_CAP_VM 98 | #define KVM_CAP_ENABLE_CAP_VM 98 | ||||||
| #define KVM_CAP_S390_IRQCHIP 99 | #define KVM_CAP_S390_IRQCHIP 99 | ||||||
|  | #define KVM_CAP_IOEVENTFD_NO_LENGTH 100 | ||||||
|  | #define KVM_CAP_VM_ATTRIBUTES 101 | ||||||
|  |  | ||||||
| #ifdef KVM_CAP_IRQ_ROUTING | #ifdef KVM_CAP_IRQ_ROUTING | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										153
									
								
								monitor.c
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								monitor.c
									
									
									
									
									
								
							| @@ -4269,6 +4269,55 @@ static const char *next_arg_type(const char *typestr) | |||||||
|     return (p != NULL ? ++p : typestr); |     return (p != NULL ? ++p : typestr); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void add_completion_option(ReadLineState *rs, const char *str, | ||||||
|  |                                   const char *option) | ||||||
|  | { | ||||||
|  |     if (!str || !option) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     if (!strncmp(option, str, strlen(str))) { | ||||||
|  |         readline_add_completion(rs, option); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str) | ||||||
|  | { | ||||||
|  |     size_t len; | ||||||
|  |     ChardevBackendInfoList *list, *start; | ||||||
|  |  | ||||||
|  |     if (nb_args != 2) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     len = strlen(str); | ||||||
|  |     readline_set_completion_index(rs, len); | ||||||
|  |  | ||||||
|  |     start = list = qmp_query_chardev_backends(NULL); | ||||||
|  |     while (list) { | ||||||
|  |         const char *chr_name = list->value->name; | ||||||
|  |  | ||||||
|  |         if (!strncmp(chr_name, str, len)) { | ||||||
|  |             readline_add_completion(rs, chr_name); | ||||||
|  |         } | ||||||
|  |         list = list->next; | ||||||
|  |     } | ||||||
|  |     qapi_free_ChardevBackendInfoList(start); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str) | ||||||
|  | { | ||||||
|  |     size_t len; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     if (nb_args != 2) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     len = strlen(str); | ||||||
|  |     readline_set_completion_index(rs, len); | ||||||
|  |     for (i = 0; NetClientOptionsKind_lookup[i]; i++) { | ||||||
|  |         add_completion_option(rs, str, NetClientOptionsKind_lookup[i]); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| void device_add_completion(ReadLineState *rs, int nb_args, const char *str) | void device_add_completion(ReadLineState *rs, int nb_args, const char *str) | ||||||
| { | { | ||||||
|     GSList *list, *elt; |     GSList *list, *elt; | ||||||
| @@ -4339,6 +4388,29 @@ static void device_del_bus_completion(ReadLineState *rs,  BusState *bus, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str) | ||||||
|  | { | ||||||
|  |     size_t len; | ||||||
|  |     ChardevInfoList *list, *start; | ||||||
|  |  | ||||||
|  |     if (nb_args != 2) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     len = strlen(str); | ||||||
|  |     readline_set_completion_index(rs, len); | ||||||
|  |  | ||||||
|  |     start = list = qmp_query_chardev(NULL); | ||||||
|  |     while (list) { | ||||||
|  |         ChardevInfo *chr = list->value; | ||||||
|  |  | ||||||
|  |         if (!strncmp(chr->label, str, len)) { | ||||||
|  |             readline_add_completion(rs, chr->label); | ||||||
|  |         } | ||||||
|  |         list = list->next; | ||||||
|  |     } | ||||||
|  |     qapi_free_ChardevInfoList(start); | ||||||
|  | } | ||||||
|  |  | ||||||
| void device_del_completion(ReadLineState *rs, int nb_args, const char *str) | void device_del_completion(ReadLineState *rs, int nb_args, const char *str) | ||||||
| { | { | ||||||
|     size_t len; |     size_t len; | ||||||
| @@ -4376,6 +4448,77 @@ void object_del_completion(ReadLineState *rs, int nb_args, const char *str) | |||||||
|     qapi_free_ObjectPropertyInfoList(start); |     qapi_free_ObjectPropertyInfoList(start); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void sendkey_completion(ReadLineState *rs, int nb_args, const char *str) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     char *sep; | ||||||
|  |     size_t len; | ||||||
|  |  | ||||||
|  |     if (nb_args != 2) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     sep = strrchr(str, '-'); | ||||||
|  |     if (sep) { | ||||||
|  |         str = sep + 1; | ||||||
|  |     } | ||||||
|  |     len = strlen(str); | ||||||
|  |     readline_set_completion_index(rs, len); | ||||||
|  |     for (i = 0; i < Q_KEY_CODE_MAX; i++) { | ||||||
|  |         if (!strncmp(str, QKeyCode_lookup[i], len)) { | ||||||
|  |             readline_add_completion(rs, QKeyCode_lookup[i]); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void set_link_completion(ReadLineState *rs, int nb_args, const char *str) | ||||||
|  | { | ||||||
|  |     size_t len; | ||||||
|  |  | ||||||
|  |     len = strlen(str); | ||||||
|  |     readline_set_completion_index(rs, len); | ||||||
|  |     if (nb_args == 2) { | ||||||
|  |         NetClientState *ncs[255]; | ||||||
|  |         int count, i; | ||||||
|  |         count = qemu_find_net_clients_except(NULL, ncs, | ||||||
|  |                                              NET_CLIENT_OPTIONS_KIND_NONE, 255); | ||||||
|  |         for (i = 0; i < count; i++) { | ||||||
|  |             const char *name = ncs[i]->name; | ||||||
|  |             if (!strncmp(str, name, len)) { | ||||||
|  |                 readline_add_completion(rs, name); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } else if (nb_args == 3) { | ||||||
|  |         add_completion_option(rs, str, "on"); | ||||||
|  |         add_completion_option(rs, str, "off"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) | ||||||
|  | { | ||||||
|  |     int len, count, i; | ||||||
|  |     NetClientState *ncs[255]; | ||||||
|  |  | ||||||
|  |     if (nb_args != 2) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     len = strlen(str); | ||||||
|  |     readline_set_completion_index(rs, len); | ||||||
|  |     count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC, | ||||||
|  |                                          255); | ||||||
|  |     for (i = 0; i < count; i++) { | ||||||
|  |         QemuOpts *opts; | ||||||
|  |         const char *name = ncs[i]->name; | ||||||
|  |         if (strncmp(str, name, len)) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), name); | ||||||
|  |         if (opts) { | ||||||
|  |             readline_add_completion(rs, name); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| static void monitor_find_completion_by_table(Monitor *mon, | static void monitor_find_completion_by_table(Monitor *mon, | ||||||
|                                              const mon_cmd_t *cmd_table, |                                              const mon_cmd_t *cmd_table, | ||||||
|                                              char **args, |                                              char **args, | ||||||
| @@ -4444,15 +4587,7 @@ static void monitor_find_completion_by_table(Monitor *mon, | |||||||
|             break; |             break; | ||||||
|         case 's': |         case 's': | ||||||
|         case 'S': |         case 'S': | ||||||
|             if (!strcmp(cmd->name, "sendkey")) { |             if (!strcmp(cmd->name, "help|?")) { | ||||||
|                 char *sep = strrchr(str, '-'); |  | ||||||
|                 if (sep) |  | ||||||
|                     str = sep + 1; |  | ||||||
|                 readline_set_completion_index(mon->rs, strlen(str)); |  | ||||||
|                 for (i = 0; i < Q_KEY_CODE_MAX; i++) { |  | ||||||
|                     cmd_completion(mon, str, QKeyCode_lookup[i]); |  | ||||||
|                 } |  | ||||||
|             } else if (!strcmp(cmd->name, "help|?")) { |  | ||||||
|                 monitor_find_completion_by_table(mon, cmd_table, |                 monitor_find_completion_by_table(mon, cmd_table, | ||||||
|                                                  &args[1], nb_args - 1); |                                                  &args[1], nb_args - 1); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -633,7 +633,7 @@ int qemu_find_net_clients_except(const char *id, NetClientState **ncs, | |||||||
|         if (nc->info->type == type) { |         if (nc->info->type == type) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         if (!strcmp(nc->name, id)) { |         if (!id || !strcmp(nc->name, id)) { | ||||||
|             if (ret < max) { |             if (ret < max) { | ||||||
|                 ncs[ret] = nc; |                 ncs[ret] = nc; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -691,7 +691,7 @@ | |||||||
| # Information about current migration process. | # Information about current migration process. | ||||||
| # | # | ||||||
| # @status: #optional string describing the current migration status. | # @status: #optional string describing the current migration status. | ||||||
| #          As of 0.14.0 this can be 'active', 'completed', 'failed' or | #          As of 0.14.0 this can be 'setup', 'active', 'completed', 'failed' or | ||||||
| #          'cancelled'. If this field is not returned, no migration process | #          'cancelled'. If this field is not returned, no migration process | ||||||
| #          has been initiated | #          has been initiated | ||||||
| # | # | ||||||
| @@ -942,6 +942,8 @@ | |||||||
| # @encryption_key_missing: true if the backing device is encrypted but an | # @encryption_key_missing: true if the backing device is encrypted but an | ||||||
| #                          valid encryption key is missing | #                          valid encryption key is missing | ||||||
| # | # | ||||||
|  | # @detect_zeroes: detect and optimize zero writes (Since 2.1) | ||||||
|  | # | ||||||
| # @bps: total throughput limit in bytes per second is specified | # @bps: total throughput limit in bytes per second is specified | ||||||
| # | # | ||||||
| # @bps_rd: read throughput limit in bytes per second is specified | # @bps_rd: read throughput limit in bytes per second is specified | ||||||
| @@ -977,6 +979,7 @@ | |||||||
|   'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', |   'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', | ||||||
|             '*backing_file': 'str', 'backing_file_depth': 'int', |             '*backing_file': 'str', 'backing_file_depth': 'int', | ||||||
|             'encrypted': 'bool', 'encryption_key_missing': 'bool', |             'encrypted': 'bool', 'encryption_key_missing': 'bool', | ||||||
|  |             'detect_zeroes': 'BlockdevDetectZeroesOptions', | ||||||
|             'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', |             'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', | ||||||
|             'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', |             'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int', | ||||||
|             'image': 'ImageInfo', |             'image': 'ImageInfo', | ||||||
| @@ -4254,6 +4257,22 @@ | |||||||
| { 'enum': 'BlockdevDiscardOptions', | { 'enum': 'BlockdevDiscardOptions', | ||||||
|   'data': [ 'ignore', 'unmap' ] } |   'data': [ 'ignore', 'unmap' ] } | ||||||
|  |  | ||||||
|  | ## | ||||||
|  | # @BlockdevDetectZeroesOptions | ||||||
|  | # | ||||||
|  | # Describes the operation mode for the automatic conversion of plain | ||||||
|  | # zero writes by the OS to driver specific optimized zero write commands. | ||||||
|  | # | ||||||
|  | # @off:      Disabled (default) | ||||||
|  | # @on:       Enabled | ||||||
|  | # @unmap:    Enabled and even try to unmap blocks if possible. This requires | ||||||
|  | #            also that @BlockdevDiscardOptions is set to unmap for this device. | ||||||
|  | # | ||||||
|  | # Since: 2.1 | ||||||
|  | ## | ||||||
|  | { 'enum': 'BlockdevDetectZeroesOptions', | ||||||
|  |   'data': [ 'off', 'on', 'unmap' ] } | ||||||
|  |  | ||||||
| ## | ## | ||||||
| # @BlockdevAioOptions | # @BlockdevAioOptions | ||||||
| # | # | ||||||
| @@ -4320,6 +4339,8 @@ | |||||||
| #                 (default: enospc) | #                 (default: enospc) | ||||||
| # @read-only:     #optional whether the block device should be read-only | # @read-only:     #optional whether the block device should be read-only | ||||||
| #                 (default: false) | #                 (default: false) | ||||||
|  | # @detect-zeroes: #optional detect and optimize zero writes (Since 2.1) | ||||||
|  | #                 (default: off) | ||||||
| # | # | ||||||
| # Since: 1.7 | # Since: 1.7 | ||||||
| ## | ## | ||||||
| @@ -4332,7 +4353,8 @@ | |||||||
|             '*aio': 'BlockdevAioOptions', |             '*aio': 'BlockdevAioOptions', | ||||||
|             '*rerror': 'BlockdevOnError', |             '*rerror': 'BlockdevOnError', | ||||||
|             '*werror': 'BlockdevOnError', |             '*werror': 'BlockdevOnError', | ||||||
|             '*read-only': 'bool' } } |             '*read-only': 'bool', | ||||||
|  |             '*detect-zeroes': 'BlockdevDetectZeroesOptions' } } | ||||||
|  |  | ||||||
| ## | ## | ||||||
| # @BlockdevOptionsFile | # @BlockdevOptionsFile | ||||||
|   | |||||||
| @@ -484,8 +484,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) | |||||||
|  |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| opts_start_optional(Visitor *v, bool *present, const char *name, | opts_optional(Visitor *v, bool *present, const char *name, Error **errp) | ||||||
|                        Error **errp) |  | ||||||
| { | { | ||||||
|     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); |     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); | ||||||
|  |  | ||||||
| @@ -528,7 +527,7 @@ opts_visitor_new(const QemuOpts *opts) | |||||||
|     /* type_number() is not filled in, but this is not the first visitor to |     /* type_number() is not filled in, but this is not the first visitor to | ||||||
|      * skip some mandatory methods... */ |      * skip some mandatory methods... */ | ||||||
|  |  | ||||||
|     ov->visitor.start_optional = &opts_start_optional; |     ov->visitor.optional = &opts_optional; | ||||||
|  |  | ||||||
|     ov->opts_root = opts; |     ov->opts_root = opts; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,46 +17,27 @@ | |||||||
| #include "qapi/visitor.h" | #include "qapi/visitor.h" | ||||||
| #include "qapi/visitor-impl.h" | #include "qapi/visitor-impl.h" | ||||||
|  |  | ||||||
| void visit_start_handle(Visitor *v, void **obj, const char *kind, |  | ||||||
|                         const char *name, Error **errp) |  | ||||||
| { |  | ||||||
|     if (!error_is_set(errp) && v->start_handle) { |  | ||||||
|         v->start_handle(v, obj, kind, name, errp); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void visit_end_handle(Visitor *v, Error **errp) |  | ||||||
| { |  | ||||||
|     if (!error_is_set(errp) && v->end_handle) { |  | ||||||
|         v->end_handle(v, errp); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void visit_start_struct(Visitor *v, void **obj, const char *kind, | void visit_start_struct(Visitor *v, void **obj, const char *kind, | ||||||
|                         const char *name, size_t size, Error **errp) |                         const char *name, size_t size, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     v->start_struct(v, obj, kind, name, size, errp); |     v->start_struct(v, obj, kind, name, size, errp); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_end_struct(Visitor *v, Error **errp) | void visit_end_struct(Visitor *v, Error **errp) | ||||||
| { | { | ||||||
|     assert(!error_is_set(errp)); |  | ||||||
|     v->end_struct(v, errp); |     v->end_struct(v, errp); | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, | void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, | ||||||
|                                  Error **errp) |                                  Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp) && v->start_implicit_struct) { |     if (v->start_implicit_struct) { | ||||||
|         v->start_implicit_struct(v, obj, size, errp); |         v->start_implicit_struct(v, obj, size, errp); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_end_implicit_struct(Visitor *v, Error **errp) | void visit_end_implicit_struct(Visitor *v, Error **errp) | ||||||
| { | { | ||||||
|     assert(!error_is_set(errp)); |  | ||||||
|     if (v->end_implicit_struct) { |     if (v->end_implicit_struct) { | ||||||
|         v->end_implicit_struct(v, errp); |         v->end_implicit_struct(v, errp); | ||||||
|     } |     } | ||||||
| @@ -64,45 +45,31 @@ void visit_end_implicit_struct(Visitor *v, Error **errp) | |||||||
|  |  | ||||||
| void visit_start_list(Visitor *v, const char *name, Error **errp) | void visit_start_list(Visitor *v, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     v->start_list(v, name, errp); |     v->start_list(v, name, errp); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp) | GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     return v->next_list(v, list, errp); |     return v->next_list(v, list, errp); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_end_list(Visitor *v, Error **errp) | void visit_end_list(Visitor *v, Error **errp) | ||||||
| { | { | ||||||
|     assert(!error_is_set(errp)); |  | ||||||
|     v->end_list(v, errp); |     v->end_list(v, errp); | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_start_optional(Visitor *v, bool *present, const char *name, | void visit_optional(Visitor *v, bool *present, const char *name, | ||||||
|                     Error **errp) |                     Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp) && v->start_optional) { |     if (v->optional) { | ||||||
|         v->start_optional(v, present, name, errp); |         v->optional(v, present, name, errp); | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void visit_end_optional(Visitor *v, Error **errp) |  | ||||||
| { |  | ||||||
|     if (!error_is_set(errp) && v->end_optional) { |  | ||||||
|         v->end_optional(v, errp); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, | void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, | ||||||
|                          const char *name, Error **errp) |                          const char *name, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp) && v->get_next_type) { |     if (v->get_next_type) { | ||||||
|         v->get_next_type(v, obj, qtypes, name, errp); |         v->get_next_type(v, obj, qtypes, name, errp); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -110,22 +77,18 @@ void visit_get_next_type(Visitor *v, int *obj, const int *qtypes, | |||||||
| void visit_type_enum(Visitor *v, int *obj, const char *strings[], | void visit_type_enum(Visitor *v, int *obj, const char *strings[], | ||||||
|                      const char *kind, const char *name, Error **errp) |                      const char *kind, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     v->type_enum(v, obj, strings, kind, name, errp); |     v->type_enum(v, obj, strings, kind, name, errp); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) | void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     v->type_int(v, obj, name, errp); |     v->type_int(v, obj, name, errp); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp) | void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     int64_t value; |     int64_t value; | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_uint8) { |     if (v->type_uint8) { | ||||||
|         v->type_uint8(v, obj, name, errp); |         v->type_uint8(v, obj, name, errp); | ||||||
|     } else { |     } else { | ||||||
| @@ -138,13 +101,12 @@ void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp) | |||||||
|         } |         } | ||||||
|         *obj = value; |         *obj = value; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp) | void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     int64_t value; |     int64_t value; | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_uint16) { |     if (v->type_uint16) { | ||||||
|         v->type_uint16(v, obj, name, errp); |         v->type_uint16(v, obj, name, errp); | ||||||
|     } else { |     } else { | ||||||
| @@ -157,13 +119,12 @@ void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp | |||||||
|         } |         } | ||||||
|         *obj = value; |         *obj = value; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp) | void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     int64_t value; |     int64_t value; | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_uint32) { |     if (v->type_uint32) { | ||||||
|         v->type_uint32(v, obj, name, errp); |         v->type_uint32(v, obj, name, errp); | ||||||
|     } else { |     } else { | ||||||
| @@ -176,13 +137,12 @@ void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp | |||||||
|         } |         } | ||||||
|         *obj = value; |         *obj = value; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) | void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     int64_t value; |     int64_t value; | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_uint64) { |     if (v->type_uint64) { | ||||||
|         v->type_uint64(v, obj, name, errp); |         v->type_uint64(v, obj, name, errp); | ||||||
|     } else { |     } else { | ||||||
| @@ -190,13 +150,12 @@ void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp | |||||||
|         v->type_int(v, &value, name, errp); |         v->type_int(v, &value, name, errp); | ||||||
|         *obj = value; |         *obj = value; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp) | void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     int64_t value; |     int64_t value; | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_int8) { |     if (v->type_int8) { | ||||||
|         v->type_int8(v, obj, name, errp); |         v->type_int8(v, obj, name, errp); | ||||||
|     } else { |     } else { | ||||||
| @@ -209,13 +168,12 @@ void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp) | |||||||
|         } |         } | ||||||
|         *obj = value; |         *obj = value; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp) | void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     int64_t value; |     int64_t value; | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_int16) { |     if (v->type_int16) { | ||||||
|         v->type_int16(v, obj, name, errp); |         v->type_int16(v, obj, name, errp); | ||||||
|     } else { |     } else { | ||||||
| @@ -228,13 +186,12 @@ void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp) | |||||||
|         } |         } | ||||||
|         *obj = value; |         *obj = value; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp) | void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     int64_t value; |     int64_t value; | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_int32) { |     if (v->type_int32) { | ||||||
|         v->type_int32(v, obj, name, errp); |         v->type_int32(v, obj, name, errp); | ||||||
|     } else { |     } else { | ||||||
| @@ -247,24 +204,21 @@ void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp) | |||||||
|         } |         } | ||||||
|         *obj = value; |         *obj = value; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp) | void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_int64) { |     if (v->type_int64) { | ||||||
|         v->type_int64(v, obj, name, errp); |         v->type_int64(v, obj, name, errp); | ||||||
|     } else { |     } else { | ||||||
|         v->type_int(v, obj, name, errp); |         v->type_int(v, obj, name, errp); | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) | void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     int64_t value; |     int64_t value; | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     if (v->type_size) { |     if (v->type_size) { | ||||||
|         v->type_size(v, obj, name, errp); |         v->type_size(v, obj, name, errp); | ||||||
|     } else if (v->type_uint64) { |     } else if (v->type_uint64) { | ||||||
| @@ -274,28 +228,21 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) | |||||||
|         v->type_int(v, &value, name, errp); |         v->type_int(v, &value, name, errp); | ||||||
|         *obj = value; |         *obj = value; | ||||||
|     } |     } | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) | void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     v->type_bool(v, obj, name, errp); |     v->type_bool(v, obj, name, errp); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp) | void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     v->type_str(v, obj, name, errp); |     v->type_str(v, obj, name, errp); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) | void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     v->type_number(v, obj, name, errp); |     v->type_number(v, obj, name, errp); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void output_type_enum(Visitor *v, int *obj, const char *strings[], | void output_type_enum(Visitor *v, int *obj, const char *strings[], | ||||||
| @@ -321,13 +268,15 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[], | |||||||
|                      const char *kind, const char *name, |                      const char *kind, const char *name, | ||||||
|                      Error **errp) |                      Error **errp) | ||||||
| { | { | ||||||
|  |     Error *local_err = NULL; | ||||||
|     int64_t value = 0; |     int64_t value = 0; | ||||||
|     char *enum_str; |     char *enum_str; | ||||||
|  |  | ||||||
|     assert(strings); |     assert(strings); | ||||||
|  |  | ||||||
|     visit_type_str(v, &enum_str, name, errp); |     visit_type_str(v, &enum_str, name, &local_err); | ||||||
|     if (error_is_set(errp)) { |     if (local_err) { | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -286,8 +286,8 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void qmp_input_start_optional(Visitor *v, bool *present, | static void qmp_input_optional(Visitor *v, bool *present, const char *name, | ||||||
|                                      const char *name, Error **errp) |                                Error **errp) | ||||||
| { | { | ||||||
|     QmpInputVisitor *qiv = to_qiv(v); |     QmpInputVisitor *qiv = to_qiv(v); | ||||||
|     QObject *qobj = qmp_input_get_object(qiv, name, true); |     QObject *qobj = qmp_input_get_object(qiv, name, true); | ||||||
| @@ -329,7 +329,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj) | |||||||
|     v->visitor.type_bool = qmp_input_type_bool; |     v->visitor.type_bool = qmp_input_type_bool; | ||||||
|     v->visitor.type_str = qmp_input_type_str; |     v->visitor.type_str = qmp_input_type_str; | ||||||
|     v->visitor.type_number = qmp_input_type_number; |     v->visitor.type_number = qmp_input_type_number; | ||||||
|     v->visitor.start_optional = qmp_input_start_optional; |     v->visitor.optional = qmp_input_optional; | ||||||
|     v->visitor.get_next_type = qmp_input_get_next_type; |     v->visitor.get_next_type = qmp_input_get_next_type; | ||||||
|  |  | ||||||
|     qmp_input_push(v, obj, NULL); |     qmp_input_push(v, obj, NULL); | ||||||
|   | |||||||
| @@ -120,8 +120,8 @@ static void parse_type_number(Visitor *v, double *obj, const char *name, | |||||||
|     *obj = val; |     *obj = val; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void parse_start_optional(Visitor *v, bool *present, | static void parse_optional(Visitor *v, bool *present, const char *name, | ||||||
|                                  const char *name, Error **errp) |                            Error **errp) | ||||||
| { | { | ||||||
|     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); |     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); | ||||||
|  |  | ||||||
| @@ -155,7 +155,7 @@ StringInputVisitor *string_input_visitor_new(const char *str) | |||||||
|     v->visitor.type_bool = parse_type_bool; |     v->visitor.type_bool = parse_type_bool; | ||||||
|     v->visitor.type_str = parse_type_str; |     v->visitor.type_str = parse_type_str; | ||||||
|     v->visitor.type_number = parse_type_number; |     v->visitor.type_number = parse_type_number; | ||||||
|     v->visitor.start_optional = parse_start_optional; |     v->visitor.optional = parse_optional; | ||||||
|  |  | ||||||
|     v->string = str; |     v->string = str; | ||||||
|     return v; |     return v; | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								qemu-char.c
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								qemu-char.c
									
									
									
									
									
								
							| @@ -3204,6 +3204,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, | |||||||
|                                     void (*init)(struct CharDriverState *s), |                                     void (*init)(struct CharDriverState *s), | ||||||
|                                     Error **errp) |                                     Error **errp) | ||||||
| { | { | ||||||
|  |     Error *local_err = NULL; | ||||||
|     CharDriver *cd; |     CharDriver *cd; | ||||||
|     CharDriverState *chr; |     CharDriverState *chr; | ||||||
|     GSList *i; |     GSList *i; | ||||||
| @@ -3245,13 +3246,14 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, | |||||||
|         chr = NULL; |         chr = NULL; | ||||||
|         backend->kind = cd->kind; |         backend->kind = cd->kind; | ||||||
|         if (cd->parse) { |         if (cd->parse) { | ||||||
|             cd->parse(opts, backend, errp); |             cd->parse(opts, backend, &local_err); | ||||||
|             if (error_is_set(errp)) { |             if (local_err) { | ||||||
|  |                 error_propagate(errp, local_err); | ||||||
|                 goto qapi_out; |                 goto qapi_out; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         ret = qmp_chardev_add(bid ? bid : id, backend, errp); |         ret = qmp_chardev_add(bid ? bid : id, backend, errp); | ||||||
|         if (error_is_set(errp)) { |         if (!ret) { | ||||||
|             goto qapi_out; |             goto qapi_out; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -3263,7 +3265,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, | |||||||
|             backend->kind = CHARDEV_BACKEND_KIND_MUX; |             backend->kind = CHARDEV_BACKEND_KIND_MUX; | ||||||
|             backend->mux->chardev = g_strdup(bid); |             backend->mux->chardev = g_strdup(bid); | ||||||
|             ret = qmp_chardev_add(id, backend, errp); |             ret = qmp_chardev_add(id, backend, errp); | ||||||
|             if (error_is_set(errp)) { |             if (!ret) { | ||||||
|                 chr = qemu_chr_find(bid); |                 chr = qemu_chr_find(bid); | ||||||
|                 qemu_chr_delete(chr); |                 qemu_chr_delete(chr); | ||||||
|                 chr = NULL; |                 chr = NULL; | ||||||
| @@ -3620,18 +3622,18 @@ static int qmp_chardev_open_file_source(char *src, int flags, | |||||||
|  |  | ||||||
| static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) | static CharDriverState *qmp_chardev_open_file(ChardevFile *file, Error **errp) | ||||||
| { | { | ||||||
|     int flags, in = -1, out = -1; |     int flags, in = -1, out; | ||||||
|  |  | ||||||
|     flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY; |     flags = O_WRONLY | O_TRUNC | O_CREAT | O_BINARY; | ||||||
|     out = qmp_chardev_open_file_source(file->out, flags, errp); |     out = qmp_chardev_open_file_source(file->out, flags, errp); | ||||||
|     if (error_is_set(errp)) { |     if (out < 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (file->has_in) { |     if (file->has_in) { | ||||||
|         flags = O_RDONLY; |         flags = O_RDONLY; | ||||||
|         in = qmp_chardev_open_file_source(file->in, flags, errp); |         in = qmp_chardev_open_file_source(file->in, flags, errp); | ||||||
|         if (error_is_set(errp)) { |         if (in < 0) { | ||||||
|             qemu_close(out); |             qemu_close(out); | ||||||
|             return NULL; |             return NULL; | ||||||
|         } |         } | ||||||
| @@ -3647,7 +3649,7 @@ static CharDriverState *qmp_chardev_open_serial(ChardevHostdev *serial, | |||||||
|     int fd; |     int fd; | ||||||
|  |  | ||||||
|     fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); |     fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); | ||||||
|     if (error_is_set(errp)) { |     if (fd < 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     qemu_set_nonblock(fd); |     qemu_set_nonblock(fd); | ||||||
| @@ -3665,7 +3667,7 @@ static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, | |||||||
|     int fd; |     int fd; | ||||||
|  |  | ||||||
|     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp); |     fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp); | ||||||
|     if (error_is_set(errp)) { |     if (fd < 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     return qemu_chr_open_pp_fd(fd); |     return qemu_chr_open_pp_fd(fd); | ||||||
| @@ -3692,7 +3694,7 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, | |||||||
|     } else { |     } else { | ||||||
|         fd = socket_connect(addr, errp, NULL, NULL); |         fd = socket_connect(addr, errp, NULL, NULL); | ||||||
|     } |     } | ||||||
|     if (error_is_set(errp)) { |     if (fd < 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, |     return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, | ||||||
| @@ -3705,7 +3707,7 @@ static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp, | |||||||
|     int fd; |     int fd; | ||||||
|  |  | ||||||
|     fd = socket_dgram(udp->remote, udp->local, errp); |     fd = socket_dgram(udp->remote, udp->local, errp); | ||||||
|     if (error_is_set(errp)) { |     if (fd < 0) { | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     return qemu_chr_open_udp_fd(fd); |     return qemu_chr_open_udp_fd(fd); | ||||||
| @@ -3796,7 +3798,13 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (chr == NULL && !error_is_set(errp)) { |     /* | ||||||
|  |      * Character backend open hasn't been fully converted to the Error | ||||||
|  |      * API.  Some opens fail without setting an error.  Set a generic | ||||||
|  |      * error then. | ||||||
|  |      * TODO full conversion to Error API | ||||||
|  |      */ | ||||||
|  |     if (chr == NULL && errp && !*errp) { | ||||||
|         error_setg(errp, "Failed to create chardev"); |         error_setg(errp, "Failed to create chardev"); | ||||||
|     } |     } | ||||||
|     if (chr) { |     if (chr) { | ||||||
|   | |||||||
| @@ -70,11 +70,8 @@ static void add_format_to_seq(void *opaque, const char *fmt_name) | |||||||
| { | { | ||||||
|     GSequence *seq = opaque; |     GSequence *seq = opaque; | ||||||
|  |  | ||||||
|     if (!g_sequence_lookup(seq, (gpointer)fmt_name, |  | ||||||
|                            compare_data, NULL)) { |  | ||||||
|     g_sequence_insert_sorted(seq, (gpointer)fmt_name, |     g_sequence_insert_sorted(seq, (gpointer)fmt_name, | ||||||
|                              compare_data, NULL); |                              compare_data, NULL); | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) | static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) | ||||||
|   | |||||||
| @@ -414,6 +414,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive, | |||||||
|     "       [,serial=s][,addr=A][,rerror=ignore|stop|report]\n" |     "       [,serial=s][,addr=A][,rerror=ignore|stop|report]\n" | ||||||
|     "       [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" |     "       [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" | ||||||
|     "       [,readonly=on|off][,copy-on-read=on|off]\n" |     "       [,readonly=on|off][,copy-on-read=on|off]\n" | ||||||
|  |     "       [,detect-zeroes=on|off|unmap]\n" | ||||||
|     "       [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]]\n" |     "       [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]]\n" | ||||||
|     "       [[,iops=i]|[[,iops_rd=r][,iops_wr=w]]]\n" |     "       [[,iops=i]|[[,iops_rd=r][,iops_wr=w]]]\n" | ||||||
|     "       [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n" |     "       [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n" | ||||||
| @@ -475,6 +476,11 @@ Open drive @option{file} as read-only. Guest write attempts will fail. | |||||||
| @item copy-on-read=@var{copy-on-read} | @item copy-on-read=@var{copy-on-read} | ||||||
| @var{copy-on-read} is "on" or "off" and enables whether to copy read backing | @var{copy-on-read} is "on" or "off" and enables whether to copy read backing | ||||||
| file sectors into the image file. | file sectors into the image file. | ||||||
|  | @item detect-zeroes=@var{detect-zeroes} | ||||||
|  | @var{detect-zeroes} is "off", "on" or "unmap" and enables the automatic | ||||||
|  | conversion of plain zero writes by the OS to driver specific optimized | ||||||
|  | zero write commands. You may even choose "unmap" if @var{discard} is set | ||||||
|  | to "unmap" to allow a zero write to be converted to an UNMAP operation. | ||||||
| @end table | @end table | ||||||
|  |  | ||||||
| By default, the @option{cache=writeback} mode is used. It will report data | By default, the @option{cache=writeback} mode is used. It will report data | ||||||
| @@ -2191,6 +2197,74 @@ qemu-system-x86_64 --drive file=gluster://192.0.2.1/testvol/a.img | |||||||
| @end example | @end example | ||||||
|  |  | ||||||
| See also @url{http://www.gluster.org}. | See also @url{http://www.gluster.org}. | ||||||
|  |  | ||||||
|  | @item HTTP/HTTPS/FTP/FTPS/TFTP | ||||||
|  | QEMU supports read-only access to files accessed over http(s), ftp(s) and tftp. | ||||||
|  |  | ||||||
|  | Syntax using a single filename: | ||||||
|  | @example | ||||||
|  | <protocol>://[<username>[:<password>]@@]<host>/<path> | ||||||
|  | @end example | ||||||
|  |  | ||||||
|  | where: | ||||||
|  | @table @option | ||||||
|  | @item protocol | ||||||
|  | 'http', 'https', 'ftp', 'ftps', or 'tftp'. | ||||||
|  |  | ||||||
|  | @item username | ||||||
|  | Optional username for authentication to the remote server. | ||||||
|  |  | ||||||
|  | @item password | ||||||
|  | Optional password for authentication to the remote server. | ||||||
|  |  | ||||||
|  | @item host | ||||||
|  | Address of the remote server. | ||||||
|  |  | ||||||
|  | @item path | ||||||
|  | Path on the remote server, including any query string. | ||||||
|  | @end table | ||||||
|  |  | ||||||
|  | The following options are also supported: | ||||||
|  | @table @option | ||||||
|  | @item url | ||||||
|  | The full URL when passing options to the driver explicitly. | ||||||
|  |  | ||||||
|  | @item readahead | ||||||
|  | The amount of data to read ahead with each range request to the remote server. | ||||||
|  | This value may optionally have the suffix 'T', 'G', 'M', 'K', 'k' or 'b'. If it | ||||||
|  | does not have a suffix, it will be assumed to be in bytes. The value must be a | ||||||
|  | multiple of 512 bytes. It defaults to 256k. | ||||||
|  |  | ||||||
|  | @item sslverify | ||||||
|  | Whether to verify the remote server's certificate when connecting over SSL. It | ||||||
|  | can have the value 'on' or 'off'. It defaults to 'on'. | ||||||
|  | @end table | ||||||
|  |  | ||||||
|  | Note that when passing options to qemu explicitly, @option{driver} is the value | ||||||
|  | of <protocol>. | ||||||
|  |  | ||||||
|  | Example: boot from a remote Fedora 20 live ISO image | ||||||
|  | @example | ||||||
|  | qemu-system-x86_64 --drive media=cdrom,file=http://dl.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly | ||||||
|  |  | ||||||
|  | qemu-system-x86_64 --drive media=cdrom,file.driver=http,file.url=http://dl.fedoraproject.org/pub/fedora/linux/releases/20/Live/x86_64/Fedora-Live-Desktop-x86_64-20-1.iso,readonly | ||||||
|  | @end example | ||||||
|  |  | ||||||
|  | Example: boot from a remote Fedora 20 cloud image using a local overlay for | ||||||
|  | writes, copy-on-read, and a readahead of 64k | ||||||
|  | @example | ||||||
|  | qemu-img create -f qcow2 -o backing_file='json:@{"file.driver":"http",, "file.url":"https://dl.fedoraproject.org/pub/fedora/linux/releases/20/Images/x86_64/Fedora-x86_64-20-20131211.1-sda.qcow2",, "file.readahead":"64k"@}' /tmp/Fedora-x86_64-20-20131211.1-sda.qcow2 | ||||||
|  |  | ||||||
|  | qemu-system-x86_64 -drive file=/tmp/Fedora-x86_64-20-20131211.1-sda.qcow2,copy-on-read=on | ||||||
|  | @end example | ||||||
|  |  | ||||||
|  | Example: boot from an image stored on a VMware vSphere server with a self-signed | ||||||
|  | certificate using a local overlay for writes and a readahead of 64k | ||||||
|  | @example | ||||||
|  | qemu-img create -f qcow2 -o backing_file='json:@{"file.driver":"https",, "file.url":"https://user:password@@vsphere.example.com/folder/test/test-flat.vmdk?dcPath=Datacenter&dsName=datastore1",, "file.sslverify":"off",, "file.readahead":"64k"@}' /tmp/test.qcow2 | ||||||
|  |  | ||||||
|  | qemu-system-x86_64 -drive file=/tmp/test.qcow2 | ||||||
|  | @end example | ||||||
| ETEXI | ETEXI | ||||||
|  |  | ||||||
| STEXI | STEXI | ||||||
|   | |||||||
| @@ -2032,6 +2032,8 @@ Each json-object contain the following: | |||||||
|          - "iops_rd_max":  read I/O operations max (json-int) |          - "iops_rd_max":  read I/O operations max (json-int) | ||||||
|          - "iops_wr_max":  write I/O operations max (json-int) |          - "iops_wr_max":  write I/O operations max (json-int) | ||||||
|          - "iops_size": I/O size when limiting by iops (json-int) |          - "iops_size": I/O size when limiting by iops (json-int) | ||||||
|  |          - "detect_zeroes": detect and optimize zero writing (json-string) | ||||||
|  |              - Possible values: "off", "on", "unmap" | ||||||
|          - "image": the detail of the image, it is a json-object containing |          - "image": the detail of the image, it is a json-object containing | ||||||
|             the following: |             the following: | ||||||
|              - "filename": image file name (json-string) |              - "filename": image file name (json-string) | ||||||
| @@ -2108,6 +2110,7 @@ Example: | |||||||
|                "iops_rd_max": 0, |                "iops_rd_max": 0, | ||||||
|                "iops_wr_max": 0, |                "iops_wr_max": 0, | ||||||
|                "iops_size": 0, |                "iops_size": 0, | ||||||
|  |                "detect_zeroes": "on", | ||||||
|                "image":{ |                "image":{ | ||||||
|                   "filename":"disks/test.qcow2", |                   "filename":"disks/test.qcow2", | ||||||
|                   "format":"qcow2", |                   "format":"qcow2", | ||||||
| @@ -2937,7 +2940,7 @@ block migration status. | |||||||
| The main json-object contains the following: | The main json-object contains the following: | ||||||
|  |  | ||||||
| - "status": migration status (json-string) | - "status": migration status (json-string) | ||||||
|      - Possible values: "active", "completed", "failed", "cancelled" |      - Possible values: "setup", "active", "completed", "failed", "cancelled" | ||||||
| - "total-time": total amount of ms since migration started.  If | - "total-time": total amount of ms since migration started.  If | ||||||
|                 migration has ended, it returns the total migration |                 migration has ended, it returns the total migration | ||||||
|                 time (json-int) |                 time (json-int) | ||||||
|   | |||||||
| @@ -665,3 +665,35 @@ void qdict_array_split(QDict *src, QList **dst) | |||||||
|         qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); |         qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all | ||||||
|  |  * elements from src to dest. | ||||||
|  |  * | ||||||
|  |  * If an element from src has a key already present in dest, it will not be | ||||||
|  |  * moved unless overwrite is true. | ||||||
|  |  * | ||||||
|  |  * If overwrite is true, the conflicting values in dest will be discarded and | ||||||
|  |  * replaced by the corresponding values from src. | ||||||
|  |  * | ||||||
|  |  * Therefore, with overwrite being true, the src QDict will always be empty when | ||||||
|  |  * this function returns. If overwrite is false, the src QDict will be empty | ||||||
|  |  * iff there were no conflicts. | ||||||
|  |  */ | ||||||
|  | void qdict_join(QDict *dest, QDict *src, bool overwrite) | ||||||
|  | { | ||||||
|  |     const QDictEntry *entry, *next; | ||||||
|  |  | ||||||
|  |     entry = qdict_first(src); | ||||||
|  |     while (entry) { | ||||||
|  |         next = qdict_next(src, entry); | ||||||
|  |  | ||||||
|  |         if (overwrite || !qdict_haskey(dest, entry->key)) { | ||||||
|  |             qobject_incref(entry->value); | ||||||
|  |             qdict_put_obj(dest, entry->key, entry->value); | ||||||
|  |             qdict_del(src, entry->key); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         entry = next; | ||||||
|  |     } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -2,16 +2,19 @@ | |||||||
| # QAPI command marshaller generator | # QAPI command marshaller generator | ||||||
| # | # | ||||||
| # Copyright IBM, Corp. 2011 | # Copyright IBM, Corp. 2011 | ||||||
|  | # Copyright (C) 2014 Red Hat, Inc. | ||||||
| # | # | ||||||
| # Authors: | # Authors: | ||||||
| #  Anthony Liguori <aliguori@us.ibm.com> | #  Anthony Liguori <aliguori@us.ibm.com> | ||||||
| #  Michael Roth    <mdroth@linux.vnet.ibm.com> | #  Michael Roth    <mdroth@linux.vnet.ibm.com> | ||||||
|  | #  Markus Armbruster <armbru@redhat.com> | ||||||
| # | # | ||||||
| # This work is licensed under the terms of the GNU GPL, version 2. | # This work is licensed under the terms of the GNU GPL, version 2. | ||||||
| # See the COPYING file in the top-level directory. | # See the COPYING file in the top-level directory. | ||||||
|  |  | ||||||
| from ordereddict import OrderedDict | from ordereddict import OrderedDict | ||||||
| from qapi import * | from qapi import * | ||||||
|  | import re | ||||||
| import sys | import sys | ||||||
| import os | import os | ||||||
| import getopt | import getopt | ||||||
| @@ -37,6 +40,15 @@ def generate_command_decl(name, args, ret_type): | |||||||
| ''', | ''', | ||||||
|                  ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip() |                  ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip() | ||||||
|  |  | ||||||
|  | def gen_err_check(errvar): | ||||||
|  |     if errvar: | ||||||
|  |         return mcgen(''' | ||||||
|  | if (local_err) { | ||||||
|  |     goto out; | ||||||
|  | } | ||||||
|  | ''') | ||||||
|  |     return '' | ||||||
|  |  | ||||||
| def gen_sync_call(name, args, ret_type, indent=0): | def gen_sync_call(name, args, ret_type, indent=0): | ||||||
|     ret = "" |     ret = "" | ||||||
|     arglist="" |     arglist="" | ||||||
| @@ -49,15 +61,14 @@ def gen_sync_call(name, args, ret_type, indent=0): | |||||||
|         arglist += "%s, " % (c_var(argname)) |         arglist += "%s, " % (c_var(argname)) | ||||||
|     push_indent(indent) |     push_indent(indent) | ||||||
|     ret = mcgen(''' |     ret = mcgen(''' | ||||||
| %(retval)sqmp_%(name)s(%(args)serrp); | %(retval)sqmp_%(name)s(%(args)s&local_err); | ||||||
|  |  | ||||||
| ''', | ''', | ||||||
|                 name=c_fun(name), args=arglist, retval=retval).rstrip() |                 name=c_fun(name), args=arglist, retval=retval).rstrip() | ||||||
|     if ret_type: |     if ret_type: | ||||||
|  |         ret += "\n" + gen_err_check('local_err') | ||||||
|         ret += "\n" + mcgen('''' |         ret += "\n" + mcgen('''' | ||||||
| if (!error_is_set(errp)) { | %(marshal_output_call)s | ||||||
|     %(marshal_output_call)s |  | ||||||
| } |  | ||||||
| ''', | ''', | ||||||
|                             marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() |                             marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() | ||||||
|     pop_indent(indent) |     pop_indent(indent) | ||||||
| @@ -67,18 +78,19 @@ if (!error_is_set(errp)) { | |||||||
| def gen_marshal_output_call(name, ret_type): | def gen_marshal_output_call(name, ret_type): | ||||||
|     if not ret_type: |     if not ret_type: | ||||||
|         return "" |         return "" | ||||||
|     return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name) |     return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_fun(name) | ||||||
|  |  | ||||||
| def gen_visitor_input_containers_decl(args): | def gen_visitor_input_containers_decl(args, obj): | ||||||
|     ret = "" |     ret = "" | ||||||
|  |  | ||||||
|     push_indent() |     push_indent() | ||||||
|     if len(args) > 0: |     if len(args) > 0: | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
| QmpInputVisitor *mi; | QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s); | ||||||
| QapiDeallocVisitor *md; | QapiDeallocVisitor *md; | ||||||
| Visitor *v; | Visitor *v; | ||||||
| ''') | ''', | ||||||
|  |                      obj=obj) | ||||||
|     pop_indent() |     pop_indent() | ||||||
|  |  | ||||||
|     return ret.rstrip() |     return ret.rstrip() | ||||||
| @@ -99,16 +111,17 @@ bool has_%(argname)s = false; | |||||||
|                          argname=c_var(argname), argtype=c_type(argtype)) |                          argname=c_var(argname), argtype=c_type(argtype)) | ||||||
|         else: |         else: | ||||||
|             ret += mcgen(''' |             ret += mcgen(''' | ||||||
| %(argtype)s %(argname)s; | %(argtype)s %(argname)s = {0}; | ||||||
| ''', | ''', | ||||||
|                          argname=c_var(argname), argtype=c_type(argtype)) |                          argname=c_var(argname), argtype=c_type(argtype)) | ||||||
|  |  | ||||||
|     pop_indent() |     pop_indent() | ||||||
|     return ret.rstrip() |     return ret.rstrip() | ||||||
|  |  | ||||||
| def gen_visitor_input_block(args, obj, dealloc=False): | def gen_visitor_input_block(args, dealloc=False): | ||||||
|     ret = "" |     ret = "" | ||||||
|     errparg = 'errp' |     errparg = '&local_err' | ||||||
|  |     errarg = 'local_err' | ||||||
|  |  | ||||||
|     if len(args) == 0: |     if len(args) == 0: | ||||||
|         return ret |         return ret | ||||||
| @@ -117,44 +130,44 @@ def gen_visitor_input_block(args, obj, dealloc=False): | |||||||
|  |  | ||||||
|     if dealloc: |     if dealloc: | ||||||
|         errparg = 'NULL' |         errparg = 'NULL' | ||||||
|  |         errarg = None; | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
|  | qmp_input_visitor_cleanup(mi); | ||||||
| md = qapi_dealloc_visitor_new(); | md = qapi_dealloc_visitor_new(); | ||||||
| v = qapi_dealloc_get_visitor(md); | v = qapi_dealloc_get_visitor(md); | ||||||
| ''') | ''') | ||||||
|     else: |     else: | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
| mi = qmp_input_visitor_new_strict(%(obj)s); |  | ||||||
| v = qmp_input_get_visitor(mi); | v = qmp_input_get_visitor(mi); | ||||||
| ''', | ''') | ||||||
|                      obj=obj) |  | ||||||
|  |  | ||||||
|     for argname, argtype, optional, structured in parse_args(args): |     for argname, argtype, optional, structured in parse_args(args): | ||||||
|         if optional: |         if optional: | ||||||
|             ret += mcgen(''' |             ret += mcgen(''' | ||||||
| visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); | visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); | ||||||
| if (has_%(c_name)s) { |  | ||||||
| ''', | ''', | ||||||
|                          c_name=c_var(argname), name=argname, errp=errparg) |                          c_name=c_var(argname), name=argname, errp=errparg) | ||||||
|  |             ret += gen_err_check(errarg) | ||||||
|  |             ret += mcgen(''' | ||||||
|  | if (has_%(c_name)s) { | ||||||
|  | ''', | ||||||
|  |                          c_name=c_var(argname)) | ||||||
|             push_indent() |             push_indent() | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
| %(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); | %(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); | ||||||
| ''', | ''', | ||||||
|                      c_name=c_var(argname), name=argname, argtype=argtype, |                      c_name=c_var(argname), name=argname, argtype=argtype, | ||||||
|                      visitor=type_visitor(argtype), errp=errparg) |                      visitor=type_visitor(argtype), errp=errparg) | ||||||
|  |         ret += gen_err_check(errarg) | ||||||
|         if optional: |         if optional: | ||||||
|             pop_indent() |             pop_indent() | ||||||
|             ret += mcgen(''' |             ret += mcgen(''' | ||||||
| } | } | ||||||
| visit_end_optional(v, %(errp)s); | ''') | ||||||
| ''', errp=errparg) |  | ||||||
|  |  | ||||||
|     if dealloc: |     if dealloc: | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
| qapi_dealloc_visitor_cleanup(md); | qapi_dealloc_visitor_cleanup(md); | ||||||
| ''') |  | ||||||
|     else: |  | ||||||
|         ret += mcgen(''' |  | ||||||
| qmp_input_visitor_cleanup(mi); |  | ||||||
| ''') | ''') | ||||||
|     pop_indent() |     pop_indent() | ||||||
|     return ret.rstrip() |     return ret.rstrip() | ||||||
| @@ -166,16 +179,22 @@ def gen_marshal_output(name, args, ret_type, middle_mode): | |||||||
|     ret = mcgen(''' |     ret = mcgen(''' | ||||||
| static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) | static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) | ||||||
| { | { | ||||||
|     QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); |     Error *local_err = NULL; | ||||||
|     QmpOutputVisitor *mo = qmp_output_visitor_new(); |     QmpOutputVisitor *mo = qmp_output_visitor_new(); | ||||||
|  |     QapiDeallocVisitor *md; | ||||||
|     Visitor *v; |     Visitor *v; | ||||||
|  |  | ||||||
|     v = qmp_output_get_visitor(mo); |     v = qmp_output_get_visitor(mo); | ||||||
|     %(visitor)s(v, &ret_in, "unused", errp); |     %(visitor)s(v, &ret_in, "unused", &local_err); | ||||||
|     if (!error_is_set(errp)) { |     if (local_err) { | ||||||
|         *ret_out = qmp_output_get_qobject(mo); |         goto out; | ||||||
|     } |     } | ||||||
|  |     *ret_out = qmp_output_get_qobject(mo); | ||||||
|  |  | ||||||
|  | out: | ||||||
|  |     error_propagate(errp, local_err); | ||||||
|     qmp_output_visitor_cleanup(mo); |     qmp_output_visitor_cleanup(mo); | ||||||
|  |     md = qapi_dealloc_visitor_new(); | ||||||
|     v = qapi_dealloc_get_visitor(md); |     v = qapi_dealloc_get_visitor(md); | ||||||
|     %(visitor)s(v, &ret_in, "unused", NULL); |     %(visitor)s(v, &ret_in, "unused", NULL); | ||||||
|     qapi_dealloc_visitor_cleanup(md); |     qapi_dealloc_visitor_cleanup(md); | ||||||
| @@ -200,13 +219,12 @@ def gen_marshal_input(name, args, ret_type, middle_mode): | |||||||
|     ret = mcgen(''' |     ret = mcgen(''' | ||||||
| %(header)s | %(header)s | ||||||
| { | { | ||||||
|  |     Error *local_err = NULL; | ||||||
| ''', | ''', | ||||||
|                 header=hdr) |                 header=hdr) | ||||||
|  |  | ||||||
|     if middle_mode: |     if middle_mode: | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
|     Error *local_err = NULL; |  | ||||||
|     Error **errp = &local_err; |  | ||||||
|     QDict *args = (QDict *)qdict; |     QDict *args = (QDict *)qdict; | ||||||
| ''') | ''') | ||||||
|  |  | ||||||
| @@ -228,29 +246,32 @@ def gen_marshal_input(name, args, ret_type, middle_mode): | |||||||
| %(visitor_input_block)s | %(visitor_input_block)s | ||||||
|  |  | ||||||
| ''', | ''', | ||||||
|                      visitor_input_containers_decl=gen_visitor_input_containers_decl(args), |                      visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"), | ||||||
|                      visitor_input_vars_decl=gen_visitor_input_vars_decl(args), |                      visitor_input_vars_decl=gen_visitor_input_vars_decl(args), | ||||||
|                      visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)")) |                      visitor_input_block=gen_visitor_input_block(args)) | ||||||
|     else: |     else: | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
|  |  | ||||||
|     (void)args; |     (void)args; | ||||||
| ''') | ''') | ||||||
|  |  | ||||||
|     ret += mcgen(''' |     ret += mcgen(''' | ||||||
|     if (error_is_set(errp)) { |  | ||||||
|         goto out; |  | ||||||
|     } |  | ||||||
| %(sync_call)s | %(sync_call)s | ||||||
| ''', | ''', | ||||||
|                  sync_call=gen_sync_call(name, args, ret_type, indent=4)) |                  sync_call=gen_sync_call(name, args, ret_type, indent=4)) | ||||||
|  |     if re.search('^ *goto out\\;', ret, re.MULTILINE): | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
|  |  | ||||||
| out: | out: | ||||||
|  | ''') | ||||||
|  |     if not middle_mode: | ||||||
|  |         ret += mcgen(''' | ||||||
|  |     error_propagate(errp, local_err); | ||||||
| ''') | ''') | ||||||
|     ret += mcgen(''' |     ret += mcgen(''' | ||||||
| %(visitor_input_block_cleanup)s | %(visitor_input_block_cleanup)s | ||||||
| ''', | ''', | ||||||
|                  visitor_input_block_cleanup=gen_visitor_input_block(args, None, |                  visitor_input_block_cleanup=gen_visitor_input_block(args, | ||||||
|                                                                      dealloc=True)) |                                                                      dealloc=True)) | ||||||
|  |  | ||||||
|     if middle_mode: |     if middle_mode: | ||||||
|   | |||||||
| @@ -2,21 +2,47 @@ | |||||||
| # QAPI visitor generator | # QAPI visitor generator | ||||||
| # | # | ||||||
| # Copyright IBM, Corp. 2011 | # Copyright IBM, Corp. 2011 | ||||||
|  | # Copyright (C) 2014 Red Hat, Inc. | ||||||
| # | # | ||||||
| # Authors: | # Authors: | ||||||
| #  Anthony Liguori <aliguori@us.ibm.com> | #  Anthony Liguori <aliguori@us.ibm.com> | ||||||
| #  Michael Roth    <mdroth@linux.vnet.ibm.com> | #  Michael Roth    <mdroth@linux.vnet.ibm.com> | ||||||
|  | #  Markus Armbruster <armbru@redhat.com> | ||||||
| # | # | ||||||
| # This work is licensed under the terms of the GNU GPL, version 2. | # This work is licensed under the terms of the GNU GPL, version 2. | ||||||
| # See the COPYING file in the top-level directory. | # See the COPYING file in the top-level directory. | ||||||
|  |  | ||||||
| from ordereddict import OrderedDict | from ordereddict import OrderedDict | ||||||
| from qapi import * | from qapi import * | ||||||
|  | import re | ||||||
| import sys | import sys | ||||||
| import os | import os | ||||||
| import getopt | import getopt | ||||||
| import errno | import errno | ||||||
|  |  | ||||||
|  | implicit_structs = [] | ||||||
|  |  | ||||||
|  | def generate_visit_implicit_struct(type): | ||||||
|  |     global implicit_structs | ||||||
|  |     if type in implicit_structs: | ||||||
|  |         return '' | ||||||
|  |     implicit_structs.append(type) | ||||||
|  |     return mcgen(''' | ||||||
|  |  | ||||||
|  | static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp) | ||||||
|  | { | ||||||
|  |     Error *err = NULL; | ||||||
|  |  | ||||||
|  |     visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err); | ||||||
|  |     if (!err) { | ||||||
|  |         visit_type_%(c_type)s_fields(m, obj, errp); | ||||||
|  |         visit_end_implicit_struct(m, &err); | ||||||
|  |     } | ||||||
|  |     error_propagate(errp, err); | ||||||
|  | } | ||||||
|  | ''', | ||||||
|  |                  c_type=type_name(type)) | ||||||
|  |  | ||||||
| def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None): | def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None): | ||||||
|     substructs = [] |     substructs = [] | ||||||
|     ret = '' |     ret = '' | ||||||
| @@ -35,6 +61,19 @@ def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = | |||||||
|             nested_field_prefix = "%s%s." % (field_prefix, argname) |             nested_field_prefix = "%s%s." % (field_prefix, argname) | ||||||
|             ret += generate_visit_struct_fields(name, nested_field_prefix, |             ret += generate_visit_struct_fields(name, nested_field_prefix, | ||||||
|                                                 nested_fn_prefix, argentry) |                                                 nested_fn_prefix, argentry) | ||||||
|  |             ret += mcgen(''' | ||||||
|  |  | ||||||
|  | static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp) | ||||||
|  | { | ||||||
|  | ''', | ||||||
|  |                          name=name, full_name=full_name, c_name=c_var(argname)) | ||||||
|  |             ret += generate_visit_struct_body(full_name, argname, argentry) | ||||||
|  |             ret += mcgen(''' | ||||||
|  | } | ||||||
|  | ''') | ||||||
|  |  | ||||||
|  |     if base: | ||||||
|  |         ret += generate_visit_implicit_struct(base) | ||||||
|  |  | ||||||
|     ret += mcgen(''' |     ret += mcgen(''' | ||||||
|  |  | ||||||
| @@ -47,12 +86,9 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error * | |||||||
|  |  | ||||||
|     if base: |     if base: | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
| visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(type)s), &err); | visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); | ||||||
| if (!err) { | if (err) { | ||||||
|     visit_type_%(type)s_fields(m, &(*obj)->%(c_prefix)s%(c_name)s, &err); |     goto out; | ||||||
|     error_propagate(errp, err); |  | ||||||
|     err = NULL; |  | ||||||
|     visit_end_implicit_struct(m, &err); |  | ||||||
| } | } | ||||||
| ''', | ''', | ||||||
|                      c_prefix=c_var(field_prefix), |                      c_prefix=c_var(field_prefix), | ||||||
| @@ -61,15 +97,18 @@ if (!err) { | |||||||
|     for argname, argentry, optional, structured in parse_args(members): |     for argname, argentry, optional, structured in parse_args(members): | ||||||
|         if optional: |         if optional: | ||||||
|             ret += mcgen(''' |             ret += mcgen(''' | ||||||
| visit_start_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); | visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err); | ||||||
| if ((*obj)->%(prefix)shas_%(c_name)s) { | if (!err && (*obj)->%(prefix)shas_%(c_name)s) { | ||||||
| ''', | ''', | ||||||
|                          c_prefix=c_var(field_prefix), prefix=field_prefix, |                          c_prefix=c_var(field_prefix), prefix=field_prefix, | ||||||
|                          c_name=c_var(argname), name=argname) |                          c_name=c_var(argname), name=argname) | ||||||
|             push_indent() |             push_indent() | ||||||
|  |  | ||||||
|         if structured: |         if structured: | ||||||
|             ret += generate_visit_struct_body(full_name, argname, argentry) |             ret += mcgen(''' | ||||||
|  | visit_type_%(full_name)s_field_%(c_name)s(m, obj, &err); | ||||||
|  | ''', | ||||||
|  |                          full_name=full_name, c_name=c_var(argname)) | ||||||
|         else: |         else: | ||||||
|             ret += mcgen(''' |             ret += mcgen(''' | ||||||
| visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); | visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); | ||||||
| @@ -82,12 +121,20 @@ visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); | |||||||
|             pop_indent() |             pop_indent() | ||||||
|             ret += mcgen(''' |             ret += mcgen(''' | ||||||
| } | } | ||||||
| visit_end_optional(m, &err); | ''') | ||||||
|  |         ret += mcgen(''' | ||||||
|  | if (err) { | ||||||
|  |     goto out; | ||||||
|  | } | ||||||
| ''') | ''') | ||||||
|  |  | ||||||
|     pop_indent() |     pop_indent() | ||||||
|  |     if re.search('^ *goto out\\;', ret, re.MULTILINE): | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
|  |  | ||||||
|  | out: | ||||||
|  | ''') | ||||||
|  |     ret += mcgen(''' | ||||||
|     error_propagate(errp, err); |     error_propagate(errp, err); | ||||||
| } | } | ||||||
| ''') | ''') | ||||||
| @@ -96,9 +143,9 @@ visit_end_optional(m, &err); | |||||||
|  |  | ||||||
| def generate_visit_struct_body(field_prefix, name, members): | def generate_visit_struct_body(field_prefix, name, members): | ||||||
|     ret = mcgen(''' |     ret = mcgen(''' | ||||||
| if (!error_is_set(errp)) { |     Error *err = NULL; | ||||||
|  |  | ||||||
| ''') | ''') | ||||||
|     push_indent() |  | ||||||
|  |  | ||||||
|     if not field_prefix: |     if not field_prefix: | ||||||
|         full_name = name |         full_name = name | ||||||
| @@ -107,36 +154,26 @@ if (!error_is_set(errp)) { | |||||||
|  |  | ||||||
|     if len(field_prefix): |     if len(field_prefix): | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
| Error **errp = &err; /* from outer scope */ |     visit_start_struct(m, NULL, "", "%(name)s", 0, &err); | ||||||
| Error *err = NULL; |  | ||||||
| visit_start_struct(m, NULL, "", "%(name)s", 0, &err); |  | ||||||
| ''', | ''', | ||||||
|                 name=name) |                 name=name) | ||||||
|     else: |     else: | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
| Error *err = NULL; |     visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); | ||||||
| visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); |  | ||||||
| ''', | ''', | ||||||
|                 name=name) |                 name=name) | ||||||
|  |  | ||||||
|     ret += mcgen(''' |     ret += mcgen(''' | ||||||
| if (!err) { |     if (!err) { | ||||||
|         if (*obj) { |         if (*obj) { | ||||||
|         visit_type_%(name)s_fields(m, obj, &err); |             visit_type_%(name)s_fields(m, obj, errp); | ||||||
|         error_propagate(errp, err); |  | ||||||
|         err = NULL; |  | ||||||
|         } |         } | ||||||
| ''', |  | ||||||
|         name=full_name) |  | ||||||
|  |  | ||||||
|     pop_indent() |  | ||||||
|     ret += mcgen(''' |  | ||||||
|         /* Always call end_struct if start_struct succeeded.  */ |  | ||||||
|         visit_end_struct(m, &err); |         visit_end_struct(m, &err); | ||||||
|     } |     } | ||||||
|     error_propagate(errp, err); |     error_propagate(errp, err); | ||||||
| } | ''', | ||||||
| ''') |         name=full_name) | ||||||
|  |  | ||||||
|     return ret |     return ret | ||||||
|  |  | ||||||
| def generate_visit_struct(expr): | def generate_visit_struct(expr): | ||||||
| @@ -154,9 +191,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** | |||||||
| ''', | ''', | ||||||
|                 name=name) |                 name=name) | ||||||
|  |  | ||||||
|     push_indent() |  | ||||||
|     ret += generate_visit_struct_body("", name, members) |     ret += generate_visit_struct_body("", name, members) | ||||||
|     pop_indent() |  | ||||||
|  |  | ||||||
|     ret += mcgen(''' |     ret += mcgen(''' | ||||||
| } | } | ||||||
| @@ -168,24 +203,26 @@ def generate_visit_list(name, members): | |||||||
|  |  | ||||||
| void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp) | void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     GenericList *i, **prev = (GenericList **)obj; |  | ||||||
|     Error *err = NULL; |     Error *err = NULL; | ||||||
|  |     GenericList *i, **prev; | ||||||
|  |  | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     visit_start_list(m, name, &err); |     visit_start_list(m, name, &err); | ||||||
|         if (!err) { |     if (err) { | ||||||
|             for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) { |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (prev = (GenericList **)obj; | ||||||
|  |          !err && (i = visit_next_list(m, prev, &err)) != NULL; | ||||||
|  |          prev = &i) { | ||||||
|         %(name)sList *native_i = (%(name)sList *)i; |         %(name)sList *native_i = (%(name)sList *)i; | ||||||
|         visit_type_%(name)s(m, &native_i->value, NULL, &err); |         visit_type_%(name)s(m, &native_i->value, NULL, &err); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     error_propagate(errp, err); |     error_propagate(errp, err); | ||||||
|     err = NULL; |     err = NULL; | ||||||
|  |  | ||||||
|             /* Always call end_list if start_list succeeded.  */ |  | ||||||
|     visit_end_list(m, &err); |     visit_end_list(m, &err); | ||||||
|         } | out: | ||||||
|     error_propagate(errp, err); |     error_propagate(errp, err); | ||||||
|     } |  | ||||||
| } | } | ||||||
| ''', | ''', | ||||||
|                 name=name) |                 name=name) | ||||||
| @@ -207,9 +244,14 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** | |||||||
| { | { | ||||||
|     Error *err = NULL; |     Error *err = NULL; | ||||||
|  |  | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err); |     visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|     visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err); |     visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|     switch ((*obj)->kind) { |     switch ((*obj)->kind) { | ||||||
| ''', | ''', | ||||||
|     name=name) |     name=name) | ||||||
| @@ -237,10 +279,12 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** | |||||||
|     default: |     default: | ||||||
|         abort(); |         abort(); | ||||||
|     } |     } | ||||||
|  | out_end: | ||||||
|     error_propagate(errp, err); |     error_propagate(errp, err); | ||||||
|     err = NULL; |     err = NULL; | ||||||
|     visit_end_implicit_struct(m, &err); |     visit_end_implicit_struct(m, &err); | ||||||
|     } | out: | ||||||
|  |     error_propagate(errp, err); | ||||||
| } | } | ||||||
| ''') | ''') | ||||||
|  |  | ||||||
| @@ -277,39 +321,42 @@ def generate_visit_union(expr): | |||||||
|             del base_fields[discriminator] |             del base_fields[discriminator] | ||||||
|         ret += generate_visit_struct_fields(name, "", "", base_fields) |         ret += generate_visit_struct_fields(name, "", "", base_fields) | ||||||
|  |  | ||||||
|  |     if discriminator: | ||||||
|  |         for key in members: | ||||||
|  |             ret += generate_visit_implicit_struct(members[key]) | ||||||
|  |  | ||||||
|     ret += mcgen(''' |     ret += mcgen(''' | ||||||
|  |  | ||||||
| void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) | void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp) | ||||||
| { | { | ||||||
|     Error *err = NULL; |     Error *err = NULL; | ||||||
|  |  | ||||||
|     if (!error_is_set(errp)) { |  | ||||||
|     visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); |     visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); | ||||||
|         if (!err) { |     if (err) { | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|     if (*obj) { |     if (*obj) { | ||||||
| ''', | ''', | ||||||
|                  name=name) |                  name=name) | ||||||
|  |  | ||||||
|  |  | ||||||
|     push_indent() |  | ||||||
|     push_indent() |  | ||||||
|     push_indent() |  | ||||||
|  |  | ||||||
|     if base: |     if base: | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
|         visit_type_%(name)s_fields(m, obj, &err); |         visit_type_%(name)s_fields(m, obj, &err); | ||||||
|  |         if (err) { | ||||||
|  |             goto out_obj; | ||||||
|  |         } | ||||||
| ''', | ''', | ||||||
|             name=name) |             name=name) | ||||||
|  |  | ||||||
|     pop_indent() |  | ||||||
|  |  | ||||||
|     if not discriminator: |     if not discriminator: | ||||||
|         disc_key = "type" |         disc_key = "type" | ||||||
|     else: |     else: | ||||||
|         disc_key = discriminator |         disc_key = discriminator | ||||||
|     ret += mcgen(''' |     ret += mcgen(''' | ||||||
|         visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); |         visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); | ||||||
|         if (!err) { |         if (err) { | ||||||
|  |             goto out_obj; | ||||||
|  |         } | ||||||
|         switch ((*obj)->kind) { |         switch ((*obj)->kind) { | ||||||
| ''', | ''', | ||||||
|                  disc_type = disc_type, |                  disc_type = disc_type, | ||||||
| @@ -319,13 +366,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** | |||||||
|         if not discriminator: |         if not discriminator: | ||||||
|             fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' |             fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' | ||||||
|         else: |         else: | ||||||
|             fmt = '''visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(c_type)s), &err); |             fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);' | ||||||
|                 if (!err) { |  | ||||||
|                     visit_type_%(c_type)s_fields(m, &(*obj)->%(c_name)s, &err); |  | ||||||
|                     error_propagate(errp, err); |  | ||||||
|                     err = NULL; |  | ||||||
|                     visit_end_implicit_struct(m, &err); |  | ||||||
|                 }''' |  | ||||||
|  |  | ||||||
|         enum_full_value = generate_enum_full_value(disc_type, key) |         enum_full_value = generate_enum_full_value(disc_type, key) | ||||||
|         ret += mcgen(''' |         ret += mcgen(''' | ||||||
| @@ -341,23 +382,14 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** | |||||||
|         default: |         default: | ||||||
|             abort(); |             abort(); | ||||||
|         } |         } | ||||||
|         } | out_obj: | ||||||
|         error_propagate(errp, err); |         error_propagate(errp, err); | ||||||
|         err = NULL; |         err = NULL; | ||||||
|     } |     } | ||||||
| ''') |  | ||||||
|     pop_indent() |  | ||||||
|     ret += mcgen(''' |  | ||||||
|         /* Always call end_struct if start_struct succeeded.  */ |  | ||||||
|     visit_end_struct(m, &err); |     visit_end_struct(m, &err); | ||||||
|     } | out: | ||||||
|     error_propagate(errp, err); |     error_propagate(errp, err); | ||||||
| } | } | ||||||
| ''') |  | ||||||
|  |  | ||||||
|     pop_indent(); |  | ||||||
|     ret += mcgen(''' |  | ||||||
| } |  | ||||||
| ''') | ''') | ||||||
|  |  | ||||||
|     return ret |     return ret | ||||||
| @@ -476,7 +508,7 @@ fdecl.write(mcgen(''' | |||||||
| /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * schema-defined QAPI visitor function |  * schema-defined QAPI visitor functions | ||||||
|  * |  * | ||||||
|  * Copyright IBM, Corp. 2011 |  * Copyright IBM, Corp. 2011 | ||||||
|  * |  * | ||||||
|   | |||||||
| @@ -73,13 +73,18 @@ class QAPIExprError(Exception): | |||||||
|  |  | ||||||
| class QAPISchema: | class QAPISchema: | ||||||
|  |  | ||||||
|     def __init__(self, fp, input_relname=None, include_hist=[], parent_info=None): |     def __init__(self, fp, input_relname=None, include_hist=[], | ||||||
|  |                  previously_included=[], parent_info=None): | ||||||
|  |         """ include_hist is a stack used to detect inclusion cycles | ||||||
|  |             previously_included is a global state used to avoid multiple | ||||||
|  |                                 inclusions of the same file""" | ||||||
|         input_fname = os.path.abspath(fp.name) |         input_fname = os.path.abspath(fp.name) | ||||||
|         if input_relname is None: |         if input_relname is None: | ||||||
|             input_relname = fp.name |             input_relname = fp.name | ||||||
|         self.input_dir = os.path.dirname(input_fname) |         self.input_dir = os.path.dirname(input_fname) | ||||||
|         self.input_file = input_relname |         self.input_file = input_relname | ||||||
|         self.include_hist = include_hist + [(input_relname, input_fname)] |         self.include_hist = include_hist + [(input_relname, input_fname)] | ||||||
|  |         previously_included.append(input_fname) | ||||||
|         self.parent_info = parent_info |         self.parent_info = parent_info | ||||||
|         self.src = fp.read() |         self.src = fp.read() | ||||||
|         if self.src == '' or self.src[-1] != '\n': |         if self.src == '' or self.src[-1] != '\n': | ||||||
| @@ -106,13 +111,16 @@ class QAPISchema: | |||||||
|                        for elem in self.include_hist): |                        for elem in self.include_hist): | ||||||
|                     raise QAPIExprError(expr_info, "Inclusion loop for %s" |                     raise QAPIExprError(expr_info, "Inclusion loop for %s" | ||||||
|                                         % include) |                                         % include) | ||||||
|  |                 # skip multiple include of the same file | ||||||
|  |                 if include_path in previously_included: | ||||||
|  |                     continue | ||||||
|                 try: |                 try: | ||||||
|                     fobj = open(include_path, 'r') |                     fobj = open(include_path, 'r') | ||||||
|                 except IOError as e: |                 except IOError, e: | ||||||
|                     raise QAPIExprError(expr_info, |                     raise QAPIExprError(expr_info, | ||||||
|                                         '%s: %s' % (e.strerror, include)) |                                         '%s: %s' % (e.strerror, include)) | ||||||
|                 exprs_include = QAPISchema(fobj, include, |                 exprs_include = QAPISchema(fobj, include, self.include_hist, | ||||||
|                                            self.include_hist, expr_info) |                                            previously_included, expr_info) | ||||||
|                 self.exprs.extend(exprs_include.exprs) |                 self.exprs.extend(exprs_include.exprs) | ||||||
|             else: |             else: | ||||||
|                 expr_elem = {'expr': expr, |                 expr_elem = {'expr': expr, | ||||||
|   | |||||||
| @@ -86,6 +86,7 @@ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, | |||||||
| int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, | int s390_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, | ||||||
|                                   CPUState *cpu, void *opaque); |                                   CPUState *cpu, void *opaque); | ||||||
| hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); | hwaddr s390_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); | ||||||
|  | hwaddr s390_cpu_get_phys_addr_debug(CPUState *cpu, vaddr addr); | ||||||
| int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); | int s390_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); | ||||||
| int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); | int s390_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -489,6 +489,18 @@ hwaddr s390_cpu_get_phys_page_debug(CPUState *cs, vaddr vaddr) | |||||||
|     return raddr; |     return raddr; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | hwaddr s390_cpu_get_phys_addr_debug(CPUState *cs, vaddr vaddr) | ||||||
|  | { | ||||||
|  |     hwaddr phys_addr; | ||||||
|  |     target_ulong page; | ||||||
|  |  | ||||||
|  |     page = vaddr & TARGET_PAGE_MASK; | ||||||
|  |     phys_addr = cpu_get_phys_page_debug(cs, page); | ||||||
|  |     phys_addr += (vaddr & ~TARGET_PAGE_MASK); | ||||||
|  |  | ||||||
|  |     return phys_addr; | ||||||
|  | } | ||||||
|  |  | ||||||
| void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) | void load_psw(CPUS390XState *env, uint64_t mask, uint64_t addr) | ||||||
| { | { | ||||||
|     if (mask & PSW_MASK_WAIT) { |     if (mask & PSW_MASK_WAIT) { | ||||||
|   | |||||||
| @@ -36,6 +36,7 @@ | |||||||
| #include "sysemu/device_tree.h" | #include "sysemu/device_tree.h" | ||||||
| #include "qapi/qmp/qjson.h" | #include "qapi/qmp/qjson.h" | ||||||
| #include "monitor/monitor.h" | #include "monitor/monitor.h" | ||||||
|  | #include "exec/gdbstub.h" | ||||||
| #include "trace.h" | #include "trace.h" | ||||||
|  |  | ||||||
| /* #define DEBUG_KVM */ | /* #define DEBUG_KVM */ | ||||||
| @@ -86,6 +87,14 @@ | |||||||
| #define ICPT_CPU_STOP                   0x28 | #define ICPT_CPU_STOP                   0x28 | ||||||
| #define ICPT_IO                         0x40 | #define ICPT_IO                         0x40 | ||||||
|  |  | ||||||
|  | static CPUWatchpoint hw_watchpoint; | ||||||
|  | /* | ||||||
|  |  * We don't use a list because this structure is also used to transmit the | ||||||
|  |  * hardware breakpoints to the kernel. | ||||||
|  |  */ | ||||||
|  | static struct kvm_hw_breakpoint *hw_breakpoints; | ||||||
|  | static int nb_hw_breakpoints; | ||||||
|  |  | ||||||
| const KVMCapabilityInfo kvm_arch_required_capabilities[] = { | const KVMCapabilityInfo kvm_arch_required_capabilities[] = { | ||||||
|     KVM_CAP_LAST_INFO |     KVM_CAP_LAST_INFO | ||||||
| }; | }; | ||||||
| @@ -320,12 +329,16 @@ static void *legacy_s390_alloc(size_t size) | |||||||
|     return mem == MAP_FAILED ? NULL : mem; |     return mem == MAP_FAILED ? NULL : mem; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* DIAG 501 is used for sw breakpoints */ | ||||||
|  | static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; | ||||||
|  |  | ||||||
| int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) | int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) | ||||||
| { | { | ||||||
|     static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; |  | ||||||
|  |  | ||||||
|     if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) || |     if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, | ||||||
|         cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501, 4, 1)) { |                             sizeof(diag_501), 0) || | ||||||
|  |         cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)diag_501, | ||||||
|  |                             sizeof(diag_501), 1)) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
| @@ -333,38 +346,140 @@ int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) | |||||||
|  |  | ||||||
| int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) | int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) | ||||||
| { | { | ||||||
|     uint8_t t[4]; |     uint8_t t[sizeof(diag_501)]; | ||||||
|     static const uint8_t diag_501[] = {0x83, 0x24, 0x05, 0x01}; |  | ||||||
|  |  | ||||||
|     if (cpu_memory_rw_debug(cs, bp->pc, t, 4, 0)) { |     if (cpu_memory_rw_debug(cs, bp->pc, t, sizeof(diag_501), 0)) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } else if (memcmp(t, diag_501, 4)) { |     } else if (memcmp(t, diag_501, sizeof(diag_501))) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) { |     } else if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, | ||||||
|  |                                    sizeof(diag_501), 1)) { | ||||||
|         return -EINVAL; |         return -EINVAL; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static struct kvm_hw_breakpoint *find_hw_breakpoint(target_ulong addr, | ||||||
|  |                                                     int len, int type) | ||||||
|  | { | ||||||
|  |     int n; | ||||||
|  |  | ||||||
|  |     for (n = 0; n < nb_hw_breakpoints; n++) { | ||||||
|  |         if (hw_breakpoints[n].addr == addr && hw_breakpoints[n].type == type && | ||||||
|  |             (hw_breakpoints[n].len == len || len == -1)) { | ||||||
|  |             return &hw_breakpoints[n]; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int insert_hw_breakpoint(target_ulong addr, int len, int type) | ||||||
|  | { | ||||||
|  |     int size; | ||||||
|  |  | ||||||
|  |     if (find_hw_breakpoint(addr, len, type)) { | ||||||
|  |         return -EEXIST; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     size = (nb_hw_breakpoints + 1) * sizeof(struct kvm_hw_breakpoint); | ||||||
|  |  | ||||||
|  |     if (!hw_breakpoints) { | ||||||
|  |         nb_hw_breakpoints = 0; | ||||||
|  |         hw_breakpoints = (struct kvm_hw_breakpoint *)g_try_malloc(size); | ||||||
|  |     } else { | ||||||
|  |         hw_breakpoints = | ||||||
|  |             (struct kvm_hw_breakpoint *)g_try_realloc(hw_breakpoints, size); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!hw_breakpoints) { | ||||||
|  |         nb_hw_breakpoints = 0; | ||||||
|  |         return -ENOMEM; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     hw_breakpoints[nb_hw_breakpoints].addr = addr; | ||||||
|  |     hw_breakpoints[nb_hw_breakpoints].len = len; | ||||||
|  |     hw_breakpoints[nb_hw_breakpoints].type = type; | ||||||
|  |  | ||||||
|  |     nb_hw_breakpoints++; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| int kvm_arch_insert_hw_breakpoint(target_ulong addr, | int kvm_arch_insert_hw_breakpoint(target_ulong addr, | ||||||
|                                   target_ulong len, int type) |                                   target_ulong len, int type) | ||||||
| { | { | ||||||
|  |     switch (type) { | ||||||
|  |     case GDB_BREAKPOINT_HW: | ||||||
|  |         type = KVM_HW_BP; | ||||||
|  |         break; | ||||||
|  |     case GDB_WATCHPOINT_WRITE: | ||||||
|  |         if (len < 1) { | ||||||
|  |             return -EINVAL; | ||||||
|  |         } | ||||||
|  |         type = KVM_HW_WP_WRITE; | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|         return -ENOSYS; |         return -ENOSYS; | ||||||
|  |     } | ||||||
|  |     return insert_hw_breakpoint(addr, len, type); | ||||||
| } | } | ||||||
|  |  | ||||||
| int kvm_arch_remove_hw_breakpoint(target_ulong addr, | int kvm_arch_remove_hw_breakpoint(target_ulong addr, | ||||||
|                                   target_ulong len, int type) |                                   target_ulong len, int type) | ||||||
| { | { | ||||||
|     return -ENOSYS; |     int size; | ||||||
|  |     struct kvm_hw_breakpoint *bp = find_hw_breakpoint(addr, len, type); | ||||||
|  |  | ||||||
|  |     if (bp == NULL) { | ||||||
|  |         return -ENOENT; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     nb_hw_breakpoints--; | ||||||
|  |     if (nb_hw_breakpoints > 0) { | ||||||
|  |         /* | ||||||
|  |          * In order to trim the array, move the last element to the position to | ||||||
|  |          * be removed - if necessary. | ||||||
|  |          */ | ||||||
|  |         if (bp != &hw_breakpoints[nb_hw_breakpoints]) { | ||||||
|  |             *bp = hw_breakpoints[nb_hw_breakpoints]; | ||||||
|  |         } | ||||||
|  |         size = nb_hw_breakpoints * sizeof(struct kvm_hw_breakpoint); | ||||||
|  |         hw_breakpoints = | ||||||
|  |              (struct kvm_hw_breakpoint *)g_realloc(hw_breakpoints, size); | ||||||
|  |     } else { | ||||||
|  |         g_free(hw_breakpoints); | ||||||
|  |         hw_breakpoints = NULL; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void kvm_arch_remove_all_hw_breakpoints(void) | void kvm_arch_remove_all_hw_breakpoints(void) | ||||||
| { | { | ||||||
|  |     nb_hw_breakpoints = 0; | ||||||
|  |     g_free(hw_breakpoints); | ||||||
|  |     hw_breakpoints = NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) | void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) | ||||||
| { | { | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     if (nb_hw_breakpoints > 0) { | ||||||
|  |         dbg->arch.nr_hw_bp = nb_hw_breakpoints; | ||||||
|  |         dbg->arch.hw_bp = hw_breakpoints; | ||||||
|  |  | ||||||
|  |         for (i = 0; i < nb_hw_breakpoints; ++i) { | ||||||
|  |             hw_breakpoints[i].phys_addr = s390_cpu_get_phys_addr_debug(cpu, | ||||||
|  |                                                        hw_breakpoints[i].addr); | ||||||
|  |         } | ||||||
|  |         dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; | ||||||
|  |     } else { | ||||||
|  |         dbg->arch.nr_hw_bp = 0; | ||||||
|  |         dbg->arch.hw_bp = NULL; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) | void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) | ||||||
| @@ -579,6 +694,22 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run) | |||||||
|     handle_diag_308(&cpu->env, r1, r3); |     handle_diag_308(&cpu->env, r1, r3); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int handle_sw_breakpoint(S390CPU *cpu, struct kvm_run *run) | ||||||
|  | { | ||||||
|  |     CPUS390XState *env = &cpu->env; | ||||||
|  |     unsigned long pc; | ||||||
|  |  | ||||||
|  |     cpu_synchronize_state(CPU(cpu)); | ||||||
|  |  | ||||||
|  |     pc = env->psw.addr - 4; | ||||||
|  |     if (kvm_find_sw_breakpoint(CPU(cpu), pc)) { | ||||||
|  |         env->psw.addr = pc; | ||||||
|  |         return EXCP_DEBUG; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return -ENOENT; | ||||||
|  | } | ||||||
|  |  | ||||||
| #define DIAG_KVM_CODE_MASK 0x000000000000ffff | #define DIAG_KVM_CODE_MASK 0x000000000000ffff | ||||||
|  |  | ||||||
| static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) | static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) | ||||||
| @@ -599,7 +730,7 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) | |||||||
|         r = handle_hypercall(cpu, run); |         r = handle_hypercall(cpu, run); | ||||||
|         break; |         break; | ||||||
|     case DIAG_KVM_BREAKPOINT: |     case DIAG_KVM_BREAKPOINT: | ||||||
|         sleep(10); |         r = handle_sw_breakpoint(cpu, run); | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code); |         DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code); | ||||||
| @@ -701,7 +832,7 @@ out: | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void handle_instruction(S390CPU *cpu, struct kvm_run *run) | static int handle_instruction(S390CPU *cpu, struct kvm_run *run) | ||||||
| { | { | ||||||
|     unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00); |     unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00); | ||||||
|     uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff; |     uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff; | ||||||
| @@ -728,8 +859,11 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (r < 0) { |     if (r < 0) { | ||||||
|  |         r = 0; | ||||||
|         enter_pgmcheck(cpu, 0x0001); |         enter_pgmcheck(cpu, 0x0001); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     return r; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool is_special_wait_psw(CPUState *cs) | static bool is_special_wait_psw(CPUState *cs) | ||||||
| @@ -749,7 +883,7 @@ static int handle_intercept(S390CPU *cpu) | |||||||
|             (long)cs->kvm_run->psw_addr); |             (long)cs->kvm_run->psw_addr); | ||||||
|     switch (icpt_code) { |     switch (icpt_code) { | ||||||
|         case ICPT_INSTRUCTION: |         case ICPT_INSTRUCTION: | ||||||
|             handle_instruction(cpu, run); |             r = handle_instruction(cpu, run); | ||||||
|             break; |             break; | ||||||
|         case ICPT_WAITPSW: |         case ICPT_WAITPSW: | ||||||
|             /* disabled wait, since enabled wait is handled in kernel */ |             /* disabled wait, since enabled wait is handled in kernel */ | ||||||
| @@ -830,7 +964,36 @@ static int handle_tsch(S390CPU *cpu) | |||||||
|  |  | ||||||
| static int kvm_arch_handle_debug_exit(S390CPU *cpu) | static int kvm_arch_handle_debug_exit(S390CPU *cpu) | ||||||
| { | { | ||||||
|     return -ENOSYS; |     CPUState *cs = CPU(cpu); | ||||||
|  |     struct kvm_run *run = cs->kvm_run; | ||||||
|  |  | ||||||
|  |     int ret = 0; | ||||||
|  |     struct kvm_debug_exit_arch *arch_info = &run->debug.arch; | ||||||
|  |  | ||||||
|  |     switch (arch_info->type) { | ||||||
|  |     case KVM_HW_WP_WRITE: | ||||||
|  |         if (find_hw_breakpoint(arch_info->addr, -1, arch_info->type)) { | ||||||
|  |             cs->watchpoint_hit = &hw_watchpoint; | ||||||
|  |             hw_watchpoint.vaddr = arch_info->addr; | ||||||
|  |             hw_watchpoint.flags = BP_MEM_WRITE; | ||||||
|  |             ret = EXCP_DEBUG; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case KVM_HW_BP: | ||||||
|  |         if (find_hw_breakpoint(arch_info->addr, -1, arch_info->type)) { | ||||||
|  |             ret = EXCP_DEBUG; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case KVM_SINGLESTEP: | ||||||
|  |         if (cs->singlestep_enabled) { | ||||||
|  |             ret = EXCP_DEBUG; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         ret = -ENOSYS; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) | int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) | ||||||
| @@ -911,6 +1074,16 @@ void kvm_s390_enable_css_support(S390CPU *cpu) | |||||||
|  |  | ||||||
| void kvm_arch_init_irq_routing(KVMState *s) | void kvm_arch_init_irq_routing(KVMState *s) | ||||||
| { | { | ||||||
|  |     /* | ||||||
|  |      * Note that while irqchip capabilities generally imply that cpustates | ||||||
|  |      * are handled in-kernel, it is not true for s390 (yet); therefore, we | ||||||
|  |      * have to override the common code kvm_halt_in_kernel_allowed setting. | ||||||
|  |      */ | ||||||
|  |     if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { | ||||||
|  |         kvm_irqfds_allowed = true; | ||||||
|  |         kvm_gsi_routing_allowed = true; | ||||||
|  |         kvm_halt_in_kernel_allowed = false; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, | int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, | ||||||
|   | |||||||
| @@ -193,7 +193,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ | |||||||
|         flat-union-string-discriminator.json \ |         flat-union-string-discriminator.json \ | ||||||
|         include-simple.json include-relpath.json include-format-err.json \ |         include-simple.json include-relpath.json include-format-err.json \ | ||||||
|         include-non-file.json include-no-file.json include-before-err.json \ |         include-non-file.json include-no-file.json include-before-err.json \ | ||||||
|         include-nested-err.json include-self-cycle.json include-cycle.json) |         include-nested-err.json include-self-cycle.json include-cycle.json \ | ||||||
|  |         include-repetition.json) | ||||||
|  |  | ||||||
| GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h | GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h | ||||||
|  |  | ||||||
|   | |||||||
| @@ -444,6 +444,92 @@ static void qdict_array_split_test(void) | |||||||
|     QDECREF(test_dict); |     QDECREF(test_dict); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void qdict_join_test(void) | ||||||
|  | { | ||||||
|  |     QDict *dict1, *dict2; | ||||||
|  |     bool overwrite = false; | ||||||
|  |     int i; | ||||||
|  |  | ||||||
|  |     dict1 = qdict_new(); | ||||||
|  |     dict2 = qdict_new(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /* Test everything once without overwrite and once with */ | ||||||
|  |     do | ||||||
|  |     { | ||||||
|  |         /* Test empty dicts */ | ||||||
|  |         qdict_join(dict1, dict2, overwrite); | ||||||
|  |  | ||||||
|  |         g_assert(qdict_size(dict1) == 0); | ||||||
|  |         g_assert(qdict_size(dict2) == 0); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         /* First iteration: Test movement */ | ||||||
|  |         /* Second iteration: Test empty source and non-empty destination */ | ||||||
|  |         qdict_put(dict2, "foo", qint_from_int(42)); | ||||||
|  |  | ||||||
|  |         for (i = 0; i < 2; i++) { | ||||||
|  |             qdict_join(dict1, dict2, overwrite); | ||||||
|  |  | ||||||
|  |             g_assert(qdict_size(dict1) == 1); | ||||||
|  |             g_assert(qdict_size(dict2) == 0); | ||||||
|  |  | ||||||
|  |             g_assert(qdict_get_int(dict1, "foo") == 42); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         /* Test non-empty source and destination without conflict */ | ||||||
|  |         qdict_put(dict2, "bar", qint_from_int(23)); | ||||||
|  |  | ||||||
|  |         qdict_join(dict1, dict2, overwrite); | ||||||
|  |  | ||||||
|  |         g_assert(qdict_size(dict1) == 2); | ||||||
|  |         g_assert(qdict_size(dict2) == 0); | ||||||
|  |  | ||||||
|  |         g_assert(qdict_get_int(dict1, "foo") == 42); | ||||||
|  |         g_assert(qdict_get_int(dict1, "bar") == 23); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         /* Test conflict */ | ||||||
|  |         qdict_put(dict2, "foo", qint_from_int(84)); | ||||||
|  |  | ||||||
|  |         qdict_join(dict1, dict2, overwrite); | ||||||
|  |  | ||||||
|  |         g_assert(qdict_size(dict1) == 2); | ||||||
|  |         g_assert(qdict_size(dict2) == !overwrite); | ||||||
|  |  | ||||||
|  |         g_assert(qdict_get_int(dict1, "foo") == overwrite ? 84 : 42); | ||||||
|  |         g_assert(qdict_get_int(dict1, "bar") == 23); | ||||||
|  |  | ||||||
|  |         if (!overwrite) { | ||||||
|  |             g_assert(qdict_get_int(dict2, "foo") == 84); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         /* Check the references */ | ||||||
|  |         g_assert(qdict_get(dict1, "foo")->refcnt == 1); | ||||||
|  |         g_assert(qdict_get(dict1, "bar")->refcnt == 1); | ||||||
|  |  | ||||||
|  |         if (!overwrite) { | ||||||
|  |             g_assert(qdict_get(dict2, "foo")->refcnt == 1); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         /* Clean up */ | ||||||
|  |         qdict_del(dict1, "foo"); | ||||||
|  |         qdict_del(dict1, "bar"); | ||||||
|  |  | ||||||
|  |         if (!overwrite) { | ||||||
|  |             qdict_del(dict2, "foo"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     while (overwrite ^= true); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     QDECREF(dict1); | ||||||
|  |     QDECREF(dict2); | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Errors test-cases |  * Errors test-cases | ||||||
|  */ |  */ | ||||||
| @@ -584,6 +670,7 @@ int main(int argc, char **argv) | |||||||
|     g_test_add_func("/public/iterapi", qdict_iterapi_test); |     g_test_add_func("/public/iterapi", qdict_iterapi_test); | ||||||
|     g_test_add_func("/public/flatten", qdict_flatten_test); |     g_test_add_func("/public/flatten", qdict_flatten_test); | ||||||
|     g_test_add_func("/public/array_split", qdict_array_split_test); |     g_test_add_func("/public/array_split", qdict_array_split_test); | ||||||
|  |     g_test_add_func("/public/join", qdict_join_test); | ||||||
|  |  | ||||||
|     g_test_add_func("/errors/put_exists", qdict_put_exists_test); |     g_test_add_func("/errors/put_exists", qdict_put_exists_test); | ||||||
|     g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); |     g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								tests/qapi-schema/include-repetition-sub.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/qapi-schema/include-repetition-sub.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | { 'include': 'comments.json' } | ||||||
|  | { 'include': 'comments.json' } | ||||||
							
								
								
									
										0
									
								
								tests/qapi-schema/include-repetition.err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/qapi-schema/include-repetition.err
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								tests/qapi-schema/include-repetition.exit
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/qapi-schema/include-repetition.exit
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | 0 | ||||||
							
								
								
									
										3
									
								
								tests/qapi-schema/include-repetition.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/qapi-schema/include-repetition.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | { 'include': 'comments.json' } | ||||||
|  | { 'include': 'include-repetition-sub.json' } | ||||||
|  | { 'include': 'comments.json' } | ||||||
							
								
								
									
										3
									
								
								tests/qapi-schema/include-repetition.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/qapi-schema/include-repetition.out
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | [OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])] | ||||||
|  | [{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}] | ||||||
|  | [] | ||||||
| @@ -35,7 +35,7 @@ class TestSingleDrive(iotests.QMPTestCase): | |||||||
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) |         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) | ||||||
|         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) |         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) | ||||||
|         qemu_io('-c', 'write -P 0x1 0 512', backing_img) |         qemu_io('-c', 'write -P 0x1 0 512', backing_img) | ||||||
|         self.vm = iotests.VM().add_drive(test_img) |         self.vm = iotests.VM().add_drive("blkdebug::" + test_img) | ||||||
|         self.vm.launch() |         self.vm.launch() | ||||||
|  |  | ||||||
|     def tearDown(self): |     def tearDown(self): | ||||||
|   | |||||||
| @@ -47,6 +47,11 @@ _supported_os Linux | |||||||
| _default_cache_mode "writethrough" | _default_cache_mode "writethrough" | ||||||
| _supported_cache_modes "writethrough" | _supported_cache_modes "writethrough" | ||||||
|  |  | ||||||
|  | _no_dump_exec() | ||||||
|  | { | ||||||
|  |     (ulimit -c 0; exec "$@") | ||||||
|  | } | ||||||
|  |  | ||||||
| size=128M | size=128M | ||||||
|  |  | ||||||
| echo | echo | ||||||
| @@ -67,10 +72,7 @@ echo "== Creating a dirty image file ==" | |||||||
| IMGOPTS="compat=1.1,lazy_refcounts=on" | IMGOPTS="compat=1.1,lazy_refcounts=on" | ||||||
| _make_test_img $size | _make_test_img $size | ||||||
|  |  | ||||||
| old_ulimit=$(ulimit -c) | _no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io | ||||||
| ulimit -c 0 # do not produce a core dump on abort(3) |  | ||||||
| $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io |  | ||||||
| ulimit -c "$old_ulimit" |  | ||||||
|  |  | ||||||
| # The dirty bit must be set | # The dirty bit must be set | ||||||
| ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features | ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features | ||||||
| @@ -103,10 +105,7 @@ echo "== Opening a dirty image read/write should repair it ==" | |||||||
| IMGOPTS="compat=1.1,lazy_refcounts=on" | IMGOPTS="compat=1.1,lazy_refcounts=on" | ||||||
| _make_test_img $size | _make_test_img $size | ||||||
|  |  | ||||||
| old_ulimit=$(ulimit -c) | _no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io | ||||||
| ulimit -c 0 # do not produce a core dump on abort(3) |  | ||||||
| $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io |  | ||||||
| ulimit -c "$old_ulimit" |  | ||||||
|  |  | ||||||
| # The dirty bit must be set | # The dirty bit must be set | ||||||
| ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features | ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features | ||||||
| @@ -122,10 +121,7 @@ echo "== Creating an image file with lazy_refcounts=off ==" | |||||||
| IMGOPTS="compat=1.1,lazy_refcounts=off" | IMGOPTS="compat=1.1,lazy_refcounts=off" | ||||||
| _make_test_img $size | _make_test_img $size | ||||||
|  |  | ||||||
| old_ulimit=$(ulimit -c) | _no_dump_exec $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" 2>&1 | _filter_qemu_io | ||||||
| ulimit -c 0 # do not produce a core dump on abort(3) |  | ||||||
| $QEMU_IO -c "write -P 0x5a 0 512" -c "abort" "$TEST_IMG" | _filter_qemu_io |  | ||||||
| ulimit -c "$old_ulimit" |  | ||||||
|  |  | ||||||
| # The dirty bit must not be set since lazy_refcounts=off | # The dirty bit must not be set since lazy_refcounts=off | ||||||
| ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features | ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ No errors were found on the image. | |||||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728  | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728  | ||||||
| wrote 512/512 bytes at offset 0 | wrote 512/512 bytes at offset 0 | ||||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | ./039: Aborted                 ( ulimit -c 0; exec "$@" ) | ||||||
| incompatible_features     0x1 | incompatible_features     0x1 | ||||||
| ERROR cluster 5 refcount=0 reference=1 | ERROR cluster 5 refcount=0 reference=1 | ||||||
| ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 | ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 | ||||||
| @@ -42,6 +43,7 @@ read 512/512 bytes at offset 0 | |||||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728  | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728  | ||||||
| wrote 512/512 bytes at offset 0 | wrote 512/512 bytes at offset 0 | ||||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | ./039: Aborted                 ( ulimit -c 0; exec "$@" ) | ||||||
| incompatible_features     0x1 | incompatible_features     0x1 | ||||||
| Repairing cluster 5 refcount=0 reference=1 | Repairing cluster 5 refcount=0 reference=1 | ||||||
| wrote 512/512 bytes at offset 0 | wrote 512/512 bytes at offset 0 | ||||||
| @@ -52,6 +54,7 @@ incompatible_features     0x0 | |||||||
| Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728  | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728  | ||||||
| wrote 512/512 bytes at offset 0 | wrote 512/512 bytes at offset 0 | ||||||
| 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | ./039: Aborted                 ( ulimit -c 0; exec "$@" ) | ||||||
| incompatible_features     0x0 | incompatible_features     0x0 | ||||||
| No errors were found on the image. | No errors were found on the image. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 | |||||||
| Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0 | Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0 | ||||||
| QMP_VERSION | QMP_VERSION | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} | {"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} | {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} | ||||||
| @@ -24,7 +24,7 @@ QMP_VERSION | |||||||
| Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk | Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk | ||||||
| QMP_VERSION | QMP_VERSION | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} | {"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| @@ -44,7 +44,7 @@ Testing: | |||||||
| QMP_VERSION | QMP_VERSION | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": "OK\r\n"} | {"return": "OK\r\n"} | ||||||
| {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} | {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| @@ -64,14 +64,14 @@ Testing: | |||||||
| QMP_VERSION | QMP_VERSION | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} | {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} | {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} | ||||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}} | {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}} | ||||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "RESET"} | {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "RESET"} | ||||||
| {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} | {"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "detect_zeroes": "off", "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} | ||||||
| {"return": {}} | {"return": {}} | ||||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"} | {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "SHUTDOWN"} | ||||||
| {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} | {"timestamp": {"seconds":  TIMESTAMP, "microseconds":  TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} | ||||||
|   | |||||||
| @@ -72,6 +72,13 @@ echo "=== Verify open image read-only succeeds after log replay ===" | |||||||
| $QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | _filter_testdir \ | $QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | _filter_testdir \ | ||||||
|                                                       | _filter_qemu_io |                                                       | _filter_qemu_io | ||||||
|  |  | ||||||
|  | _cleanup_test_img | ||||||
|  | _use_sample_img test-disk2vhd.vhdx.bz2 | ||||||
|  |  | ||||||
|  | echo | ||||||
|  | echo "=== Verify image created by Disk2VHD can be opened ===" | ||||||
|  | $QEMU_IMG info "$TEST_IMG" 2>&1 | _filter_testdir | _filter_qemu | ||||||
|  |  | ||||||
| # success, all done | # success, all done | ||||||
| echo "*** done" | echo "*** done" | ||||||
| rm -f $seq.full | rm -f $seq.full | ||||||
|   | |||||||
| @@ -18,4 +18,11 @@ No errors were found on the image. | |||||||
| === Verify open image read-only succeeds after log replay === | === Verify open image read-only succeeds after log replay === | ||||||
| read 18874368/18874368 bytes at offset 0 | read 18874368/18874368 bytes at offset 0 | ||||||
| 18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | 18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  |  | ||||||
|  | === Verify image created by Disk2VHD can be opened === | ||||||
|  | image: TEST_DIR/test-disk2vhd.vhdx | ||||||
|  | file format: vhdx | ||||||
|  | virtual size: 256M (268435456 bytes) | ||||||
|  | disk size: 260M | ||||||
|  | cluster_size: 2097152 | ||||||
| *** done | *** done | ||||||
|   | |||||||
							
								
								
									
										130
									
								
								tests/qemu-iotests/089
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										130
									
								
								tests/qemu-iotests/089
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,130 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | # | ||||||
|  | # Test case for support of JSON filenames | ||||||
|  | # | ||||||
|  | # Copyright (C) 2014 Red Hat, Inc. | ||||||
|  | # | ||||||
|  | # 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 of the License, or | ||||||
|  | # (at your option) any later version. | ||||||
|  | # | ||||||
|  | # 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/>. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | # creator | ||||||
|  | owner=mreitz@redhat.com | ||||||
|  |  | ||||||
|  | seq="$(basename $0)" | ||||||
|  | echo "QA output created by $seq" | ||||||
|  |  | ||||||
|  | here="$PWD" | ||||||
|  | tmp=/tmp/$$ | ||||||
|  | status=1	# failure is the default! | ||||||
|  |  | ||||||
|  | _cleanup() | ||||||
|  | { | ||||||
|  | 	_cleanup_test_img | ||||||
|  | } | ||||||
|  | trap "_cleanup; exit \$status" 0 1 2 3 15 | ||||||
|  |  | ||||||
|  | # get standard environment, filters and checks | ||||||
|  | . ./common.rc | ||||||
|  | . ./common.filter | ||||||
|  |  | ||||||
|  | _supported_fmt qcow2 | ||||||
|  | _supported_proto file | ||||||
|  | _supported_os Linux | ||||||
|  |  | ||||||
|  | # Using an image filename containing quotation marks will render the JSON data | ||||||
|  | # below invalid. In that case, we have little choice but simply not to run this | ||||||
|  | # test. | ||||||
|  | case $TEST_IMG in | ||||||
|  |     *'"'*) | ||||||
|  |         _notrun "image filename may not contain quotation marks" | ||||||
|  |         ;; | ||||||
|  | esac | ||||||
|  |  | ||||||
|  | IMG_SIZE=64M | ||||||
|  |  | ||||||
|  | # Taken from test 072 | ||||||
|  | echo | ||||||
|  | echo "=== Testing nested image formats ===" | ||||||
|  | echo | ||||||
|  |  | ||||||
|  | TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | ||||||
|  |  | ||||||
|  | $QEMU_IO -c 'write -P 42 0 512' -c 'write -P 23 512 512' \ | ||||||
|  |          -c 'write -P 66 1024 512' "$TEST_IMG.base" | _filter_qemu_io | ||||||
|  |  | ||||||
|  | $QEMU_IMG convert -f raw -O $IMGFMT "$TEST_IMG.base" "$TEST_IMG" | ||||||
|  |  | ||||||
|  | $QEMU_IO -c 'read -P 42 0 512' -c 'read -P 23 512 512' \ | ||||||
|  |          -c 'read -P 66 1024 512' "json:{ | ||||||
|  |     \"driver\": \"$IMGFMT\", | ||||||
|  |     \"file\": { | ||||||
|  |         \"driver\": \"$IMGFMT\", | ||||||
|  |         \"file\": { | ||||||
|  |             \"filename\": \"$TEST_IMG\" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | }" | _filter_qemu_io | ||||||
|  |  | ||||||
|  | # This should fail (see test 072) | ||||||
|  | $QEMU_IO -c 'read -P 42 0 512' "$TEST_IMG" | _filter_qemu_io | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Taken from test 071 | ||||||
|  | echo | ||||||
|  | echo "=== Testing blkdebug ===" | ||||||
|  | echo | ||||||
|  |  | ||||||
|  | _make_test_img $IMG_SIZE | ||||||
|  |  | ||||||
|  | $QEMU_IO -c 'write -P 42 0x38000 512' "$TEST_IMG" | _filter_qemu_io | ||||||
|  |  | ||||||
|  | # The "image.filename" part tests whether "a": { "b": "c" } and "a.b": "c" do | ||||||
|  | # the same (which they should). | ||||||
|  | $QEMU_IO -c 'read -P 42 0x38000 512' "json:{ | ||||||
|  |     \"driver\": \"$IMGFMT\", | ||||||
|  |     \"file\": { | ||||||
|  |         \"driver\": \"blkdebug\", | ||||||
|  |         \"inject-error\": [{ | ||||||
|  |             \"event\": \"l2_load\" | ||||||
|  |         }], | ||||||
|  |         \"image.filename\": \"$TEST_IMG\" | ||||||
|  |     } | ||||||
|  | }" | _filter_qemu_io | ||||||
|  |  | ||||||
|  |  | ||||||
|  | echo | ||||||
|  | echo "=== Testing qemu-img info output ===" | ||||||
|  | echo | ||||||
|  |  | ||||||
|  | $QEMU_IMG info "json:{\"driver\":\"qcow2\",\"file.filename\":\"$TEST_IMG\"}" \ | ||||||
|  |     | _filter_testdir | _filter_imgfmt | ||||||
|  |  | ||||||
|  |  | ||||||
|  | echo | ||||||
|  | echo "=== Testing option merging ===" | ||||||
|  | echo | ||||||
|  |  | ||||||
|  | # Both options given directly and those given in the filename should be used | ||||||
|  | $QEMU_IO -c "open -o driver=qcow2 json:{\"file.filename\":\"$TEST_IMG\"}" \ | ||||||
|  |          -c "info" 2>&1 | _filter_testdir | _filter_imgfmt | ||||||
|  |  | ||||||
|  | # Options given directly should be prioritized over those given in the filename | ||||||
|  | $QEMU_IO -c "open -o driver=qcow2 json:{\"driver\":\"raw\",\"file.filename\":\"$TEST_IMG\"}" \ | ||||||
|  |          -c "info" 2>&1 | _filter_testdir | _filter_imgfmt | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # success, all done | ||||||
|  | echo "*** done" | ||||||
|  | rm -f $seq.full | ||||||
|  | status=0 | ||||||
							
								
								
									
										54
									
								
								tests/qemu-iotests/089.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								tests/qemu-iotests/089.out
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | QA output created by 089 | ||||||
|  |  | ||||||
|  | === Testing nested image formats === | ||||||
|  |  | ||||||
|  | Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864  | ||||||
|  | wrote 512/512 bytes at offset 0 | ||||||
|  | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | wrote 512/512 bytes at offset 512 | ||||||
|  | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | wrote 512/512 bytes at offset 1024 | ||||||
|  | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | read 512/512 bytes at offset 0 | ||||||
|  | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | read 512/512 bytes at offset 512 | ||||||
|  | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | read 512/512 bytes at offset 1024 | ||||||
|  | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | Pattern verification failed at offset 0, 512 bytes | ||||||
|  | read 512/512 bytes at offset 0 | ||||||
|  | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  |  | ||||||
|  | === Testing blkdebug === | ||||||
|  |  | ||||||
|  | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864  | ||||||
|  | wrote 512/512 bytes at offset 229376 | ||||||
|  | 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||||||
|  | read failed: Input/output error | ||||||
|  |  | ||||||
|  | === Testing qemu-img info output === | ||||||
|  |  | ||||||
|  | image: TEST_DIR/t.IMGFMT | ||||||
|  | file format: IMGFMT | ||||||
|  | virtual size: 64M (67108864 bytes) | ||||||
|  | disk size: 324K | ||||||
|  | cluster_size: 65536 | ||||||
|  | Format specific information: | ||||||
|  |     compat: 1.1 | ||||||
|  |     lazy refcounts: false | ||||||
|  |  | ||||||
|  | === Testing option merging === | ||||||
|  |  | ||||||
|  | format name: IMGFMT | ||||||
|  | cluster size: 64 KiB | ||||||
|  | vm state offset: 512 MiB | ||||||
|  | Format specific information: | ||||||
|  |     compat: 1.1 | ||||||
|  |     lazy refcounts: false | ||||||
|  | format name: IMGFMT | ||||||
|  | cluster size: 64 KiB | ||||||
|  | vm state offset: 512 MiB | ||||||
|  | Format specific information: | ||||||
|  |     compat: 1.1 | ||||||
|  |     lazy refcounts: false | ||||||
|  | *** done | ||||||
							
								
								
									
										98
									
								
								tests/qemu-iotests/092
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										98
									
								
								tests/qemu-iotests/092
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,98 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | # | ||||||
|  | # qcow1 format input validation tests | ||||||
|  | # | ||||||
|  | # Copyright (C) 2014 Red Hat, Inc. | ||||||
|  | # | ||||||
|  | # 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 of the License, or | ||||||
|  | # (at your option) any later version. | ||||||
|  | # | ||||||
|  | # 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/>. | ||||||
|  | # | ||||||
|  |  | ||||||
|  | # creator | ||||||
|  | owner=kwolf@redhat.com | ||||||
|  |  | ||||||
|  | seq=`basename $0` | ||||||
|  | echo "QA output created by $seq" | ||||||
|  |  | ||||||
|  | here=`pwd` | ||||||
|  | tmp=/tmp/$$ | ||||||
|  | status=1	# failure is the default! | ||||||
|  |  | ||||||
|  | _cleanup() | ||||||
|  | { | ||||||
|  |     rm -f $TEST_IMG.snap | ||||||
|  |     _cleanup_test_img | ||||||
|  | } | ||||||
|  | trap "_cleanup; exit \$status" 0 1 2 3 15 | ||||||
|  |  | ||||||
|  | # get standard environment, filters and checks | ||||||
|  | . ./common.rc | ||||||
|  | . ./common.filter | ||||||
|  |  | ||||||
|  | _supported_fmt qcow | ||||||
|  | _supported_proto generic | ||||||
|  | _supported_os Linux | ||||||
|  |  | ||||||
|  | offset_backing_file_offset=8 | ||||||
|  | offset_backing_file_size=16 | ||||||
|  | offset_size=24 | ||||||
|  | offset_cluster_bits=32 | ||||||
|  | offset_l2_bits=33 | ||||||
|  |  | ||||||
|  | echo | ||||||
|  | echo "== Invalid cluster size ==" | ||||||
|  | _make_test_img 64M | ||||||
|  | poke_file "$TEST_IMG" "$offset_cluster_bits" "\xff" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  | poke_file "$TEST_IMG" "$offset_cluster_bits" "\x1f" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  | poke_file "$TEST_IMG" "$offset_cluster_bits" "\x08" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  | poke_file "$TEST_IMG" "$offset_cluster_bits" "\x11" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  |  | ||||||
|  | echo | ||||||
|  | echo "== Invalid L2 table size ==" | ||||||
|  | _make_test_img 64M | ||||||
|  | poke_file "$TEST_IMG" "$offset_l2_bits" "\xff" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  | poke_file "$TEST_IMG" "$offset_l2_bits" "\x05" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  | poke_file "$TEST_IMG" "$offset_l2_bits" "\x0e" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  |  | ||||||
|  | # 1 << 0x1b = 2^31 / L2_CACHE_SIZE | ||||||
|  | poke_file "$TEST_IMG" "$offset_l2_bits" "\x1b" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  |  | ||||||
|  | echo | ||||||
|  | echo "== Invalid size ==" | ||||||
|  | _make_test_img 64M | ||||||
|  | poke_file "$TEST_IMG" "$offset_size" "\xee\xee\xee\xee\xee\xee\xee\xee" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  | poke_file "$TEST_IMG" "$offset_size" "\x7f\xff\xff\xff\xff\xff\xff\xff" | ||||||
|  | { $QEMU_IO -c "write 0 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  |  | ||||||
|  | echo | ||||||
|  | echo "== Invalid backing file length ==" | ||||||
|  | _make_test_img 64M | ||||||
|  | poke_file "$TEST_IMG" "$offset_backing_file_offset" "\x00\x00\x00\xff" | ||||||
|  | poke_file "$TEST_IMG" "$offset_backing_file_size" "\xff\xff\xff\xff" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  | poke_file "$TEST_IMG" "$offset_backing_file_size" "\x7f\xff\xff\xff" | ||||||
|  | { $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir | ||||||
|  |  | ||||||
|  | # success, all done | ||||||
|  | echo "*** done" | ||||||
|  | rm -f $seq.full | ||||||
|  | status=0 | ||||||
							
								
								
									
										38
									
								
								tests/qemu-iotests/092.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tests/qemu-iotests/092.out
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | QA output created by 092 | ||||||
|  |  | ||||||
|  | == Invalid cluster size == | ||||||
|  | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864  | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k | ||||||
|  | no file open, try 'help open' | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k | ||||||
|  | no file open, try 'help open' | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k | ||||||
|  | no file open, try 'help open' | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k | ||||||
|  | no file open, try 'help open' | ||||||
|  |  | ||||||
|  | == Invalid L2 table size == | ||||||
|  | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864  | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k | ||||||
|  | no file open, try 'help open' | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k | ||||||
|  | no file open, try 'help open' | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k | ||||||
|  | no file open, try 'help open' | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k | ||||||
|  | no file open, try 'help open' | ||||||
|  |  | ||||||
|  | == Invalid size == | ||||||
|  | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864  | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: Image too large | ||||||
|  | no file open, try 'help open' | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: Image too large | ||||||
|  | no file open, try 'help open' | ||||||
|  |  | ||||||
|  | == Invalid backing file length == | ||||||
|  | Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864  | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long | ||||||
|  | no file open, try 'help open' | ||||||
|  | qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long | ||||||
|  | no file open, try 'help open' | ||||||
|  | *** done | ||||||
| @@ -150,6 +150,7 @@ _filter_win32() | |||||||
| _filter_qemu_io() | _filter_qemu_io() | ||||||
| { | { | ||||||
|     _filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ |     _filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ | ||||||
|  |         -e "s/: line [0-9][0-9]*:  *[0-9][0-9]*\( Aborted\)/:\1/" \ | ||||||
|         -e "s/qemu-io> //g" |         -e "s/qemu-io> //g" | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -95,5 +95,7 @@ | |||||||
| 086 rw auto quick | 086 rw auto quick | ||||||
| 087 rw auto | 087 rw auto | ||||||
| 088 rw auto | 088 rw auto | ||||||
|  | 089 rw auto quick | ||||||
| 090 rw auto quick | 090 rw auto quick | ||||||
| 091 rw auto | 091 rw auto | ||||||
|  | 092 rw auto quick | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								tests/qemu-iotests/sample_images/test-disk2vhd.vhdx.bz2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/qemu-iotests/sample_images/test-disk2vhd.vhdx.bz2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -72,14 +72,30 @@ typedef struct TestStruct | |||||||
| static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | ||||||
|                                   const char *name, Error **errp) |                                   const char *name, Error **errp) | ||||||
| { | { | ||||||
|  |     Error *err = NULL; | ||||||
|  |  | ||||||
|     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), |     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), | ||||||
|                        errp); |                        &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     visit_type_int(v, &(*obj)->integer, "integer", errp); |     visit_type_int(v, &(*obj)->integer, "integer", &err); | ||||||
|     visit_type_bool(v, &(*obj)->boolean, "boolean", errp); |     if (err) { | ||||||
|     visit_type_str(v, &(*obj)->string, "string", errp); |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_bool(v, &(*obj)->boolean, "boolean", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_str(v, &(*obj)->string, "string", &err); | ||||||
|  |  | ||||||
|     visit_end_struct(v, errp); | out_end: | ||||||
|  |     error_propagate(errp, err); | ||||||
|  |     err = NULL; | ||||||
|  |     visit_end_struct(v, &err); | ||||||
|  | out: | ||||||
|  |     error_propagate(errp, err); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void test_validate_struct(TestInputVisitorData *data, | static void test_validate_struct(TestInputVisitorData *data, | ||||||
|   | |||||||
| @@ -199,16 +199,24 @@ static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | |||||||
|  |  | ||||||
|     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), |     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), | ||||||
|                        &err); |                        &err); | ||||||
|     if (!err) { |     if (err) { | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|     visit_type_int(v, &(*obj)->integer, "integer", &err); |     visit_type_int(v, &(*obj)->integer, "integer", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|     visit_type_bool(v, &(*obj)->boolean, "boolean", &err); |     visit_type_bool(v, &(*obj)->boolean, "boolean", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|     visit_type_str(v, &(*obj)->string, "string", &err); |     visit_type_str(v, &(*obj)->string, "string", &err); | ||||||
|  |  | ||||||
|         /* Always call end_struct if start_struct succeeded.  */ | out_end: | ||||||
|     error_propagate(errp, err); |     error_propagate(errp, err); | ||||||
|     err = NULL; |     err = NULL; | ||||||
|     visit_end_struct(v, &err); |     visit_end_struct(v, &err); | ||||||
|     } | out: | ||||||
|     error_propagate(errp, err); |     error_propagate(errp, err); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -176,14 +176,30 @@ typedef struct TestStruct | |||||||
| static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | ||||||
|                                   const char *name, Error **errp) |                                   const char *name, Error **errp) | ||||||
| { | { | ||||||
|  |     Error *err = NULL; | ||||||
|  |  | ||||||
|     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), |     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), | ||||||
|                        errp); |                        &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     visit_type_int(v, &(*obj)->integer, "integer", errp); |     visit_type_int(v, &(*obj)->integer, "integer", &err); | ||||||
|     visit_type_bool(v, &(*obj)->boolean, "boolean", errp); |     if (err) { | ||||||
|     visit_type_str(v, &(*obj)->string, "string", errp); |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_bool(v, &(*obj)->boolean, "boolean", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_str(v, &(*obj)->string, "string", &err); | ||||||
|  |  | ||||||
|     visit_end_struct(v, errp); | out_end: | ||||||
|  |     error_propagate(errp, err); | ||||||
|  |     err = NULL; | ||||||
|  |     visit_end_struct(v, &err); | ||||||
|  | out: | ||||||
|  |     error_propagate(errp, err); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void test_visitor_out_struct(TestOutputVisitorData *data, | static void test_visitor_out_struct(TestOutputVisitorData *data, | ||||||
|   | |||||||
| @@ -195,13 +195,29 @@ typedef struct TestStruct | |||||||
| static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | static void visit_type_TestStruct(Visitor *v, TestStruct **obj, | ||||||
|                                   const char *name, Error **errp) |                                   const char *name, Error **errp) | ||||||
| { | { | ||||||
|     visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), errp); |     Error *err = NULL; | ||||||
|  |  | ||||||
|     visit_type_int(v, &(*obj)->integer, "integer", errp); |     visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err); | ||||||
|     visit_type_bool(v, &(*obj)->boolean, "boolean", errp); |     if (err) { | ||||||
|     visit_type_str(v, &(*obj)->string, "string", errp); |         goto out; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     visit_end_struct(v, errp); |     visit_type_int(v, &(*obj)->integer, "integer", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_bool(v, &(*obj)->boolean, "boolean", &err); | ||||||
|  |     if (err) { | ||||||
|  |         goto out_end; | ||||||
|  |     } | ||||||
|  |     visit_type_str(v, &(*obj)->string, "string", &err); | ||||||
|  |  | ||||||
|  | out_end: | ||||||
|  |     error_propagate(errp, err); | ||||||
|  |     err = NULL; | ||||||
|  |     visit_end_struct(v, &err); | ||||||
|  | out: | ||||||
|  |     error_propagate(errp, err); | ||||||
| } | } | ||||||
|  |  | ||||||
| static TestStruct *struct_create(void) | static TestStruct *struct_create(void) | ||||||
|   | |||||||
| @@ -688,17 +688,21 @@ megasas_dcmd_ld_get_list(int cmd, int num, int max) "scmd %d: DCMD LD get list: | |||||||
| megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: DCMD LD get info for dev %d" | megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: DCMD LD get info for dev %d" | ||||||
| megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d" | megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d" | ||||||
| megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x" | megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x" | ||||||
|  | megasas_dcmd_ld_list_query(int cmd, int flags) "scmd %d: DCMD LD list query flags %x" | ||||||
| megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld" | megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld" | ||||||
| megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: aborting frame %x" | megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: aborting frame %x" | ||||||
| megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 "" | megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 "" | ||||||
| megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x" | megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x" | ||||||
| megasas_reset(void) "Reset" | megasas_reset(void) "Reset" | ||||||
| megasas_init(int sges, int cmds, const char *intr, const char *mode) "Using %d sges, %d cmds, %s, %s mode" | megasas_init(int sges, int cmds, const char *mode) "Using %d sges, %d cmds, %s mode" | ||||||
| megasas_msix_raise(int vector) "vector %d" | megasas_msix_raise(int vector) "vector %d" | ||||||
|  | megasas_msi_raise(int vector) "vector %d" | ||||||
| megasas_irq_lower(void) "INTx" | megasas_irq_lower(void) "INTx" | ||||||
| megasas_irq_raise(void) "INTx" | megasas_irq_raise(void) "INTx" | ||||||
| megasas_intr_enabled(void) "Interrupts enabled" | megasas_intr_enabled(void) "Interrupts enabled" | ||||||
| megasas_intr_disabled(void) "Interrupts disabled" | megasas_intr_disabled(void) "Interrupts disabled" | ||||||
|  | megasas_msix_enabled(int vector) "vector %d" | ||||||
|  | megasas_msi_enabled(int vector) "vector %d" | ||||||
| megasas_mmio_readl(unsigned long addr, uint32_t val) "addr 0x%lx: 0x%x" | megasas_mmio_readl(unsigned long addr, uint32_t val) "addr 0x%lx: 0x%x" | ||||||
| megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx" | megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx" | ||||||
| megasas_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x" | megasas_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x" | ||||||
| @@ -1046,7 +1050,7 @@ gd_update(int x, int y, int w, int h) "x=%d, y=%d, w=%d, h=%d" | |||||||
| gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)" | gd_key_event(int gdk_keycode, int qemu_keycode, const char *action) "translated GDK keycode %d to QEMU keycode %d (%s)" | ||||||
|  |  | ||||||
| # ui/input.c | # ui/input.c | ||||||
| input_event_key_number(int conidx, int number, bool down) "con %d, key number 0x%x, down %d" | input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d" | ||||||
| input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d" | input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d" | ||||||
| input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d" | input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d" | ||||||
| input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d" | input_event_rel(int conidx, const char *axis, int value) "con %d, axis %s, value %d" | ||||||
|   | |||||||
| @@ -288,8 +288,8 @@ static void curses_refresh(DisplayChangeListener *dcl) | |||||||
|                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true); |                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             qemu_input_event_send_key_number(NULL, keycode, true); |             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true); | ||||||
|             qemu_input_event_send_key_number(NULL, keycode, false); |             qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false); | ||||||
|  |  | ||||||
|             if (keycode & ALTGR) { |             if (keycode & ALTGR) { | ||||||
|                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false); |                 qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false); | ||||||
|   | |||||||
| @@ -13,6 +13,8 @@ static const int qcode_to_number[] = { | |||||||
|     [Q_KEY_CODE_CTRL] = 0x1d, |     [Q_KEY_CODE_CTRL] = 0x1d, | ||||||
|     [Q_KEY_CODE_CTRL_R] = 0x9d, |     [Q_KEY_CODE_CTRL_R] = 0x9d, | ||||||
|  |  | ||||||
|  |     [Q_KEY_CODE_META_L] = 0xdb, | ||||||
|  |     [Q_KEY_CODE_META_R] = 0xdc, | ||||||
|     [Q_KEY_CODE_MENU] = 0xdd, |     [Q_KEY_CODE_MENU] = 0xdd, | ||||||
|  |  | ||||||
|     [Q_KEY_CODE_ESC] = 0x01, |     [Q_KEY_CODE_ESC] = 0x01, | ||||||
| @@ -129,7 +131,7 @@ static const int qcode_to_number[] = { | |||||||
|     [Q_KEY_CODE_MAX] = 0, |     [Q_KEY_CODE_MAX] = 0, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static int number_to_qcode[0xff]; | static int number_to_qcode[0x100]; | ||||||
|  |  | ||||||
| int qemu_input_key_value_to_number(const KeyValue *value) | int qemu_input_key_value_to_number(const KeyValue *value) | ||||||
| { | { | ||||||
| @@ -141,7 +143,7 @@ int qemu_input_key_value_to_number(const KeyValue *value) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| int qemu_input_key_value_to_qcode(const KeyValue *value) | int qemu_input_key_number_to_qcode(uint8_t nr) | ||||||
| { | { | ||||||
|     static int first = true; |     static int first = true; | ||||||
|  |  | ||||||
| @@ -155,11 +157,16 @@ int qemu_input_key_value_to_qcode(const KeyValue *value) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     return number_to_qcode[nr]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int qemu_input_key_value_to_qcode(const KeyValue *value) | ||||||
|  | { | ||||||
|     if (value->kind == KEY_VALUE_KIND_QCODE) { |     if (value->kind == KEY_VALUE_KIND_QCODE) { | ||||||
|         return value->qcode; |         return value->qcode; | ||||||
|     } else { |     } else { | ||||||
|         assert(value->kind == KEY_VALUE_KIND_NUMBER); |         assert(value->kind == KEY_VALUE_KIND_NUMBER); | ||||||
|         return number_to_qcode[value->number]; |         return qemu_input_key_number_to_qcode(value->number); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								ui/input.c
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								ui/input.c
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | |||||||
|  | #include "hw/qdev.h" | ||||||
| #include "sysemu/sysemu.h" | #include "sysemu/sysemu.h" | ||||||
| #include "qapi-types.h" | #include "qapi-types.h" | ||||||
| #include "qmp-commands.h" | #include "qmp-commands.h" | ||||||
| @@ -10,6 +11,7 @@ struct QemuInputHandlerState { | |||||||
|     QemuInputHandler  *handler; |     QemuInputHandler  *handler; | ||||||
|     int               id; |     int               id; | ||||||
|     int               events; |     int               events; | ||||||
|  |     QemuConsole       *con; | ||||||
|     QTAILQ_ENTRY(QemuInputHandlerState) node; |     QTAILQ_ENTRY(QemuInputHandlerState) node; | ||||||
| }; | }; | ||||||
| static QTAILQ_HEAD(, QemuInputHandlerState) handlers = | static QTAILQ_HEAD(, QemuInputHandlerState) handlers = | ||||||
| @@ -53,12 +55,46 @@ void qemu_input_handler_unregister(QemuInputHandlerState *s) | |||||||
|     qemu_input_check_mode_change(); |     qemu_input_check_mode_change(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void qemu_input_handler_bind(QemuInputHandlerState *s, | ||||||
|  |                              const char *device_id, int head, | ||||||
|  |                              Error **errp) | ||||||
|  | { | ||||||
|  |     DeviceState *dev; | ||||||
|  |     QemuConsole *con; | ||||||
|  |  | ||||||
|  |     dev = qdev_find_recursive(sysbus_get_default(), device_id); | ||||||
|  |     if (dev == NULL) { | ||||||
|  |         error_set(errp, QERR_DEVICE_NOT_FOUND, device_id); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     con = qemu_console_lookup_by_device(dev, head); | ||||||
|  |     if (con == NULL) { | ||||||
|  |         error_setg(errp, "Device %s is not bound to a QemuConsole", device_id); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     s->con = con; | ||||||
|  | } | ||||||
|  |  | ||||||
| static QemuInputHandlerState* | static QemuInputHandlerState* | ||||||
| qemu_input_find_handler(uint32_t mask) | qemu_input_find_handler(uint32_t mask, QemuConsole *con) | ||||||
| { | { | ||||||
|     QemuInputHandlerState *s; |     QemuInputHandlerState *s; | ||||||
|  |  | ||||||
|     QTAILQ_FOREACH(s, &handlers, node) { |     QTAILQ_FOREACH(s, &handlers, node) { | ||||||
|  |         if (s->con == NULL || s->con != con) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (mask & s->handler->mask) { | ||||||
|  |             return s; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     QTAILQ_FOREACH(s, &handlers, node) { | ||||||
|  |         if (s->con != NULL) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|         if (mask & s->handler->mask) { |         if (mask & s->handler->mask) { | ||||||
|             return s; |             return s; | ||||||
|         } |         } | ||||||
| @@ -94,7 +130,7 @@ static void qemu_input_transform_abs_rotate(InputEvent *evt) | |||||||
| static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) | static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) | ||||||
| { | { | ||||||
|     const char *name; |     const char *name; | ||||||
|     int idx = -1; |     int qcode, idx = -1; | ||||||
|  |  | ||||||
|     if (src) { |     if (src) { | ||||||
|         idx = qemu_console_get_index(src); |         idx = qemu_console_get_index(src); | ||||||
| @@ -103,8 +139,10 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) | |||||||
|     case INPUT_EVENT_KIND_KEY: |     case INPUT_EVENT_KIND_KEY: | ||||||
|         switch (evt->key->key->kind) { |         switch (evt->key->key->kind) { | ||||||
|         case KEY_VALUE_KIND_NUMBER: |         case KEY_VALUE_KIND_NUMBER: | ||||||
|  |             qcode = qemu_input_key_number_to_qcode(evt->key->key->number); | ||||||
|  |             name = QKeyCode_lookup[qcode]; | ||||||
|             trace_input_event_key_number(idx, evt->key->key->number, |             trace_input_event_key_number(idx, evt->key->key->number, | ||||||
|                                          evt->key->down); |                                          name, evt->key->down); | ||||||
|             break; |             break; | ||||||
|         case KEY_VALUE_KIND_QCODE: |         case KEY_VALUE_KIND_QCODE: | ||||||
|             name = QKeyCode_lookup[evt->key->key->qcode]; |             name = QKeyCode_lookup[evt->key->key->qcode]; | ||||||
| @@ -149,7 +187,7 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt) | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /* send event */ |     /* send event */ | ||||||
|     s = qemu_input_find_handler(1 << evt->kind); |     s = qemu_input_find_handler(1 << evt->kind, src); | ||||||
|     if (!s) { |     if (!s) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -250,7 +288,8 @@ bool qemu_input_is_absolute(void) | |||||||
| { | { | ||||||
|     QemuInputHandlerState *s; |     QemuInputHandlerState *s; | ||||||
|  |  | ||||||
|     s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS); |     s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS, | ||||||
|  |                                 NULL); | ||||||
|     return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS); |     return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								ui/sdl2.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								ui/sdl2.c
									
									
									
									
									
								
							| @@ -190,30 +190,33 @@ static void sdl_switch(DisplayChangeListener *dcl, | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void reset_keys(void) | static void reset_keys(struct sdl2_state *scon) | ||||||
| { | { | ||||||
|  |     QemuConsole *con = scon ? scon->dcl.con : NULL; | ||||||
|     int i; |     int i; | ||||||
|  |  | ||||||
|     for (i = 0; i < 256; i++) { |     for (i = 0; i < 256; i++) { | ||||||
|         if (modifiers_state[i]) { |         if (modifiers_state[i]) { | ||||||
|             int qcode = sdl2_scancode_to_qcode[i]; |             int qcode = sdl2_scancode_to_qcode[i]; | ||||||
|             qemu_input_event_send_key_qcode(NULL, qcode, false); |             qemu_input_event_send_key_qcode(con, qcode, false); | ||||||
|             modifiers_state[i] = 0; |             modifiers_state[i] = 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| static void sdl_process_key(SDL_KeyboardEvent *ev) | static void sdl_process_key(struct sdl2_state *scon, | ||||||
|  |                             SDL_KeyboardEvent *ev) | ||||||
| { | { | ||||||
|     int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; |     int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; | ||||||
|  |     QemuConsole *con = scon ? scon->dcl.con : NULL; | ||||||
|  |  | ||||||
|     switch (ev->keysym.scancode) { |     switch (ev->keysym.scancode) { | ||||||
| #if 0 | #if 0 | ||||||
|     case SDL_SCANCODE_NUMLOCKCLEAR: |     case SDL_SCANCODE_NUMLOCKCLEAR: | ||||||
|     case SDL_SCANCODE_CAPSLOCK: |     case SDL_SCANCODE_CAPSLOCK: | ||||||
|         /* SDL does not send the key up event, so we generate it */ |         /* SDL does not send the key up event, so we generate it */ | ||||||
|         qemu_input_event_send_key_qcode(NULL, qcode, true); |         qemu_input_event_send_key_qcode(con, qcode, true); | ||||||
|         qemu_input_event_send_key_qcode(NULL, qcode, false); |         qemu_input_event_send_key_qcode(con, qcode, false); | ||||||
|         return; |         return; | ||||||
| #endif | #endif | ||||||
|     case SDL_SCANCODE_LCTRL: |     case SDL_SCANCODE_LCTRL: | ||||||
| @@ -231,7 +234,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev) | |||||||
|         } |         } | ||||||
|         /* fall though */ |         /* fall though */ | ||||||
|     default: |     default: | ||||||
|         qemu_input_event_send_key_qcode(NULL, qcode, |         qemu_input_event_send_key_qcode(con, qcode, | ||||||
|                                         ev->type == SDL_KEYDOWN); |                                         ev->type == SDL_KEYDOWN); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -506,7 +509,7 @@ static void handle_keydown(SDL_Event *ev) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if (!gui_keysym) { |     if (!gui_keysym) { | ||||||
|         sdl_process_key(&ev->key); |         sdl_process_key(scon, &ev->key); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -531,13 +534,13 @@ static void handle_keyup(SDL_Event *ev) | |||||||
|             } |             } | ||||||
|             /* SDL does not send back all the modifiers key, so we must |             /* SDL does not send back all the modifiers key, so we must | ||||||
|              * correct it. */ |              * correct it. */ | ||||||
|             reset_keys(); |             reset_keys(scon); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         gui_keysym = 0; |         gui_keysym = 0; | ||||||
|     } |     } | ||||||
|     if (!gui_keysym) { |     if (!gui_keysym) { | ||||||
|         sdl_process_key(&ev->key); |         sdl_process_key(scon, &ev->key); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -142,11 +142,6 @@ Error *error_copy(const Error *err) | |||||||
|     return err_new; |     return err_new; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool error_is_set(Error **errp) |  | ||||||
| { |  | ||||||
|     return (errp && *errp); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ErrorClass error_get_class(const Error *err) | ErrorClass error_get_class(const Error *err) | ||||||
| { | { | ||||||
|     return err->err_class; |     return err->err_class; | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								util/iov.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								util/iov.c
									
									
									
									
									
								
							| @@ -335,6 +335,27 @@ void qemu_iovec_concat(QEMUIOVector *dst, | |||||||
|     qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes); |     qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Check if the contents of the iovecs are all zero | ||||||
|  |  */ | ||||||
|  | bool qemu_iovec_is_zero(QEMUIOVector *qiov) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  |     for (i = 0; i < qiov->niov; i++) { | ||||||
|  |         size_t offs = QEMU_ALIGN_DOWN(qiov->iov[i].iov_len, 4 * sizeof(long)); | ||||||
|  |         uint8_t *ptr = qiov->iov[i].iov_base; | ||||||
|  |         if (offs && !buffer_is_zero(qiov->iov[i].iov_base, offs)) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         for (; offs < qiov->iov[i].iov_len; offs++) { | ||||||
|  |             if (ptr[offs]) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| void qemu_iovec_destroy(QEMUIOVector *qiov) | void qemu_iovec_destroy(QEMUIOVector *qiov) | ||||||
| { | { | ||||||
|     assert(qiov->nalloc != -1); |     assert(qiov->nalloc != -1); | ||||||
|   | |||||||
| @@ -354,6 +354,7 @@ static struct addrinfo *inet_parse_connect_opts(QemuOpts *opts, Error **errp) | |||||||
| int inet_connect_opts(QemuOpts *opts, Error **errp, | int inet_connect_opts(QemuOpts *opts, Error **errp, | ||||||
|                       NonBlockingConnectHandler *callback, void *opaque) |                       NonBlockingConnectHandler *callback, void *opaque) | ||||||
| { | { | ||||||
|  |     Error *local_err = NULL; | ||||||
|     struct addrinfo *res, *e; |     struct addrinfo *res, *e; | ||||||
|     int sock = -1; |     int sock = -1; | ||||||
|     bool in_progress; |     bool in_progress; | ||||||
| @@ -372,24 +373,27 @@ int inet_connect_opts(QemuOpts *opts, Error **errp, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (e = res; e != NULL; e = e->ai_next) { |     for (e = res; e != NULL; e = e->ai_next) { | ||||||
|         if (error_is_set(errp)) { |         error_free(local_err); | ||||||
|             error_free(*errp); |         local_err = NULL; | ||||||
|             *errp = NULL; |  | ||||||
|         } |  | ||||||
|         if (connect_state != NULL) { |         if (connect_state != NULL) { | ||||||
|             connect_state->current_addr = e; |             connect_state->current_addr = e; | ||||||
|         } |         } | ||||||
|         sock = inet_connect_addr(e, &in_progress, connect_state, errp); |         sock = inet_connect_addr(e, &in_progress, connect_state, &local_err); | ||||||
|         if (in_progress) { |         if (sock >= 0) { | ||||||
|             return sock; |  | ||||||
|         } else if (sock >= 0) { |  | ||||||
|             /* non blocking socket immediate success, call callback */ |  | ||||||
|             if (callback != NULL) { |  | ||||||
|                 callback(sock, opaque); |  | ||||||
|             } |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (sock < 0) { | ||||||
|  |         error_propagate(errp, local_err); | ||||||
|  |     } else if (in_progress) { | ||||||
|  |         /* wait_for_connect() will do the rest */ | ||||||
|  |         return sock; | ||||||
|  |     } else { | ||||||
|  |         if (callback) { | ||||||
|  |             callback(sock, opaque); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     g_free(connect_state); |     g_free(connect_state); | ||||||
|     freeaddrinfo(res); |     freeaddrinfo(res); | ||||||
|     return sock; |     return sock; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user