diff options
author | Avi Kivity <avi@redhat.com> | 2009-12-07 10:57:57 +0200 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2009-12-07 10:57:57 +0200 |
commit | 733bd8964598df596b362ee529200ea3694b1b3a (patch) | |
tree | cd562dae077e59a5290432c210ea6925bde4c9d6 | |
parent | Merge commit '5fa737a4792c0aef9cf0588242336eefb0cb8ca8' into upstream-merge (diff) | |
parent | Introduce rerror option for drives (diff) | |
download | qemu-kvm-733bd8964598df596b362ee529200ea3694b1b3a.tar.gz qemu-kvm-733bd8964598df596b362ee529200ea3694b1b3a.tar.bz2 qemu-kvm-733bd8964598df596b362ee529200ea3694b1b3a.zip |
Merge commit 'e9b2e81889d9877415710484b876ee57a42b0bcb' into upstream-merge
* commit 'e9b2e81889d9877415710484b876ee57a42b0bcb': (140 commits)
Introduce rerror option for drives
Rename DriveInfo.onerror to on_write_error
ram migration: Properly reset statistics
qemu-opts: Release id on deletion
live migration: Serialize vmstate saving in stage 2
block migration: Skip zero-sized disks
block migration: Increase dirty chunk size to 1M
block migration: Add support for restore progress reporting
block migration: Report progress also via info migration
block migration: Fix outgoing progress output
live migration: Propagate output monitor to callback handler
block migration: Report overall migration progress
live migration: Allow cleanup after cancellation or error
ram migration: Stop loading on error
block migration: Add error handling/propagation
block migration: Consolidate block transmission
block migration: Consolidate mig_read_device_bulk into mig_save_device_bulk
block migration: Clean up use of total_sectors
block migration: Initialize remaining BlkMigState fields
block migration: Switch device and block lists to QSIMPLEQ
...
Conflicts:
vl.c
Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | QMP/README | 51 | ||||
-rw-r--r-- | QMP/qmp-events.txt | 26 | ||||
-rwxr-xr-x | QMP/qmp-shell | 72 | ||||
-rw-r--r-- | QMP/qmp-spec.txt | 192 | ||||
-rw-r--r-- | QMP/qmp.py | 72 | ||||
-rwxr-xr-x | QMP/vm-info | 32 | ||||
-rw-r--r-- | audio/audio.c | 26 | ||||
-rw-r--r-- | audio/audio_template.h | 6 | ||||
-rw-r--r-- | block-migration.c | 742 | ||||
-rw-r--r-- | block-migration.h | 16 | ||||
-rw-r--r-- | block.c | 127 | ||||
-rw-r--r-- | block.h | 11 | ||||
-rw-r--r-- | block_int.h | 3 | ||||
-rw-r--r-- | hw/ac97.c | 113 | ||||
-rw-r--r-- | hw/cirrus_vga.c | 2 | ||||
-rw-r--r-- | hw/cs4231a.c | 58 | ||||
-rw-r--r-- | hw/dp8393x.c | 41 | ||||
-rw-r--r-- | hw/e1000.c | 51 | ||||
-rw-r--r-- | hw/eepro100.c | 43 | ||||
-rw-r--r-- | hw/es1370.c | 77 | ||||
-rw-r--r-- | hw/esp.h | 5 | ||||
-rw-r--r-- | hw/etraxfs_eth.c | 45 | ||||
-rw-r--r-- | hw/gus.c | 47 | ||||
-rw-r--r-- | hw/hw.h | 85 | ||||
-rw-r--r-- | hw/ide/core.c | 2 | ||||
-rw-r--r-- | hw/lan9118.c | 45 | ||||
-rw-r--r-- | hw/lance.c | 29 | ||||
-rw-r--r-- | hw/lm832x.c | 2 | ||||
-rw-r--r-- | hw/lsi53c895a.c | 68 | ||||
-rw-r--r-- | hw/max7310.c | 2 | ||||
-rw-r--r-- | hw/mcf_fec.c | 58 | ||||
-rw-r--r-- | hw/mipsnet.c | 45 | ||||
-rw-r--r-- | hw/musicpal.c | 31 | ||||
-rw-r--r-- | hw/ne2000-isa.c | 34 | ||||
-rw-r--r-- | hw/ne2000.c | 38 | ||||
-rw-r--r-- | hw/ne2000.h | 2 | ||||
-rw-r--r-- | hw/pci.c | 4 | ||||
-rw-r--r-- | hw/pckbd.c | 13 | ||||
-rw-r--r-- | hw/pcnet.c | 47 | ||||
-rw-r--r-- | hw/pcnet.h | 7 | ||||
-rw-r--r-- | hw/piix_pci.c | 4 | ||||
-rw-r--r-- | hw/qdev.c | 2 | ||||
-rw-r--r-- | hw/qdev.h | 10 | ||||
-rw-r--r-- | hw/rtl8139.c | 47 | ||||
-rw-r--r-- | hw/sb16.c | 168 | ||||
-rw-r--r-- | hw/scsi-bus.c | 386 | ||||
-rw-r--r-- | hw/scsi-defs.h | 162 | ||||
-rw-r--r-- | hw/scsi-disk.c | 1248 | ||||
-rw-r--r-- | hw/scsi-disk.h | 67 | ||||
-rw-r--r-- | hw/scsi-generic.c | 425 | ||||
-rw-r--r-- | hw/scsi.h | 50 | ||||
-rw-r--r-- | hw/smc91c111.c | 35 | ||||
-rw-r--r-- | hw/ssd0303.c | 2 | ||||
-rw-r--r-- | hw/stellaris_enet.c | 34 | ||||
-rw-r--r-- | hw/tmp105.c | 2 | ||||
-rw-r--r-- | hw/twl92230.c | 2 | ||||
-rw-r--r-- | hw/usb-net.c | 57 | ||||
-rw-r--r-- | hw/usb-uhci.c | 3 | ||||
-rw-r--r-- | hw/vga-pci.c | 2 | ||||
-rw-r--r-- | hw/virtio-blk.c | 2 | ||||
-rw-r--r-- | hw/virtio-net.c | 73 | ||||
-rw-r--r-- | hw/vmware_vga.c | 2 | ||||
-rw-r--r-- | hw/wdt_i6300esb.c | 3 | ||||
-rw-r--r-- | hw/wdt_ib700.c | 2 | ||||
-rw-r--r-- | hw/wm8750.c | 2 | ||||
-rw-r--r-- | hw/xen_nic.c | 46 | ||||
-rw-r--r-- | hw/xilinx_ethlite.c | 35 | ||||
-rw-r--r-- | migration-exec.c | 12 | ||||
-rw-r--r-- | migration-fd.c | 9 | ||||
-rw-r--r-- | migration-tcp.c | 10 | ||||
-rw-r--r-- | migration-unix.c | 10 | ||||
-rw-r--r-- | migration.c | 52 | ||||
-rw-r--r-- | migration.h | 13 | ||||
-rw-r--r-- | monitor.c | 100 | ||||
-rw-r--r-- | monitor.h | 11 | ||||
-rw-r--r-- | net.c | 1725 | ||||
-rw-r--r-- | net.h | 50 | ||||
-rw-r--r-- | net/dump.c | 158 | ||||
-rw-r--r-- | net/dump.h | 33 | ||||
-rw-r--r-- | net/slirp.c | 765 | ||||
-rw-r--r-- | net/slirp.h | 51 | ||||
-rw-r--r-- | net/socket.c | 577 | ||||
-rw-r--r-- | net/socket.h | 33 | ||||
-rw-r--r-- | net/tap-linux.c | 7 | ||||
-rw-r--r-- | net/tap-solaris.c | 11 | ||||
-rw-r--r-- | net/tap-win32.c | 39 | ||||
-rw-r--r-- | net/tap.c | 84 | ||||
-rw-r--r-- | net/util.c | 60 | ||||
-rw-r--r-- | net/util.h | 32 | ||||
-rw-r--r-- | net/vde.c | 135 | ||||
-rw-r--r-- | net/vde.h | 36 | ||||
-rw-r--r-- | osdep.h | 10 | ||||
-rw-r--r-- | qemu-config.c | 3 | ||||
-rw-r--r-- | qemu-option.c | 3 | ||||
-rw-r--r-- | qemu-queue.h | 109 | ||||
-rw-r--r-- | qemu-tool.c | 4 | ||||
-rw-r--r-- | savevm.c | 124 | ||||
-rw-r--r-- | sysemu.h | 16 | ||||
-rw-r--r-- | vl.c | 87 |
100 files changed, 5614 insertions, 4002 deletions
@@ -112,13 +112,17 @@ block-nested-$(CONFIG_CURL) += curl.o block-obj-y += $(addprefix block/, $(block-nested-y)) net-obj-y = net.o -net-nested-y = queue.o checksum.o +net-nested-y = queue.o checksum.o util.o +net-nested-y += socket.o +net-nested-y += dump.o net-nested-$(CONFIG_POSIX) += tap.o net-nested-$(CONFIG_LINUX) += tap-linux.o net-nested-$(CONFIG_WIN32) += tap-win32.o net-nested-$(CONFIG_BSD) += tap-bsd.o net-nested-$(CONFIG_SOLARIS) += tap-solaris.o net-nested-$(CONFIG_AIX) += tap-aix.o +net-nested-$(CONFIG_SLIRP) += slirp.o +net-nested-$(CONFIG_VDE) += vde.o net-obj-y += $(addprefix net/, $(net-nested-y)) ###################################################################### diff --git a/QMP/README b/QMP/README new file mode 100644 index 000000000..50c31f20c --- /dev/null +++ b/QMP/README @@ -0,0 +1,51 @@ + QEMU Monitor Protocol + ===================== + +Introduction +------------- + +The QEMU Monitor Protocol (QMP) is a JSON[1] based protocol for QEMU. + +By using it applications can control QEMU in reliable and "parseable" way, +QMP also provides asynchronous events support. + +For more information, please, refer to the following files: + +o qmp-spec.txt QEMU Monitor Protocol current draft specification +o qmp-events.txt List of available asynchronous events + +There are also two simple Python scripts available: + +o qmp-shell A shell +o vm-info Show some informations about the Virtal Machine + +[1] http://www.json.org + +Usage +----- + +To enable QMP, QEMU has to be started in "control mode". This is done +by passing the flag "control" to the "-monitor" command-line option. + +For example: + +$ qemu [...] -monitor control,tcp:localhost:4444,server + +Will start QEMU in control mode, waiting for a client TCP connection +on localhost port 4444. + +To manually test it you can connect with telnet and issue commands: + +$ telnet localhost 4444 +Trying ::1... +Connected to localhost. +Escape character is '^]'. +{"QMP": {"capabilities": []}} +{ "execute": "query-version" } +{"return": "0.11.50"} + +Contact +------- + +http://www.linux-kvm.org/page/MonitorProtocol +Luiz Fernando N. Capitulino <lcapitulino@redhat.com> diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt new file mode 100644 index 000000000..682a5e53a --- /dev/null +++ b/QMP/qmp-events.txt @@ -0,0 +1,26 @@ + QEMU Monitor Protocol: Events + ============================= + +1 SHUTDOWN +----------- + +Description: Issued when the Virtual Machine is powered down. +Data: None. + +2 RESET +------- + +Description: Issued when the Virtual Machine is reseted. +Data: None. + +3 STOP +------ + +Description: Issued when the Virtual Machine is stopped. +Data: None. + +4 DEBUG +------- + +Description: Issued when the Virtual Machine enters debug mode. +Data: None. diff --git a/QMP/qmp-shell b/QMP/qmp-shell new file mode 100755 index 000000000..f89b9af87 --- /dev/null +++ b/QMP/qmp-shell @@ -0,0 +1,72 @@ +#!/usr/bin/python +# +# Simple QEMU shell on top of QMP +# +# Copyright (C) 2009 Red Hat Inc. +# +# Authors: +# Luiz Capitulino <lcapitulino@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Usage: +# +# Start QEMU with: +# +# $ qemu [...] -monitor control,unix:./qmp,server +# +# Run the shell: +# +# $ qmp-shell ./qmp +# +# Commands have the following format: +# +# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] +# +# For example: +# +# (QEMU) info item=network + +import qmp +import readline +from sys import argv,exit + +def shell_help(): + print 'bye exit from the shell' + +def main(): + if len(argv) != 2: + print 'qemu-shell <unix-socket>' + exit(1) + + qemu = qmp.QEMUMonitorProtocol(argv[1]) + qemu.connect() + + print 'Connected!' + + while True: + try: + cmd = raw_input('(QEMU) ') + except EOFError: + print + break + if cmd == '': + continue + elif cmd == 'bye': + break + elif cmd == 'help': + shell_help() + else: + try: + resp = qemu.send(cmd) + if resp == None: + print 'Disconnected' + break + print resp + except IndexError: + print '-> command format: <command-name> ', + print '[arg-name1=arg1] ... [arg-nameN=argN]' + +if __name__ == '__main__': + main() diff --git a/QMP/qmp-spec.txt b/QMP/qmp-spec.txt new file mode 100644 index 000000000..8429789a9 --- /dev/null +++ b/QMP/qmp-spec.txt @@ -0,0 +1,192 @@ + QEMU Monitor Protocol Draft Specification - Version 0.1 + +1. Introduction +=============== + +This document specifies the QEMU Monitor Protocol (QMP), a JSON-based protocol +which is available for applications to control QEMU at the machine-level. + +To enable QMP support, QEMU has to be run in "control mode". This is done by +starting QEMU with the appropriate command-line options. Please, refer to the +QEMU manual page for more information. + +2. Protocol Specification +========================= + +This section details the protocol format. For the purpose of this document +"Client" is any application which is communicating with QEMU in control mode, +and "Server" is QEMU itself. + +JSON data structures, when mentioned in this document, are always in the +following format: + + json-DATA-STRUCTURE-NAME + +Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by +the JSON standard: + +http://www.ietf.org/rfc/rfc4627.txt + +For convenience, json-objects mentioned in this document will have its members +in a certain order. However, in real protocol usage json-objects members can +be in ANY order, thus no particular order should be assumed. + +2.1 General Definitions +----------------------- + +2.1.1 All interactions transmitted by the Server are json-objects, always + terminating with CRLF + +2.1.2 All json-objects members are mandatory when not specified otherwise + +2.2 Server Greeting +------------------- + +Right when connected the Server will issue a greeting message, which signals +that the connection has been successfully established and that the Server is +waiting for commands. + +The format is: + +{ "QMP": { "capabilities": json-array } } + + Where, + +- The "capabilities" member specify the availability of features beyond the + baseline specification + +2.3 Issuing Commands +-------------------- + +The format for command execution is: + +{ "execute": json-string, "arguments": json-object, "id": json-value } + + Where, + +- The "execute" member identifies the command to be executed by the Server +- The "arguments" member is used to pass any arguments required for the + execution of the command, it is optional when no arguments are required +- The "id" member is a transaction identification associated with the + command execution, it is optional and will be part of the response if + provided + +2.4 Commands Responses +---------------------- + +There are two possible responses which the Server will issue as the result +of a command execution: success or error. + +2.4.1 success +------------- + +The success response is issued when the command execution has finished +without errors. + +The format is: + +{ "return": json-value, "id": json-value } + + Where, + +- The "return" member contains the command returned data, which is defined + in a per-command basis or "OK" if the command does not return data +- The "id" member contains the transaction identification associated + with the command execution (if issued by the Client) + +2.4.2 error +----------- + +The error response is issued when the command execution could not be +completed because of an error condition. + +The format is: + +{ "error": { "class": json-string, "data": json-value }, "id": json-value } + + Where, + +- The "class" member contains the error class name (eg. "ServiceUnavailable") +- The "data" member contains specific error data and is defined in a + per-command basis, it will be an empty json-object if the error has no data +- The "id" member contains the transaction identification associated with + the command execution (if issued by the Client) + +NOTE: Some errors can occur before the Server is able to read the "id" member, +in these cases the "id" member will not be part of the error response, even +if provided by the client. + +2.5 Asynchronous events +----------------------- + +As a result of state changes, the Server may send messages unilaterally +to the Client at any time. They are called 'asynchronous events'. + +The format is: + +{ "event": json-string, "data": json-value, + "timestamp": { "seconds": json-number, "microseconds": json-number } } + + Where, + +- The "event" member contains the event's name +- The "data" member contains event specific data, which is defined in a + per-event basis, it is optional +- The "timestamp" member contains the exact time of when the event ocurred + in the Server. It is a fixed json-object with time in seconds and + microseconds + +For a listing of supported asynchronous events, please, refer to the +qmp-events.txt file. + +3. QMP Examples +=============== + +This section provides some examples of real QMP usage, in all of them +'C' stands for 'Client' and 'S' stands for 'Server'. + +3.1 Server greeting +------------------- + +S: {"QMP": {"capabilities": []}} + +3.2 Simple 'stop' execution +--------------------------- + +C: { "execute": "stop" } +S: {"return": "OK"} + +3.3 KVM information +------------------- + +C: {"execute": "query-kvm", "id": "example"} +S: {"return": "enabled", "id": "example"} + +3.4 Parsing error +------------------ + +C: { "execute": } +S: {"error": {"class": "JSONParsing", "data": {}}} + +3.5 Powerdown event +------------------- + +S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event": +"POWERDOWN"} + +4. Notes to Client implementors +------------------------------- + +4.1 It is recommended to always start the Server in pause mode, thus the + Client is able to perform any setup procedure without the risk of + race conditions and related problems + +4.2 It is recommended to always check the capabilities json-array, issued + with the greeting message, at connection time + +4.3 Json-objects or json-arrays mentioned in this document are not fixed + and no particular size or number of members/elements should be assumed. + New members/elements can be added at any time. + +4.4 No particular order of json-objects members should be assumed, they + can change at any time diff --git a/QMP/qmp.py b/QMP/qmp.py new file mode 100644 index 000000000..d9da603be --- /dev/null +++ b/QMP/qmp.py @@ -0,0 +1,72 @@ +# QEMU Monitor Protocol Python class +# +# Copyright (C) 2009 Red Hat Inc. +# +# Authors: +# Luiz Capitulino <lcapitulino@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. + +import socket, json + +class QMPError(Exception): + pass + +class QMPConnectError(QMPError): + pass + +class QEMUMonitorProtocol: + def connect(self): + self.sock.connect(self.filename) + data = self.__json_read() + if data == None: + raise QMPConnectError + if not data.has_key('QMP'): + raise QMPConnectError + return data['QMP']['capabilities'] + + def close(self): + self.sock.close() + + def send_raw(self, line): + self.sock.send(str(line)) + return self.__json_read() + + def send(self, cmdline): + cmd = self.__build_cmd(cmdline) + self.__json_send(cmd) + resp = self.__json_read() + if resp == None: + return + elif resp.has_key('error'): + return resp['error'] + else: + return resp['return'] + + def __build_cmd(self, cmdline): + cmdargs = cmdline.split() + qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } + for arg in cmdargs[1:]: + opt = arg.split('=') + try: + value = int(opt[1]) + except ValueError: + value = opt[1] + qmpcmd['arguments'][opt[0]] = value + return qmpcmd + + def __json_send(self, cmd): + # XXX: We have to send any additional char, otherwise + # the Server won't read our input + self.sock.send(json.dumps(cmd) + ' ') + + def __json_read(self): + try: + return json.loads(self.sock.recv(1024)) + except ValueError: + return + + def __init__(self, filename): + self.filename = filename + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) diff --git a/QMP/vm-info b/QMP/vm-info new file mode 100755 index 000000000..b150d8240 --- /dev/null +++ b/QMP/vm-info @@ -0,0 +1,32 @@ +#!/usr/bin/python +# +# Print Virtual Machine information +# +# Usage: +# +# Start QEMU with: +# +# $ qemu [...] -monitor control,unix:./qmp,server +# +# Run vm-info: +# +# $ vm-info ./qmp +# +# Luiz Capitulino <lcapitulino@redhat.com> + +import qmp +from sys import argv,exit + +def main(): + if len(argv) != 2: + print 'vm-info <unix-socket>' + exit(1) + + qemu = qmp.QEMUMonitorProtocol(argv[1]) + qemu.connect() + + for cmd in [ 'version', 'hpet', 'kvm', 'status', 'uuid', 'balloon' ]: + print cmd + ': ' + str(qemu.send('query-' + cmd)) + +if __name__ == '__main__': + main() diff --git a/audio/audio.c b/audio/audio.c index 80a717bd2..a5305c492 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1784,23 +1784,15 @@ static void audio_atexit (void) } } -static void audio_save (QEMUFile *f, void *opaque) -{ - (void) f; - (void) opaque; -} - -static int audio_load (QEMUFile *f, void *opaque, int version_id) -{ - (void) f; - (void) opaque; - - if (version_id != 1) { - return -EINVAL; +static const VMStateDescription vmstate_audio = { + .name = "audio", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_END_OF_LIST() } - - return 0; -} +}; static void audio_init (void) { @@ -1900,7 +1892,7 @@ static void audio_init (void) } QLIST_INIT (&s->card_head); - register_savevm ("audio", 0, 1, audio_save, audio_load, s); + vmstate_register (0, &vmstate_audio, s); } void AUD_register_card (const char *name, QEMUSoundCard *card) diff --git a/audio/audio_template.h b/audio/audio_template.h index 1a4707b2e..6b19848af 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -445,9 +445,9 @@ SW *glue (AUD_open_, TYPE) ( SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels); dolog ("New %s freq %d, bits %d, channels %d\n", name, - freq, - (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8, - nchannels); + as->freq, + (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8, + as->nchannels); #endif if (live) { diff --git a/block-migration.c b/block-migration.c index 09771eded..258a88afc 100644 --- a/block-migration.c +++ b/block-migration.c @@ -14,17 +14,16 @@ #include "qemu-common.h" #include "block_int.h" #include "hw/hw.h" +#include "qemu-queue.h" +#include "monitor.h" #include "block-migration.h" #include <assert.h> -#define SECTOR_BITS 9 -#define SECTOR_SIZE (1 << SECTOR_BITS) -#define SECTOR_MASK ~(SECTOR_SIZE - 1); - -#define BLOCK_SIZE (block_mig_state->sectors_per_block << SECTOR_BITS) +#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) #define BLK_MIG_FLAG_DEVICE_BLOCK 0x01 #define BLK_MIG_FLAG_EOS 0x02 +#define BLK_MIG_FLAG_PROGRESS 0x04 #define MAX_IS_ALLOCATED_SEARCH 65536 #define MAX_BLOCKS_READ 10000 @@ -34,13 +33,24 @@ //#define DEBUG_BLK_MIGRATION #ifdef DEBUG_BLK_MIGRATION -#define dprintf(fmt, ...) \ +#define dprintf(fmt, ...) \ do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define dprintf(fmt, ...) \ do { } while (0) #endif +typedef struct BlkMigDevState { + BlockDriverState *bs; + int bulk_completed; + int shared_base; + int64_t cur_sector; + int64_t completed_sectors; + int64_t total_sectors; + int64_t dirty; + QSIMPLEQ_ENTRY(BlkMigDevState) entry; +} BlkMigDevState; + typedef struct BlkMigBlock { uint8_t *buf; BlkMigDevState *bmds; @@ -49,509 +59,483 @@ typedef struct BlkMigBlock { QEMUIOVector qiov; BlockDriverAIOCB *aiocb; int ret; - struct BlkMigBlock *next; + QSIMPLEQ_ENTRY(BlkMigBlock) entry; } BlkMigBlock; typedef struct BlkMigState { - int bulk_completed; int blk_enable; int shared_base; - int no_dirty; - QEMUFile *load_file; - BlkMigDevState *bmds_first; - int sectors_per_block; - BlkMigBlock *first_blk; - BlkMigBlock *last_blk; + QSIMPLEQ_HEAD(bmds_list, BlkMigDevState) bmds_list; + QSIMPLEQ_HEAD(blk_list, BlkMigBlock) blk_list; int submitted; int read_done; int transferred; - int64_t print_completion; + int64_t total_sector_sum; + int prev_progress; } BlkMigState; -static BlkMigState *block_mig_state = NULL; +static BlkMigState block_mig_state; + +static void blk_send(QEMUFile *f, BlkMigBlock * blk) +{ + int len; + + /* sector number and flags */ + qemu_put_be64(f, (blk->sector << BDRV_SECTOR_BITS) + | BLK_MIG_FLAG_DEVICE_BLOCK); + + /* device name */ + len = strlen(blk->bmds->bs->device_name); + qemu_put_byte(f, len); + qemu_put_buffer(f, (uint8_t *)blk->bmds->bs->device_name, len); + + qemu_put_buffer(f, blk->buf, BLOCK_SIZE); +} + +int blk_mig_active(void) +{ + return !QSIMPLEQ_EMPTY(&block_mig_state.bmds_list); +} + +uint64_t blk_mig_bytes_transferred(void) +{ + BlkMigDevState *bmds; + uint64_t sum = 0; + + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + sum += bmds->completed_sectors; + } + return sum << BDRV_SECTOR_BITS; +} + +uint64_t blk_mig_bytes_remaining(void) +{ + return blk_mig_bytes_total() - blk_mig_bytes_transferred(); +} + +uint64_t blk_mig_bytes_total(void) +{ + BlkMigDevState *bmds; + uint64_t sum = 0; + + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + sum += bmds->total_sectors; + } + return sum << BDRV_SECTOR_BITS; +} static void blk_mig_read_cb(void *opaque, int ret) { BlkMigBlock *blk = opaque; - + blk->ret = ret; - - /* insert at the end */ - if(block_mig_state->last_blk == NULL) { - block_mig_state->first_blk = blk; - block_mig_state->last_blk = blk; - } else { - block_mig_state->last_blk->next = blk; - block_mig_state->last_blk = blk; - } - - block_mig_state->submitted--; - block_mig_state->read_done++; - assert(block_mig_state->submitted >= 0); - - return; + + QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); + + block_mig_state.submitted--; + block_mig_state.read_done++; + assert(block_mig_state.submitted >= 0); } -static int mig_read_device_bulk(QEMUFile *f, BlkMigDevState *bms) -{ - int nr_sectors; - int64_t total_sectors, cur_sector = 0; - BlockDriverState *bs = bms->bs; +static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, + BlkMigDevState *bmds, int is_async) +{ + int64_t total_sectors = bmds->total_sectors; + int64_t cur_sector = bmds->cur_sector; + BlockDriverState *bs = bmds->bs; BlkMigBlock *blk; - - blk = qemu_malloc(sizeof(BlkMigBlock)); - blk->buf = qemu_malloc(BLOCK_SIZE); - - cur_sector = bms->cur_sector; - total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; - - if(bms->shared_base) { - while(cur_sector < bms->total_sectors && - !bdrv_is_allocated(bms->bs, cur_sector, - MAX_IS_ALLOCATED_SEARCH, &nr_sectors)) { + int nr_sectors; + + if (bmds->shared_base) { + while (cur_sector < total_sectors && + !bdrv_is_allocated(bs, cur_sector, MAX_IS_ALLOCATED_SEARCH, + &nr_sectors)) { cur_sector += nr_sectors; } } - - if(cur_sector >= total_sectors) { - bms->cur_sector = total_sectors; - qemu_free(blk->buf); - qemu_free(blk); + + if (cur_sector >= total_sectors) { + bmds->cur_sector = bmds->completed_sectors = total_sectors; return 1; } - - if(cur_sector >= block_mig_state->print_completion) { - printf("Completed %" PRId64 " %%\r", cur_sector * 100 / total_sectors); - fflush(stdout); - block_mig_state->print_completion += - (block_mig_state->sectors_per_block * 10000); - } - - /* we going to transfder BLOCK_SIZE any way even if it is not allocated */ - nr_sectors = block_mig_state->sectors_per_block; - - cur_sector &= ~((int64_t)block_mig_state->sectors_per_block -1); - - if(total_sectors - cur_sector < block_mig_state->sectors_per_block) { - nr_sectors = (total_sectors - cur_sector); - } - - bms->cur_sector = cur_sector + nr_sectors; - blk->sector = cur_sector; - blk->bmds = bms; - blk->next = NULL; - - blk->iov.iov_base = blk->buf; - blk->iov.iov_len = nr_sectors * SECTOR_SIZE; - qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); - - blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, - nr_sectors, blk_mig_read_cb, blk); - - if(!blk->aiocb) { - printf("Error reading sector %" PRId64 "\n", cur_sector); - qemu_free(blk->buf); - qemu_free(blk); - return 0; + + bmds->completed_sectors = cur_sector; + + cur_sector &= ~((int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK - 1); + + /* we are going to transfer a full block even if it is not allocated */ + nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; + + if (total_sectors - cur_sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { + nr_sectors = total_sectors - cur_sector; } - bdrv_reset_dirty(bms->bs, cur_sector, nr_sectors); - block_mig_state->submitted++; - - return (bms->cur_sector >= total_sectors); -} + blk = qemu_malloc(sizeof(BlkMigBlock)); + blk->buf = qemu_malloc(BLOCK_SIZE); + blk->bmds = bmds; + blk->sector = cur_sector; -static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) -{ - int len, nr_sectors; - int64_t total_sectors = bmds->total_sectors, cur_sector = 0; - uint8_t *tmp_buf = NULL; - BlockDriverState *bs = bmds->bs; + if (is_async) { + blk->iov.iov_base = blk->buf; + blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); - tmp_buf = qemu_malloc(BLOCK_SIZE); - - cur_sector = bmds->cur_sector; - - if(bmds->shared_base) { - while(cur_sector < bmds->total_sectors && - !bdrv_is_allocated(bmds->bs, cur_sector, - MAX_IS_ALLOCATED_SEARCH, &nr_sectors)) { - cur_sector += nr_sectors; + blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, + nr_sectors, blk_mig_read_cb, blk); + if (!blk->aiocb) { + goto error; } - } - - if(cur_sector >= total_sectors) { - bmds->cur_sector = total_sectors; - qemu_free(tmp_buf); - return 1; - } - - if(cur_sector >= block_mig_state->print_completion) { - printf("Completed %" PRId64 " %%\r", cur_sector * 100 / total_sectors); - fflush(stdout); - block_mig_state->print_completion += - (block_mig_state->sectors_per_block * 10000); - } - - cur_sector &= ~((int64_t)block_mig_state->sectors_per_block -1); - - /* we going to transfer - BLOCK_SIZE - any way even if it is not allocated */ - nr_sectors = block_mig_state->sectors_per_block; - - if(total_sectors - cur_sector < block_mig_state->sectors_per_block) { - nr_sectors = (total_sectors - cur_sector); - } - - if(bdrv_read(bs, cur_sector, tmp_buf, nr_sectors) < 0) { - printf("Error reading sector %" PRId64 "\n", cur_sector); + block_mig_state.submitted++; + } else { + if (bdrv_read(bs, cur_sector, blk->buf, nr_sectors) < 0) { + goto error; + } + blk_send(f, blk); + + qemu_free(blk->buf); + qemu_free(blk); } bdrv_reset_dirty(bs, cur_sector, nr_sectors); - - /* Device name */ - qemu_put_be64(f,(cur_sector << SECTOR_BITS) | BLK_MIG_FLAG_DEVICE_BLOCK); - - len = strlen(bs->device_name); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *)bs->device_name, len); - - qemu_put_buffer(f, tmp_buf, - BLOCK_SIZE); - - bmds->cur_sector = cur_sector + block_mig_state->sectors_per_block; - - qemu_free(tmp_buf); - - return (bmds->cur_sector >= total_sectors); -} + bmds->cur_sector = cur_sector + nr_sectors; -static void send_blk(QEMUFile *f, BlkMigBlock * blk) -{ - int len; - - /* Device name */ - qemu_put_be64(f,(blk->sector << SECTOR_BITS) | BLK_MIG_FLAG_DEVICE_BLOCK); - - len = strlen(blk->bmds->bs->device_name); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *)blk->bmds->bs->device_name, len); - - qemu_put_buffer(f, blk->buf, - BLOCK_SIZE); - - return; -} + return (bmds->cur_sector >= total_sectors); -static void blk_mig_save_dev_info(QEMUFile *f, BlkMigDevState *bmds) -{ +error: + monitor_printf(mon, "Error reading sector %" PRId64 "\n", cur_sector); + qemu_file_set_error(f); + qemu_free(blk->buf); + qemu_free(blk); + return 0; } static void set_dirty_tracking(int enable) { BlkMigDevState *bmds; - for(bmds = block_mig_state->bmds_first; bmds != NULL; bmds = bmds->next) { - bdrv_set_dirty_tracking(bmds->bs,enable); + + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + bdrv_set_dirty_tracking(bmds->bs, enable); } - - return; } -static void init_blk_migration(QEMUFile *f) +static void init_blk_migration(Monitor *mon, QEMUFile *f) { - BlkMigDevState **pbmds, *bmds; + BlkMigDevState *bmds; BlockDriverState *bs; - + int64_t sectors; + + block_mig_state.submitted = 0; + block_mig_state.read_done = 0; + block_mig_state.transferred = 0; + block_mig_state.total_sector_sum = 0; + block_mig_state.prev_progress = -1; + for (bs = bdrv_first; bs != NULL; bs = bs->next) { - if(bs->type == BDRV_TYPE_HD) { + if (bs->type == BDRV_TYPE_HD) { + sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; + if (sectors == 0) { + continue; + } + bmds = qemu_mallocz(sizeof(BlkMigDevState)); bmds->bs = bs; bmds->bulk_completed = 0; - bmds->total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; - bmds->shared_base = block_mig_state->shared_base; - - if(bmds->shared_base) { - printf("Start migration for %s with shared base image\n", - bs->device_name); + bmds->total_sectors = sectors; + bmds->completed_sectors = 0; + bmds->shared_base = block_mig_state.shared_base; + + block_mig_state.total_sector_sum += sectors; + + if (bmds->shared_base) { + monitor_printf(mon, "Start migration for %s with shared base " + "image\n", + bs->device_name); } else { - printf("Start full migration for %s\n", bs->device_name); + monitor_printf(mon, "Start full migration for %s\n", + bs->device_name); } - - /* insert at the end */ - pbmds = &block_mig_state->bmds_first; - while (*pbmds != NULL) - pbmds = &(*pbmds)->next; - *pbmds = bmds; - - blk_mig_save_dev_info(f, bmds); - + + QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); } - } - - block_mig_state->sectors_per_block = bdrv_get_sectors_per_chunk(); - - return; + } } -static int blk_mig_save_bulked_block(QEMUFile *f, int is_async) +static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f, int is_async) { + int64_t completed_sector_sum = 0; BlkMigDevState *bmds; - - for (bmds = block_mig_state->bmds_first; bmds != NULL; bmds = bmds->next) { - if(bmds->bulk_completed == 0) { - if(is_async) { - if(mig_read_device_bulk(f, bmds) == 1) { - /* completed bulk section for this device */ - bmds->bulk_completed = 1; - } - } else { - if(mig_save_device_bulk(f,bmds) == 1) { - /* completed bulk section for this device */ - bmds->bulk_completed = 1; - } + int progress; + int ret = 0; + + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + if (bmds->bulk_completed == 0) { + if (mig_save_device_bulk(mon, f, bmds, is_async) == 1) { + /* completed bulk section for this device */ + bmds->bulk_completed = 1; } - return 1; + completed_sector_sum += bmds->completed_sectors; + ret = 1; + break; + } else { + completed_sector_sum += bmds->completed_sectors; } } - - /* we reached here means bulk is completed */ - block_mig_state->bulk_completed = 1; - - return 0; - + + progress = completed_sector_sum * 100 / block_mig_state.total_sector_sum; + if (progress != block_mig_state.prev_progress) { + block_mig_state.prev_progress = progress; + qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) + | BLK_MIG_FLAG_PROGRESS); + monitor_printf(mon, "Completed %d %%\r", progress); + monitor_flush(mon); + } + + return ret; } #define MAX_NUM_BLOCKS 4 -static void blk_mig_save_dirty_blocks(QEMUFile *f) +static void blk_mig_save_dirty_blocks(Monitor *mon, QEMUFile *f) { BlkMigDevState *bmds; - uint8_t buf[BLOCK_SIZE]; + BlkMigBlock blk; int64_t sector; - int len; - - for(bmds = block_mig_state->bmds_first; bmds != NULL; bmds = bmds->next) { - for(sector = 0; sector < bmds->cur_sector;) { - - if(bdrv_get_dirty(bmds->bs,sector)) { - - if(bdrv_read(bmds->bs, sector, buf, - block_mig_state->sectors_per_block) < 0) { + + blk.buf = qemu_malloc(BLOCK_SIZE); + + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + for (sector = 0; sector < bmds->cur_sector;) { + if (bdrv_get_dirty(bmds->bs, sector)) { + if (bdrv_read(bmds->bs, sector, blk.buf, + BDRV_SECTORS_PER_DIRTY_CHUNK) < 0) { + monitor_printf(mon, "Error reading sector %" PRId64 "\n", + sector); + qemu_file_set_error(f); + qemu_free(blk.buf); + return; } - - /* device name */ - qemu_put_be64(f,(sector << SECTOR_BITS) - | BLK_MIG_FLAG_DEVICE_BLOCK); - - len = strlen(bmds->bs->device_name); - - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *)bmds->bs->device_name, len); - - qemu_put_buffer(f, buf, - (block_mig_state->sectors_per_block * - SECTOR_SIZE)); - - bdrv_reset_dirty(bmds->bs, sector, - block_mig_state->sectors_per_block); - - sector += block_mig_state->sectors_per_block; - } else { - /* sector is clean */ - sector += block_mig_state->sectors_per_block; - } + blk.bmds = bmds; + blk.sector = sector; + blk_send(f, &blk); + + bdrv_reset_dirty(bmds->bs, sector, + BDRV_SECTORS_PER_DIRTY_CHUNK); + } + sector += BDRV_SECTORS_PER_DIRTY_CHUNK; } } - - return; + + qemu_free(blk.buf); } static void flush_blks(QEMUFile* f) { - BlkMigBlock *blk, *tmp; - - dprintf("%s Enter submitted %d read_done %d transfered\n", __FUNCTION__, - submitted, read_done, transfered); - - for(blk = block_mig_state->first_blk; - blk != NULL && !qemu_file_rate_limit(f); blk = tmp) { - send_blk(f, blk); - - tmp = blk->next; + BlkMigBlock *blk; + + dprintf("%s Enter submitted %d read_done %d transferred %d\n", + __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, + block_mig_state.transferred); + + while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { + if (qemu_file_rate_limit(f)) { + break; + } + if (blk->ret < 0) { + qemu_file_set_error(f); + break; + } + blk_send(f, blk); + + QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry); qemu_free(blk->buf); qemu_free(blk); - - block_mig_state->read_done--; - block_mig_state->transferred++; - assert(block_mig_state->read_done >= 0); - } - block_mig_state->first_blk = blk; - - if(block_mig_state->first_blk == NULL) { - block_mig_state->last_blk = NULL; - } - dprintf("%s Exit submitted %d read_done %d transferred%d\n", __FUNCTION__, - block_mig_state->submitted, block_mig_state->read_done, - block_mig_state->transferred); + block_mig_state.read_done--; + block_mig_state.transferred++; + assert(block_mig_state.read_done >= 0); + } - return; + dprintf("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, + block_mig_state.submitted, block_mig_state.read_done, + block_mig_state.transferred); } static int is_stage2_completed(void) { BlkMigDevState *bmds; - - if(block_mig_state->submitted > 0) { + + if (block_mig_state.submitted > 0) { return 0; } - - for (bmds = block_mig_state->bmds_first; bmds != NULL; bmds = bmds->next) { - if(bmds->bulk_completed == 0) { + + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + if (bmds->bulk_completed == 0) { return 0; } } - + return 1; } -static int block_save_live(QEMUFile *f, int stage, void *opaque) +static void blk_mig_cleanup(Monitor *mon) { - int ret = 1; - - dprintf("Enter save live stage %d submitted %d transferred %d\n", stage, - submitted, transferred); - - if(block_mig_state->blk_enable != 1) { + BlkMigDevState *bmds; + BlkMigBlock *blk; + + while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { + QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); + qemu_free(bmds); + } + + while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { + QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry); + qemu_free(blk->buf); + qemu_free(blk); + } + + set_dirty_tracking(0); + + monitor_printf(mon, "\n"); +} + +static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) +{ + dprintf("Enter save live stage %d submitted %d transferred %d\n", + stage, block_mig_state.submitted, block_mig_state.transferred); + + if (stage < 0) { + blk_mig_cleanup(mon); + return 0; + } + + if (block_mig_state.blk_enable != 1) { /* no need to migrate storage */ - - qemu_put_be64(f,BLK_MIG_FLAG_EOS); + qemu_put_be64(f, BLK_MIG_FLAG_EOS); return 1; } - - if(stage == 1) { - init_blk_migration(f); - + + if (stage == 1) { + init_blk_migration(mon, f); + /* start track dirty blocks */ set_dirty_tracking(1); - } flush_blks(f); - + + if (qemu_file_has_error(f)) { + blk_mig_cleanup(mon); + return 0; + } + /* control the rate of transfer */ - while ((block_mig_state->submitted + block_mig_state->read_done) * - (BLOCK_SIZE) < - (qemu_file_get_rate_limit(f))) { - - ret = blk_mig_save_bulked_block(f, 1); - - if (ret == 0) /* no more bulk blocks for now*/ + while ((block_mig_state.submitted + + block_mig_state.read_done) * BLOCK_SIZE < + qemu_file_get_rate_limit(f)) { + if (blk_mig_save_bulked_block(mon, f, 1) == 0) { + /* no more bulk blocks for now */ break; + } } - + flush_blks(f); - - if(stage == 3) { - - while(blk_mig_save_bulked_block(f, 0) != 0); - - blk_mig_save_dirty_blocks(f); - - /* stop track dirty blocks */ - set_dirty_tracking(0);; - - printf("\nBlock migration completed\n"); + + if (qemu_file_has_error(f)) { + blk_mig_cleanup(mon); + return 0; } - - qemu_put_be64(f,BLK_MIG_FLAG_EOS); - + + if (stage == 3) { + while (blk_mig_save_bulked_block(mon, f, 0) != 0) { + /* empty */ + } + + blk_mig_save_dirty_blocks(mon, f); + blk_mig_cleanup(mon); + + /* report completion */ + qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); + + if (qemu_file_has_error(f)) { + return 0; + } + + monitor_printf(mon, "Block migration completed\n"); + } + + qemu_put_be64(f, BLK_MIG_FLAG_EOS); + return ((stage == 2) && is_stage2_completed()); } static int block_load(QEMUFile *f, void *opaque, int version_id) { + static int banner_printed; int len, flags; char device_name[256]; int64_t addr; BlockDriverState *bs; uint8_t *buf; - - block_mig_state->sectors_per_block = bdrv_get_sectors_per_chunk(); - buf = qemu_malloc(BLOCK_SIZE); - + do { - addr = qemu_get_be64(f); - - flags = addr & ~SECTOR_MASK; - addr &= SECTOR_MASK; - - if(flags & BLK_MIG_FLAG_DEVICE_BLOCK) { - + + flags = addr & ~BDRV_SECTOR_MASK; + addr >>= BDRV_SECTOR_BITS; + + if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) { /* get device name */ len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)device_name, len); device_name[len] = '\0'; - + bs = bdrv_find(device_name); - - qemu_get_buffer(f, buf, - BLOCK_SIZE); - if(bs != NULL) { - - bdrv_write(bs, (addr >> SECTOR_BITS), - buf, block_mig_state->sectors_per_block); - } else { - printf("Error unknown block device %s\n", device_name); + if (!bs) { + fprintf(stderr, "Error unknown block device %s\n", + device_name); + return -EINVAL; } - } else if(flags & BLK_MIG_FLAG_EOS) { - - } else { - printf("Unknown flags\n"); + + buf = qemu_malloc(BLOCK_SIZE); + + qemu_get_buffer(f, buf, BLOCK_SIZE); + bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK); + + qemu_free(buf); + } else if (flags & BLK_MIG_FLAG_PROGRESS) { + if (!banner_printed) { + printf("Receiving block device images\n"); + banner_printed = 1; + } + printf("Completed %d %%%c", (int)addr, + (addr == 100) ? '\n' : '\r'); + fflush(stdout); + } else if (!(flags & BLK_MIG_FLAG_EOS)) { + fprintf(stderr, "Unknown flags\n"); + return -EINVAL; } - } while(!(flags & BLK_MIG_FLAG_EOS)); - - qemu_free(buf); + if (qemu_file_has_error(f)) { + return -EIO; + } + } while (!(flags & BLK_MIG_FLAG_EOS)); return 0; } static void block_set_params(int blk_enable, int shared_base, void *opaque) { - assert(opaque == block_mig_state); + block_mig_state.blk_enable = blk_enable; + block_mig_state.shared_base = shared_base; - block_mig_state->blk_enable = blk_enable; - block_mig_state->shared_base = shared_base; - /* shared base means that blk_enable = 1 */ - block_mig_state->blk_enable |= shared_base; - - return; + block_mig_state.blk_enable |= shared_base; } -void blk_mig_info(void) +void blk_mig_init(void) { - BlockDriverState *bs; - - for (bs = bdrv_first; bs != NULL; bs = bs->next) { - printf("Device %s\n", bs->device_name); - if(bs->type == BDRV_TYPE_HD) { - printf("device %s format %s\n", - bs->device_name, bs->drv->format_name); - } - } -} + QSIMPLEQ_INIT(&block_mig_state.bmds_list); + QSIMPLEQ_INIT(&block_mig_state.blk_list); -void blk_mig_init(void) -{ - - block_mig_state = qemu_mallocz(sizeof(BlkMigState)); - - register_savevm_live("block", 0, 1, block_set_params, block_save_live, - NULL, block_load, block_mig_state); - - + register_savevm_live("block", 0, 1, block_set_params, block_save_live, + NULL, block_load, &block_mig_state); } diff --git a/block-migration.h b/block-migration.h index c33d3cbf0..ffa8ac0bd 100644 --- a/block-migration.h +++ b/block-migration.h @@ -14,16 +14,10 @@ #ifndef BLOCK_MIGRATION_H #define BLOCK_MIGRATION_H -typedef struct BlkMigDevState { - BlockDriverState *bs; - int bulk_completed; - int shared_base; - struct BlkMigDevState *next; - int64_t cur_sector; - int64_t total_sectors; - int64_t dirty; -} BlkMigDevState; - void blk_mig_init(void); -void blk_mig_info(void); +int blk_mig_active(void); +uint64_t blk_mig_bytes_transferred(void); +uint64_t blk_mig_bytes_remaining(void); +uint64_t blk_mig_bytes_total(void); + #endif /* BLOCK_MIGRATION_H */ @@ -41,10 +41,6 @@ #include <windows.h> #endif -#define SECTOR_BITS 9 -#define SECTOR_SIZE (1 << SECTOR_BITS) -#define SECTORS_PER_DIRTY_CHUNK 8 - static BlockDriverAIOCB *bdrv_aio_readv_em(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque); @@ -386,7 +382,7 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, bdrv_delete(bs1); return ret; } - total_size = bdrv_getlength(bs1) >> SECTOR_BITS; + total_size = bdrv_getlength(bs1) >> BDRV_SECTOR_BITS; if (bs1->drv && bs1->drv->protocol_name) is_protocol = 1; @@ -473,7 +469,7 @@ int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, return ret; } if (drv->bdrv_getlength) { - bs->total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; + bs->total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; } #ifndef _WIN32 if (bs->is_temporary) { @@ -576,7 +572,7 @@ int bdrv_commit(BlockDriverState *bs) return -ENOTSUP; } - total_sectors = bdrv_getlength(bs) >> SECTOR_BITS; + total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; for (i = 0; i < total_sectors;) { if (drv->bdrv_is_allocated(bs, i, 65536, &n)) { for(j = 0; j < n; j++) { @@ -643,14 +639,24 @@ int bdrv_read(BlockDriverState *bs, int64_t sector_num, } static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int dirty) + int nb_sectors, int dirty) { int64_t start, end; - start = sector_num / SECTORS_PER_DIRTY_CHUNK; - end = (sector_num + nb_sectors) / SECTORS_PER_DIRTY_CHUNK; - - for(; start <= end; start++) { - bs->dirty_bitmap[start] = dirty; + unsigned long val, idx, bit; + + start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; + end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK; + + for (; start <= end; start++) { + idx = start / (sizeof(unsigned long) * 8); + bit = start % (sizeof(unsigned long) * 8); + val = bs->dirty_bitmap[idx]; + if (dirty) { + val |= 1 << bit; + } else { + val &= ~(1 << bit); + } + bs->dirty_bitmap[idx] = val; } } @@ -670,31 +676,31 @@ int bdrv_write(BlockDriverState *bs, int64_t sector_num, return -EACCES; if (bdrv_check_request(bs, sector_num, nb_sectors)) return -EIO; - - if(bs->dirty_tracking) { + + if (bs->dirty_bitmap) { set_dirty_bitmap(bs, sector_num, nb_sectors, 1); } - + return drv->bdrv_write(bs, sector_num, buf, nb_sectors); } int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int count1) { - uint8_t tmp_buf[SECTOR_SIZE]; + uint8_t tmp_buf[BDRV_SECTOR_SIZE]; int len, nb_sectors, count; int64_t sector_num; count = count1; /* first read to align to sector start */ - len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1); + len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1); if (len > count) len = count; - sector_num = offset >> SECTOR_BITS; + sector_num = offset >> BDRV_SECTOR_BITS; if (len > 0) { if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) return -EIO; - memcpy(buf, tmp_buf + (offset & (SECTOR_SIZE - 1)), len); + memcpy(buf, tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), len); count -= len; if (count == 0) return count1; @@ -703,12 +709,12 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, } /* read the sectors "in place" */ - nb_sectors = count >> SECTOR_BITS; + nb_sectors = count >> BDRV_SECTOR_BITS; if (nb_sectors > 0) { if (bdrv_read(bs, sector_num, buf, nb_sectors) < 0) return -EIO; sector_num += nb_sectors; - len = nb_sectors << SECTOR_BITS; + len = nb_sectors << BDRV_SECTOR_BITS; buf += len; count -= len; } @@ -725,20 +731,20 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, int bdrv_pwrite(BlockDriverState *bs, int64_t offset, const void *buf, int count1) { - uint8_t tmp_buf[SECTOR_SIZE]; + uint8_t tmp_buf[BDRV_SECTOR_SIZE]; int len, nb_sectors, count; int64_t sector_num; count = count1; /* first write to align to sector start */ - len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1); + len = (BDRV_SECTOR_SIZE - offset) & (BDRV_SECTOR_SIZE - 1); if (len > count) len = count; - sector_num = offset >> SECTOR_BITS; + sector_num = offset >> BDRV_SECTOR_BITS; if (len > 0) { if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0) return -EIO; - memcpy(tmp_buf + (offset & (SECTOR_SIZE - 1)), buf, len); + memcpy(tmp_buf + (offset & (BDRV_SECTOR_SIZE - 1)), buf, len); if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0) return -EIO; count -= len; @@ -749,12 +755,12 @@ int bdrv_pwrite(BlockDriverState *bs, int64_t offset, } /* write the sectors "in place" */ - nb_sectors = count >> SECTOR_BITS; + nb_sectors = count >> BDRV_SECTOR_BITS; if (nb_sectors > 0) { if (bdrv_write(bs, sector_num, buf, nb_sectors) < 0) return -EIO; sector_num += nb_sectors; - len = nb_sectors << SECTOR_BITS; + len = nb_sectors << BDRV_SECTOR_BITS; buf += len; count -= len; } @@ -795,7 +801,7 @@ int64_t bdrv_getlength(BlockDriverState *bs) return -ENOMEDIUM; if (!drv->bdrv_getlength) { /* legacy mode */ - return bs->total_sectors * SECTOR_SIZE; + return bs->total_sectors * BDRV_SECTOR_SIZE; } return drv->bdrv_getlength(bs); } @@ -808,7 +814,7 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) if (length < 0) length = 0; else - length = length >> SECTOR_BITS; + length = length >> BDRV_SECTOR_BITS; *nb_sectors_ptr = length; } @@ -1220,11 +1226,11 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num, return -ENOTSUP; if (bdrv_check_request(bs, sector_num, nb_sectors)) return -EIO; - - if(bs->dirty_tracking) { + + if (bs->dirty_bitmap) { set_dirty_bitmap(bs, sector_num, nb_sectors, 1); } - + return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors); } @@ -1401,7 +1407,7 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num, if (ret) { /* Update stats even though technically transfer has not happened. */ - bs->rd_bytes += (unsigned) nb_sectors * SECTOR_SIZE; + bs->rd_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE; bs->rd_ops ++; } @@ -1422,16 +1428,16 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, if (bdrv_check_request(bs, sector_num, nb_sectors)) return NULL; - if(bs->dirty_tracking) { + if (bs->dirty_bitmap) { set_dirty_bitmap(bs, sector_num, nb_sectors, 1); } - + ret = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors, cb, opaque); if (ret) { /* Update stats even though technically transfer has not happened. */ - bs->wr_bytes += (unsigned) nb_sectors * SECTOR_SIZE; + bs->wr_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE; bs->wr_ops ++; } @@ -1966,47 +1972,38 @@ void *qemu_blockalign(BlockDriverState *bs, size_t size) void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable) { int64_t bitmap_size; - if(enable) { - if(bs->dirty_tracking == 0) { - int64_t i; - uint8_t test; - bitmap_size = (bdrv_getlength(bs) >> SECTOR_BITS); - bitmap_size /= SECTORS_PER_DIRTY_CHUNK; - bitmap_size++; - + + if (enable) { + if (!bs->dirty_bitmap) { + bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) + + BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1; + bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8; + bs->dirty_bitmap = qemu_mallocz(bitmap_size); - - bs->dirty_tracking = enable; - for(i = 0; i < bitmap_size; i++) test = bs->dirty_bitmap[i]; - } + } } else { - if(bs->dirty_tracking != 0) { + if (bs->dirty_bitmap) { qemu_free(bs->dirty_bitmap); - bs->dirty_tracking = enable; - } + bs->dirty_bitmap = NULL; + } } } int bdrv_get_dirty(BlockDriverState *bs, int64_t sector) { - int64_t chunk = sector / (int64_t)SECTORS_PER_DIRTY_CHUNK; - - if(bs->dirty_bitmap != NULL && - (sector << SECTOR_BITS) <= bdrv_getlength(bs)) { - return bs->dirty_bitmap[chunk]; + int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; + + if (bs->dirty_bitmap && + (sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) { + return bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] & + (1 << (chunk % (sizeof(unsigned long) * 8))); } else { return 0; } } -void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, - int nr_sectors) +void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, + int nr_sectors) { set_dirty_bitmap(bs, cur_sector, nr_sectors, 0); } - -int bdrv_get_sectors_per_chunk(void) -{ - /* size must be 2^x */ - return SECTORS_PER_DIRTY_CHUNK; -} @@ -41,6 +41,10 @@ typedef struct QEMUSnapshotInfo { #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB) +#define BDRV_SECTOR_BITS 9 +#define BDRV_SECTOR_SIZE (1 << BDRV_SECTOR_BITS) +#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1); + void bdrv_info(Monitor *mon); void bdrv_info_stats(Monitor *mon); @@ -188,9 +192,10 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size); +#define BDRV_SECTORS_PER_DIRTY_CHUNK 2048 + void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable); int bdrv_get_dirty(BlockDriverState *bs, int64_t sector); -void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, - int nr_sectors); -int bdrv_get_sectors_per_chunk(void); +void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, + int nr_sectors); #endif diff --git a/block_int.h b/block_int.h index 7ebe926f5..907e86410 100644 --- a/block_int.h +++ b/block_int.h @@ -168,8 +168,7 @@ struct BlockDriverState { int cyls, heads, secs, translation; int type; char device_name[32]; - int dirty_tracking; - uint8_t *dirty_bitmap; + unsigned long *dirty_bitmap; BlockDriverState *next; void *private; }; @@ -323,7 +323,7 @@ static void reset_bm_regs (AC97LinkState *s, AC97BusMasterRegs *r) static void mixer_store (AC97LinkState *s, uint32_t i, uint16_t v) { if (i + 2 > sizeof (s->mixer_data)) { - dolog ("mixer_store: index %d out of bounds %d\n", + dolog ("mixer_store: index %d out of bounds %zd\n", i, sizeof (s->mixer_data)); return; } @@ -337,7 +337,7 @@ static uint16_t mixer_load (AC97LinkState *s, uint32_t i) uint16_t val = 0xffff; if (i + 2 > sizeof (s->mixer_data)) { - dolog ("mixer_store: index %d out of bounds %d\n", + dolog ("mixer_store: index %d out of bounds %zd\n", i, sizeof (s->mixer_data)); } else { @@ -1167,73 +1167,31 @@ static void po_callback (void *opaque, int free) transfer_audio (opaque, PO_INDEX, free); } -static void ac97_save (QEMUFile *f, void *opaque) -{ - size_t i; - uint8_t active[LAST_INDEX]; - AC97LinkState *s = opaque; - - pci_device_save (&s->dev, f); - - qemu_put_be32s (f, &s->glob_cnt); - qemu_put_be32s (f, &s->glob_sta); - qemu_put_be32s (f, &s->cas); - - for (i = 0; i < ARRAY_SIZE (s->bm_regs); ++i) { - AC97BusMasterRegs *r = &s->bm_regs[i]; - qemu_put_be32s (f, &r->bdbar); - qemu_put_8s (f, &r->civ); - qemu_put_8s (f, &r->lvi); - qemu_put_be16s (f, &r->sr); - qemu_put_be16s (f, &r->picb); - qemu_put_8s (f, &r->piv); - qemu_put_8s (f, &r->cr); - qemu_put_be32s (f, &r->bd_valid); - qemu_put_be32s (f, &r->bd.addr); - qemu_put_be32s (f, &r->bd.ctl_len); +static const VMStateDescription vmstate_ac97_bm_regs = { + .name = "ac97_bm_regs", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(bdbar, AC97BusMasterRegs), + VMSTATE_UINT8(civ, AC97BusMasterRegs), + VMSTATE_UINT8(lvi, AC97BusMasterRegs), + VMSTATE_UINT16(sr, AC97BusMasterRegs), + VMSTATE_UINT16(picb, AC97BusMasterRegs), + VMSTATE_UINT8(piv, AC97BusMasterRegs), + VMSTATE_UINT8(cr, AC97BusMasterRegs), + VMSTATE_UINT32(bd_valid, AC97BusMasterRegs), + VMSTATE_UINT32(bd.addr, AC97BusMasterRegs), + VMSTATE_UINT32(bd.ctl_len, AC97BusMasterRegs), + VMSTATE_END_OF_LIST() } - qemu_put_buffer (f, s->mixer_data, sizeof (s->mixer_data)); - - active[PI_INDEX] = AUD_is_active_in (s->voice_pi) ? 1 : 0; - active[PO_INDEX] = AUD_is_active_out (s->voice_po) ? 1 : 0; - active[MC_INDEX] = AUD_is_active_in (s->voice_mc) ? 1 : 0; - qemu_put_buffer (f, active, sizeof (active)); -} +}; -static int ac97_load (QEMUFile *f, void *opaque, int version_id) +static int ac97_post_load (void *opaque, int version_id) { - int ret; - size_t i; uint8_t active[LAST_INDEX]; AC97LinkState *s = opaque; - if (version_id != 2) - return -EINVAL; - - ret = pci_device_load (&s->dev, f); - if (ret) - return ret; - - qemu_get_be32s (f, &s->glob_cnt); - qemu_get_be32s (f, &s->glob_sta); - qemu_get_be32s (f, &s->cas); - - for (i = 0; i < ARRAY_SIZE (s->bm_regs); ++i) { - AC97BusMasterRegs *r = &s->bm_regs[i]; - qemu_get_be32s (f, &r->bdbar); - qemu_get_8s (f, &r->civ); - qemu_get_8s (f, &r->lvi); - qemu_get_be16s (f, &r->sr); - qemu_get_be16s (f, &r->picb); - qemu_get_8s (f, &r->piv); - qemu_get_8s (f, &r->cr); - qemu_get_be32s (f, &r->bd_valid); - qemu_get_be32s (f, &r->bd.addr); - qemu_get_be32s (f, &r->bd.ctl_len); - } - qemu_get_buffer (f, s->mixer_data, sizeof (s->mixer_data)); - qemu_get_buffer (f, active, sizeof (active)); - #ifdef USE_MIXER record_select (s, mixer_load (s, AC97_Record_Select)); #define V_(a, b) set_volume (s, a, b, mixer_load (s, a)) @@ -1242,6 +1200,9 @@ static int ac97_load (QEMUFile *f, void *opaque, int version_id) V_ (AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN); #undef V_ #endif + active[PI_INDEX] = !!(s->bm_regs[PI_INDEX].cr & CR_RPBM); + active[PO_INDEX] = !!(s->bm_regs[PO_INDEX].cr & CR_RPBM); + active[MC_INDEX] = !!(s->bm_regs[MC_INDEX].cr & CR_RPBM); reset_voices (s, active); s->bup_flag = 0; @@ -1249,6 +1210,30 @@ static int ac97_load (QEMUFile *f, void *opaque, int version_id) return 0; } +static bool is_version_2 (void *opaque, int version_id) +{ + return version_id == 2; +} + +static const VMStateDescription vmstate_ac97 = { + .name = "ac97", + .version_id = 3, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .post_load = ac97_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, AC97LinkState), + VMSTATE_UINT32(glob_cnt, AC97LinkState), + VMSTATE_UINT32(glob_sta, AC97LinkState), + VMSTATE_UINT32(cas, AC97LinkState), + VMSTATE_STRUCT_ARRAY(bm_regs, AC97LinkState, 3, 1, + vmstate_ac97_bm_regs, AC97BusMasterRegs), + VMSTATE_BUFFER(mixer_data, AC97LinkState), + VMSTATE_UNUSED_TEST(is_version_2, 3), + VMSTATE_END_OF_LIST() + } +}; + static void ac97_map (PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type) { @@ -1334,7 +1319,6 @@ static int ac97_initfn (PCIDevice *dev) pci_register_bar (&s->dev, 0, 256 * 4, PCI_BASE_ADDRESS_SPACE_IO, ac97_map); pci_register_bar (&s->dev, 1, 64 * 4, PCI_BASE_ADDRESS_SPACE_IO, ac97_map); - register_savevm ("ac97", 0, 2, ac97_save, ac97_load, s); qemu_register_reset (ac97_on_reset, s); AUD_register_card ("ac97", &s->card); ac97_on_reset (s); @@ -1351,6 +1335,7 @@ static PCIDeviceInfo ac97_info = { .qdev.name = "AC97", .qdev.desc = "Intel 82801AA AC97 Audio", .qdev.size = sizeof (AC97LinkState), + .qdev.vmsd = &vmstate_ac97, .init = ac97_initfn, }; diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 9390d2c66..ae309dc3c 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -3263,7 +3263,6 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev) pci_register_bar((PCIDevice *)d, 1, CIRRUS_PNPMMIO_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY, cirrus_pci_mmio_map); } - vmstate_register(0, &vmstate_pci_cirrus_vga, d); /* ROM BIOS */ rom_add_vga(VGABIOS_CIRRUS_FILENAME); @@ -3278,6 +3277,7 @@ void pci_cirrus_vga_init(PCIBus *bus) static PCIDeviceInfo cirrus_vga_info = { .qdev.name = "Cirrus VGA", .qdev.size = sizeof(PCICirrusVGAState), + .qdev.vmsd = &vmstate_pci_cirrus_vga, .init = pci_cirrus_vga_initfn, .config_write = pci_cirrus_write_config, }; diff --git a/hw/cs4231a.c b/hw/cs4231a.c index e03c5d242..4d5ce5c27 100644 --- a/hw/cs4231a.c +++ b/hw/cs4231a.c @@ -596,45 +596,47 @@ static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len) return dma_pos; } -static void cs_save (QEMUFile *f, void *opaque) +static int cs4231a_pre_load (void *opaque) { CSState *s = opaque; - unsigned int i; - uint32_t val; - for (i = 0; i < CS_REGS; i++) - qemu_put_be32s (f, &s->regs[i]); - - qemu_put_buffer (f, s->dregs, CS_DREGS); - val = s->dma_running; qemu_put_be32s (f, &val); - val = s->audio_free; qemu_put_be32s (f, &val); - val = s->transferred; qemu_put_be32s (f, &val); - val = s->aci_counter; qemu_put_be32s (f, &val); + if (s->dma_running) { + DMA_release_DREQ (s->dma); + AUD_set_active_out (s->voice, 0); + } + s->dma_running = 0; + return 0; } -static int cs_load (QEMUFile *f, void *opaque, int version_id) +static int cs4231a_post_load (void *opaque, int version_id) { CSState *s = opaque; - unsigned int i; - uint32_t val, dma_running; - - if (version_id > 1) - return -EINVAL; - for (i = 0; i < CS_REGS; i++) - qemu_get_be32s (f, &s->regs[i]); - - qemu_get_buffer (f, s->dregs, CS_DREGS); - - qemu_get_be32s (f, &dma_running); - qemu_get_be32s (f, &val); s->audio_free = val; - qemu_get_be32s (f, &val); s->transferred = val; - qemu_get_be32s (f, &val); s->aci_counter = val; - if (dma_running && (s->dregs[Interface_Configuration] & PEN)) + if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) { + s->dma_running = 0; cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]); + } return 0; } +static const VMStateDescription vmstate_cs4231a = { + .name = "cs4231a", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_load = cs4231a_pre_load, + .post_load = cs4231a_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT32_ARRAY(regs, CSState, CS_REGS), + VMSTATE_BUFFER(dregs, CSState), + VMSTATE_INT32(dma_running, CSState), + VMSTATE_INT32(audio_free, CSState), + VMSTATE_INT32(transferred, CSState), + VMSTATE_INT32(aci_counter, CSState), + VMSTATE_END_OF_LIST() + } +}; + static int cs4231a_initfn (ISADevice *dev) { CSState *s = DO_UPCAST (CSState, dev, dev); @@ -649,7 +651,6 @@ static int cs4231a_initfn (ISADevice *dev) DMA_register_channel (s->dma, cs_dma_read, s); - register_savevm ("cs4231a", 0, 1, cs_save, cs_load, s); qemu_register_reset (cs_reset, s); cs_reset (s); @@ -667,6 +668,7 @@ static ISADeviceInfo cs4231a_info = { .qdev.name = "cs4231a", .qdev.desc = "Crystal Semiconductor CS4231A", .qdev.size = sizeof (CSState), + .qdev.vmsd = &vmstate_cs4231a, .init = cs4231a_initfn, .qdev.props = (Property[]) { DEFINE_PROP_HEX32 ("iobase", CSState, port, 0x534), diff --git a/hw/dp8393x.c b/hw/dp8393x.c index ae8b16e31..e65e4d153 100644 --- a/hw/dp8393x.c +++ b/hw/dp8393x.c @@ -154,7 +154,8 @@ typedef struct dp8393xState { #endif QEMUTimer *watchdog; int64_t wt_last_update; - VLANClientState *vc; + NICConf conf; + NICState *nic; int mmio_index; /* Registers */ @@ -406,13 +407,13 @@ static void do_transmit_packets(dp8393xState *s) if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { /* Loopback */ s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; - if (s->vc->can_receive(s->vc)) { + if (s->nic->nc.info->can_receive(&s->nic->nc)) { s->loopback_packet = 1; - s->vc->receive(s->vc, s->tx_buffer, tx_len); + s->nic->nc.info->receive(&s->nic->nc, s->tx_buffer, tx_len); } } else { /* Transmit packet */ - qemu_send_packet(s->vc, s->tx_buffer, tx_len); + qemu_send_packet(&s->nic->nc, s->tx_buffer, tx_len); } s->regs[SONIC_TCR] |= SONIC_TCR_PTX; @@ -675,9 +676,9 @@ static CPUWriteMemoryFunc * const dp8393x_write[3] = { dp8393x_writel, }; -static int nic_can_receive(VLANClientState *vc) +static int nic_can_receive(VLANClientState *nc) { - dp8393xState *s = vc->opaque; + dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN)) return 0; @@ -724,10 +725,10 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size) return -1; } -static ssize_t nic_receive(VLANClientState *vc, const uint8_t * buf, size_t size) +static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size) { + dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; uint16_t data[10]; - dp8393xState *s = vc->opaque; int packet_type; uint32_t available, address; int width, rx_len = size; @@ -860,9 +861,9 @@ static void nic_reset(void *opaque) dp8393x_update_irq(s); } -static void nic_cleanup(VLANClientState *vc) +static void nic_cleanup(VLANClientState *nc) { - dp8393xState *s = vc->opaque; + dp8393xState *s = DO_UPCAST(NICState, nc, nc)->opaque; cpu_unregister_io_memory(s->mmio_index); @@ -872,6 +873,14 @@ static void nic_cleanup(VLANClientState *vc) qemu_free(s); } +static NetClientInfo net_dp83932_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = nic_can_receive, + .receive = nic_receive, + .cleanup = nic_cleanup, +}; + void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift, qemu_irq irq, void* mem_opaque, void (*memory_rw)(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write)) @@ -889,13 +898,13 @@ void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift, s->watchdog = qemu_new_timer(vm_clock, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - s->vc = nd->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - nd->vlan, nd->netdev, - nd->model, nd->name, - nic_can_receive, nic_receive, NULL, NULL, - nic_cleanup, s); + memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(s->conf.macaddr)); + s->conf.vlan = nd->vlan; + s->conf.peer = nd->netdev; + + s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s); - qemu_format_nic_info_str(s->vc, nd->macaddr); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); qemu_register_reset(nic_reset, s); nic_reset(s); diff --git a/hw/e1000.c b/hw/e1000.c index 00f6a57da..b65a74fe9 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -75,7 +75,7 @@ enum { typedef struct E1000State_st { PCIDevice dev; - VLANClientState *vc; + NICState *nic; NICConf conf; int mmio_index; @@ -385,9 +385,9 @@ xmit_seg(E1000State *s) if (tp->vlan_needed) { memmove(tp->vlan, tp->data, 12); memcpy(tp->data + 8, tp->vlan_header, 4); - qemu_send_packet(s->vc, tp->vlan, tp->size + 4); + qemu_send_packet(&s->nic->nc, tp->vlan, tp->size + 4); } else - qemu_send_packet(s->vc, tp->data, tp->size); + qemu_send_packet(&s->nic->nc, tp->data, tp->size); s->mac_reg[TPT]++; s->mac_reg[GPTC]++; n = s->mac_reg[TOTL]; @@ -590,12 +590,12 @@ receive_filter(E1000State *s, const uint8_t *buf, int size) } static void -e1000_set_link_status(VLANClientState *vc) +e1000_set_link_status(VLANClientState *nc) { - E1000State *s = vc->opaque; + E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; uint32_t old_status = s->mac_reg[STATUS]; - if (vc->link_down) + if (nc->link_down) s->mac_reg[STATUS] &= ~E1000_STATUS_LU; else s->mac_reg[STATUS] |= E1000_STATUS_LU; @@ -605,17 +605,17 @@ e1000_set_link_status(VLANClientState *vc) } static int -e1000_can_receive(VLANClientState *vc) +e1000_can_receive(VLANClientState *nc) { - E1000State *s = vc->opaque; + E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; return (s->mac_reg[RCTL] & E1000_RCTL_EN); } static ssize_t -e1000_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - E1000State *s = vc->opaque; + E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; struct e1000_rx_desc desc; target_phys_addr_t base; unsigned int n, rdt; @@ -1037,11 +1037,11 @@ e1000_mmio_map(PCIDevice *pci_dev, int region_num, } static void -e1000_cleanup(VLANClientState *vc) +e1000_cleanup(VLANClientState *nc) { - E1000State *d = vc->opaque; + E1000State *s = DO_UPCAST(NICState, nc, nc)->opaque; - d->vc = NULL; + s->nic = NULL; } static int @@ -1050,8 +1050,7 @@ pci_e1000_uninit(PCIDevice *dev) E1000State *d = DO_UPCAST(E1000State, dev, dev); cpu_unregister_io_memory(d->mmio_index); - qemu_del_vlan_client(d->vc); - vmstate_unregister(&vmstate_e1000, d); + qemu_del_vlan_client(&d->nic->nc); return 0; } @@ -1067,6 +1066,15 @@ static void e1000_reset(void *opaque) memset(&d->tx, 0, sizeof d->tx); } +static NetClientInfo net_e1000_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = e1000_can_receive, + .receive = e1000_receive, + .cleanup = e1000_cleanup, + .link_status_changed = e1000_set_link_status, +}; + static int pci_e1000_init(PCIDevice *pci_dev) { E1000State *d = DO_UPCAST(E1000State, dev, pci_dev); @@ -1107,16 +1115,10 @@ static int pci_e1000_init(PCIDevice *pci_dev) checksum = (uint16_t) EEPROM_SUM - checksum; d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum; - d->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - d->conf.vlan, d->conf.peer, - d->dev.qdev.info->name, d->dev.qdev.id, - e1000_can_receive, e1000_receive, NULL, - NULL, e1000_cleanup, d); - d->vc->link_status_changed = e1000_set_link_status; - - qemu_format_nic_info_str(d->vc, macaddr); + d->nic = qemu_new_nic(&net_e1000_info, &d->conf, + d->dev.qdev.info->name, d->dev.qdev.id, d); - vmstate_register(-1, &vmstate_e1000, d); + qemu_format_nic_info_str(&d->nic->nc, macaddr); if (!pci_dev->qdev.hotplugged) { static int loaded = 0; @@ -1139,6 +1141,7 @@ static PCIDeviceInfo e1000_info = { .qdev.desc = "Intel Gigabit Ethernet", .qdev.size = sizeof(E1000State), .qdev.reset = qdev_e1000_reset, + .qdev.vmsd = &vmstate_e1000, .init = pci_e1000_init, .exit = pci_e1000_uninit, .qdev.props = (Property[]) { diff --git a/hw/eepro100.c b/hw/eepro100.c index 8734907e4..eb16a51a4 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -195,7 +195,7 @@ typedef struct { PCIDevice dev; uint8_t mult[8]; /* multicast mask array */ int mmio_index; - VLANClientState *vc; + NICState *nic; NICConf conf; uint8_t scb_stat; /* SCB stat/ack byte */ uint8_t int_stat; /* PCI interrupt status */ @@ -868,7 +868,7 @@ static void action_command(EEPRO100State *s) } } TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); - qemu_send_packet(s->vc, buf, size); + qemu_send_packet(&s->nic->nc, buf, size); s->statistics.tx_good_frames++; /* Transmit with bad status would raise an CX/TNO interrupt. * (82557 only). Emulation never has bad status. */ @@ -1599,21 +1599,21 @@ static void pci_mmio_map(PCIDevice * pci_dev, int region_num, } } -static int nic_can_receive(VLANClientState *vc) +static int nic_can_receive(VLANClientState *nc) { - EEPRO100State *s = vc->opaque; + EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; TRACE(RXTX, logout("%p\n", s)); return get_ru_state(s) == ru_ready; //~ return !eepro100_buffer_full(s); } -static ssize_t nic_receive(VLANClientState *vc, const uint8_t * buf, size_t size) +static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size) { /* TODO: * - Magic packets should set bit 30 in power management driver register. * - Interesting packets should set bit 29 in power management driver register. */ - EEPRO100State *s = vc->opaque; + EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; uint16_t rfd_status = 0xa000; static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -1779,11 +1779,11 @@ static const VMStateDescription vmstate_eepro100 = { } }; -static void nic_cleanup(VLANClientState *vc) +static void nic_cleanup(VLANClientState *nc) { - EEPRO100State *s = vc->opaque; + EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; - s->vc = NULL; + s->nic = NULL; } static int pci_nic_uninit(PCIDevice *pci_dev) @@ -1793,10 +1793,18 @@ static int pci_nic_uninit(PCIDevice *pci_dev) cpu_unregister_io_memory(s->mmio_index); vmstate_unregister(s->vmstate, s); eeprom93xx_free(s->eeprom); - qemu_del_vlan_client(s->vc); + qemu_del_vlan_client(&s->nic->nc); return 0; } +static NetClientInfo net_eepro100_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = nic_can_receive, + .receive = nic_receive, + .cleanup = nic_cleanup, +}; + static int nic_init(PCIDevice *pci_dev, uint32_t device) { EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); @@ -1829,27 +1837,24 @@ static int nic_init(PCIDevice *pci_dev, uint32_t device) nic_reset(s); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - s->conf.vlan, s->conf.peer, - pci_dev->qdev.info->name, pci_dev->qdev.id, - nic_can_receive, nic_receive, NULL, NULL, - nic_cleanup, s); + s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, + pci_dev->qdev.info->name, pci_dev->qdev.id, s); - qemu_format_nic_info_str(s->vc, s->conf.macaddr.a); - TRACE(OTHER, logout("%s\n", s->vc->info_str)); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + TRACE(OTHER, logout("%s\n", s->nic->nc.info_str)); qemu_register_reset(nic_reset, s); s->vmstate = qemu_malloc(sizeof(vmstate_eepro100)); memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100)); - s->vmstate->name = s->vc->model; + s->vmstate->name = s->nic->nc.model; vmstate_register(-1, s->vmstate, s); if (!pci_dev->qdev.hotplugged) { static int loaded = 0; if (!loaded) { char fname[32]; - snprintf(fname, sizeof(fname), "pxe-%s.bin", s->vc->model); + snprintf(fname, sizeof(fname), "pxe-%s.bin", s->nic->nc.model); rom_add_option(fname); loaded = 1; } diff --git a/hw/es1370.c b/hw/es1370.c index 10da250bc..c3582539d 100644 --- a/hw/es1370.c +++ b/hw/es1370.c @@ -924,48 +924,28 @@ static void es1370_map (PCIDevice *pci_dev, int region_num, register_ioport_read (addr, 0x40, 4, es1370_readl, s); } -static void es1370_save (QEMUFile *f, void *opaque) -{ - ES1370State *s = opaque; - size_t i; - - pci_device_save (&s->dev, f); - for (i = 0; i < NB_CHANNELS; ++i) { - struct chan *d = &s->chan[i]; - qemu_put_be32s (f, &d->shift); - qemu_put_be32s (f, &d->leftover); - qemu_put_be32s (f, &d->scount); - qemu_put_be32s (f, &d->frame_addr); - qemu_put_be32s (f, &d->frame_cnt); +static const VMStateDescription vmstate_es1370_channel = { + .name = "es1370_channel", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField []) { + VMSTATE_UINT32(shift, struct chan), + VMSTATE_UINT32(leftover, struct chan), + VMSTATE_UINT32(scount, struct chan), + VMSTATE_UINT32(frame_addr, struct chan), + VMSTATE_UINT32(frame_cnt, struct chan), + VMSTATE_END_OF_LIST() } - qemu_put_be32s (f, &s->ctl); - qemu_put_be32s (f, &s->status); - qemu_put_be32s (f, &s->mempage); - qemu_put_be32s (f, &s->codec); - qemu_put_be32s (f, &s->sctl); -} +}; -static int es1370_load (QEMUFile *f, void *opaque, int version_id) +static int es1370_post_load (void *opaque, int version_id) { - int ret; uint32_t ctl, sctl; ES1370State *s = opaque; size_t i; - if (version_id != 2) - return -EINVAL; - - ret = pci_device_load (&s->dev, f); - if (ret) - return ret; - for (i = 0; i < NB_CHANNELS; ++i) { - struct chan *d = &s->chan[i]; - qemu_get_be32s (f, &d->shift); - qemu_get_be32s (f, &d->leftover); - qemu_get_be32s (f, &d->scount); - qemu_get_be32s (f, &d->frame_addr); - qemu_get_be32s (f, &d->frame_cnt); if (i == ADC_CHANNEL) { if (s->adc_voice) { AUD_close_in (&s->card, s->adc_voice); @@ -980,18 +960,33 @@ static int es1370_load (QEMUFile *f, void *opaque, int version_id) } } - qemu_get_be32s (f, &ctl); - qemu_get_be32s (f, &s->status); - qemu_get_be32s (f, &s->mempage); - qemu_get_be32s (f, &s->codec); - qemu_get_be32s (f, &sctl); - + ctl = s->ctl; + sctl = s->sctl; s->ctl = 0; s->sctl = 0; es1370_update_voices (s, ctl, sctl); return 0; } +static const VMStateDescription vmstate_es1370 = { + .name = "es1370", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .post_load = es1370_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, ES1370State), + VMSTATE_STRUCT_ARRAY(chan, ES1370State, NB_CHANNELS, 2, + vmstate_es1370_channel, struct chan), + VMSTATE_UINT32(ctl, ES1370State), + VMSTATE_UINT32(status, ES1370State), + VMSTATE_UINT32(mempage, ES1370State), + VMSTATE_UINT32(codec, ES1370State), + VMSTATE_UINT32(sctl, ES1370State), + VMSTATE_END_OF_LIST() + } +}; + static void es1370_on_reset (void *opaque) { ES1370State *s = opaque; @@ -1028,7 +1023,6 @@ static int es1370_initfn (PCIDevice *dev) c[0x3f] = 0x80; pci_register_bar (&s->dev, 0, 256, PCI_BASE_ADDRESS_SPACE_IO, es1370_map); - register_savevm ("es1370", 0, 2, es1370_save, es1370_load, s); qemu_register_reset (es1370_on_reset, s); AUD_register_card ("es1370", &s->card); @@ -1046,6 +1040,7 @@ static PCIDeviceInfo es1370_info = { .qdev.name = "ES1370", .qdev.desc = "ENSONIQ AudioPCI ES1370", .qdev.size = sizeof (ES1370State), + .qdev.vmsd = &vmstate_es1370, .init = es1370_initfn, }; @@ -1,3 +1,6 @@ +#ifndef QEMU_HW_ESP_H +#define QEMU_HW_ESP_H + /* esp.c */ #define ESP_MAX_DEVS 7 typedef void (*espdma_memory_read_write)(void *opaque, uint8_t *buf, int len); @@ -5,3 +8,5 @@ void esp_init(target_phys_addr_t espaddr, int it_shift, espdma_memory_read_write dma_memory_read, espdma_memory_read_write dma_memory_write, void *dma_opaque, qemu_irq irq, qemu_irq *reset); + +#endif diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c index ffe708253..dedd1077f 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -319,7 +319,8 @@ static void mdio_cycle(struct qemu_mdio *bus) struct fs_eth { - VLANClientState *vc; + NICState *nic; + NICConf conf; int ethregs; /* Two addrs in the filter. */ @@ -495,15 +496,15 @@ static int eth_match_groupaddr(struct fs_eth *eth, const unsigned char *sa) return match; } -static int eth_can_receive(VLANClientState *vc) +static int eth_can_receive(VLANClientState *nc) { return 1; } -static ssize_t eth_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t eth_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - struct fs_eth *eth = vc->opaque; + struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; int use_ma0 = eth->regs[RW_REC_CTRL] & 1; int use_ma1 = eth->regs[RW_REC_CTRL] & 2; int r_bcast = eth->regs[RW_REC_CTRL] & 8; @@ -533,15 +534,15 @@ static int eth_tx_push(void *opaque, unsigned char *buf, int len) struct fs_eth *eth = opaque; D(printf("%s buf=%p len=%d\n", __func__, buf, len)); - qemu_send_packet(eth->vc, buf, len); + qemu_send_packet(ð->nic->nc, buf, len); return len; } -static void eth_set_link(VLANClientState *vc) +static void eth_set_link(VLANClientState *nc) { - struct fs_eth *eth = vc->opaque; - D(printf("%s %d\n", __func__, vc->link_down)); - eth->phy.link = !vc->link_down; + struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; + D(printf("%s %d\n", __func__, nc->link_down)); + eth->phy.link = !nc->link_down; } static CPUReadMemoryFunc * const eth_read[] = { @@ -554,9 +555,9 @@ static CPUWriteMemoryFunc * const eth_write[] = { ð_writel, }; -static void eth_cleanup(VLANClientState *vc) +static void eth_cleanup(VLANClientState *nc) { - struct fs_eth *eth = vc->opaque; + struct fs_eth *eth = DO_UPCAST(NICState, nc, nc)->opaque; cpu_unregister_io_memory(eth->ethregs); @@ -564,6 +565,15 @@ static void eth_cleanup(VLANClientState *vc) qemu_free(eth); } +static NetClientInfo net_etraxfs_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = eth_can_receive, + .receive = eth_receive, + .cleanup = eth_cleanup, + .link_status_changed = eth_set_link, +}; + void *etraxfs_eth_init(NICInfo *nd, target_phys_addr_t base, int phyaddr) { struct etraxfs_dma_client *dma = NULL; @@ -590,13 +600,12 @@ void *etraxfs_eth_init(NICInfo *nd, target_phys_addr_t base, int phyaddr) eth->ethregs = cpu_register_io_memory(eth_read, eth_write, eth); cpu_register_physical_memory (base, 0x5c, eth->ethregs); - eth->vc = nd->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - nd->vlan, nd->netdev, - nd->model, nd->name, - eth_can_receive, eth_receive, - NULL, NULL, eth_cleanup, eth); - eth->vc->opaque = eth; - eth->vc->link_status_changed = eth_set_link; + memcpy(eth->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + eth->conf.vlan = nd->vlan; + eth->conf.peer = nd->netdev; + + eth->nic = qemu_new_nic(&net_etraxfs_info, ð->conf, + nd->model, nd->name, eth); return dma; } @@ -215,35 +215,22 @@ static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) return dma_len; } -static void GUS_save (QEMUFile *f, void *opaque) -{ - GUSState *s = opaque; - - qemu_put_be32 (f, s->pos); - qemu_put_be32 (f, s->left); - qemu_put_be32 (f, s->shift); - qemu_put_be32 (f, s->irqs); - qemu_put_be32 (f, s->samples); - qemu_put_be64 (f, s->last_ticks); - qemu_put_buffer (f, s->himem, sizeof (s->himem)); -} - -static int GUS_load (QEMUFile *f, void *opaque, int version_id) -{ - GUSState *s = opaque; - - if (version_id != 2) - return -EINVAL; - - s->pos = qemu_get_be32 (f); - s->left = qemu_get_be32 (f); - s->shift = qemu_get_be32 (f); - s->irqs = qemu_get_be32 (f); - s->samples = qemu_get_be32 (f); - s->last_ticks = qemu_get_be64 (f); - qemu_get_buffer (f, s->himem, sizeof (s->himem)); - return 0; -} +static const VMStateDescription vmstate_gus = { + .name = "gus", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField []) { + VMSTATE_INT32(pos, GUSState), + VMSTATE_INT32(left, GUSState), + VMSTATE_INT32(shift, GUSState), + VMSTATE_INT32(irqs, GUSState), + VMSTATE_INT32(samples, GUSState), + VMSTATE_INT64(last_ticks, GUSState), + VMSTATE_BUFFER(himem, GUSState), + VMSTATE_END_OF_LIST() + } +}; static int gus_initfn (ISADevice *dev) { @@ -300,7 +287,6 @@ static int gus_initfn (ISADevice *dev) AUD_set_active_out (s->voice, 1); - register_savevm ("gus", 0, 2, GUS_save, GUS_load, s); return 0; } @@ -314,6 +300,7 @@ static ISADeviceInfo gus_info = { .qdev.name = "gus", .qdev.desc = "Gravis Ultrasound GF1", .qdev.size = sizeof (GUSState), + .qdev.vmsd = &vmstate_gus, .init = gus_initfn, .qdev.props = (Property[]) { DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), @@ -244,7 +244,8 @@ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence); typedef void SaveSetParamsHandler(int blk_enable, int shared, void * opaque); typedef void SaveStateHandler(QEMUFile *f, void *opaque); -typedef int SaveLiveStateHandler(QEMUFile *f, int stage, void *opaque); +typedef int SaveLiveStateHandler(Monitor *mon, QEMUFile *f, int stage, + void *opaque); typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); int register_savevm(const char *idstr, @@ -294,14 +295,18 @@ enum VMStateFlags { VMS_BUFFER = 0x020, /* static sized buffer */ VMS_ARRAY_OF_POINTER = 0x040, VMS_VARRAY_UINT16 = 0x080, /* Array with size in uint16_t field */ + VMS_VBUFFER = 0x100, /* Buffer with size in int32_t field */ + VMS_MULTIPLY = 0x200, /* multiply "size" field by field_size */ }; typedef struct { const char *name; size_t offset; size_t size; + size_t start; int num; size_t num_offset; + size_t size_offset; const VMStateInfo *info; enum VMStateFlags flags; const VMStateDescription *vmsd; @@ -437,21 +442,23 @@ extern const VMStateInfo vmstate_info_unused_buffer; .offset = offsetof(_state, _field), \ } -#define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) { \ - .name = (stringify(_field)), \ - .version_id = (_version), \ - .vmsd = &(_vmsd), \ - .size = sizeof(_type), \ - .flags = VMS_STRUCT, \ - .offset = vmstate_offset_value(_state, _field, _type), \ +#define VMSTATE_STRUCT_TEST(_field, _state, _test, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, _type), \ } -#define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) { \ - .name = (stringify(_field)), \ - .vmsd = &(_vmsd), \ - .size = sizeof(_type), \ - .flags = VMS_STRUCT|VMS_POINTER, \ - .offset = vmstate_offset_value(_state, _field, _type), \ +#define VMSTATE_STRUCT_POINTER_TEST(_field, _state, _test, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .field_exists = (_test), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_value(_state, _field, _type), \ } #define VMSTATE_ARRAY_OF_POINTER(_field, _state, _num, _version, _info, _type) {\ @@ -474,16 +481,6 @@ extern const VMStateInfo vmstate_info_unused_buffer; .offset = vmstate_offset_array(_state, _field, _type, _num), \ } -#define VMSTATE_STRUCT_ARRAY_SIZE_UINT8(_field, _state, _field__num, _version, _vmsd, _type) { \ - .name = (stringify(_field)), \ - .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \ - .version_id = (_version), \ - .vmsd = &(_vmsd), \ - .size = sizeof(_type), \ - .flags = VMS_STRUCT|VMS_ARRAY, \ - .offset = vmstate_offset_array(_state, _field, _type, _num), \ -} - #define VMSTATE_STATIC_BUFFER(_field, _state, _version, _test, _start, _size) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -494,6 +491,29 @@ extern const VMStateInfo vmstate_info_unused_buffer; .offset = vmstate_offset_buffer(_state, _field) + _start, \ } +#define VMSTATE_BUFFER_MULTIPLY(_field, _state, _version, _test, _start, _field_size, _multiply) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\ + .size = (_multiply), \ + .info = &vmstate_info_buffer, \ + .flags = VMS_VBUFFER|VMS_MULTIPLY, \ + .offset = offsetof(_state, _field), \ + .start = (_start), \ +} + +#define VMSTATE_VBUFFER(_field, _state, _version, _test, _start, _field_size) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .size_offset = vmstate_offset_value(_state, _field_size, int32_t),\ + .info = &vmstate_info_buffer, \ + .flags = VMS_VBUFFER|VMS_POINTER, \ + .offset = offsetof(_state, _field), \ + .start = (_start), \ +} + #define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -549,7 +569,7 @@ extern const VMStateDescription vmstate_i2c_slave; #define VMSTATE_MACADDR(_field, _state) { \ .name = (stringify(_field)), \ .size = sizeof(MACAddr), \ - .info = &vmstate_info_uint8, \ + .info = &vmstate_info_buffer, \ .flags = VMS_BUFFER, \ .offset = vmstate_offset_macaddr(_state, _field), \ } @@ -564,6 +584,12 @@ extern const VMStateDescription vmstate_i2c_slave; #define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \ VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type) +#define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) \ + VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type) + +#define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \ + VMSTATE_STRUCT_POINTER_TEST(_field, _state, NULL, _vmsd, _type) + #define VMSTATE_INT8_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int8, int8_t) #define VMSTATE_INT16_V(_f, _s, _v) \ @@ -624,6 +650,9 @@ extern const VMStateDescription vmstate_i2c_slave; #define VMSTATE_INT32_LE(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t) +#define VMSTATE_UINT16_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint16, uint16_t) + #define VMSTATE_UINT32_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_uint32, uint32_t) @@ -696,6 +725,12 @@ extern const VMStateDescription vmstate_i2c_slave; #define VMSTATE_BUFFER_START_MIDDLE(_f, _s, _start) \ VMSTATE_STATIC_BUFFER(_f, _s, 0, NULL, _start, sizeof(typeof_field(_s, _f))) +#define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \ + VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size) + +#define VMSTATE_SUB_VBUFFER(_f, _s, _start, _size) \ + VMSTATE_VBUFFER(_f, _s, 0, NULL, _start, _size) + #define VMSTATE_BUFFER_TEST(_f, _s, _test) \ VMSTATE_STATIC_BUFFER(_f, _s, 0, _test, 0, sizeof(typeof_field(_s, _f))) diff --git a/hw/ide/core.c b/hw/ide/core.c index 7b1ff8f95..49bbdcde1 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -473,7 +473,7 @@ void ide_dma_error(IDEState *s) static int ide_handle_write_error(IDEState *s, int error, int op) { - BlockInterfaceErrorAction action = drive_get_onerror(s->bs); + BlockInterfaceErrorAction action = drive_get_on_error(s->bs, 0); if (action == BLOCK_ERR_IGNORE) return 0; diff --git a/hw/lan9118.c b/hw/lan9118.c index 6394f3a73..ba982d111 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -137,7 +137,7 @@ typedef struct { typedef struct { SysBusDevice busdev; - VLANClientState *vc; + NICState *nic; NICConf conf; qemu_irq irq; int mmio_index; @@ -212,7 +212,7 @@ static void lan9118_update(lan9118_state *s) static void lan9118_mac_changed(lan9118_state *s) { - qemu_format_nic_info_str(s->vc, s->conf.macaddr.a); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); } static void lan9118_reload_eeprom(lan9118_state *s) @@ -234,16 +234,16 @@ static void lan9118_reload_eeprom(lan9118_state *s) static void phy_update_link(lan9118_state *s) { /* Autonegotiation status mirrors link status. */ - if (s->vc->link_down) { + if (s->nic->nc.link_down) { s->phy_status &= ~0x0024; } else { s->phy_status |= 0x0024; } } -static void lan9118_set_link(VLANClientState *vc) +static void lan9118_set_link(VLANClientState *nc) { - phy_update_link(vc->opaque); + phy_update_link(DO_UPCAST(NICState, nc, nc)->opaque); } static void phy_reset(lan9118_state *s) @@ -305,7 +305,7 @@ static void lan9118_reset(DeviceState *d) lan9118_reload_eeprom(s); } -static int lan9118_can_receive(VLANClientState *vc) +static int lan9118_can_receive(VLANClientState *nc) { return 1; } @@ -358,10 +358,10 @@ static int lan9118_filter(lan9118_state *s, const uint8_t *addr) } } -static ssize_t lan9118_receive(VLANClientState *vc, const uint8_t *buf, +static ssize_t lan9118_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - lan9118_state *s = vc->opaque; + lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque; int fifo_len; int offset; int src_pos; @@ -506,9 +506,9 @@ static void do_tx_packet(lan9118_state *s) /* FIXME: Honor TX disable, and allow queueing of packets. */ if (s->phy_control & 0x4000) { /* This assumes the receive routine doesn't touch the VLANClient. */ - lan9118_receive(s->vc, s->txp->data, s->txp->len); + lan9118_receive(&s->nic->nc, s->txp->data, s->txp->len); } else { - qemu_send_packet(s->vc, s->txp->data, s->txp->len); + qemu_send_packet(&s->nic->nc, s->txp->data, s->txp->len); } s->txp->fifo_used = 0; @@ -1022,13 +1022,22 @@ static CPUWriteMemoryFunc * const lan9118_writefn[] = { lan9118_writel }; -static void lan9118_cleanup(VLANClientState *vc) +static void lan9118_cleanup(VLANClientState *nc) { - lan9118_state *s = vc->opaque; + lan9118_state *s = DO_UPCAST(NICState, nc, nc)->opaque; - s->vc = NULL; + s->nic = NULL; } +static NetClientInfo net_lan9118_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = lan9118_can_receive, + .receive = lan9118_receive, + .cleanup = lan9118_cleanup, + .link_status_changed = lan9118_set_link, +}; + static int lan9118_init1(SysBusDevice *dev) { lan9118_state *s = FROM_SYSBUS(lan9118_state, dev); @@ -1040,13 +1049,9 @@ static int lan9118_init1(SysBusDevice *dev) sysbus_init_irq(dev, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - s->conf.vlan, s->conf.peer, - dev->qdev.info->name, dev->qdev.id, - lan9118_can_receive, lan9118_receive, NULL, - NULL, lan9118_cleanup, s); - s->vc->link_status_changed = lan9118_set_link; - qemu_format_nic_info_str(s->vc, s->conf.macaddr.a); + s->nic = qemu_new_nic(&net_lan9118_info, &s->conf, + dev->qdev.info->name, dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); s->eeprom[0] = 0xa5; for (i = 0; i < 6; i++) { s->eeprom[i + 1] = s->conf.macaddr.a[i]; diff --git a/hw/lance.c b/hw/lance.c index 0a96644b4..b6b04ddb9 100644 --- a/hw/lance.c +++ b/hw/lance.c @@ -92,14 +92,32 @@ static CPUWriteMemoryFunc * const lance_mem_write[3] = { NULL, }; -static void lance_cleanup(VLANClientState *vc) +static void lance_cleanup(VLANClientState *nc) { - PCNetState *d = vc->opaque; + PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; - vmstate_unregister(&vmstate_pcnet, d); pcnet_common_cleanup(d); } +static NetClientInfo net_lance_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = pcnet_can_receive, + .receive = pcnet_receive, + .cleanup = lance_cleanup, +}; + +static const VMStateDescription vmstate_lance = { + .name = "pcnet", + .version_id = 3, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField []) { + VMSTATE_STRUCT(state, SysBusPCNetState, 0, vmstate_pcnet, PCNetState), + VMSTATE_END_OF_LIST() + } +}; + static int lance_init(SysBusDevice *dev) { SysBusPCNetState *d = FROM_SYSBUS(SysBusPCNetState, dev); @@ -116,9 +134,7 @@ static int lance_init(SysBusDevice *dev) s->phys_mem_read = ledma_memory_read; s->phys_mem_write = ledma_memory_write; - - vmstate_register(-1, &vmstate_pcnet, d); - return pcnet_common_init(&dev->qdev, s, lance_cleanup); + return pcnet_common_init(&dev->qdev, s, &net_lance_info); } static void lance_reset(DeviceState *dev) @@ -133,6 +149,7 @@ static SysBusDeviceInfo lance_info = { .qdev.name = "lance", .qdev.size = sizeof(SysBusPCNetState), .qdev.reset = lance_reset, + .qdev.vmsd = &vmstate_lance, .qdev.props = (Property[]) { DEFINE_PROP_PTR("dma", SysBusPCNetState, state.dma_opaque), DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf), diff --git a/hw/lm832x.c b/hw/lm832x.c index 7e552c720..ce7dcac68 100644 --- a/hw/lm832x.c +++ b/hw/lm832x.c @@ -471,7 +471,6 @@ static int lm8323_init(i2c_slave *i2c) lm_kbd_reset(s); qemu_register_reset((void *) lm_kbd_reset, s); - vmstate_register(-1, &vmstate_lm_kbd, s); return 0; } @@ -498,6 +497,7 @@ void lm832x_key_event(struct i2c_slave *i2c, int key, int state) static I2CSlaveInfo lm8323_info = { .qdev.name = "lm8323", .qdev.size = sizeof(LM823KbdState), + .qdev.vmsd = &vmstate_lm_kbd, .init = lm8323_init, .event = lm_i2c_event, .recv = lm_i2c_rx, diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 8b8a80b89..9abac94c2 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -10,7 +10,7 @@ /* ??? Need to check if the {read,write}[wl] routines work properly on big-endian targets. */ -#include <assert.h> \ +#include <assert.h> #include "hw.h" #include "pci.h" @@ -154,6 +154,9 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) #define LSI_CCNTL1_DDAC 0x08 #define LSI_CCNTL1_ZMOD 0x80 +/* Enable Response to Reselection */ +#define LSI_SCID_RRE 0x60 + #define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD) #define PHASE_DO 0 @@ -185,7 +188,7 @@ typedef struct { int carry; /* ??? Should this be an a visible register somewhere? */ int sense; /* Action to take at the end of a MSG IN phase. - 0 = COMMAND, 1 = disconect, 2 = DATA OUT, 3 = DATA IN. */ + 0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN. */ int msg_action; int msg_len; uint8_t msg[LSI_MAX_MSGIN_LEN]; @@ -272,6 +275,11 @@ typedef struct { uint32_t script_ram[2048]; } LSIState; +static inline int lsi_irq_on_rsl(LSIState *s) +{ + return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE); +} + static void lsi_soft_reset(LSIState *s) { DPRINTF("Reset\n"); @@ -362,6 +370,7 @@ static int lsi_dma_64bit(LSIState *s) static uint8_t lsi_reg_readb(LSIState *s, int offset); static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val); static void lsi_execute_script(LSIState *s); +static void lsi_reselect(LSIState *s, uint32_t tag); static inline uint32_t read_dword(LSIState *s, uint32_t addr) { @@ -382,6 +391,7 @@ static void lsi_stop_script(LSIState *s) static void lsi_update_irq(LSIState *s) { + int i; int level; static int last_level; @@ -413,6 +423,17 @@ static void lsi_update_irq(LSIState *s) last_level = level; } qemu_set_irq(s->dev.irq[0], level); + + if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { + DPRINTF("Handled IRQs & disconnected, looking for pending " + "processes\n"); + for (i = 0; i < s->active_commands; i++) { + if (s->queue[i].pending) { + lsi_reselect(s, s->queue[i].tag); + break; + } + } + } } /* Stop SCRIPTS execution and raise a SCSI interrupt. */ @@ -585,6 +606,10 @@ static void lsi_reselect(LSIState *s, uint32_t tag) } id = (tag >> 8) & 0xf; s->ssid = id | 0x80; + /* LSI53C700 Family Compatibility, see LSI53C895A 4-73 */ + if (!s->dcntl & LSI_DCNTL_COM) { + s->sfbr = 1 << (id & 0x7); + } DPRINTF("Reselected target %d\n", id); s->current_dev = s->bus.devs[id]; s->current_tag = tag; @@ -603,6 +628,10 @@ static void lsi_reselect(LSIState *s, uint32_t tag) if (n != s->active_commands) { s->queue[n] = s->queue[s->active_commands]; } + + if (lsi_irq_on_rsl(s)) { + lsi_script_scsi_interrupt(s, LSI_SIST0_RSL, 0); + } } /* Record that data is available for a queued command. Returns zero if @@ -618,7 +647,14 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg) BADF("Multiple IO pending for tag %d\n", tag); } p->pending = arg; - if (s->waiting == 1) { + /* Reselect if waiting for it, or if reselection triggers an IRQ + and the bus is free. + Since no interrupt stacking is implemented in the emulation, it + is also required that there are no pending interrupts waiting + for service from the device driver. */ + if (s->waiting == 1 || + (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && + !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { /* Reselect device. */ lsi_reselect(s, tag); return 0; @@ -655,10 +691,13 @@ static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag, return; } - if (s->waiting == 1 || tag != s->current_tag) { + if (s->waiting == 1 || tag != s->current_tag || + (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { if (lsi_queue_tag(s, tag, arg)) return; } + + /* host adapter (re)connected */ DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg); s->current_dma_len = arg; s->command_complete = 1; @@ -786,7 +825,7 @@ static void lsi_do_msgout(LSIState *s) s->sfbr = msg; switch (msg) { - case 0x00: + case 0x04: DPRINTF("MSG: Disconnect\n"); lsi_disconnect(s); break; @@ -1026,7 +1065,7 @@ again: if (insn & (1 << 25)) { id = read_dword(s, s->dsa + sxt24(insn)); } else { - id = addr; + id = insn; } id = (id >> 16) & 0xf; if (insn & (1 << 26)) { @@ -1036,8 +1075,9 @@ again: switch (opcode) { case 0: /* Select */ s->sdid = id; - if (s->current_dma_len && (s->ssid & 0xf) == id) { - DPRINTF("Already reselected by target %d\n", id); + if (s->scntl1 & LSI_SCNTL1_CON) { + DPRINTF("Already reselected, jumping to alternative address\n"); + s->dsp = s->dnad; break; } s->sstat0 |= LSI_SSTAT0_WOA; @@ -1062,11 +1102,13 @@ again: lsi_set_phase(s, PHASE_MO); break; case 1: /* Disconnect */ - DPRINTF("Wait Disconect\n"); + DPRINTF("Wait Disconnect\n"); s->scntl1 &= ~LSI_SCNTL1_CON; break; case 2: /* Wait Reselect */ - lsi_wait_reselect(s); + if (!lsi_irq_on_rsl(s)) { + lsi_wait_reselect(s); + } break; case 3: /* Set */ DPRINTF("Set%s%s%s%s\n", @@ -1554,9 +1596,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) SCRIPTS register move instructions are. */ s->sfbr = val; break; - case 0x0a: case 0x0b: + case 0x0a: case 0x0b: /* Openserver writes to these readonly registers on startup */ - return; + return; case 0x0c: case 0x0d: case 0x0e: case 0x0f: /* Linux writes to these readonly registers on startup. */ return; @@ -2106,7 +2148,6 @@ static int lsi_scsi_init(PCIDevice *dev) if (!dev->qdev.hotplugged) { scsi_bus_legacy_handle_cmdline(&s->bus); } - vmstate_register(-1, &vmstate_lsi_scsi, s); return 0; } @@ -2114,6 +2155,7 @@ static PCIDeviceInfo lsi_info = { .qdev.name = "lsi53c895a", .qdev.alias = "lsi", .qdev.size = sizeof(LSIState), + .qdev.vmsd = &vmstate_lsi_scsi, .init = lsi_scsi_init, .exit = lsi_scsi_uninit, }; diff --git a/hw/max7310.c b/hw/max7310.c index 0ce6ac9ec..c302eb6aa 100644 --- a/hw/max7310.c +++ b/hw/max7310.c @@ -184,7 +184,6 @@ static int max7310_init(i2c_slave *i2c) max7310_reset(&s->i2c); - vmstate_register(-1, &vmstate_max7310, s); return 0; } @@ -206,6 +205,7 @@ void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler) static I2CSlaveInfo max7310_info = { .qdev.name = "max7310", .qdev.size = sizeof(MAX7310State), + .qdev.vmsd = &vmstate_max7310, .init = max7310_init, .event = max7310_event, .recv = max7310_rx, diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c index 9f0d0f4cb..4e7fbed16 100644 --- a/hw/mcf_fec.c +++ b/hw/mcf_fec.c @@ -25,7 +25,8 @@ do { printf("mcf_fec: " fmt , ## __VA_ARGS__); } while (0) typedef struct { qemu_irq *irq; int mmio_index; - VLANClientState *vc; + NICState *nic; + NICConf conf; uint32_t irq_state; uint32_t eir; uint32_t eimr; @@ -42,7 +43,6 @@ typedef struct { uint32_t erdsr; uint32_t etdsr; uint32_t emrbr; - uint8_t macaddr[6]; } mcf_fec_state; #define FEC_INT_HB 0x80000000 @@ -172,7 +172,7 @@ static void mcf_fec_do_tx(mcf_fec_state *s) if (bd.flags & FEC_BD_L) { /* Last buffer in frame. */ DPRINTF("Sending packet\n"); - qemu_send_packet(s->vc, frame, len); + qemu_send_packet(&s->nic->nc, frame, len); ptr = frame; frame_size = 0; s->eir |= FEC_INT_TXF; @@ -229,11 +229,11 @@ static uint32_t mcf_fec_read(void *opaque, target_phys_addr_t addr) case 0x084: return s->rcr; case 0x0c4: return s->tcr; case 0x0e4: /* PALR */ - return (s->macaddr[0] << 24) | (s->macaddr[1] << 16) - | (s->macaddr[2] << 8) | s->macaddr[3]; + return (s->conf.macaddr.a[0] << 24) | (s->conf.macaddr.a[1] << 16) + | (s->conf.macaddr.a[2] << 8) | s->conf.macaddr.a[3]; break; case 0x0e8: /* PAUR */ - return (s->macaddr[4] << 24) | (s->macaddr[5] << 16) | 0x8808; + return (s->conf.macaddr.a[4] << 24) | (s->conf.macaddr.a[5] << 16) | 0x8808; case 0x0ec: return 0x10000; /* OPD */ case 0x118: return 0; case 0x11c: return 0; @@ -303,14 +303,14 @@ static void mcf_fec_write(void *opaque, target_phys_addr_t addr, uint32_t value) s->eir |= FEC_INT_GRA; break; case 0x0e4: /* PALR */ - s->macaddr[0] = value >> 24; - s->macaddr[1] = value >> 16; - s->macaddr[2] = value >> 8; - s->macaddr[3] = value; + s->conf.macaddr.a[0] = value >> 24; + s->conf.macaddr.a[1] = value >> 16; + s->conf.macaddr.a[2] = value >> 8; + s->conf.macaddr.a[3] = value; break; case 0x0e8: /* PAUR */ - s->macaddr[4] = value >> 24; - s->macaddr[5] = value >> 16; + s->conf.macaddr.a[4] = value >> 24; + s->conf.macaddr.a[5] = value >> 16; break; case 0x0ec: /* OPD */ @@ -347,15 +347,15 @@ static void mcf_fec_write(void *opaque, target_phys_addr_t addr, uint32_t value) mcf_fec_update(s); } -static int mcf_fec_can_receive(VLANClientState *vc) +static int mcf_fec_can_receive(VLANClientState *nc) { - mcf_fec_state *s = vc->opaque; + mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; return s->rx_enabled; } -static ssize_t mcf_fec_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t mcf_fec_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - mcf_fec_state *s = vc->opaque; + mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; mcf_fec_bd bd; uint32_t flags = 0; uint32_t addr; @@ -441,15 +441,23 @@ static CPUWriteMemoryFunc * const mcf_fec_writefn[] = { mcf_fec_write }; -static void mcf_fec_cleanup(VLANClientState *vc) +static void mcf_fec_cleanup(VLANClientState *nc) { - mcf_fec_state *s = vc->opaque; + mcf_fec_state *s = DO_UPCAST(NICState, nc, nc)->opaque; cpu_unregister_io_memory(s->mmio_index); qemu_free(s); } +static NetClientInfo net_mcf_fec_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = mcf_fec_can_receive, + .receive = mcf_fec_receive, + .cleanup = mcf_fec_cleanup, +}; + void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq) { mcf_fec_state *s; @@ -462,11 +470,11 @@ void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq) mcf_fec_writefn, s); cpu_register_physical_memory(base, 0x400, s->mmio_index); - s->vc = nd->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - nd->vlan, nd->netdev, - nd->model, nd->name, - mcf_fec_can_receive, mcf_fec_receive, - NULL, NULL, mcf_fec_cleanup, s); - memcpy(s->macaddr, nd->macaddr, 6); - qemu_format_nic_info_str(s->vc, s->macaddr); + memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.vlan = nd->vlan; + s->conf.peer = nd->netdev; + + s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, nd->model, nd->name, s); + + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); } diff --git a/hw/mipsnet.c b/hw/mipsnet.c index 65e1d5960..a066f6313 100644 --- a/hw/mipsnet.c +++ b/hw/mipsnet.c @@ -35,7 +35,8 @@ typedef struct MIPSnetState { uint8_t tx_buffer[MAX_ETH_FRAME_SIZE]; int io_base; qemu_irq irq; - VLANClientState *vc; + NICState *nic; + NICConf conf; } MIPSnetState; static void mipsnet_reset(MIPSnetState *s) @@ -66,23 +67,23 @@ static int mipsnet_buffer_full(MIPSnetState *s) return 0; } -static int mipsnet_can_receive(VLANClientState *vc) +static int mipsnet_can_receive(VLANClientState *nc) { - MIPSnetState *s = vc->opaque; + MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; if (s->busy) return 0; return !mipsnet_buffer_full(s); } -static ssize_t mipsnet_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t mipsnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - MIPSnetState *s = vc->opaque; + MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; #ifdef DEBUG_MIPSNET_RECEIVE printf("mipsnet: receiving len=%d\n", size); #endif - if (!mipsnet_can_receive(vc)) + if (!mipsnet_can_receive(nc)) return -1; s->busy = 1; @@ -183,7 +184,7 @@ static void mipsnet_ioport_write(void *opaque, uint32_t addr, uint32_t val) #ifdef DEBUG_MIPSNET_SEND printf("mipsnet: sending len=%d\n", s->tx_count); #endif - qemu_send_packet(s->vc, s->tx_buffer, s->tx_count); + qemu_send_packet(&s->nic->nc, s->tx_buffer, s->tx_count); s->tx_count = s->tx_written = 0; s->intctl |= MIPSNET_INTCTL_TXDONE; s->busy = 1; @@ -234,9 +235,9 @@ static int mipsnet_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static void mipsnet_cleanup(VLANClientState *vc) +static void mipsnet_cleanup(VLANClientState *nc) { - MIPSnetState *s = vc->opaque; + MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; unregister_savevm("mipsnet", s); @@ -245,6 +246,14 @@ static void mipsnet_cleanup(VLANClientState *vc) qemu_free(s); } +static NetClientInfo net_mipsnet_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = mipsnet_can_receive, + .receive = mipsnet_receive, + .cleanup = mipsnet_cleanup, +}; + void mipsnet_init (int base, qemu_irq irq, NICInfo *nd) { MIPSnetState *s; @@ -262,17 +271,17 @@ void mipsnet_init (int base, qemu_irq irq, NICInfo *nd) s->io_base = base; s->irq = irq; + if (nd) { - s->vc = nd->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - nd->vlan, nd->netdev, - nd->model, nd->name, - mipsnet_can_receive, mipsnet_receive, - NULL, NULL, mipsnet_cleanup, s); - } else { - s->vc = NULL; - } + memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.vlan = nd->vlan; + s->conf.peer = nd->netdev; - qemu_format_nic_info_str(s->vc, nd->macaddr); + s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, + nd->model, nd->name, s); + + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); + } mipsnet_reset(s); register_savevm("mipsnet", 0, 0, mipsnet_save, mipsnet_load, s); diff --git a/hw/musicpal.c b/hw/musicpal.c index 264669f61..4a33e28bc 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -151,7 +151,7 @@ typedef struct mv88w8618_eth_state { uint32_t rx_queue[4]; uint32_t frx_queue[4]; uint32_t cur_rx[4]; - VLANClientState *vc; + NICState *nic; NICConf conf; } mv88w8618_eth_state; @@ -175,14 +175,14 @@ static void eth_rx_desc_get(uint32_t addr, mv88w8618_rx_desc *desc) le32_to_cpus(&desc->next); } -static int eth_can_receive(VLANClientState *vc) +static int eth_can_receive(VLANClientState *nc) { return 1; } -static ssize_t eth_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t eth_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - mv88w8618_eth_state *s = vc->opaque; + mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque; uint32_t desc_addr; mv88w8618_rx_desc desc; int i; @@ -250,7 +250,7 @@ static void eth_send(mv88w8618_eth_state *s, int queue_index) len = desc.bytes; if (len < 2048) { cpu_physical_memory_read(desc.buffer, buf, len); - qemu_send_packet(s->vc, buf, len); + qemu_send_packet(&s->nic->nc, buf, len); } desc.cmdstat &= ~MP_ETH_TX_OWN; s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index); @@ -365,23 +365,28 @@ static CPUWriteMemoryFunc * const mv88w8618_eth_writefn[] = { mv88w8618_eth_write }; -static void eth_cleanup(VLANClientState *vc) +static void eth_cleanup(VLANClientState *nc) { - mv88w8618_eth_state *s = vc->opaque; + mv88w8618_eth_state *s = DO_UPCAST(NICState, nc, nc)->opaque; - s->vc = NULL; + s->nic = NULL; } +static NetClientInfo net_mv88w8618_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = eth_can_receive, + .receive = eth_receive, + .cleanup = eth_cleanup, +}; + static int mv88w8618_eth_init(SysBusDevice *dev) { mv88w8618_eth_state *s = FROM_SYSBUS(mv88w8618_eth_state, dev); sysbus_init_irq(dev, &s->irq); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - s->conf.vlan, s->conf.peer, - dev->qdev.info->name, dev->qdev.id, - eth_can_receive, eth_receive, NULL, - NULL, eth_cleanup, s); + s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, + dev->qdev.info->name, dev->qdev.id, s); s->mmio_index = cpu_register_io_memory(mv88w8618_eth_readfn, mv88w8618_eth_writefn, s); sysbus_init_mmio(dev, MP_ETH_SIZE, s->mmio_index); diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c index 729e8e225..d2e9283be 100644 --- a/hw/ne2000-isa.c +++ b/hw/ne2000-isa.c @@ -35,13 +35,32 @@ typedef struct ISANE2000State { NE2000State ne2000; } ISANE2000State; -static void isa_ne2000_cleanup(VLANClientState *vc) +static void isa_ne2000_cleanup(VLANClientState *nc) { - NE2000State *s = vc->opaque; + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; - s->vc = NULL; + s->nic = NULL; } +static NetClientInfo net_ne2000_isa_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = ne2000_can_receive, + .receive = ne2000_receive, + .cleanup = isa_ne2000_cleanup, +}; + +const VMStateDescription vmstate_isa_ne2000 = { + .name = "ne2000", + .version_id = 2, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField []) { + VMSTATE_STRUCT(ne2000, ISANE2000State, 0, vmstate_ne2000, NE2000State), + VMSTATE_END_OF_LIST() + } +}; + static int isa_ne2000_initfn(ISADevice *dev) { ISANE2000State *isa = DO_UPCAST(ISANE2000State, dev, dev); @@ -63,13 +82,10 @@ static int isa_ne2000_initfn(ISADevice *dev) qemu_macaddr_default_if_unset(&s->c.macaddr); ne2000_reset(s); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, s->c.vlan, s->c.peer, - dev->qdev.info->name, dev->qdev.id, - ne2000_can_receive, ne2000_receive, NULL, - NULL, isa_ne2000_cleanup, s); - qemu_format_nic_info_str(s->vc, s->c.macaddr.a); + s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c, + dev->qdev.info->name, dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a); - vmstate_register(-1, &vmstate_ne2000, s); return 0; } diff --git a/hw/ne2000.c b/hw/ne2000.c index 63efc3a1d..e0655ffa4 100644 --- a/hw/ne2000.c +++ b/hw/ne2000.c @@ -188,9 +188,9 @@ static int ne2000_buffer_full(NE2000State *s) return 0; } -int ne2000_can_receive(VLANClientState *vc) +int ne2000_can_receive(VLANClientState *nc) { - NE2000State *s = vc->opaque; + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; if (s->cmd & E8390_STOP) return 1; @@ -199,9 +199,9 @@ int ne2000_can_receive(VLANClientState *vc) #define MIN_BUF_SIZE 60 -ssize_t ne2000_receive(VLANClientState *vc, const uint8_t *buf, size_t size_) +ssize_t ne2000_receive(VLANClientState *nc, const uint8_t *buf, size_t size_) { - NE2000State *s = vc->opaque; + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; int size = size_; uint8_t *p; unsigned int total_len, next, avail, len, index, mcast_idx; @@ -323,7 +323,7 @@ void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) index -= NE2000_PMEM_SIZE; /* fail safe: check range on the transmitted length */ if (index + s->tcnt <= NE2000_PMEM_END) { - qemu_send_packet(s->vc, s->mem + index, s->tcnt); + qemu_send_packet(&s->nic->nc, s->mem + index, s->tcnt); } /* signal end of transfer */ s->tsr = ENTSR_PTX; @@ -698,13 +698,21 @@ static void ne2000_map(PCIDevice *pci_dev, int region_num, register_ioport_read(addr + 0x1f, 1, 1, ne2000_reset_ioport_read, s); } -static void ne2000_cleanup(VLANClientState *vc) +static void ne2000_cleanup(VLANClientState *nc) { - NE2000State *s = vc->opaque; + NE2000State *s = DO_UPCAST(NICState, nc, nc)->opaque; - s->vc = NULL; + s->nic = NULL; } +static NetClientInfo net_ne2000_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = ne2000_can_receive, + .receive = ne2000_receive, + .cleanup = ne2000_cleanup, +}; + static int pci_ne2000_init(PCIDevice *pci_dev) { PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); @@ -725,11 +733,10 @@ static int pci_ne2000_init(PCIDevice *pci_dev) qemu_macaddr_default_if_unset(&s->c.macaddr); ne2000_reset(s); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, s->c.vlan, s->c.peer, - pci_dev->qdev.info->name, pci_dev->qdev.id, - ne2000_can_receive, ne2000_receive, NULL, - NULL, ne2000_cleanup, s); - qemu_format_nic_info_str(s->vc, s->c.macaddr.a); + + s->nic = qemu_new_nic(&net_ne2000_info, &s->c, + pci_dev->qdev.info->name, pci_dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->c.macaddr.a); if (!pci_dev->qdev.hotplugged) { static int loaded = 0; @@ -739,7 +746,6 @@ static int pci_ne2000_init(PCIDevice *pci_dev) } } - vmstate_register(-1, &vmstate_pci_ne2000, d); return 0; } @@ -748,14 +754,14 @@ static int pci_ne2000_exit(PCIDevice *pci_dev) PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); NE2000State *s = &d->ne2000; - vmstate_unregister(&vmstate_pci_ne2000, s); - qemu_del_vlan_client(s->vc); + qemu_del_vlan_client(&s->nic->nc); return 0; } static PCIDeviceInfo ne2000_info = { .qdev.name = "ne2k_pci", .qdev.size = sizeof(PCINE2000State), + .qdev.vmsd = &vmstate_pci_ne2000, .init = pci_ne2000_init, .exit = pci_ne2000_exit, .qdev.props = (Property[]) { diff --git a/hw/ne2000.h b/hw/ne2000.h index 2bbce71e6..54fdfca13 100644 --- a/hw/ne2000.h +++ b/hw/ne2000.h @@ -22,7 +22,7 @@ typedef struct NE2000State { uint8_t curpag; uint8_t mult[8]; /* multicast mask array */ qemu_irq irq; - VLANClientState *vc; + NICState *nic; NICConf c; uint8_t mem[NE2000_MEM_SIZE]; } NE2000State; @@ -164,8 +164,6 @@ PCIBus *pci_find_root_bus(int domain) void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, const char *name, int devfn_min) { - static int nbus = 0; - qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name); bus->devfn_min = devfn_min; @@ -173,7 +171,7 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, QLIST_INIT(&bus->child); pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */ - vmstate_register(nbus++, &vmstate_pcibus, bus); + vmstate_register(-1, &vmstate_pcibus, bus); qemu_register_reset(pci_bus_reset, bus); } diff --git a/hw/pckbd.c b/hw/pckbd.c index a81b303da..7e0d68df2 100644 --- a/hw/pckbd.c +++ b/hw/pckbd.c @@ -414,6 +414,17 @@ typedef struct ISAKBDState { KBDState kbd; } ISAKBDState; +const VMStateDescription vmstate_kbd_isa = { + .name = "pckbd", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, + .fields = (VMStateField []) { + VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState), + VMSTATE_END_OF_LIST() + } +}; + static int i8042_initfn(ISADevice *dev) { KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd); @@ -421,7 +432,6 @@ static int i8042_initfn(ISADevice *dev) isa_init_irq(dev, &s->irq_kbd, 1); isa_init_irq(dev, &s->irq_mouse, 12); - vmstate_register(0, &vmstate_kbd, s); register_ioport_read(0x60, 1, 1, kbd_read_data, s); register_ioport_write(0x60, 1, 1, kbd_write_data, s); register_ioport_read(0x64, 1, 1, kbd_read_status, s); @@ -439,6 +449,7 @@ static int i8042_initfn(ISADevice *dev) static ISADeviceInfo i8042_info = { .qdev.name = "i8042", .qdev.size = sizeof(ISAKBDState), + .qdev.vmsd = &vmstate_kbd_isa, .qdev.no_user = 1, .init = i8042_initfn, }; diff --git a/hw/pcnet.c b/hw/pcnet.c index ee3db0939..138fbc6d0 100644 --- a/hw/pcnet.c +++ b/hw/pcnet.c @@ -1028,9 +1028,9 @@ static int pcnet_tdte_poll(PCNetState *s) return !!(CSR_CXST(s) & 0x8000); } -static int pcnet_can_receive(VLANClientState *vc) +int pcnet_can_receive(VLANClientState *nc) { - PCNetState *s = vc->opaque; + PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; if (CSR_STOP(s) || CSR_SPND(s)) return 0; @@ -1039,9 +1039,9 @@ static int pcnet_can_receive(VLANClientState *vc) #define MIN_BUF_SIZE 60 -static ssize_t pcnet_receive(VLANClientState *vc, const uint8_t *buf, size_t size_) +ssize_t pcnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size_) { - PCNetState *s = vc->opaque; + PCNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; int is_padr = 0, is_bcast = 0, is_ladr = 0; uint8_t buf1[60]; int remaining; @@ -1268,11 +1268,11 @@ static void pcnet_transmit(PCNetState *s) if (BCR_SWSTYLE(s) == 1) add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; - pcnet_receive(s->vc, s->buffer, s->xmit_pos); + pcnet_receive(&s->nic->nc, s->buffer, s->xmit_pos); s->looptest = 0; } else - if (s->vc) - qemu_send_packet(s->vc, s->buffer, s->xmit_pos); + if (s->nic) + qemu_send_packet(&s->nic->nc, s->buffer, s->xmit_pos); s->csr[0] &= ~0x0008; /* clear TDMD */ s->csr[4] |= 0x0004; /* set TXSTRT */ @@ -1888,21 +1888,16 @@ static const VMStateDescription vmstate_pci_pcnet = { void pcnet_common_cleanup(PCNetState *d) { - d->vc = NULL; + d->nic = NULL; } -int pcnet_common_init(DeviceState *dev, PCNetState *s, - NetCleanup *cleanup) +int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) { s->poll_timer = qemu_new_timer(vm_clock, pcnet_poll_timer, s); qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - s->conf.vlan, s->conf.peer, - dev->info->name, dev->id, - pcnet_can_receive, pcnet_receive, NULL, NULL, - cleanup, s); - qemu_format_nic_info_str(s->vc, s->conf.macaddr.a); + s->nic = qemu_new_nic(info, &s->conf, dev->info->name, dev->id, s); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); return 0; } @@ -1945,9 +1940,9 @@ static void pci_physical_memory_read(void *dma_opaque, target_phys_addr_t addr, cpu_physical_memory_read(addr, buf, len); } -static void pci_pcnet_cleanup(VLANClientState *vc) +static void pci_pcnet_cleanup(VLANClientState *nc) { - PCNetState *d = vc->opaque; + PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; pcnet_common_cleanup(d); } @@ -1957,13 +1952,20 @@ static int pci_pcnet_uninit(PCIDevice *dev) PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev); cpu_unregister_io_memory(d->state.mmio_index); - vmstate_unregister(&vmstate_pci_pcnet, d); qemu_del_timer(d->state.poll_timer); qemu_free_timer(d->state.poll_timer); - qemu_del_vlan_client(d->state.vc); + qemu_del_vlan_client(&d->state.nic->nc); return 0; } +static NetClientInfo net_pci_pcnet_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = pcnet_can_receive, + .receive = pcnet_receive, + .cleanup = pci_pcnet_cleanup, +}; + static int pci_pcnet_init(PCIDevice *pci_dev) { PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev); @@ -2007,8 +2009,6 @@ static int pci_pcnet_init(PCIDevice *pci_dev) s->phys_mem_read = pci_physical_memory_read; s->phys_mem_write = pci_physical_memory_write; - vmstate_register(-1, &vmstate_pci_pcnet, d); - if (!pci_dev->qdev.hotplugged) { static int loaded = 0; if (!loaded) { @@ -2017,7 +2017,7 @@ static int pci_pcnet_init(PCIDevice *pci_dev) } } - return pcnet_common_init(&pci_dev->qdev, s, pci_pcnet_cleanup); + return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info); } static void pci_reset(DeviceState *dev) @@ -2031,6 +2031,7 @@ static PCIDeviceInfo pcnet_info = { .qdev.name = "pcnet", .qdev.size = sizeof(PCIPCNetState), .qdev.reset = pci_reset, + .qdev.vmsd = &vmstate_pci_pcnet, .init = pci_pcnet_init, .exit = pci_pcnet_uninit, .qdev.props = (Property[]) { diff --git a/hw/pcnet.h b/hw/pcnet.h index e61d5a447..efacc9fa5 100644 --- a/hw/pcnet.h +++ b/hw/pcnet.h @@ -8,7 +8,7 @@ typedef struct PCNetState_st PCNetState; struct PCNetState_st { - VLANClientState *vc; + NICState *nic; NICConf conf; QEMUTimer *poll_timer; int rap, isr, lnkst; @@ -32,7 +32,8 @@ struct PCNetState_st { void pcnet_h_reset(void *opaque); void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val); uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr); +int pcnet_can_receive(VLANClientState *nc); +ssize_t pcnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size_); void pcnet_common_cleanup(PCNetState *d); -int pcnet_common_init(DeviceState *dev, PCNetState *s, - NetCleanup *cleanup); +int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info); extern const VMStateDescription vmstate_pcnet; diff --git a/hw/piix_pci.c b/hw/piix_pci.c index ee6b725cf..001bc93cd 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -204,7 +204,6 @@ static int i440fx_initfn(PCIDevice *dev) d->dev.config[0x72] = 0x02; /* SMRAM */ - vmstate_register(0, &vmstate_i440fx, d); return 0; } @@ -329,7 +328,6 @@ static int piix3_initfn(PCIDevice *dev) uint8_t *pci_conf; isa_bus_new(&d->dev.qdev); - vmstate_register(0, &vmstate_piix3, d); pci_conf = d->dev.config; pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); @@ -347,6 +345,7 @@ static PCIDeviceInfo i440fx_info[] = { .qdev.name = "i440FX", .qdev.desc = "Host bridge", .qdev.size = sizeof(PCII440FXState), + .qdev.vmsd = &vmstate_i440fx, .qdev.no_user = 1, .init = i440fx_initfn, .config_write = i440fx_write_config, @@ -354,6 +353,7 @@ static PCIDeviceInfo i440fx_info[] = { .qdev.name = "PIIX3", .qdev.desc = "ISA bridge", .qdev.size = sizeof(PIIX3State), + .qdev.vmsd = &vmstate_piix3, .qdev.no_user = 1, .init = piix3_initfn, },{ @@ -296,10 +296,8 @@ void qdev_free(DeviceState *dev) bus = QLIST_FIRST(&dev->child_bus); qbus_free(bus); } -#if 0 /* FIXME: need sane vmstate_unregister function */ if (dev->info->vmsd) vmstate_unregister(dev->info->vmsd, dev); -#endif if (dev->info->exit) dev->info->exit(dev); if (dev->opts) @@ -154,16 +154,6 @@ CharDriverState *qdev_init_chardev(DeviceState *dev); BusState *qdev_get_parent_bus(DeviceState *dev); -/* Convert from a base type to a parent type, with compile time checking. */ -#ifdef __GNUC__ -#define DO_UPCAST(type, field, dev) ( __extension__ ( { \ - char __attribute__((unused)) offset_must_be_zero[ \ - -offsetof(type, field)]; \ - container_of(dev, type, field);})) -#else -#define DO_UPCAST(type, field, dev) container_of(dev, type, field) -#endif - /*** BUS API. ***/ void qbus_create_inplace(BusState *bus, BusInfo *info, diff --git a/hw/rtl8139.c b/hw/rtl8139.c index be47f61c2..9fd05a8a1 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -459,7 +459,7 @@ typedef struct RTL8139State { uint16_t CpCmd; uint8_t TxThresh; - VLANClientState *vc; + NICState *nic; NICConf conf; int rtl8139_mmio_io_addr; @@ -785,9 +785,9 @@ static inline target_phys_addr_t rtl8139_addr64(uint32_t low, uint32_t high) #endif } -static int rtl8139_can_receive(VLANClientState *vc) +static int rtl8139_can_receive(VLANClientState *nc) { - RTL8139State *s = vc->opaque; + RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; int avail; /* Receive (drop) packets if card is disabled. */ @@ -807,9 +807,9 @@ static int rtl8139_can_receive(VLANClientState *vc) } } -static ssize_t rtl8139_do_receive(VLANClientState *vc, const uint8_t *buf, size_t size_, int do_interrupt) +static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt) { - RTL8139State *s = vc->opaque; + RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; int size = size_; uint32_t packet_header = 0; @@ -1156,9 +1156,9 @@ static ssize_t rtl8139_do_receive(VLANClientState *vc, const uint8_t *buf, size_ return size_; } -static ssize_t rtl8139_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t rtl8139_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - return rtl8139_do_receive(vc, buf, size, 1); + return rtl8139_do_receive(nc, buf, size, 1); } static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize) @@ -1744,11 +1744,11 @@ static void rtl8139_transfer_frame(RTL8139State *s, const uint8_t *buf, int size if (TxLoopBack == (s->TxConfig & TxLoopBack)) { DEBUG_PRINT(("RTL8139: +++ transmit loopback mode\n")); - rtl8139_do_receive(s->vc, buf, size, do_interrupt); + rtl8139_do_receive(&s->nic->nc, buf, size, do_interrupt); } else { - qemu_send_packet(s->vc, buf, size); + qemu_send_packet(&s->nic->nc, buf, size); } } @@ -3280,11 +3280,11 @@ static void rtl8139_timer(void *opaque) } #endif /* RTL8139_ONBOARD_TIMER */ -static void rtl8139_cleanup(VLANClientState *vc) +static void rtl8139_cleanup(VLANClientState *nc) { - RTL8139State *s = vc->opaque; + RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque; - s->vc = NULL; + s->nic = NULL; } static int pci_rtl8139_uninit(PCIDevice *dev) @@ -3300,11 +3300,18 @@ static int pci_rtl8139_uninit(PCIDevice *dev) qemu_del_timer(s->timer); qemu_free_timer(s->timer); #endif - vmstate_unregister(&vmstate_rtl8139, s); - qemu_del_vlan_client(s->vc); + qemu_del_vlan_client(&s->nic->nc); return 0; } +static NetClientInfo net_rtl8139_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = rtl8139_can_receive, + .receive = rtl8139_receive, + .cleanup = rtl8139_cleanup, +}; + static int pci_rtl8139_init(PCIDevice *dev) { RTL8139State * s = DO_UPCAST(RTL8139State, dev, dev); @@ -3332,19 +3339,14 @@ static int pci_rtl8139_init(PCIDevice *dev) qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - s->conf.vlan, s->conf.peer, - dev->qdev.info->name, dev->qdev.id, - rtl8139_can_receive, rtl8139_receive, NULL, - NULL, rtl8139_cleanup, s); - qemu_format_nic_info_str(s->vc, s->conf.macaddr.a); + s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf, + dev->qdev.info->name, dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); s->cplus_txbuffer = NULL; s->cplus_txbuffer_len = 0; s->cplus_txbuffer_offset = 0; - vmstate_register(-1, &vmstate_rtl8139, s); - #ifdef RTL8139_ONBOARD_TIMER s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s); @@ -3366,6 +3368,7 @@ static PCIDeviceInfo rtl8139_info = { .qdev.name = "rtl8139", .qdev.size = sizeof(RTL8139State), .qdev.reset = rtl8139_reset, + .qdev.vmsd = &vmstate_rtl8139, .init = pci_rtl8139_init, .exit = pci_rtl8139_uninit, .qdev.props = (Property[]) { @@ -1244,115 +1244,10 @@ static void SB_audio_callback (void *opaque, int free) s->audio_free = free; } -static void SB_save (QEMUFile *f, void *opaque) +static int sb16_post_load (void *opaque, int version_id) { SB16State *s = opaque; - qemu_put_be32 (f, s->irq); - qemu_put_be32 (f, s->dma); - qemu_put_be32 (f, s->hdma); - qemu_put_be32 (f, s->port); - qemu_put_be32 (f, s->ver); - qemu_put_be32 (f, s->in_index); - qemu_put_be32 (f, s->out_data_len); - qemu_put_be32 (f, s->fmt_stereo); - qemu_put_be32 (f, s->fmt_signed); - qemu_put_be32 (f, s->fmt_bits); - qemu_put_be32s (f, &s->fmt); - qemu_put_be32 (f, s->dma_auto); - qemu_put_be32 (f, s->block_size); - qemu_put_be32 (f, s->fifo); - qemu_put_be32 (f, s->freq); - qemu_put_be32 (f, s->time_const); - qemu_put_be32 (f, s->speaker); - qemu_put_be32 (f, s->needed_bytes); - qemu_put_be32 (f, s->cmd); - qemu_put_be32 (f, s->use_hdma); - qemu_put_be32 (f, s->highspeed); - qemu_put_be32 (f, s->can_write); - qemu_put_be32 (f, s->v2x6); - - qemu_put_8s (f, &s->csp_param); - qemu_put_8s (f, &s->csp_value); - qemu_put_8s (f, &s->csp_mode); - qemu_put_8s (f, &s->csp_param); - qemu_put_buffer (f, s->csp_regs, 256); - qemu_put_8s (f, &s->csp_index); - qemu_put_buffer (f, s->csp_reg83, 4); - qemu_put_be32 (f, s->csp_reg83r); - qemu_put_be32 (f, s->csp_reg83w); - - qemu_put_buffer (f, s->in2_data, sizeof (s->in2_data)); - qemu_put_buffer (f, s->out_data, sizeof (s->out_data)); - qemu_put_8s (f, &s->test_reg); - qemu_put_8s (f, &s->last_read_byte); - - qemu_put_be32 (f, s->nzero); - qemu_put_be32 (f, s->left_till_irq); - qemu_put_be32 (f, s->dma_running); - qemu_put_be32 (f, s->bytes_per_second); - qemu_put_be32 (f, s->align); - - qemu_put_be32 (f, s->mixer_nreg); - qemu_put_buffer (f, s->mixer_regs, 256); -} - -static int SB_load (QEMUFile *f, void *opaque, int version_id) -{ - SB16State *s = opaque; - - if (version_id != 1) { - return -EINVAL; - } - - s->irq=qemu_get_be32 (f); - s->dma=qemu_get_be32 (f); - s->hdma=qemu_get_be32 (f); - s->port=qemu_get_be32 (f); - s->ver=qemu_get_be32 (f); - s->in_index=qemu_get_be32 (f); - s->out_data_len=qemu_get_be32 (f); - s->fmt_stereo=qemu_get_be32 (f); - s->fmt_signed=qemu_get_be32 (f); - s->fmt_bits=qemu_get_be32 (f); - qemu_get_be32s (f, &s->fmt); - s->dma_auto=qemu_get_be32 (f); - s->block_size=qemu_get_be32 (f); - s->fifo=qemu_get_be32 (f); - s->freq=qemu_get_be32 (f); - s->time_const=qemu_get_be32 (f); - s->speaker=qemu_get_be32 (f); - s->needed_bytes=qemu_get_be32 (f); - s->cmd=qemu_get_be32 (f); - s->use_hdma=qemu_get_be32 (f); - s->highspeed=qemu_get_be32 (f); - s->can_write=qemu_get_be32 (f); - s->v2x6=qemu_get_be32 (f); - - qemu_get_8s (f, &s->csp_param); - qemu_get_8s (f, &s->csp_value); - qemu_get_8s (f, &s->csp_mode); - qemu_get_8s (f, &s->csp_param); - qemu_get_buffer (f, s->csp_regs, 256); - qemu_get_8s (f, &s->csp_index); - qemu_get_buffer (f, s->csp_reg83, 4); - s->csp_reg83r=qemu_get_be32 (f); - s->csp_reg83w=qemu_get_be32 (f); - - qemu_get_buffer (f, s->in2_data, sizeof (s->in2_data)); - qemu_get_buffer (f, s->out_data, sizeof (s->out_data)); - qemu_get_8s (f, &s->test_reg); - qemu_get_8s (f, &s->last_read_byte); - - s->nzero=qemu_get_be32 (f); - s->left_till_irq=qemu_get_be32 (f); - s->dma_running=qemu_get_be32 (f); - s->bytes_per_second=qemu_get_be32 (f); - s->align=qemu_get_be32 (f); - - s->mixer_nreg=qemu_get_be32 (f); - qemu_get_buffer (f, s->mixer_regs, 256); - if (s->voice) { AUD_close_out (&s->card, s->voice); s->voice = NULL; @@ -1385,6 +1280,65 @@ static int SB_load (QEMUFile *f, void *opaque, int version_id) return 0; } +static const VMStateDescription vmstate_sb16 = { + .name = "sb16", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = sb16_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT32(irq, SB16State), + VMSTATE_UINT32(dma, SB16State), + VMSTATE_UINT32(hdma, SB16State), + VMSTATE_UINT32(port, SB16State), + VMSTATE_UINT32(ver, SB16State), + VMSTATE_INT32(in_index, SB16State), + VMSTATE_INT32(out_data_len, SB16State), + VMSTATE_INT32(fmt_stereo, SB16State), + VMSTATE_INT32(fmt_signed, SB16State), + VMSTATE_INT32(fmt_bits, SB16State), + VMSTATE_UINT32(fmt, SB16State), + VMSTATE_INT32(dma_auto, SB16State), + VMSTATE_INT32(block_size, SB16State), + VMSTATE_INT32(fifo, SB16State), + VMSTATE_INT32(freq, SB16State), + VMSTATE_INT32(time_const, SB16State), + VMSTATE_INT32(speaker, SB16State), + VMSTATE_INT32(needed_bytes, SB16State), + VMSTATE_INT32(cmd, SB16State), + VMSTATE_INT32(use_hdma, SB16State), + VMSTATE_INT32(highspeed, SB16State), + VMSTATE_INT32(can_write, SB16State), + VMSTATE_INT32(v2x6, SB16State), + + VMSTATE_UINT8(csp_param, SB16State), + VMSTATE_UINT8(csp_value, SB16State), + VMSTATE_UINT8(csp_mode, SB16State), + VMSTATE_UINT8(csp_param, SB16State), + VMSTATE_BUFFER(csp_regs, SB16State), + VMSTATE_UINT8(csp_index, SB16State), + VMSTATE_BUFFER(csp_reg83, SB16State), + VMSTATE_INT32(csp_reg83r, SB16State), + VMSTATE_INT32(csp_reg83w, SB16State), + + VMSTATE_BUFFER(in2_data, SB16State), + VMSTATE_BUFFER(out_data, SB16State), + VMSTATE_UINT8(test_reg, SB16State), + VMSTATE_UINT8(last_read_byte, SB16State), + + VMSTATE_INT32(nzero, SB16State), + VMSTATE_INT32(left_till_irq, SB16State), + VMSTATE_INT32(dma_running, SB16State), + VMSTATE_INT32(bytes_per_second, SB16State), + VMSTATE_INT32(align, SB16State), + + VMSTATE_INT32(mixer_nreg, SB16State), + VMSTATE_BUFFER(mixer_regs, SB16State), + + VMSTATE_END_OF_LIST() + } +}; + static int sb16_initfn (ISADevice *dev) { static const uint8_t dsp_write_ports[] = {0x6, 0xc}; @@ -1427,7 +1381,6 @@ static int sb16_initfn (ISADevice *dev) DMA_register_channel (s->dma, SB_read_DMA, s); s->can_write = 1; - register_savevm ("sb16", 0, 1, SB_save, SB_load, s); AUD_register_card ("sb16", &s->card); return 0; } @@ -1442,6 +1395,7 @@ static ISADeviceInfo sb16_info = { .qdev.name = "sb16", .qdev.desc = "Creative Sound Blaster 16", .qdev.size = sizeof (SB16State), + .qdev.vmsd = &vmstate_sb16, .init = sb16_initfn, .qdev.props = (Property[]) { DEFINE_PROP_HEX32 ("version", SB16State, ver, 0x0405), /* 4.5 */ diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 641db812f..736e91e91 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1,6 +1,7 @@ #include "hw.h" #include "sysemu.h" #include "scsi.h" +#include "scsi-defs.h" #include "block.h" #include "qdev.h" @@ -50,6 +51,7 @@ static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base) bus->devs[dev->id] = dev; dev->info = info; + QTAILQ_INIT(&dev->requests); rc = dev->info->init(dev); if (rc != 0) { bus->devs[dev->id] = NULL; @@ -110,3 +112,387 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) scsi_bus_legacy_add_drive(bus, dinfo, unit); } } + +void scsi_dev_clear_sense(SCSIDevice *dev) +{ + memset(&dev->sense, 0, sizeof(dev->sense)); +} + +void scsi_dev_set_sense(SCSIDevice *dev, uint8_t key) +{ + dev->sense.key = key; +} + +SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun) +{ + SCSIRequest *req; + + req = qemu_mallocz(size); + req->bus = scsi_bus_from_device(d); + req->dev = d; + req->tag = tag; + req->lun = lun; + req->status = -1; + QTAILQ_INSERT_TAIL(&d->requests, req, next); + return req; +} + +SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag) +{ + SCSIRequest *req; + + QTAILQ_FOREACH(req, &d->requests, next) { + if (req->tag == tag) { + return req; + } + } + return NULL; +} + +void scsi_req_free(SCSIRequest *req) +{ + QTAILQ_REMOVE(&req->dev->requests, req, next); + qemu_free(req); +} + +static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) +{ + switch (cmd[0] >> 5) { + case 0: + req->cmd.xfer = cmd[4]; + req->cmd.len = 6; + /* length 0 means 256 blocks */ + if (req->cmd.xfer == 0) + req->cmd.xfer = 256; + break; + case 1: + case 2: + req->cmd.xfer = cmd[8] | (cmd[7] << 8); + req->cmd.len = 10; + break; + case 4: + req->cmd.xfer = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24); + req->cmd.len = 16; + break; + case 5: + req->cmd.xfer = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24); + req->cmd.len = 12; + break; + default: + return -1; + } + + switch(cmd[0]) { + case TEST_UNIT_READY: + case REZERO_UNIT: + case START_STOP: + case SEEK_6: + case WRITE_FILEMARKS: + case SPACE: + case ERASE: + case ALLOW_MEDIUM_REMOVAL: + case VERIFY: + case SEEK_10: + case SYNCHRONIZE_CACHE: + case LOCK_UNLOCK_CACHE: + case LOAD_UNLOAD: + case SET_CD_SPEED: + case SET_LIMITS: + case WRITE_LONG: + case MOVE_MEDIUM: + case UPDATE_BLOCK: + req->cmd.xfer = 0; + break; + case MODE_SENSE: + break; + case WRITE_SAME: + req->cmd.xfer = 1; + break; + case READ_CAPACITY: + req->cmd.xfer = 8; + break; + case READ_BLOCK_LIMITS: + req->cmd.xfer = 6; + break; + case READ_POSITION: + req->cmd.xfer = 20; + break; + case SEND_VOLUME_TAG: + req->cmd.xfer *= 40; + break; + case MEDIUM_SCAN: + req->cmd.xfer *= 8; + break; + case WRITE_10: + case WRITE_VERIFY: + case WRITE_6: + case WRITE_12: + case WRITE_VERIFY_12: + case WRITE_16: + case WRITE_VERIFY_16: + req->cmd.xfer *= req->dev->blocksize; + break; + case READ_10: + case READ_6: + case READ_REVERSE: + case RECOVER_BUFFERED_DATA: + case READ_12: + case READ_16: + req->cmd.xfer *= req->dev->blocksize; + break; + case INQUIRY: + req->cmd.xfer = cmd[4] | (cmd[3] << 8); + break; + } + return 0; +} + +static int scsi_req_stream_length(SCSIRequest *req, uint8_t *cmd) +{ + switch(cmd[0]) { + /* stream commands */ + case READ_6: + case READ_REVERSE: + case RECOVER_BUFFERED_DATA: + case WRITE_6: + req->cmd.len = 6; + req->cmd.xfer = cmd[4] | (cmd[3] << 8) | (cmd[2] << 16); + if (cmd[1] & 0x01) /* fixed */ + req->cmd.xfer *= req->dev->blocksize; + break; + case REWIND: + case START_STOP: + req->cmd.len = 6; + req->cmd.xfer = 0; + break; + /* generic commands */ + default: + return scsi_req_length(req, cmd); + } + return 0; +} + +static void scsi_req_xfer_mode(SCSIRequest *req) +{ + switch (req->cmd.buf[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_VERIFY: + case WRITE_12: + case WRITE_VERIFY_12: + case WRITE_16: + case WRITE_VERIFY_16: + case COPY: + case COPY_VERIFY: + case COMPARE: + case CHANGE_DEFINITION: + case LOG_SELECT: + case MODE_SELECT: + case MODE_SELECT_10: + case SEND_DIAGNOSTIC: + case WRITE_BUFFER: + case FORMAT_UNIT: + case REASSIGN_BLOCKS: + case RESERVE: + case SEARCH_EQUAL: + case SEARCH_HIGH: + case SEARCH_LOW: + case UPDATE_BLOCK: + case WRITE_LONG: + case WRITE_SAME: + case SEARCH_HIGH_12: + case SEARCH_EQUAL_12: + case SEARCH_LOW_12: + case SET_WINDOW: + case MEDIUM_SCAN: + case SEND_VOLUME_TAG: + case WRITE_LONG_2: + req->cmd.mode = SCSI_XFER_TO_DEV; + break; + default: + if (req->cmd.xfer) + req->cmd.mode = SCSI_XFER_FROM_DEV; + else { + req->cmd.mode = SCSI_XFER_NONE; + } + break; + } +} + +static uint64_t scsi_req_lba(SCSIRequest *req) +{ + uint8_t *buf = req->cmd.buf; + uint64_t lba; + + switch (buf[0] >> 5) { + case 0: + lba = (uint64_t) buf[3] | ((uint64_t) buf[2] << 8) | + (((uint64_t) buf[1] & 0x1f) << 16); + break; + case 1: + case 2: + lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) | + ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24); + break; + case 4: + lba = (uint64_t) buf[9] | ((uint64_t) buf[8] << 8) | + ((uint64_t) buf[7] << 16) | ((uint64_t) buf[6] << 24) | + ((uint64_t) buf[5] << 32) | ((uint64_t) buf[4] << 40) | + ((uint64_t) buf[3] << 48) | ((uint64_t) buf[2] << 56); + break; + case 5: + lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) | + ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24); + break; + default: + lba = -1; + + } + return lba; +} + +int scsi_req_parse(SCSIRequest *req, uint8_t *buf) +{ + int rc; + + if (req->dev->type == TYPE_TAPE) { + rc = scsi_req_stream_length(req, buf); + } else { + rc = scsi_req_length(req, buf); + } + if (rc != 0) + return rc; + + memcpy(req->cmd.buf, buf, req->cmd.len); + scsi_req_xfer_mode(req); + req->cmd.lba = scsi_req_lba(req); + return 0; +} + +static const char *scsi_command_name(uint8_t cmd) +{ + static const char *names[] = { + [ TEST_UNIT_READY ] = "TEST_UNIT_READY", + [ REZERO_UNIT ] = "REZERO_UNIT", + [ REQUEST_SENSE ] = "REQUEST_SENSE", + [ FORMAT_UNIT ] = "FORMAT_UNIT", + [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", + [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS", + [ READ_6 ] = "READ_6", + [ WRITE_6 ] = "WRITE_6", + [ SEEK_6 ] = "SEEK_6", + [ READ_REVERSE ] = "READ_REVERSE", + [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS", + [ SPACE ] = "SPACE", + [ INQUIRY ] = "INQUIRY", + [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA", + [ MODE_SELECT ] = "MODE_SELECT", + [ RESERVE ] = "RESERVE", + [ RELEASE ] = "RELEASE", + [ COPY ] = "COPY", + [ ERASE ] = "ERASE", + [ MODE_SENSE ] = "MODE_SENSE", + [ START_STOP ] = "START_STOP", + [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", + [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", + [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", + + [ SET_WINDOW ] = "SET_WINDOW", + [ READ_CAPACITY ] = "READ_CAPACITY", + [ READ_10 ] = "READ_10", + [ WRITE_10 ] = "WRITE_10", + [ SEEK_10 ] = "SEEK_10", + [ WRITE_VERIFY ] = "WRITE_VERIFY", + [ VERIFY ] = "VERIFY", + [ SEARCH_HIGH ] = "SEARCH_HIGH", + [ SEARCH_EQUAL ] = "SEARCH_EQUAL", + [ SEARCH_LOW ] = "SEARCH_LOW", + [ SET_LIMITS ] = "SET_LIMITS", + [ PRE_FETCH ] = "PRE_FETCH", + [ READ_POSITION ] = "READ_POSITION", + [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", + [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", + [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA", + [ MEDIUM_SCAN ] = "MEDIUM_SCAN", + [ COMPARE ] = "COMPARE", + [ COPY_VERIFY ] = "COPY_VERIFY", + [ WRITE_BUFFER ] = "WRITE_BUFFER", + [ READ_BUFFER ] = "READ_BUFFER", + [ UPDATE_BLOCK ] = "UPDATE_BLOCK", + [ READ_LONG ] = "READ_LONG", + [ WRITE_LONG ] = "WRITE_LONG", + [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", + [ WRITE_SAME ] = "WRITE_SAME", + [ READ_TOC ] = "READ_TOC", + [ LOG_SELECT ] = "LOG_SELECT", + [ LOG_SENSE ] = "LOG_SENSE", + [ MODE_SELECT_10 ] = "MODE_SELECT_10", + [ RESERVE_10 ] = "RESERVE_10", + [ RELEASE_10 ] = "RELEASE_10", + [ MODE_SENSE_10 ] = "MODE_SENSE_10", + [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN", + [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT", + [ MOVE_MEDIUM ] = "MOVE_MEDIUM", + [ READ_12 ] = "READ_12", + [ WRITE_12 ] = "WRITE_12", + [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12", + [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12", + [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12", + [ SEARCH_LOW_12 ] = "SEARCH_LOW_12", + [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS", + [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG", + [ WRITE_LONG_2 ] = "WRITE_LONG_2", + + [ REWIND ] = "REWIND", + [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", + [ GET_CONFIGURATION ] = "GET_CONFIGURATION", + [ READ_16 ] = "READ_16", + [ WRITE_16 ] = "WRITE_16", + [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", + [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN", + [ REPORT_LUNS ] = "REPORT_LUNS", + [ LOAD_UNLOAD ] = "LOAD_UNLOAD", + [ SET_CD_SPEED ] = "SET_CD_SPEED", + [ BLANK ] = "BLANK", + }; + + if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) + return "*UNKNOWN*"; + return names[cmd]; +} + +void scsi_req_print(SCSIRequest *req) +{ + FILE *fp = stderr; + int i; + + fprintf(fp, "[%s id=%d] %s", + req->dev->qdev.parent_bus->name, + req->dev->id, + scsi_command_name(req->cmd.buf[0])); + for (i = 1; i < req->cmd.len; i++) { + fprintf(fp, " 0x%02x", req->cmd.buf[i]); + } + switch (req->cmd.mode) { + case SCSI_XFER_NONE: + fprintf(fp, " - none\n"); + break; + case SCSI_XFER_FROM_DEV: + fprintf(fp, " - from-dev len=%zd\n", req->cmd.xfer); + break; + case SCSI_XFER_TO_DEV: + fprintf(fp, " - to-dev len=%zd\n", req->cmd.xfer); + break; + default: + fprintf(fp, " - Oops\n"); + break; + } +} + +void scsi_req_complete(SCSIRequest *req) +{ + assert(req->status != -1); + req->bus->complete(req->bus, SCSI_REASON_DONE, + req->tag, + req->status); +} diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h new file mode 100644 index 000000000..4759d8004 --- /dev/null +++ b/hw/scsi-defs.h @@ -0,0 +1,162 @@ +/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * This header file contains public constants and structures used by + * the scsi code for linux. + */ + +/* + * SCSI opcodes + */ + +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define SET_WINDOW 0x24 +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG 0x3e +#define WRITE_LONG 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME 0x41 +#define READ_TOC 0x43 +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define MOVE_MEDIUM 0xa5 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define WRITE_VERIFY_12 0xae +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define WRITE_LONG_2 0xea + +/* from hw/scsi-generic.c */ +#define REWIND 0x01 +#define REPORT_DENSITY_SUPPORT 0x44 +#define GET_CONFIGURATION 0x46 +#define READ_16 0x88 +#define WRITE_16 0x8a +#define WRITE_VERIFY_16 0x8e +#define SERVICE_ACTION_IN 0x9e +#define REPORT_LUNS 0xa0 +#define LOAD_UNLOAD 0xa6 +#define SET_CD_SPEED 0xbb +#define BLANK 0xa1 + +/* + * Status codes + */ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x01 +#define CONDITION_GOOD 0x02 +#define BUSY 0x04 +#define INTERMEDIATE_GOOD 0x08 +#define INTERMEDIATE_C_GOOD 0x0a +#define RESERVATION_CONFLICT 0x0c +#define COMMAND_TERMINATED 0x11 +#define QUEUE_FULL 0x14 + +#define STATUS_MASK 0x3e + +/* + * SENSE KEYS + */ + +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define BLANK_CHECK 0x08 +#define COPY_ABORTED 0x0a +#define ABORTED_COMMAND 0x0b +#define VOLUME_OVERFLOW 0x0d +#define MISCOMPARE 0x0e + + +/* + * DEVICE TYPES + */ + +#define TYPE_DISK 0x00 +#define TYPE_TAPE 0x01 +#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ +#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define TYPE_ROM 0x05 +#define TYPE_SCANNER 0x06 +#define TYPE_MOD 0x07 /* Magneto-optical disk - + * - treated as TYPE_DISK */ +#define TYPE_MEDIUM_CHANGER 0x08 +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_NO_LUN 0x7f + diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index a92b62f01..67e300898 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -30,14 +30,7 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "qemu-common.h" #include "block.h" #include "scsi.h" - -#define SENSE_NO_SENSE 0 -#define SENSE_NOT_READY 2 -#define SENSE_HARDWARE_ERROR 4 -#define SENSE_ILLEGAL_REQUEST 5 - -#define STATUS_GOOD 0 -#define STATUS_CHECK_CONDITION 2 +#include "scsi-defs.h" #define SCSI_DMA_BUF_SIZE 131072 #define SCSI_MAX_INQUIRY_LEN 256 @@ -46,10 +39,8 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) typedef struct SCSIDiskState SCSIDiskState; -typedef struct SCSIRequest { - SCSIBus *bus; - SCSIDiskState *dev; - uint32_t tag; +typedef struct SCSIDiskReq { + SCSIRequest req; /* ??? We should probably keep track of whether the data transfer is a read or a write. Currently we rely on the host getting it right. */ /* Both sector and sector_count are in terms of qemu 512 byte blocks. */ @@ -57,150 +48,110 @@ typedef struct SCSIRequest { uint32_t sector_count; struct iovec iov; QEMUIOVector qiov; - BlockDriverAIOCB *aiocb; - struct SCSIRequest *next; uint32_t status; -} SCSIRequest; +} SCSIDiskReq; struct SCSIDiskState { SCSIDevice qdev; - DriveInfo *dinfo; - SCSIRequest *requests; /* The qemu block layer uses a fixed 512 byte sector size. This is the number of 512 byte blocks in a single scsi sector. */ int cluster_size; uint64_t max_lba; - int sense; - char drive_serial_str[21]; QEMUBH *bh; }; -/* Global pool of SCSIRequest structures. */ -static SCSIRequest *free_requests = NULL; - -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag) +static SCSIDiskReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIRequest *r; + SCSIRequest *req; + SCSIDiskReq *r; - if (free_requests) { - r = free_requests; - free_requests = r->next; - } else { - r = qemu_malloc(sizeof(SCSIRequest)); - r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE); - } - r->bus = scsi_bus_from_device(d); - r->dev = s; - r->tag = tag; - r->sector_count = 0; - r->iov.iov_len = 0; - r->aiocb = NULL; - r->status = 0; - - r->next = s->requests; - s->requests = r; + req = scsi_req_alloc(sizeof(SCSIDiskReq), d, tag, lun); + r = DO_UPCAST(SCSIDiskReq, req, req); + r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE); return r; } -static void scsi_remove_request(SCSIRequest *r) +static void scsi_remove_request(SCSIDiskReq *r) { - SCSIRequest *last; - SCSIDiskState *s = r->dev; - - if (s->requests == r) { - s->requests = r->next; - } else { - last = s->requests; - while (last && last->next != r) - last = last->next; - if (last) { - last->next = r->next; - } else { - BADF("Orphaned request\n"); - } - } - r->next = free_requests; - free_requests = r; + qemu_free(r->iov.iov_base); + scsi_req_free(&r->req); } -static SCSIRequest *scsi_find_request(SCSIDiskState *s, uint32_t tag) +static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag) { - SCSIRequest *r; - - r = s->requests; - while (r && r->tag != tag) - r = r->next; + return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag)); +} - return r; +static void scsi_req_set_status(SCSIRequest *req, int status, int sense_code) +{ + req->status = status; + scsi_dev_set_sense(req->dev, sense_code); } /* Helper function for command completion. */ -static void scsi_command_complete(SCSIRequest *r, int status, int sense) +static void scsi_command_complete(SCSIDiskReq *r, int status, int sense) { - SCSIDiskState *s = r->dev; - uint32_t tag; - DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", r->tag, status, sense); - s->sense = sense; - tag = r->tag; + DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", + r->req.tag, status, sense); + scsi_req_set_status(&r->req, status, sense); + scsi_req_complete(&r->req); scsi_remove_request(r); - r->bus->complete(r->bus, SCSI_REASON_DONE, tag, status); } /* Cancel a pending data transfer. */ static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIRequest *r; + SCSIDiskReq *r; DPRINTF("Cancel tag=0x%x\n", tag); r = scsi_find_request(s, tag); if (r) { - if (r->aiocb) - bdrv_aio_cancel(r->aiocb); - r->aiocb = NULL; + if (r->req.aiocb) + bdrv_aio_cancel(r->req.aiocb); + r->req.aiocb = NULL; scsi_remove_request(r); } } static void scsi_read_complete(void * opaque, int ret) { - SCSIRequest *r = (SCSIRequest *)opaque; + SCSIDiskReq *r = (SCSIDiskReq *)opaque; if (ret) { DPRINTF("IO error\n"); - r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, 0); - scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NO_SENSE); + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); + scsi_command_complete(r, CHECK_CONDITION, NO_SENSE); return; } - DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->tag, r->iov.iov_len); + DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->req.tag, r->iov.iov_len); - r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->iov.iov_len); + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); } /* Read more data from scsi device into buffer. */ static void scsi_read_data(SCSIDevice *d, uint32_t tag) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIRequest *r; + SCSIDiskReq *r; uint32_t n; r = scsi_find_request(s, tag); if (!r) { BADF("Bad read tag 0x%x\n", tag); /* ??? This is the wrong error. */ - scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR); + scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); return; } if (r->sector_count == (uint32_t)-1) { DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len); r->sector_count = 0; - r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->iov.iov_len); + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); return; } DPRINTF("Read sector_count=%d\n", r->sector_count); if (r->sector_count == 0) { - scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE); + scsi_command_complete(r, GOOD, NO_SENSE); return; } @@ -210,17 +161,19 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) r->iov.iov_len = n * 512; qemu_iovec_init_external(&r->qiov, &r->iov, 1); - r->aiocb = bdrv_aio_readv(s->dinfo->bdrv, r->sector, &r->qiov, n, + r->req.aiocb = bdrv_aio_readv(s->qdev.dinfo->bdrv, r->sector, &r->qiov, n, scsi_read_complete, r); - if (r->aiocb == NULL) - scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR); + if (r->req.aiocb == NULL) + scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); r->sector += n; r->sector_count -= n; } -static int scsi_handle_write_error(SCSIRequest *r, int error) +static int scsi_handle_write_error(SCSIDiskReq *r, int error) { - BlockInterfaceErrorAction action = drive_get_onerror(r->dev->dinfo->bdrv); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + BlockInterfaceErrorAction action = + drive_get_on_error(s->qdev.dinfo->bdrv, 0); if (action == BLOCK_ERR_IGNORE) return 0; @@ -230,8 +183,8 @@ static int scsi_handle_write_error(SCSIRequest *r, int error) r->status |= SCSI_REQ_STATUS_RETRY; vm_stop(0); } else { - scsi_command_complete(r, STATUS_CHECK_CONDITION, - SENSE_HARDWARE_ERROR); + scsi_command_complete(r, CHECK_CONDITION, + HARDWARE_ERROR); } return 1; @@ -239,11 +192,11 @@ static int scsi_handle_write_error(SCSIRequest *r, int error) static void scsi_write_complete(void * opaque, int ret) { - SCSIRequest *r = (SCSIRequest *)opaque; + SCSIDiskReq *r = (SCSIDiskReq *)opaque; uint32_t len; uint32_t n; - r->aiocb = NULL; + r->req.aiocb = NULL; if (ret) { if (scsi_handle_write_error(r, -ret)) @@ -254,31 +207,31 @@ static void scsi_write_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE); + scsi_command_complete(r, GOOD, NO_SENSE); } else { len = r->sector_count * 512; if (len > SCSI_DMA_BUF_SIZE) { len = SCSI_DMA_BUF_SIZE; } r->iov.iov_len = len; - DPRINTF("Write complete tag=0x%x more=%d\n", r->tag, len); - r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, len); + DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len); + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); } } -static void scsi_write_request(SCSIRequest *r) +static void scsi_write_request(SCSIDiskReq *r) { - SCSIDiskState *s = r->dev; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; n = r->iov.iov_len / 512; if (n) { qemu_iovec_init_external(&r->qiov, &r->iov, 1); - r->aiocb = bdrv_aio_writev(s->dinfo->bdrv, r->sector, &r->qiov, n, + r->req.aiocb = bdrv_aio_writev(s->qdev.dinfo->bdrv, r->sector, &r->qiov, n, scsi_write_complete, r); - if (r->aiocb == NULL) - scsi_command_complete(r, STATUS_CHECK_CONDITION, - SENSE_HARDWARE_ERROR); + if (r->req.aiocb == NULL) + scsi_command_complete(r, CHECK_CONDITION, + HARDWARE_ERROR); } else { /* Invoke completion routine to fetch data from host. */ scsi_write_complete(r, 0); @@ -290,17 +243,17 @@ static void scsi_write_request(SCSIRequest *r) static int scsi_write_data(SCSIDevice *d, uint32_t tag) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIRequest *r; + SCSIDiskReq *r; DPRINTF("Write data tag=0x%x\n", tag); r = scsi_find_request(s, tag); if (!r) { BADF("Bad write tag 0x%x\n", tag); - scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR); + scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); return 1; } - if (r->aiocb) + if (r->req.aiocb) BADF("Data transfer already in progress\n"); scsi_write_request(r); @@ -311,17 +264,18 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag) static void scsi_dma_restart_bh(void *opaque) { SCSIDiskState *s = opaque; - SCSIRequest *r = s->requests; + SCSIRequest *req; + SCSIDiskReq *r; qemu_bh_delete(s->bh); s->bh = NULL; - while (r) { + QTAILQ_FOREACH(req, &s->qdev.requests, next) { + r = DO_UPCAST(SCSIDiskReq, req, req); if (r->status & SCSI_REQ_STATUS_RETRY) { r->status &= ~SCSI_REQ_STATUS_RETRY; scsi_write_request(r); } - r = r->next; } } @@ -342,7 +296,7 @@ static void scsi_dma_restart_cb(void *opaque, int running, int reason) static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIRequest *r; + SCSIDiskReq *r; r = scsi_find_request(s, tag); if (!r) { @@ -352,6 +306,512 @@ static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) return (uint8_t *)r->iov.iov_base; } +static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) +{ + BlockDriverState *bdrv = req->dev->dinfo->bdrv; + int buflen = 0; + + if (req->cmd.buf[1] & 0x2) { + /* Command support data - optional, not implemented */ + BADF("optional INQUIRY command support request not implemented\n"); + return -1; + } + + if (req->cmd.buf[1] & 0x1) { + /* Vital product data */ + uint8_t page_code = req->cmd.buf[2]; + if (req->cmd.xfer < 4) { + BADF("Error: Inquiry (EVPD[%02X]) buffer size %zd is " + "less than 4\n", page_code, req->cmd.xfer); + return -1; + } + + if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM) { + outbuf[buflen++] = 5; + } else { + outbuf[buflen++] = 0; + } + outbuf[buflen++] = page_code ; // this page + outbuf[buflen++] = 0x00; + + switch (page_code) { + case 0x00: /* Supported page codes, mandatory */ + DPRINTF("Inquiry EVPD[Supported pages] " + "buffer size %zd\n", req->cmd.xfer); + outbuf[buflen++] = 3; // number of pages + outbuf[buflen++] = 0x00; // list of supported pages (this page) + outbuf[buflen++] = 0x80; // unit serial number + outbuf[buflen++] = 0x83; // device identification + break; + + case 0x80: /* Device serial number, optional */ + { + const char *serial = req->dev->dinfo->serial ?: "0"; + int l = strlen(serial); + + if (l > req->cmd.xfer) + l = req->cmd.xfer; + if (l > 20) + l = 20; + + DPRINTF("Inquiry EVPD[Serial number] " + "buffer size %zd\n", req->cmd.xfer); + outbuf[buflen++] = l; + memcpy(outbuf+buflen, serial, l); + buflen += l; + break; + } + + case 0x83: /* Device identification page, mandatory */ + { + int max_len = 255 - 8; + int id_len = strlen(bdrv_get_device_name(bdrv)); + + if (id_len > max_len) + id_len = max_len; + DPRINTF("Inquiry EVPD[Device identification] " + "buffer size %zd\n", req->cmd.xfer); + + outbuf[buflen++] = 3 + id_len; + outbuf[buflen++] = 0x2; // ASCII + outbuf[buflen++] = 0; // not officially assigned + outbuf[buflen++] = 0; // reserved + outbuf[buflen++] = id_len; // length of data following + + memcpy(outbuf+buflen, bdrv_get_device_name(bdrv), id_len); + buflen += id_len; + break; + } + default: + BADF("Error: unsupported Inquiry (EVPD[%02X]) " + "buffer size %zd\n", page_code, req->cmd.xfer); + return -1; + } + /* done with EVPD */ + return buflen; + } + + /* Standard INQUIRY data */ + if (req->cmd.buf[2] != 0) { + BADF("Error: Inquiry (STANDARD) page or code " + "is non-zero [%02X]\n", req->cmd.buf[2]); + return -1; + } + + /* PAGE CODE == 0 */ + if (req->cmd.xfer < 5) { + BADF("Error: Inquiry (STANDARD) buffer size %zd " + "is less than 5\n", req->cmd.xfer); + return -1; + } + + if (req->cmd.xfer < 36) { + BADF("Error: Inquiry (STANDARD) buffer size %zd " + "is less than 36 (TODO: only 5 required)\n", req->cmd.xfer); + } + + buflen = req->cmd.xfer; + if (buflen > SCSI_MAX_INQUIRY_LEN) + buflen = SCSI_MAX_INQUIRY_LEN; + + memset(outbuf, 0, buflen); + + if (req->lun || req->cmd.buf[1] >> 5) { + outbuf[0] = 0x7f; /* LUN not supported */ + return buflen; + } + + if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM) { + outbuf[0] = 5; + outbuf[1] = 0x80; + memcpy(&outbuf[16], "QEMU CD-ROM ", 16); + } else { + outbuf[0] = 0; + memcpy(&outbuf[16], "QEMU HARDDISK ", 16); + } + memcpy(&outbuf[8], "QEMU ", 8); + memcpy(&outbuf[32], QEMU_VERSION, 4); + /* Identify device as SCSI-3 rev 1. + Some later commands are also implemented. */ + outbuf[2] = 3; + outbuf[3] = 2; /* Format 2 */ + outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */ + /* Sync data transfer and TCQ. */ + outbuf[7] = 0x10 | (req->bus->tcq ? 0x02 : 0); + return buflen; +} + +static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + BlockDriverState *bdrv = req->dev->dinfo->bdrv; + int cylinders, heads, secs; + + switch (page) { + case 4: /* Rigid disk device geometry page. */ + p[0] = 4; + p[1] = 0x16; + /* if a geometry hint is available, use it */ + bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs); + p[2] = (cylinders >> 16) & 0xff; + p[3] = (cylinders >> 8) & 0xff; + p[4] = cylinders & 0xff; + p[5] = heads & 0xff; + /* Write precomp start cylinder, disabled */ + p[6] = (cylinders >> 16) & 0xff; + p[7] = (cylinders >> 8) & 0xff; + p[8] = cylinders & 0xff; + /* Reduced current start cylinder, disabled */ + p[9] = (cylinders >> 16) & 0xff; + p[10] = (cylinders >> 8) & 0xff; + p[11] = cylinders & 0xff; + /* Device step rate [ns], 200ns */ + p[12] = 0; + p[13] = 200; + /* Landing zone cylinder */ + p[14] = 0xff; + p[15] = 0xff; + p[16] = 0xff; + /* Medium rotation rate [rpm], 5400 rpm */ + p[20] = (5400 >> 8) & 0xff; + p[21] = 5400 & 0xff; + return 0x16; + + case 5: /* Flexible disk device geometry page. */ + p[0] = 5; + p[1] = 0x1e; + /* Transfer rate [kbit/s], 5Mbit/s */ + p[2] = 5000 >> 8; + p[3] = 5000 & 0xff; + /* if a geometry hint is available, use it */ + bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs); + p[4] = heads & 0xff; + p[5] = secs & 0xff; + p[6] = s->cluster_size * 2; + p[8] = (cylinders >> 8) & 0xff; + p[9] = cylinders & 0xff; + /* Write precomp start cylinder, disabled */ + p[10] = (cylinders >> 8) & 0xff; + p[11] = cylinders & 0xff; + /* Reduced current start cylinder, disabled */ + p[12] = (cylinders >> 8) & 0xff; + p[13] = cylinders & 0xff; + /* Device step rate [100us], 100us */ + p[14] = 0; + p[15] = 1; + /* Device step pulse width [us], 1us */ + p[16] = 1; + /* Device head settle delay [100us], 100us */ + p[17] = 0; + p[18] = 1; + /* Motor on delay [0.1s], 0.1s */ + p[19] = 1; + /* Motor off delay [0.1s], 0.1s */ + p[20] = 1; + /* Medium rotation rate [rpm], 5400 rpm */ + p[28] = (5400 >> 8) & 0xff; + p[29] = 5400 & 0xff; + return 0x1e; + + case 8: /* Caching page. */ + p[0] = 8; + p[1] = 0x12; + if (bdrv_enable_write_cache(s->qdev.dinfo->bdrv)) { + p[2] = 4; /* WCE */ + } + return 20; + + case 0x2a: /* CD Capabilities and Mechanical Status page. */ + if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM) + return 0; + p[0] = 0x2a; + p[1] = 0x14; + p[2] = 3; // CD-R & CD-RW read + p[3] = 0; // Writing not supported + p[4] = 0x7f; /* Audio, composite, digital out, + mode 2 form 1&2, multi session */ + p[5] = 0xff; /* CD DA, DA accurate, RW supported, + RW corrected, C2 errors, ISRC, + UPC, Bar code */ + p[6] = 0x2d | (bdrv_is_locked(s->qdev.dinfo->bdrv)? 2 : 0); + /* Locking supported, jumper present, eject, tray */ + p[7] = 0; /* no volume & mute control, no + changer */ + p[8] = (50 * 176) >> 8; // 50x read speed + p[9] = (50 * 176) & 0xff; + p[10] = 0 >> 8; // No volume + p[11] = 0 & 0xff; + p[12] = 2048 >> 8; // 2M buffer + p[13] = 2048 & 0xff; + p[14] = (16 * 176) >> 8; // 16x read speed current + p[15] = (16 * 176) & 0xff; + p[18] = (16 * 176) >> 8; // 16x write speed + p[19] = (16 * 176) & 0xff; + p[20] = (16 * 176) >> 8; // 16x write speed current + p[21] = (16 * 176) & 0xff; + return 22; + + default: + return 0; + } +} + +static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + BlockDriverState *bdrv = req->dev->dinfo->bdrv; + uint64_t nb_sectors; + int page, dbd, buflen; + uint8_t *p; + + dbd = req->cmd.buf[1] & 0x8; + page = req->cmd.buf[2] & 0x3f; + DPRINTF("Mode Sense (page %d, len %zd)\n", page, req->cmd.xfer); + memset(outbuf, 0, req->cmd.xfer); + p = outbuf; + + p[1] = 0; /* Default media type. */ + p[3] = 0; /* Block descriptor length. */ + if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM || + bdrv_is_read_only(bdrv)) { + p[2] = 0x80; /* Readonly. */ + } + p += 4; + + bdrv_get_geometry(bdrv, &nb_sectors); + if ((~dbd) & nb_sectors) { + outbuf[3] = 8; /* Block descriptor length */ + nb_sectors /= s->cluster_size; + nb_sectors--; + if (nb_sectors > 0xffffff) + nb_sectors = 0xffffff; + p[0] = 0; /* media density code */ + p[1] = (nb_sectors >> 16) & 0xff; + p[2] = (nb_sectors >> 8) & 0xff; + p[3] = nb_sectors & 0xff; + p[4] = 0; /* reserved */ + p[5] = 0; /* bytes 5-7 are the sector size in bytes */ + p[6] = s->cluster_size * 2; + p[7] = 0; + p += 8; + } + + switch (page) { + case 0x04: + case 0x05: + case 0x08: + case 0x2a: + p += mode_sense_page(req, page, p); + break; + case 0x3f: + p += mode_sense_page(req, 0x08, p); + p += mode_sense_page(req, 0x2a, p); + break; + } + + buflen = p - outbuf; + outbuf[0] = buflen - 4; + if (buflen > req->cmd.xfer) + buflen = req->cmd.xfer; + return buflen; +} + +static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + BlockDriverState *bdrv = req->dev->dinfo->bdrv; + int start_track, format, msf, toclen; + uint64_t nb_sectors; + + msf = req->cmd.buf[1] & 2; + format = req->cmd.buf[2] & 0xf; + start_track = req->cmd.buf[6]; + bdrv_get_geometry(bdrv, &nb_sectors); + DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); + nb_sectors /= s->cluster_size; + switch (format) { + case 0: + toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track); + break; + case 1: + /* multi session : only a single session defined */ + toclen = 12; + memset(outbuf, 0, 12); + outbuf[1] = 0x0a; + outbuf[2] = 0x01; + outbuf[3] = 0x01; + break; + case 2: + toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track); + break; + default: + return -1; + } + if (toclen > req->cmd.xfer) + toclen = req->cmd.xfer; + return toclen; +} + +static int scsi_disk_emulate_command(SCSIRequest *req, uint8_t *outbuf) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + BlockDriverState *bdrv = req->dev->dinfo->bdrv; + uint64_t nb_sectors; + int buflen = 0; + + switch (req->cmd.buf[0]) { + case TEST_UNIT_READY: + if (!bdrv_is_inserted(bdrv)) + goto not_ready; + break; + case REQUEST_SENSE: + if (req->cmd.xfer < 4) + goto illegal_request; + memset(outbuf, 0, 4); + buflen = 4; + if (req->dev->sense.key == NOT_READY && req->cmd.xfer >= 18) { + memset(outbuf, 0, 18); + buflen = 18; + outbuf[7] = 10; + /* asc 0x3a, ascq 0: Medium not present */ + outbuf[12] = 0x3a; + outbuf[13] = 0; + } + outbuf[0] = 0xf0; + outbuf[1] = 0; + outbuf[2] = req->dev->sense.key; + scsi_dev_clear_sense(req->dev); + break; + case INQUIRY: + buflen = scsi_disk_emulate_inquiry(req, outbuf); + if (buflen < 0) + goto illegal_request; + break; + case MODE_SENSE: + case MODE_SENSE_10: + buflen = scsi_disk_emulate_mode_sense(req, outbuf); + if (buflen < 0) + goto illegal_request; + break; + case READ_TOC: + buflen = scsi_disk_emulate_read_toc(req, outbuf); + if (buflen < 0) + goto illegal_request; + break; + case RESERVE: + if (req->cmd.buf[1] & 1) + goto illegal_request; + break; + case RESERVE_10: + if (req->cmd.buf[1] & 3) + goto illegal_request; + break; + case RELEASE: + if (req->cmd.buf[1] & 1) + goto illegal_request; + break; + case RELEASE_10: + if (req->cmd.buf[1] & 3) + goto illegal_request; + break; + case START_STOP: + if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) { + /* load/eject medium */ + bdrv_eject(bdrv, !(req->cmd.buf[4] & 1)); + } + break; + case ALLOW_MEDIUM_REMOVAL: + bdrv_set_locked(bdrv, req->cmd.buf[4] & 1); + break; + case READ_CAPACITY: + /* The normal LEN field for this command is zero. */ + memset(outbuf, 0, 8); + bdrv_get_geometry(bdrv, &nb_sectors); + if (!nb_sectors) + goto not_ready; + nb_sectors /= s->cluster_size; + /* Returned value is the address of the last sector. */ + nb_sectors--; + /* Remember the new size for read/write sanity checking. */ + s->max_lba = nb_sectors; + /* Clip to 2TB, instead of returning capacity modulo 2TB. */ + if (nb_sectors > UINT32_MAX) + nb_sectors = UINT32_MAX; + outbuf[0] = (nb_sectors >> 24) & 0xff; + outbuf[1] = (nb_sectors >> 16) & 0xff; + outbuf[2] = (nb_sectors >> 8) & 0xff; + outbuf[3] = nb_sectors & 0xff; + outbuf[4] = 0; + outbuf[5] = 0; + outbuf[6] = s->cluster_size * 2; + outbuf[7] = 0; + buflen = 8; + break; + case SYNCHRONIZE_CACHE: + bdrv_flush(bdrv); + break; + case GET_CONFIGURATION: + memset(outbuf, 0, 8); + /* ??? This should probably return much more information. For now + just return the basic header indicating the CD-ROM profile. */ + outbuf[7] = 8; // CD-ROM + buflen = 8; + break; + case SERVICE_ACTION_IN: + /* Service Action In subcommands. */ + if ((req->cmd.buf[1] & 31) == 0x10) { + DPRINTF("SAI READ CAPACITY(16)\n"); + memset(outbuf, 0, req->cmd.xfer); + bdrv_get_geometry(bdrv, &nb_sectors); + if (!nb_sectors) + goto not_ready; + nb_sectors /= s->cluster_size; + /* Returned value is the address of the last sector. */ + nb_sectors--; + /* Remember the new size for read/write sanity checking. */ + s->max_lba = nb_sectors; + outbuf[0] = (nb_sectors >> 56) & 0xff; + outbuf[1] = (nb_sectors >> 48) & 0xff; + outbuf[2] = (nb_sectors >> 40) & 0xff; + outbuf[3] = (nb_sectors >> 32) & 0xff; + outbuf[4] = (nb_sectors >> 24) & 0xff; + outbuf[5] = (nb_sectors >> 16) & 0xff; + outbuf[6] = (nb_sectors >> 8) & 0xff; + outbuf[7] = nb_sectors & 0xff; + outbuf[8] = 0; + outbuf[9] = 0; + outbuf[10] = s->cluster_size * 2; + outbuf[11] = 0; + /* Protection, exponent and lowest lba field left blank. */ + buflen = req->cmd.xfer; + break; + } + DPRINTF("Unsupported Service Action In\n"); + goto illegal_request; + case REPORT_LUNS: + if (req->cmd.xfer < 16) + goto illegal_request; + memset(outbuf, 0, 16); + outbuf[3] = 8; + buflen = 16; + break; + case VERIFY: + break; + default: + goto illegal_request; + } + scsi_req_set_status(req, GOOD, NO_SENSE); + return buflen; + +not_ready: + scsi_req_set_status(req, CHECK_CONDITION, NOT_READY); + return 0; + +illegal_request: + scsi_req_set_status(req, CHECK_CONDITION, ILLEGAL_REQUEST); + return 0; +} + /* Execute a scsi command. Returns the length of the data expected by the command. This will be Positive for data transfers from the device (eg. disk reads), negative for transfers to the device (eg. disk writes), @@ -361,14 +821,14 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, uint8_t *buf, int lun) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - uint64_t nb_sectors; uint64_t lba; uint32_t len; int cmdlen; int is_write; uint8_t command; uint8_t *outbuf; - SCSIRequest *r; + SCSIDiskReq *r; + int rc; command = buf[0]; r = scsi_find_request(s, tag); @@ -378,7 +838,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } /* ??? Tags are not unique for different luns. We only implement a single lun, so this should not matter. */ - r = scsi_new_request(d, tag); + r = scsi_new_request(d, tag, lun); outbuf = (uint8_t *)r->iov.iov_base; is_write = 0; DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); @@ -423,406 +883,62 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, printf("\n"); } #endif + + if (scsi_req_parse(&r->req, buf) != 0) { + BADF("Unsupported command length, command %x\n", command); + goto fail; + } + assert(r->req.cmd.len == cmdlen); + assert(r->req.cmd.lba == lba); + if (lun || buf[1] >> 5) { /* Only LUN 0 supported. */ DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5); - if (command != 0x03 && command != 0x12) /* REQUEST SENSE and INQUIRY */ + if (command != REQUEST_SENSE && command != INQUIRY) goto fail; } switch (command) { - case 0x0: - DPRINTF("Test Unit Ready\n"); - if (!bdrv_is_inserted(s->dinfo->bdrv)) - goto notready; - break; - case 0x03: - DPRINTF("Request Sense (len %d)\n", len); - if (len < 4) - goto fail; - memset(outbuf, 0, 4); - r->iov.iov_len = 4; - if (s->sense == SENSE_NOT_READY && len >= 18) { - memset(outbuf, 0, 18); - r->iov.iov_len = 18; - outbuf[7] = 10; - /* asc 0x3a, ascq 0: Medium not present */ - outbuf[12] = 0x3a; - outbuf[13] = 0; - } - outbuf[0] = 0xf0; - outbuf[1] = 0; - outbuf[2] = s->sense; - break; - case 0x12: - DPRINTF("Inquiry (len %d)\n", len); - if (buf[1] & 0x2) { - /* Command support data - optional, not implemented */ - BADF("optional INQUIRY command support request not implemented\n"); - goto fail; - } - else if (buf[1] & 0x1) { - /* Vital product data */ - uint8_t page_code = buf[2]; - if (len < 4) { - BADF("Error: Inquiry (EVPD[%02X]) buffer size %d is " - "less than 4\n", page_code, len); - goto fail; - } - - switch (page_code) { - case 0x00: - { - /* Supported page codes, mandatory */ - DPRINTF("Inquiry EVPD[Supported pages] " - "buffer size %d\n", len); - - r->iov.iov_len = 0; - - if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) { - outbuf[r->iov.iov_len++] = 5; - } else { - outbuf[r->iov.iov_len++] = 0; - } - - outbuf[r->iov.iov_len++] = 0x00; // this page - outbuf[r->iov.iov_len++] = 0x00; - outbuf[r->iov.iov_len++] = 3; // number of pages - outbuf[r->iov.iov_len++] = 0x00; // list of supported pages (this page) - outbuf[r->iov.iov_len++] = 0x80; // unit serial number - outbuf[r->iov.iov_len++] = 0x83; // device identification - } - break; - case 0x80: - { - int l; - - /* Device serial number, optional */ - if (len < 4) { - BADF("Error: EVPD[Serial number] Inquiry buffer " - "size %d too small, %d needed\n", len, 4); - goto fail; - } - - DPRINTF("Inquiry EVPD[Serial number] buffer size %d\n", len); - l = MIN(len, strlen(s->drive_serial_str)); - - r->iov.iov_len = 0; - - /* Supported page codes */ - if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) { - outbuf[r->iov.iov_len++] = 5; - } else { - outbuf[r->iov.iov_len++] = 0; - } - - outbuf[r->iov.iov_len++] = 0x80; // this page - outbuf[r->iov.iov_len++] = 0x00; - outbuf[r->iov.iov_len++] = l; - memcpy(&outbuf[r->iov.iov_len], s->drive_serial_str, l); - r->iov.iov_len += l; - } - - break; - case 0x83: - { - /* Device identification page, mandatory */ - int max_len = 255 - 8; - int id_len = strlen(bdrv_get_device_name(s->dinfo->bdrv)); - if (id_len > max_len) - id_len = max_len; - - DPRINTF("Inquiry EVPD[Device identification] " - "buffer size %d\n", len); - r->iov.iov_len = 0; - if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) { - outbuf[r->iov.iov_len++] = 5; - } else { - outbuf[r->iov.iov_len++] = 0; - } - - outbuf[r->iov.iov_len++] = 0x83; // this page - outbuf[r->iov.iov_len++] = 0x00; - outbuf[r->iov.iov_len++] = 3 + id_len; - - outbuf[r->iov.iov_len++] = 0x2; // ASCII - outbuf[r->iov.iov_len++] = 0; // not officially assigned - outbuf[r->iov.iov_len++] = 0; // reserved - outbuf[r->iov.iov_len++] = id_len; // length of data following - - memcpy(&outbuf[r->iov.iov_len], - bdrv_get_device_name(s->dinfo->bdrv), id_len); - r->iov.iov_len += id_len; - } - break; - default: - BADF("Error: unsupported Inquiry (EVPD[%02X]) " - "buffer size %d\n", page_code, len); - goto fail; - } - /* done with EVPD */ - break; - } - else { - /* Standard INQUIRY data */ - if (buf[2] != 0) { - BADF("Error: Inquiry (STANDARD) page or code " - "is non-zero [%02X]\n", buf[2]); - goto fail; - } - - /* PAGE CODE == 0 */ - if (len < 5) { - BADF("Error: Inquiry (STANDARD) buffer size %d " - "is less than 5\n", len); - goto fail; - } - - if (len < 36) { - BADF("Error: Inquiry (STANDARD) buffer size %d " - "is less than 36 (TODO: only 5 required)\n", len); - } - } - - if(len > SCSI_MAX_INQUIRY_LEN) - len = SCSI_MAX_INQUIRY_LEN; - - memset(outbuf, 0, len); - - if (lun || buf[1] >> 5) { - outbuf[0] = 0x7f; /* LUN not supported */ - } else if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) { - outbuf[0] = 5; - outbuf[1] = 0x80; - memcpy(&outbuf[16], "QEMU CD-ROM ", 16); - } else { - outbuf[0] = 0; - memcpy(&outbuf[16], "QEMU HARDDISK ", 16); - } - memcpy(&outbuf[8], "QEMU ", 8); - memcpy(&outbuf[32], QEMU_VERSION, 4); - /* Identify device as SCSI-3 rev 1. - Some later commands are also implemented. */ - outbuf[2] = 3; - outbuf[3] = 2; /* Format 2 */ - outbuf[4] = len - 5; /* Additional Length = (Len - 1) - 4 */ - /* Sync data transfer and TCQ. */ - outbuf[7] = 0x10 | (r->bus->tcq ? 0x02 : 0); - r->iov.iov_len = len; - break; - case 0x16: - DPRINTF("Reserve(6)\n"); - if (buf[1] & 1) - goto fail; - break; - case 0x17: - DPRINTF("Release(6)\n"); - if (buf[1] & 1) - goto fail; - break; - case 0x1a: - case 0x5a: - { - uint8_t *p; - int page; - int dbd; - - dbd = buf[1] & 0x8; - page = buf[2] & 0x3f; - DPRINTF("Mode Sense (page %d, len %d)\n", page, len); - p = outbuf; - memset(p, 0, 4); - outbuf[1] = 0; /* Default media type. */ - outbuf[3] = 0; /* Block descriptor length. */ - if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM || - bdrv_is_read_only(s->dinfo->bdrv)) { - outbuf[2] = 0x80; /* Readonly. */ - } - p += 4; - bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors); - if ((~dbd) & nb_sectors) { - nb_sectors /= s->cluster_size; - nb_sectors--; - if (nb_sectors > 0xffffff) - nb_sectors = 0xffffff; - outbuf[3] = 8; /* Block descriptor length */ - p[0] = 0; /* media density code */ - p[1] = (nb_sectors >> 16) & 0xff; - p[2] = (nb_sectors >> 8) & 0xff; - p[3] = nb_sectors & 0xff; - p[4] = 0; /* reserved */ - p[5] = 0; /* bytes 5-7 are the sector size in bytes */ - p[6] = s->cluster_size * 2; - p[7] = 0; - p += 8; - } - - if (page == 4) { - int cylinders, heads, secs; - - /* Rigid disk device geometry page. */ - p[0] = 4; - p[1] = 0x16; - /* if a geometry hint is available, use it */ - bdrv_get_geometry_hint(s->dinfo->bdrv, &cylinders, &heads, &secs); - p[2] = (cylinders >> 16) & 0xff; - p[3] = (cylinders >> 8) & 0xff; - p[4] = cylinders & 0xff; - p[5] = heads & 0xff; - /* Write precomp start cylinder, disabled */ - p[6] = (cylinders >> 16) & 0xff; - p[7] = (cylinders >> 8) & 0xff; - p[8] = cylinders & 0xff; - /* Reduced current start cylinder, disabled */ - p[9] = (cylinders >> 16) & 0xff; - p[10] = (cylinders >> 8) & 0xff; - p[11] = cylinders & 0xff; - /* Device step rate [ns], 200ns */ - p[12] = 0; - p[13] = 200; - /* Landing zone cylinder */ - p[14] = 0xff; - p[15] = 0xff; - p[16] = 0xff; - /* Medium rotation rate [rpm], 5400 rpm */ - p[20] = (5400 >> 8) & 0xff; - p[21] = 5400 & 0xff; - p += 0x16; - } else if (page == 5) { - int cylinders, heads, secs; - - /* Flexible disk device geometry page. */ - p[0] = 5; - p[1] = 0x1e; - /* Transfer rate [kbit/s], 5Mbit/s */ - p[2] = 5000 >> 8; - p[3] = 5000 & 0xff; - /* if a geometry hint is available, use it */ - bdrv_get_geometry_hint(s->dinfo->bdrv, &cylinders, &heads, &secs); - p[4] = heads & 0xff; - p[5] = secs & 0xff; - p[6] = s->cluster_size * 2; - p[8] = (cylinders >> 8) & 0xff; - p[9] = cylinders & 0xff; - /* Write precomp start cylinder, disabled */ - p[10] = (cylinders >> 8) & 0xff; - p[11] = cylinders & 0xff; - /* Reduced current start cylinder, disabled */ - p[12] = (cylinders >> 8) & 0xff; - p[13] = cylinders & 0xff; - /* Device step rate [100us], 100us */ - p[14] = 0; - p[15] = 1; - /* Device step pulse width [us], 1us */ - p[16] = 1; - /* Device head settle delay [100us], 100us */ - p[17] = 0; - p[18] = 1; - /* Motor on delay [0.1s], 0.1s */ - p[19] = 1; - /* Motor off delay [0.1s], 0.1s */ - p[20] = 1; - /* Medium rotation rate [rpm], 5400 rpm */ - p[28] = (5400 >> 8) & 0xff; - p[29] = 5400 & 0xff; - p += 0x1e; - } else if ((page == 8 || page == 0x3f)) { - /* Caching page. */ - memset(p,0,20); - p[0] = 8; - p[1] = 0x12; - if (bdrv_enable_write_cache(s->dinfo->bdrv)) { - p[2] = 4; /* WCE */ - } - p += 20; - } - if ((page == 0x3f || page == 0x2a) - && (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM)) { - /* CD Capabilities and Mechanical Status page. */ - p[0] = 0x2a; - p[1] = 0x14; - p[2] = 3; // CD-R & CD-RW read - p[3] = 0; // Writing not supported - p[4] = 0x7f; /* Audio, composite, digital out, - mode 2 form 1&2, multi session */ - p[5] = 0xff; /* CD DA, DA accurate, RW supported, - RW corrected, C2 errors, ISRC, - UPC, Bar code */ - p[6] = 0x2d | (bdrv_is_locked(s->dinfo->bdrv)? 2 : 0); - /* Locking supported, jumper present, eject, tray */ - p[7] = 0; /* no volume & mute control, no - changer */ - p[8] = (50 * 176) >> 8; // 50x read speed - p[9] = (50 * 176) & 0xff; - p[10] = 0 >> 8; // No volume - p[11] = 0 & 0xff; - p[12] = 2048 >> 8; // 2M buffer - p[13] = 2048 & 0xff; - p[14] = (16 * 176) >> 8; // 16x read speed current - p[15] = (16 * 176) & 0xff; - p[18] = (16 * 176) >> 8; // 16x write speed - p[19] = (16 * 176) & 0xff; - p[20] = (16 * 176) >> 8; // 16x write speed current - p[21] = (16 * 176) & 0xff; - p += 22; - } - r->iov.iov_len = p - outbuf; - outbuf[0] = r->iov.iov_len - 4; - if (r->iov.iov_len > len) - r->iov.iov_len = len; - } - break; - case 0x1b: - DPRINTF("Start Stop Unit\n"); - if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM && - (buf[4] & 2)) - /* load/eject medium */ - bdrv_eject(s->dinfo->bdrv, !(buf[4] & 1)); - break; - case 0x1e: - DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3); - bdrv_set_locked(s->dinfo->bdrv, buf[4] & 1); - break; - case 0x25: - DPRINTF("Read Capacity\n"); - /* The normal LEN field for this command is zero. */ - memset(outbuf, 0, 8); - bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors); - nb_sectors /= s->cluster_size; - /* Returned value is the address of the last sector. */ - if (nb_sectors) { - nb_sectors--; - /* Remember the new size for read/write sanity checking. */ - s->max_lba = nb_sectors; - /* Clip to 2TB, instead of returning capacity modulo 2TB. */ - if (nb_sectors > UINT32_MAX) - nb_sectors = UINT32_MAX; - outbuf[0] = (nb_sectors >> 24) & 0xff; - outbuf[1] = (nb_sectors >> 16) & 0xff; - outbuf[2] = (nb_sectors >> 8) & 0xff; - outbuf[3] = nb_sectors & 0xff; - outbuf[4] = 0; - outbuf[5] = 0; - outbuf[6] = s->cluster_size * 2; - outbuf[7] = 0; - r->iov.iov_len = 8; + case TEST_UNIT_READY: + case REQUEST_SENSE: + case INQUIRY: + case MODE_SENSE: + case MODE_SENSE_10: + case RESERVE: + case RESERVE_10: + case RELEASE: + case RELEASE_10: + case START_STOP: + case ALLOW_MEDIUM_REMOVAL: + case READ_CAPACITY: + case SYNCHRONIZE_CACHE: + case READ_TOC: + case GET_CONFIGURATION: + case SERVICE_ACTION_IN: + case REPORT_LUNS: + case VERIFY: + rc = scsi_disk_emulate_command(&r->req, outbuf); + if (rc > 0) { + r->iov.iov_len = rc; } else { - notready: - scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY); + scsi_req_complete(&r->req); + scsi_remove_request(r); return 0; } - break; - case 0x08: - case 0x28: - case 0x88: + break; + case READ_6: + case READ_10: + case READ_12: + case READ_16: DPRINTF("Read (sector %" PRId64 ", count %d)\n", lba, len); if (lba > s->max_lba) goto illegal_lba; r->sector = lba * s->cluster_size; r->sector_count = len * s->cluster_size; break; - case 0x0a: - case 0x2a: - case 0x8a: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: DPRINTF("Write (sector %" PRId64 ", count %d)\n", lba, len); if (lba > s->max_lba) goto illegal_lba; @@ -830,122 +946,17 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, r->sector_count = len * s->cluster_size; is_write = 1; break; - case 0x35: - DPRINTF("Synchronise cache (sector %" PRId64 ", count %d)\n", lba, len); - bdrv_flush(s->dinfo->bdrv); - break; - case 0x43: - { - int start_track, format, msf, toclen; - - msf = buf[1] & 2; - format = buf[2] & 0xf; - start_track = buf[6]; - bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors); - DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); - nb_sectors /= s->cluster_size; - switch(format) { - case 0: - toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track); - break; - case 1: - /* multi session : only a single session defined */ - toclen = 12; - memset(outbuf, 0, 12); - outbuf[1] = 0x0a; - outbuf[2] = 0x01; - outbuf[3] = 0x01; - break; - case 2: - toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track); - break; - default: - goto error_cmd; - } - if (toclen > 0) { - if (len > toclen) - len = toclen; - r->iov.iov_len = len; - break; - } - error_cmd: - DPRINTF("Read TOC error\n"); - goto fail; - } - case 0x46: - DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len); - memset(outbuf, 0, 8); - /* ??? This should probably return much more information. For now - just return the basic header indicating the CD-ROM profile. */ - outbuf[7] = 8; // CD-ROM - r->iov.iov_len = 8; - break; - case 0x56: - DPRINTF("Reserve(10)\n"); - if (buf[1] & 3) - goto fail; - break; - case 0x57: - DPRINTF("Release(10)\n"); - if (buf[1] & 3) - goto fail; - break; - case 0x9e: - /* Service Action In subcommands. */ - if ((buf[1] & 31) == 0x10) { - DPRINTF("SAI READ CAPACITY(16)\n"); - memset(outbuf, 0, len); - bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors); - nb_sectors /= s->cluster_size; - /* Returned value is the address of the last sector. */ - if (nb_sectors) { - nb_sectors--; - /* Remember the new size for read/write sanity checking. */ - s->max_lba = nb_sectors; - outbuf[0] = (nb_sectors >> 56) & 0xff; - outbuf[1] = (nb_sectors >> 48) & 0xff; - outbuf[2] = (nb_sectors >> 40) & 0xff; - outbuf[3] = (nb_sectors >> 32) & 0xff; - outbuf[4] = (nb_sectors >> 24) & 0xff; - outbuf[5] = (nb_sectors >> 16) & 0xff; - outbuf[6] = (nb_sectors >> 8) & 0xff; - outbuf[7] = nb_sectors & 0xff; - outbuf[8] = 0; - outbuf[9] = 0; - outbuf[10] = s->cluster_size * 2; - outbuf[11] = 0; - /* Protection, exponent and lowest lba field left blank. */ - r->iov.iov_len = len; - } else { - scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY); - return 0; - } - break; - } - DPRINTF("Unsupported Service Action In\n"); - goto fail; - case 0xa0: - DPRINTF("Report LUNs (len %d)\n", len); - if (len < 16) - goto fail; - memset(outbuf, 0, 16); - outbuf[3] = 8; - r->iov.iov_len = 16; - break; - case 0x2f: - DPRINTF("Verify (sector %" PRId64 ", count %d)\n", lba, len); - break; default: DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); fail: - scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); return 0; illegal_lba: - scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR); + scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); return 0; } if (r->sector_count == 0 && r->iov.iov_len == 0) { - scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE); + scsi_command_complete(r, GOOD, NO_SENSE); } len = r->sector_count * 512 + r->iov.iov_len; if (is_write) { @@ -960,8 +971,13 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, static void scsi_destroy(SCSIDevice *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + SCSIDiskReq *r; - drive_uninit(s->dinfo); + while (!QTAILQ_EMPTY(&s->qdev.requests)) { + r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests)); + scsi_remove_request(r); + } + drive_uninit(s->qdev.dinfo); } static int scsi_disk_initfn(SCSIDevice *dev) @@ -969,25 +985,23 @@ static int scsi_disk_initfn(SCSIDevice *dev) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); uint64_t nb_sectors; - if (!s->dinfo || !s->dinfo->bdrv) { + if (!s->qdev.dinfo || !s->qdev.dinfo->bdrv) { qemu_error("scsi-disk: drive property not set\n"); return -1; } - if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) { + if (bdrv_get_type_hint(s->qdev.dinfo->bdrv) == BDRV_TYPE_CDROM) { s->cluster_size = 4; } else { s->cluster_size = 1; } - bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors); + s->qdev.blocksize = 512 * s->cluster_size; + s->qdev.type = TYPE_DISK; + bdrv_get_geometry(s->qdev.dinfo->bdrv, &nb_sectors); nb_sectors /= s->cluster_size; if (nb_sectors) nb_sectors--; s->max_lba = nb_sectors; - strncpy(s->drive_serial_str, drive_get_serial(s->dinfo->bdrv), - sizeof(s->drive_serial_str)); - if (strlen(s->drive_serial_str) == 0) - pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), "0"); qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); return 0; } @@ -1004,7 +1018,7 @@ static SCSIDeviceInfo scsi_disk_info = { .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("drive", SCSIDiskState, dinfo), + DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.dinfo), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/scsi-disk.h b/hw/scsi-disk.h deleted file mode 100644 index b6b6c1266..000000000 --- a/hw/scsi-disk.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef SCSI_DISK_H -#define SCSI_DISK_H - -#include "qdev.h" - -/* scsi-disk.c */ -enum scsi_reason { - SCSI_REASON_DONE, /* Command complete. */ - SCSI_REASON_DATA /* Transfer complete, more data required. */ -}; - -typedef struct SCSIBus SCSIBus; -typedef struct SCSIDevice SCSIDevice; -typedef struct SCSIDeviceInfo SCSIDeviceInfo; -typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag, - uint32_t arg); - -struct SCSIDevice -{ - DeviceState qdev; - uint32_t id; - SCSIDeviceInfo *info; -}; - -/* cdrom.c */ -int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track); -int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num); - -/* scsi-bus.c */ -typedef int (*scsi_qdev_initfn)(SCSIDevice *dev); -struct SCSIDeviceInfo { - DeviceInfo qdev; - scsi_qdev_initfn init; - void (*destroy)(SCSIDevice *s); - int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf, - int lun); - void (*read_data)(SCSIDevice *s, uint32_t tag); - int (*write_data)(SCSIDevice *s, uint32_t tag); - void (*cancel_io)(SCSIDevice *s, uint32_t tag); - uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag); -}; - -typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv, - int unit); -struct SCSIBus { - BusState qbus; - int busnr; - - int tcq, ndev; - scsi_completionfn complete; - - SCSIDevice *devs[8]; -}; - -void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, - scsi_completionfn complete); -void scsi_qdev_register(SCSIDeviceInfo *info); - -static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) -{ - return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); -} - -SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, DriveInfo *dinfo, int unit); -void scsi_bus_legacy_handle_cmdline(SCSIBus *bus); - -#endif diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index cf56ea0f2..f60ad96d7 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -34,15 +34,8 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) #include <sys/stat.h> #include <unistd.h> #include <scsi/sg.h> -#include <scsi/scsi.h> +#include "scsi-defs.h" -#define REWIND 0x01 -#define REPORT_DENSITY_SUPPORT 0x44 -#define LOAD_UNLOAD 0xa6 -#define SET_CD_SPEED 0xbb -#define BLANK 0xa1 - -#define SCSI_CMD_BUF_SIZE 16 #define SCSI_SENSE_BUF_SIZE 96 #define SG_ERR_DRIVER_TIMEOUT 0x06 @@ -54,127 +47,70 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) typedef struct SCSIGenericState SCSIGenericState; -typedef struct SCSIRequest { - BlockDriverAIOCB *aiocb; - struct SCSIRequest *next; - SCSIBus *bus; - SCSIGenericState *dev; - uint32_t tag; - uint8_t cmd[SCSI_CMD_BUF_SIZE]; - int cmdlen; +typedef struct SCSIGenericReq { + SCSIRequest req; uint8_t *buf; int buflen; int len; sg_io_hdr_t io_header; -} SCSIRequest; +} SCSIGenericReq; struct SCSIGenericState { SCSIDevice qdev; - SCSIRequest *requests; - DriveInfo *dinfo; - int type; - int blocksize; int lun; int driver_status; uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; uint8_t senselen; }; -/* Global pool of SCSIRequest structures. */ -static SCSIRequest *free_requests = NULL; - -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag) +static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIRequest *r; + SCSIRequest *req; - if (free_requests) { - r = free_requests; - free_requests = r->next; - } else { - r = qemu_malloc(sizeof(SCSIRequest)); - r->buf = NULL; - r->buflen = 0; - } - r->bus = scsi_bus_from_device(d); - r->dev = s; - r->tag = tag; - memset(r->cmd, 0, sizeof(r->cmd)); - memset(&r->io_header, 0, sizeof(r->io_header)); - r->cmdlen = 0; - r->len = 0; - r->aiocb = NULL; - - /* link */ - - r->next = s->requests; - s->requests = r; - return r; + req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun); + return DO_UPCAST(SCSIGenericReq, req, req); } -static void scsi_remove_request(SCSIRequest *r) +static void scsi_remove_request(SCSIGenericReq *r) { - SCSIRequest *last; - SCSIGenericState *s = r->dev; - - if (s->requests == r) { - s->requests = r->next; - } else { - last = s->requests; - while (last && last->next != r) - last = last->next; - if (last) { - last->next = r->next; - } else { - BADF("Orphaned request\n"); - } - } - r->next = free_requests; - free_requests = r; + qemu_free(r->buf); + scsi_req_free(&r->req); } -static SCSIRequest *scsi_find_request(SCSIGenericState *s, uint32_t tag) +static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag) { - SCSIRequest *r; - - r = s->requests; - while (r && r->tag != tag) - r = r->next; - - return r; + return DO_UPCAST(SCSIGenericReq, req, scsi_req_find(&s->qdev, tag)); } /* Helper function for command completion. */ static void scsi_command_complete(void *opaque, int ret) { - SCSIRequest *r = (SCSIRequest *)opaque; - SCSIGenericState *s = r->dev; - uint32_t tag; - int status; + SCSIGenericReq *r = (SCSIGenericReq *)opaque; + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); s->driver_status = r->io_header.driver_status; if (s->driver_status & SG_ERR_DRIVER_SENSE) s->senselen = r->io_header.sb_len_wr; if (ret != 0) - status = BUSY << 1; + r->req.status = BUSY << 1; else { if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { - status = BUSY << 1; + r->req.status = BUSY << 1; BADF("Driver Timeout\n"); } else if (r->io_header.status) - status = r->io_header.status; + r->req.status = r->io_header.status; else if (s->driver_status & SG_ERR_DRIVER_SENSE) - status = CHECK_CONDITION << 1; + r->req.status = CHECK_CONDITION << 1; else - status = GOOD << 1; + r->req.status = GOOD << 1; } DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", - r, r->tag, status); - tag = r->tag; + r, r->req.tag, r->req.status); + + scsi_req_complete(&r->req); scsi_remove_request(r); - r->bus->complete(r->bus, SCSI_REASON_DONE, tag, status); } /* Cancel a pending data transfer. */ @@ -182,35 +118,37 @@ static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) { DPRINTF("scsi_cancel_io 0x%x\n", tag); SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIRequest *r; + SCSIGenericReq *r; DPRINTF("Cancel tag=0x%x\n", tag); r = scsi_find_request(s, tag); if (r) { - if (r->aiocb) - bdrv_aio_cancel(r->aiocb); - r->aiocb = NULL; + if (r->req.aiocb) + bdrv_aio_cancel(r->req.aiocb); + r->req.aiocb = NULL; scsi_remove_request(r); } } static int execute_command(BlockDriverState *bdrv, - SCSIRequest *r, int direction, + SCSIGenericReq *r, int direction, BlockDriverCompletionFunc *complete) { + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); + r->io_header.interface_id = 'S'; r->io_header.dxfer_direction = direction; r->io_header.dxferp = r->buf; r->io_header.dxfer_len = r->buflen; - r->io_header.cmdp = r->cmd; - r->io_header.cmd_len = r->cmdlen; - r->io_header.mx_sb_len = sizeof(r->dev->sensebuf); - r->io_header.sbp = r->dev->sensebuf; + r->io_header.cmdp = r->req.cmd.buf; + r->io_header.cmd_len = r->req.cmd.len; + r->io_header.mx_sb_len = sizeof(s->sensebuf); + r->io_header.sbp = s->sensebuf; r->io_header.timeout = MAX_UINT; r->io_header.usr_ptr = r; r->io_header.flags |= SG_FLAG_DIRECT_IO; - r->aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r); - if (r->aiocb == NULL) { + r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r); + if (r->req.aiocb == NULL) { BADF("execute_command: read failed !\n"); return -1; } @@ -220,7 +158,7 @@ static int execute_command(BlockDriverState *bdrv, static void scsi_read_complete(void * opaque, int ret) { - SCSIRequest *r = (SCSIRequest *)opaque; + SCSIGenericReq *r = (SCSIGenericReq *)opaque; int len; if (ret) { @@ -229,10 +167,10 @@ static void scsi_read_complete(void * opaque, int ret) return; } len = r->io_header.dxfer_len - r->io_header.resid; - DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, len); + DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); r->len = -1; - r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, len); + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); if (len == 0) scsi_command_complete(r, 0); } @@ -241,7 +179,7 @@ static void scsi_read_complete(void * opaque, int ret) static void scsi_read_data(SCSIDevice *d, uint32_t tag) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIRequest *r; + SCSIGenericReq *r; int ret; DPRINTF("scsi_read_data 0x%x\n", tag); @@ -258,7 +196,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) return; } - if (r->cmd[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE) + if (r->req.cmd.buf[0] == REQUEST_SENSE && s->driver_status & SG_ERR_DRIVER_SENSE) { s->senselen = MIN(r->len, s->senselen); memcpy(r->buf, s->sensebuf, s->senselen); @@ -266,15 +204,15 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) r->io_header.status = 0; r->io_header.dxfer_len = s->senselen; r->len = -1; - DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, s->senselen); + DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, s->senselen); DPRINTF("Sense: %d %d %d %d %d %d %d %d\n", r->buf[0], r->buf[1], r->buf[2], r->buf[3], r->buf[4], r->buf[5], r->buf[6], r->buf[7]); - r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, s->senselen); + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, s->senselen); return; } - ret = execute_command(s->dinfo->bdrv, r, SG_DXFER_FROM_DEV, scsi_read_complete); + ret = execute_command(s->qdev.dinfo->bdrv, r, SG_DXFER_FROM_DEV, scsi_read_complete); if (ret == -1) { scsi_command_complete(r, -EINVAL); return; @@ -283,7 +221,8 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag) static void scsi_write_complete(void * opaque, int ret) { - SCSIRequest *r = (SCSIRequest *)opaque; + SCSIGenericReq *r = (SCSIGenericReq *)opaque; + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev); DPRINTF("scsi_write_complete() ret = %d\n", ret); if (ret) { @@ -292,10 +231,10 @@ static void scsi_write_complete(void * opaque, int ret) return; } - if (r->cmd[0] == MODE_SELECT && r->cmd[4] == 12 && - r->dev->type == TYPE_TAPE) { - r->dev->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; - DPRINTF("block size %d\n", r->dev->blocksize); + if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && + s->qdev.type == TYPE_TAPE) { + s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; + DPRINTF("block size %d\n", s->blocksize); } scsi_command_complete(r, ret); @@ -306,7 +245,7 @@ static void scsi_write_complete(void * opaque, int ret) static int scsi_write_data(SCSIDevice *d, uint32_t tag) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIRequest *r; + SCSIGenericReq *r; int ret; DPRINTF("scsi_write_data 0x%x\n", tag); @@ -320,11 +259,11 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag) if (r->len == 0) { r->len = r->buflen; - r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->len); + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len); return 0; } - ret = execute_command(s->dinfo->bdrv, r, SG_DXFER_TO_DEV, scsi_write_complete); + ret = execute_command(s->qdev.dinfo->bdrv, r, SG_DXFER_TO_DEV, scsi_write_complete); if (ret == -1) { scsi_command_complete(r, -EINVAL); return 1; @@ -337,7 +276,7 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag) static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIRequest *r; + SCSIGenericReq *r; r = scsi_find_request(s, tag); if (!r) { BADF("Bad buffer tag 0x%x\n", tag); @@ -346,159 +285,23 @@ static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) return r->buf; } -static int scsi_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len) +static void scsi_req_fixup(SCSIRequest *req) { - switch (cmd[0] >> 5) { - case 0: - *len = cmd[4]; - *cmdlen = 6; - /* length 0 means 256 blocks */ - if (*len == 0) - *len = 256; - break; - case 1: - case 2: - *len = cmd[8] | (cmd[7] << 8); - *cmdlen = 10; - break; - case 4: - *len = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24); - *cmdlen = 16; - break; - case 5: - *len = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24); - *cmdlen = 12; - break; - default: - return -1; - } - - switch(cmd[0]) { - case TEST_UNIT_READY: - case REZERO_UNIT: - case START_STOP: - case SEEK_6: - case WRITE_FILEMARKS: - case SPACE: - case ERASE: - case ALLOW_MEDIUM_REMOVAL: - case VERIFY: - case SEEK_10: - case SYNCHRONIZE_CACHE: - case LOCK_UNLOCK_CACHE: - case LOAD_UNLOAD: - case SET_CD_SPEED: - case SET_LIMITS: - case WRITE_LONG: - case MOVE_MEDIUM: - case UPDATE_BLOCK: - *len = 0; - break; - case MODE_SENSE: - break; - case WRITE_SAME: - *len = 1; - break; - case READ_CAPACITY: - *len = 8; - break; - case READ_BLOCK_LIMITS: - *len = 6; - break; - case READ_POSITION: - *len = 20; - break; - case SEND_VOLUME_TAG: - *len *= 40; - break; - case MEDIUM_SCAN: - *len *= 8; - break; + switch(req->cmd.buf[0]) { case WRITE_10: - cmd[1] &= ~0x08; /* disable FUA */ - case WRITE_VERIFY: - case WRITE_6: - case WRITE_12: - case WRITE_VERIFY_12: - *len *= blocksize; + req->cmd.buf[1] &= ~0x08; /* disable FUA */ break; case READ_10: - cmd[1] &= ~0x08; /* disable FUA */ - case READ_6: - case READ_REVERSE: - case RECOVER_BUFFERED_DATA: - case READ_12: - *len *= blocksize; - break; - case INQUIRY: - *len = cmd[4] | (cmd[3] << 8); - break; - } - return 0; -} - -static int scsi_stream_length(uint8_t *cmd, int blocksize, int *cmdlen, uint32_t *len) -{ - switch(cmd[0]) { - /* stream commands */ - case READ_6: - case READ_REVERSE: - case RECOVER_BUFFERED_DATA: - case WRITE_6: - *cmdlen = 6; - *len = cmd[4] | (cmd[3] << 8) | (cmd[2] << 16); - if (cmd[1] & 0x01) /* fixed */ - *len *= blocksize; + req->cmd.buf[1] &= ~0x08; /* disable FUA */ break; case REWIND: case START_STOP: - *cmdlen = 6; - *len = 0; - cmd[1] = 0x01; /* force IMMED, otherwise qemu waits end of command */ + if (req->dev->type == TYPE_TAPE) { + /* force IMMED, otherwise qemu waits end of command */ + req->cmd.buf[1] = 0x01; + } break; - /* generic commands */ - default: - return scsi_length(cmd, blocksize, cmdlen, len); - } - return 0; -} - -static int is_write(int command) -{ - switch (command) { - case COPY: - case COPY_VERIFY: - case COMPARE: - case CHANGE_DEFINITION: - case LOG_SELECT: - case MODE_SELECT: - case MODE_SELECT_10: - case SEND_DIAGNOSTIC: - case WRITE_BUFFER: - case FORMAT_UNIT: - case REASSIGN_BLOCKS: - case RESERVE: - case SEARCH_EQUAL: - case SEARCH_HIGH: - case SEARCH_LOW: - case WRITE_6: - case WRITE_10: - case WRITE_VERIFY: - case UPDATE_BLOCK: - case WRITE_LONG: - case WRITE_SAME: - case SEARCH_HIGH_12: - case SEARCH_EQUAL_12: - case SEARCH_LOW_12: - case WRITE_12: - case WRITE_VERIFY_12: - case SET_WINDOW: - case MEDIUM_SCAN: - case SEND_VOLUME_TAG: - case WRITE_LONG_2: - return 1; } - return 0; } /* Execute a scsi command. Returns the length of the data expected by the @@ -510,27 +313,10 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, uint8_t *cmd, int lun) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - uint32_t len=0; - int cmdlen=0; - SCSIRequest *r; + SCSIGenericReq *r; SCSIBus *bus; int ret; - if (s->type == TYPE_TAPE) { - if (scsi_stream_length(cmd, s->blocksize, &cmdlen, &len) == -1) { - BADF("Unsupported command length, command %x\n", cmd[0]); - return 0; - } - } else { - if (scsi_length(cmd, s->blocksize, &cmdlen, &len) == -1) { - BADF("Unsupported command length, command %x\n", cmd[0]); - return 0; - } - } - - DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, - cmd[0], len); - if (cmd[0] != REQUEST_SENSE && (lun != s->lun || (cmd[1] >> 5) != s->lun)) { DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5); @@ -554,17 +340,24 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, BADF("Tag 0x%x already in use %p\n", tag, r); scsi_cancel_io(d, tag); } - r = scsi_new_request(d, tag); + r = scsi_new_request(d, tag, lun); + + if (-1 == scsi_req_parse(&r->req, cmd)) { + BADF("Unsupported command length, command %x\n", cmd[0]); + scsi_remove_request(r); + return 0; + } + scsi_req_fixup(&r->req); - memcpy(r->cmd, cmd, cmdlen); - r->cmdlen = cmdlen; + DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, + cmd[0], r->req.cmd.xfer); - if (len == 0) { + if (r->req.cmd.xfer == 0) { if (r->buf != NULL) qemu_free(r->buf); r->buflen = 0; r->buf = NULL; - ret = execute_command(s->dinfo->bdrv, r, SG_DXFER_NONE, scsi_command_complete); + ret = execute_command(s->qdev.dinfo->bdrv, r, SG_DXFER_NONE, scsi_command_complete); if (ret == -1) { scsi_command_complete(r, -EINVAL); return 0; @@ -572,21 +365,21 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, return 0; } - if (r->buflen != len) { + if (r->buflen != r->req.cmd.xfer) { if (r->buf != NULL) qemu_free(r->buf); - r->buf = qemu_malloc(len); - r->buflen = len; + r->buf = qemu_malloc(r->req.cmd.xfer); + r->buflen = r->req.cmd.xfer; } memset(r->buf, 0, r->buflen); - r->len = len; - if (is_write(cmd[0])) { + r->len = r->req.cmd.xfer; + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { r->len = 0; - return -len; + return -r->req.cmd.xfer; } - return len; + return r->req.cmd.xfer; } static int get_blocksize(BlockDriverState *bdrv) @@ -653,23 +446,13 @@ static int get_stream_blocksize(BlockDriverState *bdrv) static void scsi_destroy(SCSIDevice *d) { SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); - SCSIRequest *r, *n; + SCSIGenericReq *r; - r = s->requests; - while (r) { - n = r->next; - qemu_free(r); - r = n; - } - - r = free_requests; - while (r) { - n = r->next; - qemu_free(r); - r = n; + while (!QTAILQ_EMPTY(&s->qdev.requests)) { + r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests)); + scsi_remove_request(r); } - - drive_uninit(s->dinfo); + drive_uninit(s->qdev.dinfo); } static int scsi_generic_initfn(SCSIDevice *dev) @@ -678,26 +461,26 @@ static int scsi_generic_initfn(SCSIDevice *dev) int sg_version; struct sg_scsi_id scsiid; - if (!s->dinfo || !s->dinfo->bdrv) { + if (!s->qdev.dinfo || !s->qdev.dinfo->bdrv) { qemu_error("scsi-generic: drive property not set\n"); return -1; } /* check we are really using a /dev/sg* file */ - if (!bdrv_is_sg(s->dinfo->bdrv)) { + if (!bdrv_is_sg(s->qdev.dinfo->bdrv)) { qemu_error("scsi-generic: not /dev/sg*\n"); return -1; } /* check we are using a driver managing SG_IO (version 3 and after */ - if (bdrv_ioctl(s->dinfo->bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 || + if (bdrv_ioctl(s->qdev.dinfo->bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 || sg_version < 30000) { qemu_error("scsi-generic: scsi generic interface too old\n"); return -1; } /* get LUN of the /dev/sg? */ - if (bdrv_ioctl(s->dinfo->bdrv, SG_GET_SCSI_ID, &scsiid)) { + if (bdrv_ioctl(s->qdev.dinfo->bdrv, SG_GET_SCSI_ID, &scsiid)) { qemu_error("scsi-generic: SG_GET_SCSI_ID ioctl failed\n"); return -1; } @@ -705,23 +488,23 @@ static int scsi_generic_initfn(SCSIDevice *dev) /* define device state */ s->lun = scsiid.lun; DPRINTF("LUN %d\n", s->lun); - s->type = scsiid.scsi_type; - DPRINTF("device type %d\n", s->type); - if (s->type == TYPE_TAPE) { - s->blocksize = get_stream_blocksize(s->dinfo->bdrv); - if (s->blocksize == -1) - s->blocksize = 0; + s->qdev.type = scsiid.scsi_type; + DPRINTF("device type %d\n", s->qdev.type); + if (s->qdev.type == TYPE_TAPE) { + s->qdev.blocksize = get_stream_blocksize(s->qdev.dinfo->bdrv); + if (s->qdev.blocksize == -1) + s->qdev.blocksize = 0; } else { - s->blocksize = get_blocksize(s->dinfo->bdrv); + s->qdev.blocksize = get_blocksize(s->qdev.dinfo->bdrv); /* removable media returns 0 if not present */ - if (s->blocksize <= 0) { - if (s->type == TYPE_ROM || s->type == TYPE_WORM) - s->blocksize = 2048; + if (s->qdev.blocksize <= 0) { + if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) + s->qdev.blocksize = 2048; else - s->blocksize = 512; + s->qdev.blocksize = 512; } } - DPRINTF("block size %d\n", s->blocksize); + DPRINTF("block size %d\n", s->qdev.blocksize); s->driver_status = 0; memset(s->sensebuf, 0, sizeof(s->sensebuf)); return 0; @@ -739,7 +522,7 @@ static SCSIDeviceInfo scsi_generic_info = { .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("drive", SCSIGenericState, dinfo), + DEFINE_PROP_DRIVE("drive", SCSIGenericState, qdev.dinfo), DEFINE_PROP_END_OF_LIST(), }, }; @@ -1,7 +1,10 @@ -#ifndef SCSI_DISK_H -#define SCSI_DISK_H +#ifndef QEMU_HW_SCSI_H +#define QEMU_HW_SCSI_H #include "qdev.h" +#include "block.h" + +#define SCSI_CMD_BUF_SIZE 16 /* scsi-disk.c */ enum scsi_reason { @@ -15,11 +18,43 @@ typedef struct SCSIDeviceInfo SCSIDeviceInfo; typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag, uint32_t arg); +enum SCSIXferMode { + SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */ + SCSI_XFER_FROM_DEV, /* READ, INQUIRY, MODE_SENSE, ... */ + SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ +}; + +typedef struct SCSISense { + uint8_t key; +} SCSISense; + +typedef struct SCSIRequest { + SCSIBus *bus; + SCSIDevice *dev; + uint32_t tag; + uint32_t lun; + uint32_t status; + struct { + uint8_t buf[SCSI_CMD_BUF_SIZE]; + int len; + size_t xfer; + uint64_t lba; + enum SCSIXferMode mode; + } cmd; + BlockDriverAIOCB *aiocb; + QTAILQ_ENTRY(SCSIRequest) next; +} SCSIRequest; + struct SCSIDevice { DeviceState qdev; uint32_t id; + DriveInfo *dinfo; SCSIDeviceInfo *info; + QTAILQ_HEAD(, SCSIRequest) requests; + int blocksize; + int type; + struct SCSISense sense; }; /* cdrom.c */ @@ -64,4 +99,15 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, DriveInfo *dinfo, int unit); void scsi_bus_legacy_handle_cmdline(SCSIBus *bus); +void scsi_dev_clear_sense(SCSIDevice *dev); +void scsi_dev_set_sense(SCSIDevice *dev, uint8_t key); + +SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); +SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag); +void scsi_req_free(SCSIRequest *req); + +int scsi_req_parse(SCSIRequest *req, uint8_t *buf); +void scsi_req_print(SCSIRequest *req); +void scsi_req_complete(SCSIRequest *req); + #endif diff --git a/hw/smc91c111.c b/hw/smc91c111.c index b7398c929..c1a88c9e5 100644 --- a/hw/smc91c111.c +++ b/hw/smc91c111.c @@ -18,7 +18,7 @@ typedef struct { SysBusDevice busdev; - VLANClientState *vc; + NICState *nic; NICConf conf; uint16_t tcr; uint16_t rcr; @@ -207,7 +207,7 @@ static void smc91c111_do_tx(smc91c111_state *s) smc91c111_release_packet(s, packetnum); else if (s->tx_fifo_done_len < NUM_PACKETS) s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; - qemu_send_packet(s->vc, p, len); + qemu_send_packet(&s->nic->nc, p, len); } s->tx_fifo_len = 0; smc91c111_update(s); @@ -591,9 +591,9 @@ static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset) return val; } -static int smc91c111_can_receive(VLANClientState *vc) +static int smc91c111_can_receive(VLANClientState *nc) { - smc91c111_state *s = vc->opaque; + smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) return 1; @@ -602,9 +602,9 @@ static int smc91c111_can_receive(VLANClientState *vc) return 1; } -static ssize_t smc91c111_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t smc91c111_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - smc91c111_state *s = vc->opaque; + smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; int status; int packetsize; uint32_t crc; @@ -692,13 +692,21 @@ static CPUWriteMemoryFunc * const smc91c111_writefn[] = { smc91c111_writel }; -static void smc91c111_cleanup(VLANClientState *vc) +static void smc91c111_cleanup(VLANClientState *nc) { - smc91c111_state *s = vc->opaque; + smc91c111_state *s = DO_UPCAST(NICState, nc, nc)->opaque; - s->vc = NULL; + s->nic = NULL; } +static NetClientInfo net_smc91c111_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = smc91c111_can_receive, + .receive = smc91c111_receive, + .cleanup = smc91c111_cleanup, +}; + static int smc91c111_init1(SysBusDevice *dev) { smc91c111_state *s = FROM_SYSBUS(smc91c111_state, dev); @@ -711,12 +719,9 @@ static int smc91c111_init1(SysBusDevice *dev) smc91c111_reset(s); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - s->conf.vlan, s->conf.peer, - dev->qdev.info->name, dev->qdev.id, - smc91c111_can_receive, smc91c111_receive, NULL, - NULL, smc91c111_cleanup, s); - qemu_format_nic_info_str(s->vc, s->conf.macaddr.a); + s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf, + dev->qdev.info->name, dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); /* ??? Save/restore. */ return 0; } diff --git a/hw/ssd0303.c b/hw/ssd0303.c index f60930e3b..108c0683c 100644 --- a/hw/ssd0303.c +++ b/hw/ssd0303.c @@ -291,13 +291,13 @@ static int ssd0303_init(i2c_slave *i2c) ssd0303_invalidate_display, NULL, NULL, s); qemu_console_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY); - vmstate_register(-1, &vmstate_ssd0303, s); return 0; } static I2CSlaveInfo ssd0303_info = { .qdev.name = "ssd0303", .qdev.size = sizeof(ssd0303_state), + .qdev.vmsd = &vmstate_ssd0303, .init = ssd0303_init, .event = ssd0303_event, .recv = ssd0303_recv, diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c index 2252f1a5f..d1d755e29 100644 --- a/hw/stellaris_enet.c +++ b/hw/stellaris_enet.c @@ -66,7 +66,7 @@ typedef struct { uint8_t *rx_fifo; int rx_fifo_len; int next_packet; - VLANClientState *vc; + NICState *nic; NICConf conf; qemu_irq irq; int mmio_index; @@ -78,9 +78,9 @@ static void stellaris_enet_update(stellaris_enet_state *s) } /* TODO: Implement MAC address filtering. */ -static ssize_t stellaris_enet_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t stellaris_enet_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - stellaris_enet_state *s = vc->opaque; + stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; int n; uint8_t *p; uint32_t crc; @@ -120,9 +120,9 @@ static ssize_t stellaris_enet_receive(VLANClientState *vc, const uint8_t *buf, s return size; } -static int stellaris_enet_can_receive(VLANClientState *vc) +static int stellaris_enet_can_receive(VLANClientState *nc) { - stellaris_enet_state *s = vc->opaque; + stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; if ((s->rctl & SE_RCTL_RXEN) == 0) return 1; @@ -258,7 +258,7 @@ static void stellaris_enet_write(void *opaque, target_phys_addr_t offset, memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len); s->tx_fifo_len = 60; } - qemu_send_packet(s->vc, s->tx_fifo, s->tx_frame_len); + qemu_send_packet(&s->nic->nc, s->tx_fifo, s->tx_frame_len); s->tx_frame_len = -1; s->ris |= SE_INT_TXEMP; stellaris_enet_update(s); @@ -385,9 +385,9 @@ static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static void stellaris_enet_cleanup(VLANClientState *vc) +static void stellaris_enet_cleanup(VLANClientState *nc) { - stellaris_enet_state *s = vc->opaque; + stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; unregister_savevm("stellaris_enet", s); @@ -396,6 +396,14 @@ static void stellaris_enet_cleanup(VLANClientState *vc) qemu_free(s); } +static NetClientInfo net_stellaris_enet_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = stellaris_enet_can_receive, + .receive = stellaris_enet_receive, + .cleanup = stellaris_enet_cleanup, +}; + static int stellaris_enet_init(SysBusDevice *dev) { stellaris_enet_state *s = FROM_SYSBUS(stellaris_enet_state, dev); @@ -406,13 +414,9 @@ static int stellaris_enet_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - s->conf.vlan, s->conf.peer, - dev->qdev.info->name, dev->qdev.id, - stellaris_enet_can_receive, - stellaris_enet_receive, NULL, NULL, - stellaris_enet_cleanup, s); - qemu_format_nic_info_str(s->vc, s->conf.macaddr.a); + s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, + dev->qdev.info->name, dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); stellaris_enet_reset(s); register_savevm("stellaris_enet", -1, 1, diff --git a/hw/tmp105.c b/hw/tmp105.c index 74141b378..8343afff8 100644 --- a/hw/tmp105.c +++ b/hw/tmp105.c @@ -228,13 +228,13 @@ static int tmp105_init(i2c_slave *i2c) tmp105_reset(&s->i2c); - vmstate_register(-1, &vmstate_tmp105, s); return 0; } static I2CSlaveInfo tmp105_info = { .qdev.name = "tmp105", .qdev.size = sizeof(TMP105State), + .qdev.vmsd = &vmstate_tmp105, .init = tmp105_init, .event = tmp105_event, .recv = tmp105_rx, diff --git a/hw/twl92230.c b/hw/twl92230.c index 93232da2d..b1b2ac9ce 100644 --- a/hw/twl92230.c +++ b/hw/twl92230.c @@ -855,13 +855,13 @@ static int twl92230_init(i2c_slave *i2c) menelaus_reset(&s->i2c); - vmstate_register(-1, &vmstate_menelaus, s); return 0; } static I2CSlaveInfo twl92230_info = { .qdev.name ="twl92230", .qdev.size = sizeof(MenelausState), + .qdev.vmsd = &vmstate_menelaus, .init = twl92230_init, .event = menelaus_event, .recv = menelaus_rx, diff --git a/hw/usb-net.c b/hw/usb-net.c index 9c6549c75..2556e05dd 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -610,7 +610,6 @@ typedef struct USBNetState { uint32_t media_state; uint16_t filter; uint32_t vendorid; - uint8_t mac[6]; unsigned int out_ptr; uint8_t out_buf[2048]; @@ -620,7 +619,8 @@ typedef struct USBNetState { uint8_t in_buf[2048]; char usbstring_mac[13]; - VLANClientState *vc; + NICState *nic; + NICConf conf; QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp; } USBNetState; @@ -741,12 +741,12 @@ static int ndis_query(USBNetState *s, uint32_t oid, /* ieee802.3 OIDs (table 4-3) */ /* mandatory */ case OID_802_3_PERMANENT_ADDRESS: - memcpy(outbuf, s->mac, 6); + memcpy(outbuf, s->conf.macaddr.a, 6); return 6; /* mandatory */ case OID_802_3_CURRENT_ADDRESS: - memcpy(outbuf, s->mac, 6); + memcpy(outbuf, s->conf.macaddr.a, 6); return 6; /* mandatory */ @@ -1305,7 +1305,7 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) if (!s->rndis) { if (ret < 64) { - qemu_send_packet(s->vc, s->out_buf, s->out_ptr); + qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr); s->out_ptr = 0; } return ret; @@ -1317,7 +1317,7 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); uint32_t size = le32_to_cpu(msg->DataLength); if (offs + size <= len) - qemu_send_packet(s->vc, s->out_buf + offs, size); + qemu_send_packet(&s->nic->nc, s->out_buf + offs, size); } s->out_ptr -= len; memmove(s->out_buf, &s->out_buf[len], s->out_ptr); @@ -1369,9 +1369,9 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) return ret; } -static ssize_t usbnet_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t usbnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - USBNetState *s = vc->opaque; + USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; struct rndis_packet_msg_type *msg; if (s->rndis) { @@ -1406,9 +1406,9 @@ static ssize_t usbnet_receive(VLANClientState *vc, const uint8_t *buf, size_t si return size; } -static int usbnet_can_receive(VLANClientState *vc) +static int usbnet_can_receive(VLANClientState *nc) { - USBNetState *s = vc->opaque; + USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; if (s->rndis && !s->rndis_state == RNDIS_DATA_INITIALIZED) return 1; @@ -1416,9 +1416,9 @@ static int usbnet_can_receive(VLANClientState *vc) return !s->in_len; } -static void usbnet_cleanup(VLANClientState *vc) +static void usbnet_cleanup(VLANClientState *nc) { - USBNetState *s = vc->opaque; + USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; rndis_clear_responsequeue(s); qemu_free(s); @@ -1429,7 +1429,7 @@ static void usb_net_handle_destroy(USBDevice *dev) USBNetState *s = (USBNetState *) dev; /* TODO: remove the nd_table[] entry */ - qemu_del_vlan_client(s->vc); + qemu_del_vlan_client(&s->nic->nc); } static int usb_net_initfn(USBDevice *dev) @@ -1450,6 +1450,14 @@ static int usb_net_initfn(USBDevice *dev) return 0; } +static NetClientInfo net_usbnet_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = usbnet_can_receive, + .receive = usbnet_receive, + .cleanup = usbnet_cleanup, +}; + USBDevice *usb_net_init(NICInfo *nd) { USBDevice *dev; @@ -1458,25 +1466,22 @@ USBDevice *usb_net_init(NICInfo *nd) dev = usb_create_simple(NULL /* FIXME */, "QEMU USB Network Interface"); s = DO_UPCAST(USBNetState, dev, dev); - memcpy(s->mac, nd->macaddr, 6); + memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.vlan = nd->vlan; + s->conf.peer = nd->netdev; - s->vc = nd->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - nd->vlan, nd->netdev, - nd->model, nd->name, - usbnet_can_receive, - usbnet_receive, - NULL, NULL, - usbnet_cleanup, s); + s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, + nd->model, nd->name, s); - qemu_format_nic_info_str(s->vc, s->mac); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), "%02x%02x%02x%02x%02x%02x", - 0x40, s->mac[1], s->mac[2], - s->mac[3], s->mac[4], s->mac[5]); + 0x40, s->conf.macaddr.a[1], s->conf.macaddr.a[2], + s->conf.macaddr.a[3], s->conf.macaddr.a[4], s->conf.macaddr.a[5]); fprintf(stderr, "usbnet: initialized mac %02x:%02x:%02x:%02x:%02x:%02x\n", - s->mac[0], s->mac[1], s->mac[2], - s->mac[3], s->mac[4], s->mac[5]); + s->conf.macaddr.a[0], s->conf.macaddr.a[1], s->conf.macaddr.a[2], + s->conf.macaddr.a[3], s->conf.macaddr.a[4], s->conf.macaddr.a[5]); return (USBDevice *) s; } diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 671916e05..ba26a4efc 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -1086,7 +1086,6 @@ static int usb_uhci_common_initfn(UHCIState *s) pci_register_bar(&s->dev, 4, 0x20, PCI_BASE_ADDRESS_SPACE_IO, uhci_map); - vmstate_register(0, &vmstate_uhci, s); return 0; } @@ -1114,10 +1113,12 @@ static PCIDeviceInfo uhci_info[] = { { .qdev.name = "PIIX3 USB-UHCI", .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, .init = usb_uhci_piix3_initfn, },{ .qdev.name = "PIIX4 USB-UHCI", .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, .init = usb_uhci_piix4_initfn, },{ /* end of list */ diff --git a/hw/vga-pci.c b/hw/vga-pci.c index d0a781822..b7642ec81 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -84,7 +84,6 @@ static int pci_vga_initfn(PCIDevice *dev) // vga + console init vga_common_init(s, VGA_RAM_SIZE); vga_init(s); - vmstate_register(0, &vmstate_vga_pci, d); s->ds = graphic_console_init(s->update, s->invalidate, s->screen_dump, s->text_update, s); @@ -136,6 +135,7 @@ int pci_vga_init(PCIBus *bus, static PCIDeviceInfo vga_info = { .qdev.name = "VGA", .qdev.size = sizeof(PCIVGAState), + .qdev.vmsd = &vmstate_vga_pci, .init = pci_vga_initfn, .config_write = pci_vga_write_config, .qdev.props = (Property[]) { diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index 42b766fa2..a93d20dfa 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -100,7 +100,7 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) static int virtio_blk_handle_write_error(VirtIOBlockReq *req, int error) { - BlockInterfaceErrorAction action = drive_get_onerror(req->dev->bs); + BlockInterfaceErrorAction action = drive_get_on_error(req->dev->bs, 0); VirtIOBlock *s = req->dev; if (action == BLOCK_ERR_IGNORE) diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 2f147e522..2f201ffc7 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -31,7 +31,7 @@ typedef struct VirtIONet VirtQueue *rx_vq; VirtQueue *tx_vq; VirtQueue *ctrl_vq; - VLANClientState *vc; + NICState *nic; QEMUTimer *tx_timer; int tx_timer_active; uint32_t has_vnet_hdr; @@ -85,16 +85,16 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) if (memcmp(netcfg.mac, n->mac, ETH_ALEN)) { memcpy(n->mac, netcfg.mac, ETH_ALEN); - qemu_format_nic_info_str(n->vc, n->mac); + qemu_format_nic_info_str(&n->nic->nc, n->mac); } } -static void virtio_net_set_link_status(VLANClientState *vc) +static void virtio_net_set_link_status(VLANClientState *nc) { - VirtIONet *n = vc->opaque; + VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; uint16_t old_status = n->status; - if (vc->link_down) + if (nc->link_down) n->status &= ~VIRTIO_NET_S_LINK_UP; else n->status |= VIRTIO_NET_S_LINK_UP; @@ -126,13 +126,13 @@ static void virtio_net_reset(VirtIODevice *vdev) static int peer_has_vnet_hdr(VirtIONet *n) { - if (!n->vc->peer) + if (!n->nic->nc.peer) return 0; - if (n->vc->peer->type != NET_CLIENT_TYPE_TAP) + if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) return 0; - n->has_vnet_hdr = tap_has_vnet_hdr(n->vc->peer); + n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); return n->has_vnet_hdr; } @@ -142,7 +142,7 @@ static int peer_has_ufo(VirtIONet *n) if (!peer_has_vnet_hdr(n)) return 0; - n->has_ufo = tap_has_ufo(n->vc->peer); + n->has_ufo = tap_has_ufo(n->nic->nc.peer); return n->has_ufo; } @@ -159,7 +159,7 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) (1 << VIRTIO_NET_F_CTRL_RX_EXTRA); if (peer_has_vnet_hdr(n)) { - tap_using_vnet_hdr(n->vc->peer, 1); + tap_using_vnet_hdr(n->nic->nc.peer, 1); features |= (1 << VIRTIO_NET_F_CSUM); features |= (1 << VIRTIO_NET_F_HOST_TSO4); @@ -202,7 +202,7 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF)); if (n->has_vnet_hdr) { - tap_set_offload(n->vc->peer, + tap_set_offload(n->nic->nc.peer, (features >> VIRTIO_NET_F_GUEST_CSUM) & 1, (features >> VIRTIO_NET_F_GUEST_TSO4) & 1, (features >> VIRTIO_NET_F_GUEST_TSO6) & 1, @@ -360,16 +360,16 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); - qemu_flush_queued_packets(n->vc); + qemu_flush_queued_packets(&n->nic->nc); /* We now have RX buffers, signal to the IO thread to break out of the * select to re-poll the tap file descriptor */ qemu_notify_event(); } -static int virtio_net_can_receive(VLANClientState *vc) +static int virtio_net_can_receive(VLANClientState *nc) { - VirtIONet *n = vc->opaque; + VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; if (!virtio_queue_ready(n->rx_vq) || !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) @@ -511,13 +511,13 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) return 0; } -static ssize_t virtio_net_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - VirtIONet *n = vc->opaque; + VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; size_t hdr_len, offset, i; - if (!virtio_net_can_receive(n->vc)) + if (!virtio_net_can_receive(&n->nic->nc)) return -1; if (!virtio_net_has_buffers(n, size)) @@ -590,9 +590,9 @@ static ssize_t virtio_net_receive(VLANClientState *vc, const uint8_t *buf, size_ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); -static void virtio_net_tx_complete(VLANClientState *vc, ssize_t len) +static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len) { - VirtIONet *n = vc->opaque; + VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; virtqueue_push(n->tx_vq, &n->async_tx.elem, n->async_tx.len); virtio_notify(&n->vdev, n->tx_vq); @@ -644,7 +644,7 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) len += hdr_len; } - ret = qemu_sendv_packet_async(n->vc, out_sg, out_num, + ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, virtio_net_tx_complete); if (ret == 0) { virtio_queue_set_notification(n->tx_vq, 0); @@ -766,8 +766,8 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } if (n->has_vnet_hdr) { - tap_using_vnet_hdr(n->vc->peer, 1); - tap_set_offload(n->vc->peer, + tap_using_vnet_hdr(n->nic->nc.peer, 1); + tap_set_offload(n->nic->nc.peer, (n->vdev.features >> VIRTIO_NET_F_GUEST_CSUM) & 1, (n->vdev.features >> VIRTIO_NET_F_GUEST_TSO4) & 1, (n->vdev.features >> VIRTIO_NET_F_GUEST_TSO6) & 1, @@ -811,13 +811,22 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static void virtio_net_cleanup(VLANClientState *vc) +static void virtio_net_cleanup(VLANClientState *nc) { - VirtIONet *n = vc->opaque; + VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - n->vc = NULL; + n->nic = NULL; } +static NetClientInfo net_virtio_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = virtio_net_can_receive, + .receive = virtio_net_receive, + .cleanup = virtio_net_cleanup, + .link_status_changed = virtio_net_set_link_status, +}; + VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf) { VirtIONet *n; @@ -839,14 +848,10 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf) qemu_macaddr_default_if_unset(&conf->macaddr); memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac)); n->status = VIRTIO_NET_S_LINK_UP; - n->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, conf->vlan, conf->peer, - dev->info->name, dev->id, - virtio_net_can_receive, - virtio_net_receive, NULL, NULL, - virtio_net_cleanup, n); - n->vc->link_status_changed = virtio_net_set_link_status; - qemu_format_nic_info_str(n->vc, conf->macaddr.a); + n->nic = qemu_new_nic(&net_virtio_info, conf, dev->info->name, dev->id, n); + + qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n); n->tx_timer_active = 0; @@ -867,7 +872,7 @@ void virtio_net_exit(VirtIODevice *vdev) { VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); - qemu_purge_queued_packets(n->vc); + qemu_purge_queued_packets(&n->nic->nc); unregister_savevm("virtio-net", n); @@ -878,5 +883,5 @@ void virtio_net_exit(VirtIODevice *vdev) qemu_free_timer(n->tx_timer); virtio_cleanup(&n->vdev); - qemu_del_vlan_client(n->vc); + qemu_del_vlan_client(&n->nic->nc); } diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c index bb3410105..24ea6205b 100644 --- a/hw/vmware_vga.c +++ b/hw/vmware_vga.c @@ -1189,7 +1189,6 @@ static int pci_vmsvga_initfn(PCIDevice *dev) vmsvga_init(&s->chip, VGA_RAM_SIZE); - vmstate_register(0, &vmstate_vmware_vga, s); return 0; } @@ -1201,6 +1200,7 @@ void pci_vmsvga_init(PCIBus *bus) static PCIDeviceInfo vmsvga_info = { .qdev.name = "QEMUware SVGA", .qdev.size = sizeof(struct pci_vmsvga_state_s), + .qdev.vmsd = &vmstate_vmware_vga, .init = pci_vmsvga_initfn, }; diff --git a/hw/wdt_i6300esb.c b/hw/wdt_i6300esb.c index 27fa09e41..805b3028f 100644 --- a/hw/wdt_i6300esb.c +++ b/hw/wdt_i6300esb.c @@ -416,8 +416,6 @@ static int i6300esb_init(PCIDevice *dev) pci_register_bar(&d->dev, 0, 0x10, PCI_BASE_ADDRESS_SPACE_MEMORY, i6300esb_map); - vmstate_register(-1, &vmstate_i6300esb, d); - return 0; } @@ -429,6 +427,7 @@ static WatchdogTimerModel model = { static PCIDeviceInfo i6300esb_info = { .qdev.name = "i6300esb", .qdev.size = sizeof(I6300State), + .qdev.vmsd = &vmstate_i6300esb, .config_read = i6300esb_config_read, .config_write = i6300esb_config_write, .init = i6300esb_init, diff --git a/hw/wdt_ib700.c b/hw/wdt_ib700.c index d67bf9eb4..c34687bca 100644 --- a/hw/wdt_ib700.c +++ b/hw/wdt_ib700.c @@ -98,7 +98,6 @@ static int wdt_ib700_init(ISADevice *dev) IB700State *s = DO_UPCAST(IB700State, dev, dev); s->timer = qemu_new_timer(vm_clock, ib700_timer_expired, s); - vmstate_register(-1, &vmstate_ib700, s); register_ioport_write(0x441, 2, 1, ib700_write_disable_reg, s); register_ioport_write(0x443, 2, 1, ib700_write_enable_reg, s); @@ -113,6 +112,7 @@ static WatchdogTimerModel model = { static ISADeviceInfo wdt_ib700_info = { .qdev.name = "ib700", .qdev.size = sizeof(IB700State), + .qdev.vmsd = &vmstate_ib700, .init = wdt_ib700_init, }; diff --git a/hw/wm8750.c b/hw/wm8750.c index e6a9c0681..6064da041 100644 --- a/hw/wm8750.c +++ b/hw/wm8750.c @@ -622,7 +622,6 @@ static int wm8750_init(i2c_slave *i2c) AUD_register_card(CODEC, &s->card); wm8750_reset(&s->i2c); - vmstate_register(-1, &vmstate_wm8750, s); return 0; } @@ -699,6 +698,7 @@ void wm8750_set_bclk_in(void *opaque, int new_hz) static I2CSlaveInfo wm8750_info = { .qdev.name = "wm8750", .qdev.size = sizeof(WM8750State), + .qdev.vmsd = &vmstate_wm8750, .init = wm8750_init, .event = wm8750_event, .recv = wm8750_rx, diff --git a/hw/xen_nic.c b/hw/xen_nic.c index bcf161c86..08055b83f 100644 --- a/hw/xen_nic.c +++ b/hw/xen_nic.c @@ -41,6 +41,7 @@ #include "hw.h" #include "net.h" #include "net/checksum.h" +#include "net/util.h" #include "qemu-char.h" #include "xen_backend.h" @@ -56,7 +57,8 @@ struct XenNetDev { struct netif_rx_sring *rxs; netif_tx_back_ring_t tx_ring; netif_rx_back_ring_t rx_ring; - VLANClientState *vs; + NICConf conf; + NICState *nic; }; /* ------------------------------------------------------------- */ @@ -179,9 +181,9 @@ static void net_tx_packets(struct XenNetDev *netdev) tmpbuf = qemu_malloc(XC_PAGE_SIZE); memcpy(tmpbuf, page + txreq.offset, txreq.size); net_checksum_calculate(tmpbuf, txreq.size); - qemu_send_packet(netdev->vs, tmpbuf, txreq.size); + qemu_send_packet(&netdev->nic->nc, tmpbuf, txreq.size); } else { - qemu_send_packet(netdev->vs, page + txreq.offset, txreq.size); + qemu_send_packet(&netdev->nic->nc, page + txreq.offset, txreq.size); } xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); @@ -223,9 +225,9 @@ static void net_rx_response(struct XenNetDev *netdev, #define NET_IP_ALIGN 2 -static int net_rx_ok(VLANClientState *vc) +static int net_rx_ok(VLANClientState *nc) { - struct XenNetDev *netdev = vc->opaque; + struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque; RING_IDX rc, rp; if (netdev->xendev.be_state != XenbusStateConnected) @@ -243,9 +245,9 @@ static int net_rx_ok(VLANClientState *vc) return 1; } -static ssize_t net_rx_packet(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t net_rx_packet(VLANClientState *nc, const uint8_t *buf, size_t size) { - struct XenNetDev *netdev = vc->opaque; + struct XenNetDev *netdev = DO_UPCAST(NICState, nc, nc)->opaque; netif_rx_request_t rxreq; RING_IDX rc, rp; void *page; @@ -288,10 +290,16 @@ static ssize_t net_rx_packet(VLANClientState *vc, const uint8_t *buf, size_t siz /* ------------------------------------------------------------- */ +static NetClientInfo net_xen_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = net_rx_ok, + .receive = net_rx_packet, +}; + static int net_init(struct XenDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); - VLANState *vlan; /* read xenstore entries */ if (netdev->mac == NULL) @@ -301,12 +309,16 @@ static int net_init(struct XenDevice *xendev) if (netdev->mac == NULL) return -1; - vlan = qemu_find_vlan(netdev->xendev.dev, 1); - netdev->vs = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - vlan, NULL, "xen", NULL, - net_rx_ok, net_rx_packet, NULL, NULL, - NULL, netdev); - snprintf(netdev->vs->info_str, sizeof(netdev->vs->info_str), + if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) + return -1; + + netdev->conf.vlan = qemu_find_vlan(netdev->xendev.dev, 1); + netdev->conf.peer = NULL; + + netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, + "xen", NULL, netdev); + + snprintf(netdev->nic->nc.info_str, sizeof(netdev->nic->nc.info_str), "nic: xenbus vif macaddr=%s", netdev->mac); /* fill info */ @@ -376,9 +388,9 @@ static void net_disconnect(struct XenDevice *xendev) xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); netdev->rxs = NULL; } - if (netdev->vs) { - qemu_del_vlan_client(netdev->vs); - netdev->vs = NULL; + if (netdev->nic) { + qemu_del_vlan_client(&netdev->nic->nc); + netdev->nic = NULL; } } diff --git a/hw/xilinx_ethlite.c b/hw/xilinx_ethlite.c index b7129d5dd..37e33ec01 100644 --- a/hw/xilinx_ethlite.c +++ b/hw/xilinx_ethlite.c @@ -51,7 +51,7 @@ struct xlx_ethlite { SysBusDevice busdev; qemu_irq irq; - VLANClientState *vc; + NICState *nic; NICConf conf; uint32_t c_tx_pingpong; @@ -118,7 +118,7 @@ eth_writel (void *opaque, target_phys_addr_t addr, uint32_t value) D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value)); if ((value & (CTRL_P | CTRL_S)) == CTRL_S) { - qemu_send_packet(s->vc, + qemu_send_packet(&s->nic->nc, (void *) &s->regs[base], s->regs[base + R_TX_LEN0]); D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0])); @@ -160,17 +160,17 @@ static CPUWriteMemoryFunc * const eth_write[] = { NULL, NULL, ð_writel, }; -static int eth_can_rx(VLANClientState *vc) +static int eth_can_rx(VLANClientState *nc) { - struct xlx_ethlite *s = vc->opaque; + struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; int r; r = !(s->regs[R_RX_CTRL0] & CTRL_S); return r; } -static ssize_t eth_rx(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t eth_rx(VLANClientState *nc, const uint8_t *buf, size_t size) { - struct xlx_ethlite *s = vc->opaque; + struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; unsigned int rxbase = s->rxbuf * (0x800 / 4); int i; @@ -201,13 +201,21 @@ static ssize_t eth_rx(VLANClientState *vc, const uint8_t *buf, size_t size) return size; } -static void eth_cleanup(VLANClientState *vc) +static void eth_cleanup(VLANClientState *nc) { - struct xlx_ethlite *s = vc->opaque; + struct xlx_ethlite *s = DO_UPCAST(NICState, nc, nc)->opaque; - s->vc = NULL; + s->nic = NULL; } +static NetClientInfo net_xilinx_ethlite_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = eth_can_rx, + .receive = eth_rx, + .cleanup = eth_cleanup, +}; + static int xilinx_ethlite_init(SysBusDevice *dev) { struct xlx_ethlite *s = FROM_SYSBUS(typeof (*s), dev); @@ -220,12 +228,9 @@ static int xilinx_ethlite_init(SysBusDevice *dev) sysbus_init_mmio(dev, R_MAX * 4, regs); qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_NIC, - s->conf.vlan, s->conf.peer, - dev->qdev.info->name, dev->qdev.id, - eth_can_rx, eth_rx, NULL, - NULL, eth_cleanup, s); - qemu_format_nic_info_str(s->vc, s->conf.macaddr.a); + s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, + dev->qdev.info->name, dev->qdev.id, s); + qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); return 0; } diff --git a/migration-exec.c b/migration-exec.c index c83066959..87f645b68 100644 --- a/migration-exec.c +++ b/migration-exec.c @@ -52,7 +52,8 @@ static int exec_close(FdMigrationState *s) return 0; } -MigrationState *exec_start_outgoing_migration(const char *command, +MigrationState *exec_start_outgoing_migration(Monitor *mon, + const char *command, int64_t bandwidth_limit, int detach, int blk, @@ -88,13 +89,14 @@ MigrationState *exec_start_outgoing_migration(const char *command, s->mig_state.blk = blk; s->mig_state.shared = inc; - + s->state = MIG_STATE_ACTIVE; - s->mon_resume = NULL; + s->mon = NULL; s->bandwidth_limit = bandwidth_limit; - if (!detach) - migrate_fd_monitor_suspend(s); + if (!detach) { + migrate_fd_monitor_suspend(s, mon); + } migrate_fd_connect(s); return &s->mig_state; diff --git a/migration-fd.c b/migration-fd.c index 587f9d8e8..ef7edbc5e 100644 --- a/migration-fd.c +++ b/migration-fd.c @@ -82,13 +82,14 @@ MigrationState *fd_start_outgoing_migration(Monitor *mon, s->mig_state.blk = blk; s->mig_state.shared = inc; - + s->state = MIG_STATE_ACTIVE; - s->mon_resume = NULL; + s->mon = NULL; s->bandwidth_limit = bandwidth_limit; - if (!detach) - migrate_fd_monitor_suspend(s); + if (!detach) { + migrate_fd_monitor_suspend(s, mon); + } migrate_fd_connect(s); return &s->mig_state; diff --git a/migration-tcp.c b/migration-tcp.c index efa7c74c6..b77ed8762 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -76,7 +76,8 @@ static void tcp_wait_for_connect(void *opaque) } } -MigrationState *tcp_start_outgoing_migration(const char *host_port, +MigrationState *tcp_start_outgoing_migration(Monitor *mon, + const char *host_port, int64_t bandwidth_limit, int detach, int blk, @@ -102,7 +103,7 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, s->mig_state.shared = inc; s->state = MIG_STATE_ACTIVE; - s->mon_resume = NULL; + s->mon = NULL; s->bandwidth_limit = bandwidth_limit; s->fd = socket(PF_INET, SOCK_STREAM, 0); if (s->fd == -1) { @@ -112,8 +113,9 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, socket_set_nonblock(s->fd); - if (!detach) - migrate_fd_monitor_suspend(s); + if (!detach) { + migrate_fd_monitor_suspend(s, mon); + } do { ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); diff --git a/migration-unix.c b/migration-unix.c index 25cd6d3d2..7dd787cd9 100644 --- a/migration-unix.c +++ b/migration-unix.c @@ -75,7 +75,8 @@ static void unix_wait_for_connect(void *opaque) } } -MigrationState *unix_start_outgoing_migration(const char *path, +MigrationState *unix_start_outgoing_migration(Monitor *mon, + const char *path, int64_t bandwidth_limit, int detach, int blk, @@ -101,7 +102,7 @@ MigrationState *unix_start_outgoing_migration(const char *path, s->mig_state.shared = inc; s->state = MIG_STATE_ACTIVE; - s->mon_resume = NULL; + s->mon = NULL; s->bandwidth_limit = bandwidth_limit; s->fd = socket(PF_UNIX, SOCK_STREAM, 0); if (s->fd < 0) { @@ -111,8 +112,9 @@ MigrationState *unix_start_outgoing_migration(const char *path, socket_set_nonblock(s->fd); - if (!detach) - migrate_fd_monitor_suspend(s); + if (!detach) { + migrate_fd_monitor_suspend(s, mon); + } do { ret = connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)); diff --git a/migration.c b/migration.c index 3ae0be86c..d6a3e2615 100644 --- a/migration.c +++ b/migration.c @@ -18,6 +18,7 @@ #include "sysemu.h" #include "block.h" #include "qemu_socket.h" +#include "block-migration.h" //#define DEBUG_MIGRATION @@ -58,18 +59,24 @@ void do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) const char *p; int detach = qdict_get_int(qdict, "detach"); const char *uri = qdict_get_str(qdict, "uri"); - + + if (current_migration && + current_migration->get_status(current_migration) == MIG_STATE_ACTIVE) { + monitor_printf(mon, "migration already in progress\n"); + return; + } + if (strstart(uri, "tcp:", &p)) - s = tcp_start_outgoing_migration(p, max_throttle, detach, + s = tcp_start_outgoing_migration(mon, p, max_throttle, detach, (int)qdict_get_int(qdict, "blk"), (int)qdict_get_int(qdict, "inc")); #if !defined(WIN32) else if (strstart(uri, "exec:", &p)) - s = exec_start_outgoing_migration(p, max_throttle, detach, + s = exec_start_outgoing_migration(mon, p, max_throttle, detach, (int)qdict_get_int(qdict, "blk"), (int)qdict_get_int(qdict, "inc")); else if (strstart(uri, "unix:", &p)) - s = unix_start_outgoing_migration(p, max_throttle, detach, + s = unix_start_outgoing_migration(mon, p, max_throttle, detach, (int)qdict_get_int(qdict, "blk"), (int)qdict_get_int(qdict, "inc")); else if (strstart(uri, "fd:", &p)) @@ -118,12 +125,11 @@ void do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data) } max_throttle = (uint32_t)d; - s = migrate_to_fms(current_migration); - if (s) { + s = migrate_to_fms(current_migration); + if (s && s->file) { qemu_file_set_rate_limit(s->file, max_throttle); } - } /* amount of nanoseconds we are willing to wait for migration to be down. @@ -169,6 +175,14 @@ void do_info_migrate(Monitor *mon) monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n", ram_bytes_transferred() >> 10); monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n", ram_bytes_remaining() >> 10); monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n", ram_bytes_total() >> 10); + if (blk_mig_active()) { + monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n", + blk_mig_bytes_transferred() >> 10); + monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n", + blk_mig_bytes_remaining() >> 10); + monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n", + blk_mig_bytes_total() >> 10); + } break; case MIG_STATE_COMPLETED: monitor_printf(mon, "completed\n"); @@ -185,14 +199,15 @@ void do_info_migrate(Monitor *mon) /* shared migration helpers */ -void migrate_fd_monitor_suspend(FdMigrationState *s) +void migrate_fd_monitor_suspend(FdMigrationState *s, Monitor *mon) { - s->mon_resume = cur_mon; - if (monitor_suspend(cur_mon) == 0) + s->mon = mon; + if (monitor_suspend(mon) == 0) { dprintf("suspending monitor\n"); - else - monitor_printf(cur_mon, "terminal does not allow synchronous " + } else { + monitor_printf(mon, "terminal does not allow synchronous " "migration, continuing detached\n"); + } } void migrate_fd_error(FdMigrationState *s) @@ -209,14 +224,16 @@ void migrate_fd_cleanup(FdMigrationState *s) if (s->file) { dprintf("closing file\n"); qemu_fclose(s->file); + s->file = NULL; } if (s->fd != -1) close(s->fd); /* Don't resume monitor until we've flushed all of the buffers */ - if (s->mon_resume) - monitor_resume(s->mon_resume); + if (s->mon) { + monitor_resume(s->mon); + } s->fd = -1; } @@ -259,7 +276,7 @@ void migrate_fd_connect(FdMigrationState *s) migrate_fd_close); dprintf("beginning savevm\n"); - ret = qemu_savevm_state_begin(s->file, s->mig_state.blk, + ret = qemu_savevm_state_begin(s->mon, s->file, s->mig_state.blk, s->mig_state.shared); if (ret < 0) { dprintf("failed, %d\n", ret); @@ -280,7 +297,7 @@ void migrate_fd_put_ready(void *opaque) } dprintf("iterate\n"); - if (qemu_savevm_state_iterate(s->file) == 1) { + if (qemu_savevm_state_iterate(s->mon, s->file) == 1) { int state; int old_vm_running = vm_running; @@ -289,7 +306,7 @@ void migrate_fd_put_ready(void *opaque) qemu_aio_flush(); bdrv_flush_all(); - if ((qemu_savevm_state_complete(s->file)) < 0) { + if ((qemu_savevm_state_complete(s->mon, s->file)) < 0) { if (old_vm_running) { vm_start(); } @@ -318,6 +335,7 @@ void migrate_fd_cancel(MigrationState *mig_state) dprintf("cancelling migration\n"); s->state = MIG_STATE_CANCELLED; + qemu_savevm_state_cancel(s->mon, s->file); migrate_fd_cleanup(s); } diff --git a/migration.h b/migration.h index 56adf05e6..3f2b3df2b 100644 --- a/migration.h +++ b/migration.h @@ -42,7 +42,7 @@ struct FdMigrationState int64_t bandwidth_limit; QEMUFile *file; int fd; - Monitor *mon_resume; + Monitor *mon; int state; int (*get_error)(struct FdMigrationState*); int (*close)(struct FdMigrationState*); @@ -66,7 +66,8 @@ void do_info_migrate(Monitor *mon); int exec_start_incoming_migration(const char *host_port); -MigrationState *exec_start_outgoing_migration(const char *host_port, +MigrationState *exec_start_outgoing_migration(Monitor *mon, + const char *host_port, int64_t bandwidth_limit, int detach, int blk, @@ -74,7 +75,8 @@ MigrationState *exec_start_outgoing_migration(const char *host_port, int tcp_start_incoming_migration(const char *host_port); -MigrationState *tcp_start_outgoing_migration(const char *host_port, +MigrationState *tcp_start_outgoing_migration(Monitor *mon, + const char *host_port, int64_t bandwidth_limit, int detach, int blk, @@ -82,7 +84,8 @@ MigrationState *tcp_start_outgoing_migration(const char *host_port, int unix_start_incoming_migration(const char *path); -MigrationState *unix_start_outgoing_migration(const char *path, +MigrationState *unix_start_outgoing_migration(Monitor *mon, + const char *path, int64_t bandwidth_limit, int detach, int blk, @@ -97,7 +100,7 @@ MigrationState *fd_start_outgoing_migration(Monitor *mon, int blk, int inc); -void migrate_fd_monitor_suspend(FdMigrationState *s); +void migrate_fd_monitor_suspend(FdMigrationState *s, Monitor *mon); void migrate_fd_error(FdMigrationState *s); @@ -32,6 +32,7 @@ #include "hw/loader.h" #include "gdbstub.h" #include "net.h" +#include "net/slirp.h" #include "qemu-char.h" #include "sysemu.h" #include "monitor.h" @@ -101,6 +102,7 @@ struct mon_fd_t { typedef struct MonitorControl { QObject *id; + int print_enabled; JSONMessageParser parser; } MonitorControl; @@ -189,9 +191,13 @@ static void monitor_puts(Monitor *mon, const char *str) void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { - char buf[4096]; - vsnprintf(buf, sizeof(buf), fmt, ap); - monitor_puts(mon, buf); + if (mon->mc && !mon->mc->print_enabled) { + qemu_error_new(QERR_UNDEFINED_ERROR); + } else { + char buf[4096]; + vsnprintf(buf, sizeof(buf), fmt, ap); + monitor_puts(mon, buf); + } } void monitor_printf(Monitor *mon, const char *fmt, ...) @@ -275,7 +281,10 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data) json = qobject_to_json(data); assert(json != NULL); + mon->mc->print_enabled = 1; monitor_printf(mon, "%s\n", qstring_get_str(json)); + mon->mc->print_enabled = 0; + QDECREF(json); } @@ -310,6 +319,71 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) QDECREF(qmp); } +static void timestamp_put(QDict *qdict) +{ + int err; + QObject *obj; + struct timeval tv; + + err = gettimeofday(&tv, NULL); + if (err < 0) + return; + + obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", " + "'microseconds': %" PRId64 " }", + (int64_t) tv.tv_sec, (int64_t) tv.tv_usec); + assert(obj != NULL); + + qdict_put_obj(qdict, "timestamp", obj); +} + +/** + * monitor_protocol_event(): Generate a Monitor event + * + * Event-specific data can be emitted through the (optional) 'data' parameter. + */ +void monitor_protocol_event(MonitorEvent event, QObject *data) +{ + QDict *qmp; + const char *event_name; + Monitor *mon = cur_mon; + + assert(event < EVENT_MAX); + + if (!monitor_ctrl_mode(mon)) + return; + + switch (event) { + case EVENT_DEBUG: + event_name = "DEBUG"; + break; + case EVENT_SHUTDOWN: + event_name = "SHUTDOWN"; + break; + case EVENT_RESET: + event_name = "RESET"; + break; + case EVENT_POWERDOWN: + event_name = "POWERDOWN"; + break; + case EVENT_STOP: + event_name = "STOP"; + break; + default: + abort(); + break; + } + + qmp = qdict_new(); + timestamp_put(qmp); + qdict_put(qmp, "event", qstring_from_str(event_name)); + if (data) + qdict_put_obj(qmp, "data", data); + + monitor_json_emitter(mon, QOBJECT(qmp)); + QDECREF(qmp); +} + static int compare_cmd(const char *name, const char *list) { const char *p, *pstart; @@ -3760,9 +3834,9 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) int err; QObject *obj; QDict *input, *args; - const char *cmd_name; const mon_cmd_t *cmd; Monitor *mon = cur_mon; + const char *cmd_name, *info_item; args = NULL; qemu_errors_to_mon(mon); @@ -3793,10 +3867,24 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) } cmd_name = qstring_get_str(qobject_to_qstring(obj)); - cmd = monitor_find_command(cmd_name); - if (!cmd) { + + /* + * XXX: We need this special case until we get info handlers + * converted into 'query-' commands + */ + if (compare_cmd(cmd_name, "info")) { qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd_name); goto err_input; + } else if (strstart(cmd_name, "query-", &info_item)) { + cmd = monitor_find_command("info"); + qdict_put_obj(input, "arguments", + qobject_from_jsonf("{ 'item': %s }", info_item)); + } else { + cmd = monitor_find_command(cmd_name); + if (!cmd) { + qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd_name); + goto err_input; + } } obj = qdict_get(input, "arguments"); @@ -13,6 +13,17 @@ extern Monitor *cur_mon; #define MONITOR_USE_READLINE 0x02 #define MONITOR_USE_CONTROL 0x04 +/* QMP events */ +typedef enum MonitorEvent { + EVENT_DEBUG, + EVENT_SHUTDOWN, + EVENT_RESET, + EVENT_POWERDOWN, + EVENT_STOP, + EVENT_MAX, +} MonitorEvent; + +void monitor_protocol_event(MonitorEvent event, QObject *data); const char *monitor_cmdline_parse(const char *cmdline, int *flags); void monitor_init(CharDriverState *chr, int flags); @@ -21,90 +21,20 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include <unistd.h> -#include <fcntl.h> -#include <signal.h> -#include <time.h> -#include <errno.h> -#include <sys/time.h> -#include <zlib.h> - -/* Needed early for CONFIG_BSD etc. */ -#include "config-host.h" - -#ifndef _WIN32 -#include <sys/times.h> -#include <sys/wait.h> -#include <termios.h> -#include <sys/mman.h> -#include <sys/ioctl.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <net/if.h> -#include <arpa/inet.h> -#include <dirent.h> -#include <netdb.h> -#include <sys/select.h> -#ifdef CONFIG_BSD -#include <sys/stat.h> -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) -#include <libutil.h> -#else -#include <util.h> -#endif -#ifdef __linux__ -#include <pty.h> -#include <malloc.h> -#include <linux/rtc.h> - -/* For the benefit of older linux systems which don't supply it, - we use a local copy of hpet.h. */ -/* #include <linux/hpet.h> */ -#include "hpet.h" - -#include <linux/ppdev.h> -#include <linux/parport.h> -#endif -#ifdef __sun__ -#include <sys/stat.h> -#include <sys/ethernet.h> -#include <sys/sockio.h> -#include <netinet/arp.h> -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> // must come after ip.h -#include <netinet/udp.h> -#include <netinet/tcp.h> -#include <net/if.h> -#include <syslog.h> -#include <stropts.h> -#endif -#endif -#endif - -#if defined(__OpenBSD__) -#include <util.h> -#endif +#include "net.h" -#if defined(CONFIG_VDE) -#include <libvdeplug.h> -#endif +#include "config-host.h" -#include "qemu-common.h" -#include "net.h" #include "net/tap.h" +#include "net/socket.h" +#include "net/dump.h" +#include "net/slirp.h" +#include "net/vde.h" +#include "net/util.h" #include "monitor.h" #include "sysemu.h" -#include "qemu-timer.h" -#include "qemu-char.h" -#include "audio/audio.h" +#include "qemu-common.h" #include "qemu_socket.h" -#include "qemu-log.h" -#include "qemu-config.h" - -#include "slirp/libslirp.h" static QTAILQ_HEAD(, VLANState) vlans; static QTAILQ_HEAD(, VLANClientState) non_vlan_clients; @@ -112,7 +42,7 @@ static QTAILQ_HEAD(, VLANClientState) non_vlan_clients; /***********************************************************/ /* network device redirectors */ -#if defined(DEBUG_NET) || defined(DEBUG_SLIRP) +#if defined(DEBUG_NET) static void hex_dump(FILE *f, const uint8_t *buf, int size) { int len, i, j, c; @@ -140,38 +70,6 @@ static void hex_dump(FILE *f, const uint8_t *buf, int size) } #endif -static int parse_macaddr(uint8_t *macaddr, const char *p) -{ - int i; - char *last_char; - long int offset; - - errno = 0; - offset = strtol(p, &last_char, 0); - if (0 == errno && '\0' == *last_char && - offset >= 0 && offset <= 0xFFFFFF) { - macaddr[3] = (offset & 0xFF0000) >> 16; - macaddr[4] = (offset & 0xFF00) >> 8; - macaddr[5] = offset & 0xFF; - return 0; - } else { - for(i = 0; i < 6; i++) { - macaddr[i] = strtol(p, (char **)&p, 16); - if (i == 5) { - if (*p != '\0') - return -1; - } else { - if (*p != ':' && *p != '-') - return -1; - p++; - } - } - return 0; - } - - return -1; -} - static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) { const char *p, *p1; @@ -319,34 +217,25 @@ static ssize_t qemu_deliver_packet_iov(VLANClientState *sender, int iovcnt, void *opaque); -VLANClientState *qemu_new_vlan_client(net_client_type type, - VLANState *vlan, - VLANClientState *peer, - const char *model, - const char *name, - NetCanReceive *can_receive, - NetReceive *receive, - NetReceive *receive_raw, - NetReceiveIOV *receive_iov, - NetCleanup *cleanup, - void *opaque) +VLANClientState *qemu_new_net_client(NetClientInfo *info, + VLANState *vlan, + VLANClientState *peer, + const char *model, + const char *name) { VLANClientState *vc; - vc = qemu_mallocz(sizeof(VLANClientState)); + assert(info->size >= sizeof(VLANClientState)); - vc->type = type; + vc = qemu_mallocz(info->size); + + vc->info = info; vc->model = qemu_strdup(model); - if (name) + if (name) { vc->name = qemu_strdup(name); - else + } else { vc->name = assign_name(vc, model); - vc->can_receive = can_receive; - vc->receive = receive; - vc->receive_raw = receive_raw; - vc->receive_iov = receive_iov; - vc->cleanup = cleanup; - vc->opaque = opaque; + } if (vlan) { assert(!peer); @@ -367,6 +256,27 @@ VLANClientState *qemu_new_vlan_client(net_client_type type, return vc; } +NICState *qemu_new_nic(NetClientInfo *info, + NICConf *conf, + const char *model, + const char *name, + void *opaque) +{ + VLANClientState *nc; + NICState *nic; + + assert(info->type == NET_CLIENT_TYPE_NIC); + assert(info->size >= sizeof(NICState)); + + nc = qemu_new_net_client(info, conf->vlan, conf->peer, model, name); + + nic = DO_UPCAST(NICState, nc, nc); + nic->conf = conf; + nic->opaque = opaque; + + return nic; +} + void qemu_del_vlan_client(VLANClientState *vc) { if (vc->vlan) { @@ -381,8 +291,8 @@ void qemu_del_vlan_client(VLANClientState *vc) } } - if (vc->cleanup) { - vc->cleanup(vc); + if (vc->info->cleanup) { + vc->info->cleanup(vc); } qemu_free(vc->name); @@ -390,20 +300,7 @@ void qemu_del_vlan_client(VLANClientState *vc) qemu_free(vc); } -VLANClientState *qemu_find_vlan_client(VLANState *vlan, void *opaque) -{ - VLANClientState *vc; - - QTAILQ_FOREACH(vc, &vlan->clients, next) { - if (vc->opaque == opaque) { - return vc; - } - } - - return NULL; -} - -static VLANClientState * +VLANClientState * qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id, const char *client_str) { @@ -429,6 +326,26 @@ qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id, return vc; } +void qemu_foreach_nic(qemu_nic_foreach func, void *opaque) +{ + VLANClientState *nc; + VLANState *vlan; + + QTAILQ_FOREACH(nc, &non_vlan_clients, next) { + if (nc->info->type == NET_CLIENT_TYPE_NIC) { + func(DO_UPCAST(NICState, nc, nc), opaque); + } + } + + QTAILQ_FOREACH(vlan, &vlans, next) { + QTAILQ_FOREACH(nc, &vlan->clients, next) { + if (nc->info->type == NET_CLIENT_TYPE_NIC) { + func(DO_UPCAST(NICState, nc, nc), opaque); + } + } + } +} + int qemu_can_send_packet(VLANClientState *sender) { VLANState *vlan = sender->vlan; @@ -437,8 +354,8 @@ int qemu_can_send_packet(VLANClientState *sender) if (sender->peer) { if (sender->peer->receive_disabled) { return 0; - } else if (sender->peer->can_receive && - !sender->peer->can_receive(sender->peer)) { + } else if (sender->peer->info->can_receive && + !sender->peer->info->can_receive(sender->peer)) { return 0; } else { return 1; @@ -455,7 +372,7 @@ int qemu_can_send_packet(VLANClientState *sender) } /* no can_receive() handler, they can always receive */ - if (!vc->can_receive || vc->can_receive(vc)) { + if (!vc->info->can_receive || vc->info->can_receive(vc)) { return 1; } } @@ -479,10 +396,10 @@ static ssize_t qemu_deliver_packet(VLANClientState *sender, return 0; } - if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->receive_raw) { - ret = vc->receive_raw(vc, data, size); + if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) { + ret = vc->info->receive_raw(vc, data, size); } else { - ret = vc->receive(vc, data, size); + ret = vc->info->receive(vc, data, size); } if (ret == 0) { @@ -519,10 +436,10 @@ static ssize_t qemu_vlan_deliver_packet(VLANClientState *sender, continue; } - if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->receive_raw) { - len = vc->receive_raw(vc, buf, size); + if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) { + len = vc->info->receive_raw(vc, buf, size); } else { - len = vc->receive(vc, buf, size); + len = vc->info->receive(vc, buf, size); } if (len == 0) { @@ -627,7 +544,7 @@ static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov, offset += len; } - return vc->receive(vc, buffer, offset); + return vc->info->receive(vc, buffer, offset); } static ssize_t calc_iov_length(const struct iovec *iov, int iovcnt) @@ -652,8 +569,8 @@ static ssize_t qemu_deliver_packet_iov(VLANClientState *sender, return calc_iov_length(iov, iovcnt); } - if (vc->receive_iov) { - return vc->receive_iov(vc, iov, iovcnt); + if (vc->info->receive_iov) { + return vc->info->receive_iov(vc, iov, iovcnt); } else { return vc_sendv_compat(vc, iov, iovcnt); } @@ -683,8 +600,8 @@ static ssize_t qemu_vlan_deliver_packet_iov(VLANClientState *sender, assert(!(flags & QEMU_NET_PACKET_FLAG_RAW)); - if (vc->receive_iov) { - len = vc->receive_iov(vc, iov, iovcnt); + if (vc->info->receive_iov) { + len = vc->info->receive_iov(vc, iov, iovcnt); } else { len = vc_sendv_compat(vc, iov, iovcnt); } @@ -722,1218 +639,6 @@ qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt) return qemu_sendv_packet_async(vc, iov, iovcnt, NULL); } -#if defined(CONFIG_SLIRP) - -/* slirp network adapter */ - -#define SLIRP_CFG_HOSTFWD 1 -#define SLIRP_CFG_LEGACY 2 - -struct slirp_config_str { - struct slirp_config_str *next; - int flags; - char str[1024]; - int legacy_format; -}; - -typedef struct SlirpState { - QTAILQ_ENTRY(SlirpState) entry; - VLANClientState *vc; - Slirp *slirp; -#ifndef _WIN32 - char smb_dir[128]; -#endif -} SlirpState; - -static struct slirp_config_str *slirp_configs; -const char *legacy_tftp_prefix; -const char *legacy_bootp_filename; -static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks = - QTAILQ_HEAD_INITIALIZER(slirp_stacks); - -static int slirp_hostfwd(SlirpState *s, const char *redir_str, - int legacy_format); -static int slirp_guestfwd(SlirpState *s, const char *config_str, - int legacy_format); - -#ifndef _WIN32 -static const char *legacy_smb_export; - -static int slirp_smb(SlirpState *s, const char *exported_dir, - struct in_addr vserver_addr); -static void slirp_smb_cleanup(SlirpState *s); -#else -static inline void slirp_smb_cleanup(SlirpState *s) { } -#endif - -int slirp_can_output(void *opaque) -{ - SlirpState *s = opaque; - - return qemu_can_send_packet(s->vc); -} - -void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len) -{ - SlirpState *s = opaque; - -#ifdef DEBUG_SLIRP - printf("slirp output:\n"); - hex_dump(stdout, pkt, pkt_len); -#endif - qemu_send_packet(s->vc, pkt, pkt_len); -} - -static ssize_t slirp_receive(VLANClientState *vc, const uint8_t *buf, size_t size) -{ - SlirpState *s = vc->opaque; - -#ifdef DEBUG_SLIRP - printf("slirp input:\n"); - hex_dump(stdout, buf, size); -#endif - slirp_input(s->slirp, buf, size); - return size; -} - -static void net_slirp_cleanup(VLANClientState *vc) -{ - SlirpState *s = vc->opaque; - - slirp_cleanup(s->slirp); - slirp_smb_cleanup(s); - QTAILQ_REMOVE(&slirp_stacks, s, entry); - qemu_free(s); -} - -static int net_slirp_init(VLANState *vlan, const char *model, - const char *name, int restricted, - const char *vnetwork, const char *vhost, - const char *vhostname, const char *tftp_export, - const char *bootfile, const char *vdhcp_start, - const char *vnameserver, const char *smb_export, - const char *vsmbserver) -{ - /* default settings according to historic slirp */ - struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ - struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */ - struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */ - struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ - struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ -#ifndef _WIN32 - struct in_addr smbsrv = { .s_addr = 0 }; -#endif - SlirpState *s; - char buf[20]; - uint32_t addr; - int shift; - char *end; - struct slirp_config_str *config; - - if (!tftp_export) { - tftp_export = legacy_tftp_prefix; - } - if (!bootfile) { - bootfile = legacy_bootp_filename; - } - - if (vnetwork) { - if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) { - if (!inet_aton(vnetwork, &net)) { - return -1; - } - addr = ntohl(net.s_addr); - if (!(addr & 0x80000000)) { - mask.s_addr = htonl(0xff000000); /* class A */ - } else if ((addr & 0xfff00000) == 0xac100000) { - mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */ - } else if ((addr & 0xc0000000) == 0x80000000) { - mask.s_addr = htonl(0xffff0000); /* class B */ - } else if ((addr & 0xffff0000) == 0xc0a80000) { - mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */ - } else if ((addr & 0xffff0000) == 0xc6120000) { - mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */ - } else if ((addr & 0xe0000000) == 0xe0000000) { - mask.s_addr = htonl(0xffffff00); /* class C */ - } else { - mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */ - } - } else { - if (!inet_aton(buf, &net)) { - return -1; - } - shift = strtol(vnetwork, &end, 10); - if (*end != '\0') { - if (!inet_aton(vnetwork, &mask)) { - return -1; - } - } else if (shift < 4 || shift > 32) { - return -1; - } else { - mask.s_addr = htonl(0xffffffff << (32 - shift)); - } - } - net.s_addr &= mask.s_addr; - host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr); - dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr); - dns.s_addr = net.s_addr | (htonl(0x0203) & ~mask.s_addr); - } - - if (vhost && !inet_aton(vhost, &host)) { - return -1; - } - if ((host.s_addr & mask.s_addr) != net.s_addr) { - return -1; - } - - if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) { - return -1; - } - if ((dhcp.s_addr & mask.s_addr) != net.s_addr || - dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) { - return -1; - } - - if (vnameserver && !inet_aton(vnameserver, &dns)) { - return -1; - } - if ((dns.s_addr & mask.s_addr) != net.s_addr || - dns.s_addr == host.s_addr) { - return -1; - } - -#ifndef _WIN32 - if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) { - return -1; - } -#endif - - s = qemu_mallocz(sizeof(SlirpState)); - s->slirp = slirp_init(restricted, net, mask, host, vhostname, - tftp_export, bootfile, dhcp, dns, s); - QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); - - for (config = slirp_configs; config; config = config->next) { - if (config->flags & SLIRP_CFG_HOSTFWD) { - if (slirp_hostfwd(s, config->str, - config->flags & SLIRP_CFG_LEGACY) < 0) - return -1; - } else { - if (slirp_guestfwd(s, config->str, - config->flags & SLIRP_CFG_LEGACY) < 0) - return -1; - } - } -#ifndef _WIN32 - if (!smb_export) { - smb_export = legacy_smb_export; - } - if (smb_export) { - if (slirp_smb(s, smb_export, smbsrv) < 0) - return -1; - } -#endif - - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_SLIRP, - vlan, NULL, model, name, NULL, - slirp_receive, NULL, NULL, - net_slirp_cleanup, s); - snprintf(s->vc->info_str, sizeof(s->vc->info_str), - "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n'); - return 0; -} - -static SlirpState *slirp_lookup(Monitor *mon, const char *vlan, - const char *stack) -{ - VLANClientState *vc; - - if (vlan) { - vc = qemu_find_vlan_client_by_name(mon, strtol(vlan, NULL, 0), stack); - if (!vc) { - return NULL; - } - if (strcmp(vc->model, "user")) { - monitor_printf(mon, "invalid device specified\n"); - return NULL; - } - return vc->opaque; - } else { - if (QTAILQ_EMPTY(&slirp_stacks)) { - monitor_printf(mon, "user mode network stack not in use\n"); - return NULL; - } - return QTAILQ_FIRST(&slirp_stacks); - } -} - -void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict) -{ - struct in_addr host_addr = { .s_addr = INADDR_ANY }; - int host_port; - char buf[256] = ""; - const char *src_str, *p; - SlirpState *s; - int is_udp = 0; - int err; - const char *arg1 = qdict_get_str(qdict, "arg1"); - const char *arg2 = qdict_get_try_str(qdict, "arg2"); - const char *arg3 = qdict_get_try_str(qdict, "arg3"); - - if (arg2) { - s = slirp_lookup(mon, arg1, arg2); - src_str = arg3; - } else { - s = slirp_lookup(mon, NULL, NULL); - src_str = arg1; - } - if (!s) { - return; - } - - if (!src_str || !src_str[0]) - goto fail_syntax; - - p = src_str; - get_str_sep(buf, sizeof(buf), &p, ':'); - - if (!strcmp(buf, "tcp") || buf[0] == '\0') { - is_udp = 0; - } else if (!strcmp(buf, "udp")) { - is_udp = 1; - } else { - goto fail_syntax; - } - - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { - goto fail_syntax; - } - - host_port = atoi(p); - - err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp, - host_addr, host_port); - - monitor_printf(mon, "host forwarding rule for %s %s\n", src_str, - err ? "removed" : "not found"); - return; - - fail_syntax: - monitor_printf(mon, "invalid format\n"); -} - -static int slirp_hostfwd(SlirpState *s, const char *redir_str, - int legacy_format) -{ - struct in_addr host_addr = { .s_addr = INADDR_ANY }; - struct in_addr guest_addr = { .s_addr = 0 }; - int host_port, guest_port; - const char *p; - char buf[256]; - int is_udp; - char *end; - - p = redir_str; - if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (!strcmp(buf, "tcp") || buf[0] == '\0') { - is_udp = 0; - } else if (!strcmp(buf, "udp")) { - is_udp = 1; - } else { - goto fail_syntax; - } - - if (!legacy_format) { - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { - goto fail_syntax; - } - } - - if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) { - goto fail_syntax; - } - host_port = strtol(buf, &end, 0); - if (*end != '\0' || host_port < 1 || host_port > 65535) { - goto fail_syntax; - } - - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) { - goto fail_syntax; - } - - guest_port = strtol(p, &end, 0); - if (*end != '\0' || guest_port < 1 || guest_port > 65535) { - goto fail_syntax; - } - - if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr, - guest_port) < 0) { - qemu_error("could not set up host forwarding rule '%s'\n", - redir_str); - return -1; - } - return 0; - - fail_syntax: - qemu_error("invalid host forwarding rule '%s'\n", redir_str); - return -1; -} - -void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict) -{ - const char *redir_str; - SlirpState *s; - const char *arg1 = qdict_get_str(qdict, "arg1"); - const char *arg2 = qdict_get_try_str(qdict, "arg2"); - const char *arg3 = qdict_get_try_str(qdict, "arg3"); - - if (arg2) { - s = slirp_lookup(mon, arg1, arg2); - redir_str = arg3; - } else { - s = slirp_lookup(mon, NULL, NULL); - redir_str = arg1; - } - if (s) { - slirp_hostfwd(s, redir_str, 0); - } - -} - -int net_slirp_redir(const char *redir_str) -{ - struct slirp_config_str *config; - - if (QTAILQ_EMPTY(&slirp_stacks)) { - config = qemu_malloc(sizeof(*config)); - pstrcpy(config->str, sizeof(config->str), redir_str); - config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY; - config->next = slirp_configs; - slirp_configs = config; - return 0; - } - - return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1); -} - -#ifndef _WIN32 - -/* automatic user mode samba server configuration */ -static void slirp_smb_cleanup(SlirpState *s) -{ - char cmd[128]; - - if (s->smb_dir[0] != '\0') { - snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir); - system(cmd); - s->smb_dir[0] = '\0'; - } -} - -static int slirp_smb(SlirpState* s, const char *exported_dir, - struct in_addr vserver_addr) -{ - static int instance; - char smb_conf[128]; - char smb_cmdline[128]; - FILE *f; - - snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d", - (long)getpid(), instance++); - if (mkdir(s->smb_dir, 0700) < 0) { - qemu_error("could not create samba server dir '%s'\n", s->smb_dir); - return -1; - } - snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf"); - - f = fopen(smb_conf, "w"); - if (!f) { - slirp_smb_cleanup(s); - qemu_error("could not create samba server configuration file '%s'\n", - smb_conf); - return -1; - } - fprintf(f, - "[global]\n" - "private dir=%s\n" - "smb ports=0\n" - "socket address=127.0.0.1\n" - "pid directory=%s\n" - "lock directory=%s\n" - "log file=%s/log.smbd\n" - "smb passwd file=%s/smbpasswd\n" - "security = share\n" - "[qemu]\n" - "path=%s\n" - "read only=no\n" - "guest ok=yes\n", - s->smb_dir, - s->smb_dir, - s->smb_dir, - s->smb_dir, - s->smb_dir, - exported_dir - ); - fclose(f); - - snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s", - SMBD_COMMAND, smb_conf); - - if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) { - slirp_smb_cleanup(s); - qemu_error("conflicting/invalid smbserver address\n"); - return -1; - } - return 0; -} - -/* automatic user mode samba server configuration (legacy interface) */ -int net_slirp_smb(const char *exported_dir) -{ - struct in_addr vserver_addr = { .s_addr = 0 }; - - if (legacy_smb_export) { - fprintf(stderr, "-smb given twice\n"); - return -1; - } - legacy_smb_export = exported_dir; - if (!QTAILQ_EMPTY(&slirp_stacks)) { - return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir, - vserver_addr); - } - return 0; -} - -#endif /* !defined(_WIN32) */ - -struct GuestFwd { - CharDriverState *hd; - struct in_addr server; - int port; - Slirp *slirp; -}; - -static int guestfwd_can_read(void *opaque) -{ - struct GuestFwd *fwd = opaque; - return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port); -} - -static void guestfwd_read(void *opaque, const uint8_t *buf, int size) -{ - struct GuestFwd *fwd = opaque; - slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size); -} - -static int slirp_guestfwd(SlirpState *s, const char *config_str, - int legacy_format) -{ - struct in_addr server = { .s_addr = 0 }; - struct GuestFwd *fwd; - const char *p; - char buf[128]; - char *end; - int port; - - p = config_str; - if (legacy_format) { - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - } else { - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (strcmp(buf, "tcp") && buf[0] != '\0') { - goto fail_syntax; - } - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (buf[0] != '\0' && !inet_aton(buf, &server)) { - goto fail_syntax; - } - if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { - goto fail_syntax; - } - } - port = strtol(buf, &end, 10); - if (*end != '\0' || port < 1 || port > 65535) { - goto fail_syntax; - } - - fwd = qemu_malloc(sizeof(struct GuestFwd)); - snprintf(buf, sizeof(buf), "guestfwd.tcp:%d", port); - fwd->hd = qemu_chr_open(buf, p, NULL); - if (!fwd->hd) { - qemu_error("could not open guest forwarding device '%s'\n", buf); - qemu_free(fwd); - return -1; - } - - if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) { - qemu_error("conflicting/invalid host:port in guest forwarding " - "rule '%s'\n", config_str); - qemu_free(fwd); - return -1; - } - fwd->server = server; - fwd->port = port; - fwd->slirp = s->slirp; - - qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read, - NULL, fwd); - return 0; - - fail_syntax: - qemu_error("invalid guest forwarding rule '%s'\n", config_str); - return -1; -} - -void do_info_usernet(Monitor *mon) -{ - SlirpState *s; - - QTAILQ_FOREACH(s, &slirp_stacks, entry) { - monitor_printf(mon, "VLAN %d (%s):\n", s->vc->vlan->id, s->vc->name); - slirp_connection_info(s->slirp, mon); - } -} - -#endif /* CONFIG_SLIRP */ - -#if defined(CONFIG_VDE) -typedef struct VDEState { - VLANClientState *vc; - VDECONN *vde; -} VDEState; - -static void vde_to_qemu(void *opaque) -{ - VDEState *s = opaque; - uint8_t buf[4096]; - int size; - - size = vde_recv(s->vde, (char *)buf, sizeof(buf), 0); - if (size > 0) { - qemu_send_packet(s->vc, buf, size); - } -} - -static ssize_t vde_receive(VLANClientState *vc, const uint8_t *buf, size_t size) -{ - VDEState *s = vc->opaque; - ssize_t ret; - - do { - ret = vde_send(s->vde, (const char *)buf, size, 0); - } while (ret < 0 && errno == EINTR); - - return ret; -} - -static void vde_cleanup(VLANClientState *vc) -{ - VDEState *s = vc->opaque; - qemu_set_fd_handler(vde_datafd(s->vde), NULL, NULL, NULL); - vde_close(s->vde); - qemu_free(s); -} - -static int net_vde_init(VLANState *vlan, const char *model, - const char *name, const char *sock, - int port, const char *group, int mode) -{ - VDEState *s; - char *init_group = (char *)group; - char *init_sock = (char *)sock; - - struct vde_open_args args = { - .port = port, - .group = init_group, - .mode = mode, - }; - - s = qemu_mallocz(sizeof(VDEState)); - s->vde = vde_open(init_sock, (char *)"QEMU", &args); - if (!s->vde){ - free(s); - return -1; - } - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_VDE, - vlan, NULL, model, name, NULL, - vde_receive, NULL, NULL, - vde_cleanup, s); - qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s); - snprintf(s->vc->info_str, sizeof(s->vc->info_str), "sock=%s,fd=%d", - sock, vde_datafd(s->vde)); - return 0; -} -#endif - -/* network connection */ -typedef struct NetSocketState { - VLANClientState *vc; - int fd; - int state; /* 0 = getting length, 1 = getting data */ - unsigned int index; - unsigned int packet_len; - uint8_t buf[4096]; - struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */ -} NetSocketState; - -typedef struct NetSocketListenState { - VLANState *vlan; - char *model; - char *name; - int fd; -} NetSocketListenState; - -/* XXX: we consider we can send the whole packet without blocking */ -static ssize_t net_socket_receive(VLANClientState *vc, const uint8_t *buf, size_t size) -{ - NetSocketState *s = vc->opaque; - uint32_t len; - len = htonl(size); - - send_all(s->fd, (const uint8_t *)&len, sizeof(len)); - return send_all(s->fd, buf, size); -} - -static ssize_t net_socket_receive_dgram(VLANClientState *vc, const uint8_t *buf, size_t size) -{ - NetSocketState *s = vc->opaque; - - return sendto(s->fd, (const void *)buf, size, 0, - (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst)); -} - -static void net_socket_send(void *opaque) -{ - NetSocketState *s = opaque; - int size, err; - unsigned l; - uint8_t buf1[4096]; - const uint8_t *buf; - - size = recv(s->fd, (void *)buf1, sizeof(buf1), 0); - if (size < 0) { - err = socket_error(); - if (err != EWOULDBLOCK) - goto eoc; - } else if (size == 0) { - /* end of connection */ - eoc: - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); - closesocket(s->fd); - return; - } - buf = buf1; - while (size > 0) { - /* reassemble a packet from the network */ - switch(s->state) { - case 0: - l = 4 - s->index; - if (l > size) - l = size; - memcpy(s->buf + s->index, buf, l); - buf += l; - size -= l; - s->index += l; - if (s->index == 4) { - /* got length */ - s->packet_len = ntohl(*(uint32_t *)s->buf); - s->index = 0; - s->state = 1; - } - break; - case 1: - l = s->packet_len - s->index; - if (l > size) - l = size; - if (s->index + l <= sizeof(s->buf)) { - memcpy(s->buf + s->index, buf, l); - } else { - fprintf(stderr, "serious error: oversized packet received," - "connection terminated.\n"); - s->state = 0; - goto eoc; - } - - s->index += l; - buf += l; - size -= l; - if (s->index >= s->packet_len) { - qemu_send_packet(s->vc, s->buf, s->packet_len); - s->index = 0; - s->state = 0; - } - break; - } - } -} - -static void net_socket_send_dgram(void *opaque) -{ - NetSocketState *s = opaque; - int size; - - size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); - if (size < 0) - return; - if (size == 0) { - /* end of connection */ - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); - return; - } - qemu_send_packet(s->vc, s->buf, size); -} - -static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) -{ - struct ip_mreq imr; - int fd; - int val, ret; - if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) { - fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n", - inet_ntoa(mcastaddr->sin_addr), - (int)ntohl(mcastaddr->sin_addr.s_addr)); - return -1; - - } - fd = socket(PF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - perror("socket(PF_INET, SOCK_DGRAM)"); - return -1; - } - - val = 1; - ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, - (const char *)&val, sizeof(val)); - if (ret < 0) { - perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); - goto fail; - } - - ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr)); - if (ret < 0) { - perror("bind"); - goto fail; - } - - /* Add host to multicast group */ - imr.imr_multiaddr = mcastaddr->sin_addr; - imr.imr_interface.s_addr = htonl(INADDR_ANY); - - ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, - (const char *)&imr, sizeof(struct ip_mreq)); - if (ret < 0) { - perror("setsockopt(IP_ADD_MEMBERSHIP)"); - goto fail; - } - - /* Force mcast msgs to loopback (eg. several QEMUs in same host */ - val = 1; - ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, - (const char *)&val, sizeof(val)); - if (ret < 0) { - perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)"); - goto fail; - } - - socket_set_nonblock(fd); - return fd; -fail: - if (fd >= 0) - closesocket(fd); - return -1; -} - -static void net_socket_cleanup(VLANClientState *vc) -{ - NetSocketState *s = vc->opaque; - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); - close(s->fd); - qemu_free(s); -} - -static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, - const char *model, - const char *name, - int fd, int is_connected) -{ - struct sockaddr_in saddr; - int newfd; - socklen_t saddr_len; - NetSocketState *s; - - /* fd passed: multicast: "learn" dgram_dst address from bound address and save it - * Because this may be "shared" socket from a "master" process, datagrams would be recv() - * by ONLY ONE process: we must "clone" this dgram socket --jjo - */ - - if (is_connected) { - if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) { - /* must be bound */ - if (saddr.sin_addr.s_addr==0) { - fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n", - fd); - return NULL; - } - /* clone dgram socket */ - newfd = net_socket_mcast_create(&saddr); - if (newfd < 0) { - /* error already reported by net_socket_mcast_create() */ - close(fd); - return NULL; - } - /* clone newfd to fd, close newfd */ - dup2(newfd, fd); - close(newfd); - - } else { - fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n", - fd, strerror(errno)); - return NULL; - } - } - - s = qemu_mallocz(sizeof(NetSocketState)); - s->fd = fd; - - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_SOCKET, - vlan, NULL, model, name, NULL, - net_socket_receive_dgram, NULL, NULL, - net_socket_cleanup, s); - qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s); - - /* mcast: save bound address as dst */ - if (is_connected) s->dgram_dst=saddr; - - snprintf(s->vc->info_str, sizeof(s->vc->info_str), - "socket: fd=%d (%s mcast=%s:%d)", - fd, is_connected? "cloned" : "", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); - return s; -} - -static void net_socket_connect(void *opaque) -{ - NetSocketState *s = opaque; - qemu_set_fd_handler(s->fd, net_socket_send, NULL, s); -} - -static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, - const char *model, - const char *name, - int fd, int is_connected) -{ - NetSocketState *s; - s = qemu_mallocz(sizeof(NetSocketState)); - s->fd = fd; - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_SOCKET, - vlan, NULL, model, name, NULL, - net_socket_receive, NULL, NULL, - net_socket_cleanup, s); - snprintf(s->vc->info_str, sizeof(s->vc->info_str), - "socket: fd=%d", fd); - if (is_connected) { - net_socket_connect(s); - } else { - qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s); - } - return s; -} - -static NetSocketState *net_socket_fd_init(VLANState *vlan, - const char *model, const char *name, - int fd, int is_connected) -{ - int so_type = -1, optlen=sizeof(so_type); - - if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, - (socklen_t *)&optlen)< 0) { - fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd); - return NULL; - } - switch(so_type) { - case SOCK_DGRAM: - return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected); - case SOCK_STREAM: - return net_socket_fd_init_stream(vlan, model, name, fd, is_connected); - default: - /* who knows ... this could be a eg. a pty, do warn and continue as stream */ - fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd); - return net_socket_fd_init_stream(vlan, model, name, fd, is_connected); - } - return NULL; -} - -static void net_socket_accept(void *opaque) -{ - NetSocketListenState *s = opaque; - NetSocketState *s1; - struct sockaddr_in saddr; - socklen_t len; - int fd; - - for(;;) { - len = sizeof(saddr); - fd = accept(s->fd, (struct sockaddr *)&saddr, &len); - if (fd < 0 && errno != EINTR) { - return; - } else if (fd >= 0) { - break; - } - } - s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1); - if (!s1) { - closesocket(fd); - } else { - snprintf(s1->vc->info_str, sizeof(s1->vc->info_str), - "socket: connection from %s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); - } -} - -static int net_socket_listen_init(VLANState *vlan, - const char *model, - const char *name, - const char *host_str) -{ - NetSocketListenState *s; - int fd, val, ret; - struct sockaddr_in saddr; - - if (parse_host_port(&saddr, host_str) < 0) - return -1; - - s = qemu_mallocz(sizeof(NetSocketListenState)); - - fd = socket(PF_INET, SOCK_STREAM, 0); - if (fd < 0) { - perror("socket"); - return -1; - } - socket_set_nonblock(fd); - - /* allow fast reuse */ - val = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); - - ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); - if (ret < 0) { - perror("bind"); - return -1; - } - ret = listen(fd, 0); - if (ret < 0) { - perror("listen"); - return -1; - } - s->vlan = vlan; - s->model = qemu_strdup(model); - s->name = name ? qemu_strdup(name) : NULL; - s->fd = fd; - qemu_set_fd_handler(fd, net_socket_accept, NULL, s); - return 0; -} - -static int net_socket_connect_init(VLANState *vlan, - const char *model, - const char *name, - const char *host_str) -{ - NetSocketState *s; - int fd, connected, ret, err; - struct sockaddr_in saddr; - - if (parse_host_port(&saddr, host_str) < 0) - return -1; - - fd = socket(PF_INET, SOCK_STREAM, 0); - if (fd < 0) { - perror("socket"); - return -1; - } - socket_set_nonblock(fd); - - connected = 0; - for(;;) { - ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); - if (ret < 0) { - err = socket_error(); - if (err == EINTR || err == EWOULDBLOCK) { - } else if (err == EINPROGRESS) { - break; -#ifdef _WIN32 - } else if (err == WSAEALREADY) { - break; -#endif - } else { - perror("connect"); - closesocket(fd); - return -1; - } - } else { - connected = 1; - break; - } - } - s = net_socket_fd_init(vlan, model, name, fd, connected); - if (!s) - return -1; - snprintf(s->vc->info_str, sizeof(s->vc->info_str), - "socket: connect to %s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); - return 0; -} - -static int net_socket_mcast_init(VLANState *vlan, - const char *model, - const char *name, - const char *host_str) -{ - NetSocketState *s; - int fd; - struct sockaddr_in saddr; - - if (parse_host_port(&saddr, host_str) < 0) - return -1; - - - fd = net_socket_mcast_create(&saddr); - if (fd < 0) - return -1; - - s = net_socket_fd_init(vlan, model, name, fd, 0); - if (!s) - return -1; - - s->dgram_dst = saddr; - - snprintf(s->vc->info_str, sizeof(s->vc->info_str), - "socket: mcast=%s:%d", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); - return 0; - -} - -typedef struct DumpState { - VLANClientState *pcap_vc; - int fd; - int pcap_caplen; -} DumpState; - -#define PCAP_MAGIC 0xa1b2c3d4 - -struct pcap_file_hdr { - uint32_t magic; - uint16_t version_major; - uint16_t version_minor; - int32_t thiszone; - uint32_t sigfigs; - uint32_t snaplen; - uint32_t linktype; -}; - -struct pcap_sf_pkthdr { - struct { - int32_t tv_sec; - int32_t tv_usec; - } ts; - uint32_t caplen; - uint32_t len; -}; - -static ssize_t dump_receive(VLANClientState *vc, const uint8_t *buf, size_t size) -{ - DumpState *s = vc->opaque; - struct pcap_sf_pkthdr hdr; - int64_t ts; - int caplen; - - /* Early return in case of previous error. */ - if (s->fd < 0) { - return size; - } - - ts = muldiv64(qemu_get_clock(vm_clock), 1000000, get_ticks_per_sec()); - caplen = size > s->pcap_caplen ? s->pcap_caplen : size; - - hdr.ts.tv_sec = ts / 1000000; - hdr.ts.tv_usec = ts % 1000000; - hdr.caplen = caplen; - hdr.len = size; - if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) || - write(s->fd, buf, caplen) != caplen) { - qemu_log("-net dump write error - stop dump\n"); - close(s->fd); - s->fd = -1; - } - - return size; -} - -static void net_dump_cleanup(VLANClientState *vc) -{ - DumpState *s = vc->opaque; - - close(s->fd); - qemu_free(s); -} - -static int net_dump_init(VLANState *vlan, const char *device, - const char *name, const char *filename, int len) -{ - struct pcap_file_hdr hdr; - DumpState *s; - - s = qemu_malloc(sizeof(DumpState)); - - s->fd = open(filename, O_CREAT | O_WRONLY | O_BINARY, 0644); - if (s->fd < 0) { - qemu_error("-net dump: can't open %s\n", filename); - return -1; - } - - s->pcap_caplen = len; - - hdr.magic = PCAP_MAGIC; - hdr.version_major = 2; - hdr.version_minor = 4; - hdr.thiszone = 0; - hdr.sigfigs = 0; - hdr.snaplen = s->pcap_caplen; - hdr.linktype = 1; - - if (write(s->fd, &hdr, sizeof(hdr)) < sizeof(hdr)) { - qemu_error("-net dump write error: %s\n", strerror(errno)); - close(s->fd); - qemu_free(s); - return -1; - } - - s->pcap_vc = qemu_new_vlan_client(NET_CLIENT_TYPE_DUMP, - vlan, NULL, device, name, NULL, - dump_receive, NULL, NULL, - net_dump_cleanup, s); - snprintf(s->pcap_vc->info_str, sizeof(s->pcap_vc->info_str), - "dump to %s (len=%d)", filename, len); - return 0; -} - /* find or alloc a new VLAN */ VLANState *qemu_find_vlan(int id, int allocate) { @@ -2092,7 +797,7 @@ static int net_init_nic(QemuOpts *opts, nd->macaddr[5] = 0x56 + idx; if (qemu_opt_get(opts, "macaddr") && - parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) { + net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) { qemu_error("invalid syntax for ethernet address\n"); return -1; } @@ -2113,232 +818,6 @@ static int net_init_nic(QemuOpts *opts, return idx; } -#if defined(CONFIG_SLIRP) -static int net_init_slirp_configs(const char *name, const char *value, void *opaque) -{ - struct slirp_config_str *config; - - if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) { - return 0; - } - - config = qemu_mallocz(sizeof(*config)); - - pstrcpy(config->str, sizeof(config->str), value); - - if (!strcmp(name, "hostfwd")) { - config->flags = SLIRP_CFG_HOSTFWD; - } - - config->next = slirp_configs; - slirp_configs = config; - - return 0; -} - -static int net_init_slirp(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) -{ - struct slirp_config_str *config; - const char *vhost; - const char *vhostname; - const char *vdhcp_start; - const char *vnamesrv; - const char *tftp_export; - const char *bootfile; - const char *smb_export; - const char *vsmbsrv; - char *vnet = NULL; - int restricted = 0; - int ret; - - vhost = qemu_opt_get(opts, "host"); - vhostname = qemu_opt_get(opts, "hostname"); - vdhcp_start = qemu_opt_get(opts, "dhcpstart"); - vnamesrv = qemu_opt_get(opts, "dns"); - tftp_export = qemu_opt_get(opts, "tftp"); - bootfile = qemu_opt_get(opts, "bootfile"); - smb_export = qemu_opt_get(opts, "smb"); - vsmbsrv = qemu_opt_get(opts, "smbserver"); - - if (qemu_opt_get(opts, "ip")) { - const char *ip = qemu_opt_get(opts, "ip"); - int l = strlen(ip) + strlen("/24") + 1; - - vnet = qemu_malloc(l); - - /* emulate legacy ip= parameter */ - pstrcpy(vnet, l, ip); - pstrcat(vnet, l, "/24"); - } - - if (qemu_opt_get(opts, "net")) { - if (vnet) { - qemu_free(vnet); - } - vnet = qemu_strdup(qemu_opt_get(opts, "net")); - } - - if (qemu_opt_get(opts, "restrict") && - qemu_opt_get(opts, "restrict")[0] == 'y') { - restricted = 1; - } - - qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0); - - ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost, - vhostname, tftp_export, bootfile, vdhcp_start, - vnamesrv, smb_export, vsmbsrv); - - while (slirp_configs) { - config = slirp_configs; - slirp_configs = config->next; - qemu_free(config); - } - - if (ret != -1 && vlan) { - vlan->nb_host_devs++; - } - - qemu_free(vnet); - - return ret; -} -#endif /* CONFIG_SLIRP */ - -static int net_init_socket(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) -{ - if (qemu_opt_get(opts, "fd")) { - int fd; - - if (qemu_opt_get(opts, "listen") || - qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "mcast")) { - qemu_error("listen=, connect= and mcast= is invalid with fd=\n"); - return -1; - } - - fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); - if (fd == -1) { - return -1; - } - - if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) { - close(fd); - return -1; - } - } else if (qemu_opt_get(opts, "listen")) { - const char *listen; - - if (qemu_opt_get(opts, "fd") || - qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "mcast")) { - qemu_error("fd=, connect= and mcast= is invalid with listen=\n"); - return -1; - } - - listen = qemu_opt_get(opts, "listen"); - - if (net_socket_listen_init(vlan, "socket", name, listen) == -1) { - return -1; - } - } else if (qemu_opt_get(opts, "connect")) { - const char *connect; - - if (qemu_opt_get(opts, "fd") || - qemu_opt_get(opts, "listen") || - qemu_opt_get(opts, "mcast")) { - qemu_error("fd=, listen= and mcast= is invalid with connect=\n"); - return -1; - } - - connect = qemu_opt_get(opts, "connect"); - - if (net_socket_connect_init(vlan, "socket", name, connect) == -1) { - return -1; - } - } else if (qemu_opt_get(opts, "mcast")) { - const char *mcast; - - if (qemu_opt_get(opts, "fd") || - qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "listen")) { - qemu_error("fd=, connect= and listen= is invalid with mcast=\n"); - return -1; - } - - mcast = qemu_opt_get(opts, "mcast"); - - if (net_socket_mcast_init(vlan, "socket", name, mcast) == -1) { - return -1; - } - } else { - qemu_error("-socket requires fd=, listen=, connect= or mcast=\n"); - return -1; - } - - if (vlan) { - vlan->nb_host_devs++; - } - - return 0; -} - -#ifdef CONFIG_VDE -static int net_init_vde(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) -{ - const char *sock; - const char *group; - int port, mode; - - sock = qemu_opt_get(opts, "sock"); - group = qemu_opt_get(opts, "group"); - - port = qemu_opt_get_number(opts, "port", 0); - mode = qemu_opt_get_number(opts, "mode", 0700); - - if (net_vde_init(vlan, "vde", name, sock, port, group, mode) == -1) { - return -1; - } - - if (vlan) { - vlan->nb_host_devs++; - } - - return 0; -} -#endif - -static int net_init_dump(QemuOpts *opts, - Monitor *mon, - const char *name, - VLANState *vlan) -{ - int len; - const char *file; - char def_file[128]; - - assert(vlan); - - file = qemu_opt_get(opts, "file"); - if (!file) { - snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", vlan->id); - file = def_file; - } - - len = qemu_opt_get_size(opts, "len", 65536); - - return net_dump_init(vlan, "dump", name, file, len); -} - #define NET_COMMON_PARAMS_DESC \ { \ .name = "type", \ @@ -2576,12 +1055,18 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) int i; type = qemu_opt_get(opts, "type"); - if (!type) { - qemu_error("No type specified for -net\n"); - return -1; - } - if (is_netdev) { + if (!is_netdev) { + if (!type) { + qemu_error("No type specified for -net\n"); + return -1; + } + } else { + if (!type) { + qemu_error("No type specified for -netdev\n"); + return -1; + } + if (strcmp(type, "tap") != 0 && #ifdef CONFIG_SLIRP strcmp(type, "user") != 0 && @@ -2781,8 +1266,9 @@ done: monitor_printf(mon, "invalid link status '%s'; only 'up' or 'down' " "valid\n", up_or_down); - if (vc->link_status_changed) - vc->link_status_changed(vc); + if (vc->info->link_status_changed) { + vc->info->link_status_changed(vc); + } } void net_cleanup(void) @@ -2857,29 +1343,12 @@ int net_init_clients(void) int net_client_parse(QemuOptsList *opts_list, const char *optarg) { #if defined(CONFIG_SLIRP) - /* handle legacy -net channel,port:chr */ - if (!strcmp(opts_list->name, "net") && - !strncmp(optarg, "channel,", strlen("channel,"))) { - int ret; - - optarg += strlen("channel,"); - - if (QTAILQ_EMPTY(&slirp_stacks)) { - struct slirp_config_str *config; - - config = qemu_malloc(sizeof(*config)); - pstrcpy(config->str, sizeof(config->str), optarg); - config->flags = SLIRP_CFG_LEGACY; - config->next = slirp_configs; - slirp_configs = config; - ret = 0; - } else { - ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1); - } - + int ret; + if (net_slirp_parse_legacy(opts_list, optarg, &ret)) { return ret; } #endif + if (!qemu_opts_parse(opts_list, optarg, "type")) { return -1; } @@ -42,18 +42,20 @@ typedef ssize_t (NetReceiveIOV)(VLANClientState *, const struct iovec *, int); typedef void (NetCleanup) (VLANClientState *); typedef void (LinkStatusChanged)(VLANClientState *); -struct VLANClientState { +typedef struct NetClientInfo { net_client_type type; + size_t size; NetReceive *receive; NetReceive *receive_raw; NetReceiveIOV *receive_iov; - /* Packets may still be sent if this returns zero. It's used to - rate-limit the slirp code. */ NetCanReceive *can_receive; NetCleanup *cleanup; LinkStatusChanged *link_status_changed; +} NetClientInfo; + +struct VLANClientState { + NetClientInfo *info; int link_down; - void *opaque; QTAILQ_ENTRY(VLANClientState) next; struct VLANState *vlan; VLANClientState *peer; @@ -64,6 +66,12 @@ struct VLANClientState { unsigned receive_disabled : 1; }; +typedef struct NICState { + VLANClientState nc; + NICConf *conf; + void *opaque; +} NICState; + struct VLANState { int id; QTAILQ_HEAD(, VLANClientState) clients; @@ -74,19 +82,21 @@ struct VLANState { VLANState *qemu_find_vlan(int id, int allocate); VLANClientState *qemu_find_netdev(const char *id); -VLANClientState *qemu_new_vlan_client(net_client_type type, - VLANState *vlan, - VLANClientState *peer, - const char *model, - const char *name, - NetCanReceive *can_receive, - NetReceive *receive, - NetReceive *receive_raw, - NetReceiveIOV *receive_iov, - NetCleanup *cleanup, - void *opaque); +VLANClientState *qemu_new_net_client(NetClientInfo *info, + VLANState *vlan, + VLANClientState *peer, + const char *model, + const char *name); +NICState *qemu_new_nic(NetClientInfo *info, + NICConf *conf, + const char *model, + const char *name, + void *opaque); void qemu_del_vlan_client(VLANClientState *vc); -VLANClientState *qemu_find_vlan_client(VLANState *vlan, void *opaque); +VLANClientState *qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id, + const char *client_str); +typedef void (*qemu_nic_foreach)(NICState *nic, void *opaque); +void qemu_foreach_nic(qemu_nic_foreach func, void *opaque); int qemu_can_send_packet(VLANClientState *vc); ssize_t qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt); @@ -108,8 +118,6 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models, void do_info_network(Monitor *mon); void do_set_link(Monitor *mon, const QDict *qdict); -void do_info_usernet(Monitor *mon); - /* NIC info */ #define MAX_NICS 8 @@ -124,8 +132,6 @@ struct NICInfo { char *devaddr; VLANState *vlan; VLANClientState *netdev; - VLANClientState *vc; - void *private; int used; int bootable; int nvectors; @@ -156,10 +162,6 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev); void net_client_uninit(NICInfo *nd); int net_client_parse(QemuOptsList *opts_list, const char *str); int net_init_clients(void); -int net_slirp_smb(const char *exported_dir); -void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict); -void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict); -int net_slirp_redir(const char *redir_str); void net_cleanup(void); void net_set_boot_mask(int boot_mask); void net_host_device_add(Monitor *mon, const QDict *qdict); diff --git a/net/dump.c b/net/dump.c new file mode 100644 index 000000000..d50b4eeac --- /dev/null +++ b/net/dump.c @@ -0,0 +1,158 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "dump.h" +#include "qemu-common.h" +#include "sysemu.h" +#include "qemu-log.h" + +typedef struct DumpState { + VLANClientState nc; + int fd; + int pcap_caplen; +} DumpState; + +#define PCAP_MAGIC 0xa1b2c3d4 + +struct pcap_file_hdr { + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + int32_t thiszone; + uint32_t sigfigs; + uint32_t snaplen; + uint32_t linktype; +}; + +struct pcap_sf_pkthdr { + struct { + int32_t tv_sec; + int32_t tv_usec; + } ts; + uint32_t caplen; + uint32_t len; +}; + +static ssize_t dump_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + DumpState *s = DO_UPCAST(DumpState, nc, nc); + struct pcap_sf_pkthdr hdr; + int64_t ts; + int caplen; + + /* Early return in case of previous error. */ + if (s->fd < 0) { + return size; + } + + ts = muldiv64(qemu_get_clock(vm_clock), 1000000, get_ticks_per_sec()); + caplen = size > s->pcap_caplen ? s->pcap_caplen : size; + + hdr.ts.tv_sec = ts / 1000000; + hdr.ts.tv_usec = ts % 1000000; + hdr.caplen = caplen; + hdr.len = size; + if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) || + write(s->fd, buf, caplen) != caplen) { + qemu_log("-net dump write error - stop dump\n"); + close(s->fd); + s->fd = -1; + } + + return size; +} + +static void dump_cleanup(VLANClientState *nc) +{ + DumpState *s = DO_UPCAST(DumpState, nc, nc); + + close(s->fd); +} + +static NetClientInfo net_dump_info = { + .type = NET_CLIENT_TYPE_DUMP, + .size = sizeof(DumpState), + .receive = dump_receive, + .cleanup = dump_cleanup, +}; + +static int net_dump_init(VLANState *vlan, const char *device, + const char *name, const char *filename, int len) +{ + struct pcap_file_hdr hdr; + VLANClientState *nc; + DumpState *s; + int fd; + + fd = open(filename, O_CREAT | O_WRONLY | O_BINARY, 0644); + if (fd < 0) { + qemu_error("-net dump: can't open %s\n", filename); + return -1; + } + + hdr.magic = PCAP_MAGIC; + hdr.version_major = 2; + hdr.version_minor = 4; + hdr.thiszone = 0; + hdr.sigfigs = 0; + hdr.snaplen = len; + hdr.linktype = 1; + + if (write(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) { + qemu_error("-net dump write error: %s\n", strerror(errno)); + close(fd); + return -1; + } + + nc = qemu_new_net_client(&net_dump_info, vlan, NULL, device, name); + + snprintf(nc->info_str, sizeof(nc->info_str), + "dump to %s (len=%d)", filename, len); + + s = DO_UPCAST(DumpState, nc, nc); + + s->fd = fd; + s->pcap_caplen = len; + + return 0; +} + +int net_init_dump(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +{ + int len; + const char *file; + char def_file[128]; + + assert(vlan); + + file = qemu_opt_get(opts, "file"); + if (!file) { + snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", vlan->id); + file = def_file; + } + + len = qemu_opt_get_size(opts, "len", 65536); + + return net_dump_init(vlan, "dump", name, file, len); +} diff --git a/net/dump.h b/net/dump.h new file mode 100644 index 000000000..fdc91ad6d --- /dev/null +++ b/net/dump.h @@ -0,0 +1,33 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_DUMP_H +#define QEMU_NET_DUMP_H + +#include "net.h" +#include "qemu-common.h" + +int net_init_dump(QemuOpts *opts, Monitor *mon, + const char *name, VLANState *vlan); + +#endif /* QEMU_NET_DUMP_H */ diff --git a/net/slirp.c b/net/slirp.c new file mode 100644 index 000000000..3f91c4bb3 --- /dev/null +++ b/net/slirp.c @@ -0,0 +1,765 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "net/slirp.h" + +#include "config-host.h" + +#include "net.h" +#include "monitor.h" +#include "sysemu.h" +#include "qemu_socket.h" +#include "slirp/libslirp.h" + +static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) +{ + const char *p, *p1; + int len; + p = *pp; + p1 = strchr(p, sep); + if (!p1) + return -1; + len = p1 - p; + p1++; + if (buf_size > 0) { + if (len > buf_size - 1) + len = buf_size - 1; + memcpy(buf, p, len); + buf[len] = '\0'; + } + *pp = p1; + return 0; +} + +/* slirp network adapter */ + +#define SLIRP_CFG_HOSTFWD 1 +#define SLIRP_CFG_LEGACY 2 + +struct slirp_config_str { + struct slirp_config_str *next; + int flags; + char str[1024]; + int legacy_format; +}; + +typedef struct SlirpState { + VLANClientState nc; + QTAILQ_ENTRY(SlirpState) entry; + Slirp *slirp; +#ifndef _WIN32 + char smb_dir[128]; +#endif +} SlirpState; + +static struct slirp_config_str *slirp_configs; +const char *legacy_tftp_prefix; +const char *legacy_bootp_filename; +static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks = + QTAILQ_HEAD_INITIALIZER(slirp_stacks); + +static int slirp_hostfwd(SlirpState *s, const char *redir_str, + int legacy_format); +static int slirp_guestfwd(SlirpState *s, const char *config_str, + int legacy_format); + +#ifndef _WIN32 +static const char *legacy_smb_export; + +static int slirp_smb(SlirpState *s, const char *exported_dir, + struct in_addr vserver_addr); +static void slirp_smb_cleanup(SlirpState *s); +#else +static inline void slirp_smb_cleanup(SlirpState *s) { } +#endif + +int slirp_can_output(void *opaque) +{ + SlirpState *s = opaque; + + return qemu_can_send_packet(&s->nc); +} + +void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len) +{ + SlirpState *s = opaque; + + qemu_send_packet(&s->nc, pkt, pkt_len); +} + +static ssize_t net_slirp_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + SlirpState *s = DO_UPCAST(SlirpState, nc, nc); + + slirp_input(s->slirp, buf, size); + + return size; +} + +static void net_slirp_cleanup(VLANClientState *nc) +{ + SlirpState *s = DO_UPCAST(SlirpState, nc, nc); + + slirp_cleanup(s->slirp); + slirp_smb_cleanup(s); + QTAILQ_REMOVE(&slirp_stacks, s, entry); +} + +static NetClientInfo net_slirp_info = { + .type = NET_CLIENT_TYPE_SLIRP, + .size = sizeof(SlirpState), + .receive = net_slirp_receive, + .cleanup = net_slirp_cleanup, +}; + +static int net_slirp_init(VLANState *vlan, const char *model, + const char *name, int restricted, + const char *vnetwork, const char *vhost, + const char *vhostname, const char *tftp_export, + const char *bootfile, const char *vdhcp_start, + const char *vnameserver, const char *smb_export, + const char *vsmbserver) +{ + /* default settings according to historic slirp */ + struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ + struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */ + struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */ + struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ + struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ +#ifndef _WIN32 + struct in_addr smbsrv = { .s_addr = 0 }; +#endif + VLANClientState *nc; + SlirpState *s; + char buf[20]; + uint32_t addr; + int shift; + char *end; + struct slirp_config_str *config; + + if (!tftp_export) { + tftp_export = legacy_tftp_prefix; + } + if (!bootfile) { + bootfile = legacy_bootp_filename; + } + + if (vnetwork) { + if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) { + if (!inet_aton(vnetwork, &net)) { + return -1; + } + addr = ntohl(net.s_addr); + if (!(addr & 0x80000000)) { + mask.s_addr = htonl(0xff000000); /* class A */ + } else if ((addr & 0xfff00000) == 0xac100000) { + mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */ + } else if ((addr & 0xc0000000) == 0x80000000) { + mask.s_addr = htonl(0xffff0000); /* class B */ + } else if ((addr & 0xffff0000) == 0xc0a80000) { + mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */ + } else if ((addr & 0xffff0000) == 0xc6120000) { + mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */ + } else if ((addr & 0xe0000000) == 0xe0000000) { + mask.s_addr = htonl(0xffffff00); /* class C */ + } else { + mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */ + } + } else { + if (!inet_aton(buf, &net)) { + return -1; + } + shift = strtol(vnetwork, &end, 10); + if (*end != '\0') { + if (!inet_aton(vnetwork, &mask)) { + return -1; + } + } else if (shift < 4 || shift > 32) { + return -1; + } else { + mask.s_addr = htonl(0xffffffff << (32 - shift)); + } + } + net.s_addr &= mask.s_addr; + host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr); + dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr); + dns.s_addr = net.s_addr | (htonl(0x0203) & ~mask.s_addr); + } + + if (vhost && !inet_aton(vhost, &host)) { + return -1; + } + if ((host.s_addr & mask.s_addr) != net.s_addr) { + return -1; + } + + if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) { + return -1; + } + if ((dhcp.s_addr & mask.s_addr) != net.s_addr || + dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) { + return -1; + } + + if (vnameserver && !inet_aton(vnameserver, &dns)) { + return -1; + } + if ((dns.s_addr & mask.s_addr) != net.s_addr || + dns.s_addr == host.s_addr) { + return -1; + } + +#ifndef _WIN32 + if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) { + return -1; + } +#endif + + nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name); + + snprintf(nc->info_str, sizeof(nc->info_str), + "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n'); + + s = DO_UPCAST(SlirpState, nc, nc); + + s->slirp = slirp_init(restricted, net, mask, host, vhostname, + tftp_export, bootfile, dhcp, dns, s); + QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); + + for (config = slirp_configs; config; config = config->next) { + if (config->flags & SLIRP_CFG_HOSTFWD) { + if (slirp_hostfwd(s, config->str, + config->flags & SLIRP_CFG_LEGACY) < 0) + goto error; + } else { + if (slirp_guestfwd(s, config->str, + config->flags & SLIRP_CFG_LEGACY) < 0) + goto error; + } + } +#ifndef _WIN32 + if (!smb_export) { + smb_export = legacy_smb_export; + } + if (smb_export) { + if (slirp_smb(s, smb_export, smbsrv) < 0) + goto error; + } +#endif + + return 0; + +error: + qemu_del_vlan_client(nc); + return -1; +} + +static SlirpState *slirp_lookup(Monitor *mon, const char *vlan, + const char *stack) +{ + + if (vlan) { + VLANClientState *nc; + nc = qemu_find_vlan_client_by_name(mon, strtol(vlan, NULL, 0), stack); + if (!nc) { + return NULL; + } + if (strcmp(nc->model, "user")) { + monitor_printf(mon, "invalid device specified\n"); + return NULL; + } + return DO_UPCAST(SlirpState, nc, nc); + } else { + if (QTAILQ_EMPTY(&slirp_stacks)) { + monitor_printf(mon, "user mode network stack not in use\n"); + return NULL; + } + return QTAILQ_FIRST(&slirp_stacks); + } +} + +void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict) +{ + struct in_addr host_addr = { .s_addr = INADDR_ANY }; + int host_port; + char buf[256] = ""; + const char *src_str, *p; + SlirpState *s; + int is_udp = 0; + int err; + const char *arg1 = qdict_get_str(qdict, "arg1"); + const char *arg2 = qdict_get_try_str(qdict, "arg2"); + const char *arg3 = qdict_get_try_str(qdict, "arg3"); + + if (arg2) { + s = slirp_lookup(mon, arg1, arg2); + src_str = arg3; + } else { + s = slirp_lookup(mon, NULL, NULL); + src_str = arg1; + } + if (!s) { + return; + } + + if (!src_str || !src_str[0]) + goto fail_syntax; + + p = src_str; + get_str_sep(buf, sizeof(buf), &p, ':'); + + if (!strcmp(buf, "tcp") || buf[0] == '\0') { + is_udp = 0; + } else if (!strcmp(buf, "udp")) { + is_udp = 1; + } else { + goto fail_syntax; + } + + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + goto fail_syntax; + } + + host_port = atoi(p); + + err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp, + host_addr, host_port); + + monitor_printf(mon, "host forwarding rule for %s %s\n", src_str, + err ? "removed" : "not found"); + return; + + fail_syntax: + monitor_printf(mon, "invalid format\n"); +} + +static int slirp_hostfwd(SlirpState *s, const char *redir_str, + int legacy_format) +{ + struct in_addr host_addr = { .s_addr = INADDR_ANY }; + struct in_addr guest_addr = { .s_addr = 0 }; + int host_port, guest_port; + const char *p; + char buf[256]; + int is_udp; + char *end; + + p = redir_str; + if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (!strcmp(buf, "tcp") || buf[0] == '\0') { + is_udp = 0; + } else if (!strcmp(buf, "udp")) { + is_udp = 1; + } else { + goto fail_syntax; + } + + if (!legacy_format) { + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + goto fail_syntax; + } + } + + if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) { + goto fail_syntax; + } + host_port = strtol(buf, &end, 0); + if (*end != '\0' || host_port < 1 || host_port > 65535) { + goto fail_syntax; + } + + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) { + goto fail_syntax; + } + + guest_port = strtol(p, &end, 0); + if (*end != '\0' || guest_port < 1 || guest_port > 65535) { + goto fail_syntax; + } + + if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr, + guest_port) < 0) { + qemu_error("could not set up host forwarding rule '%s'\n", + redir_str); + return -1; + } + return 0; + + fail_syntax: + qemu_error("invalid host forwarding rule '%s'\n", redir_str); + return -1; +} + +void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict) +{ + const char *redir_str; + SlirpState *s; + const char *arg1 = qdict_get_str(qdict, "arg1"); + const char *arg2 = qdict_get_try_str(qdict, "arg2"); + const char *arg3 = qdict_get_try_str(qdict, "arg3"); + + if (arg2) { + s = slirp_lookup(mon, arg1, arg2); + redir_str = arg3; + } else { + s = slirp_lookup(mon, NULL, NULL); + redir_str = arg1; + } + if (s) { + slirp_hostfwd(s, redir_str, 0); + } + +} + +int net_slirp_redir(const char *redir_str) +{ + struct slirp_config_str *config; + + if (QTAILQ_EMPTY(&slirp_stacks)) { + config = qemu_malloc(sizeof(*config)); + pstrcpy(config->str, sizeof(config->str), redir_str); + config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY; + config->next = slirp_configs; + slirp_configs = config; + return 0; + } + + return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1); +} + +#ifndef _WIN32 + +/* automatic user mode samba server configuration */ +static void slirp_smb_cleanup(SlirpState *s) +{ + char cmd[128]; + + if (s->smb_dir[0] != '\0') { + snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir); + system(cmd); + s->smb_dir[0] = '\0'; + } +} + +static int slirp_smb(SlirpState* s, const char *exported_dir, + struct in_addr vserver_addr) +{ + static int instance; + char smb_conf[128]; + char smb_cmdline[128]; + FILE *f; + + snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d", + (long)getpid(), instance++); + if (mkdir(s->smb_dir, 0700) < 0) { + qemu_error("could not create samba server dir '%s'\n", s->smb_dir); + return -1; + } + snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf"); + + f = fopen(smb_conf, "w"); + if (!f) { + slirp_smb_cleanup(s); + qemu_error("could not create samba server configuration file '%s'\n", + smb_conf); + return -1; + } + fprintf(f, + "[global]\n" + "private dir=%s\n" + "smb ports=0\n" + "socket address=127.0.0.1\n" + "pid directory=%s\n" + "lock directory=%s\n" + "log file=%s/log.smbd\n" + "smb passwd file=%s/smbpasswd\n" + "security = share\n" + "[qemu]\n" + "path=%s\n" + "read only=no\n" + "guest ok=yes\n", + s->smb_dir, + s->smb_dir, + s->smb_dir, + s->smb_dir, + s->smb_dir, + exported_dir + ); + fclose(f); + + snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s", + SMBD_COMMAND, smb_conf); + + if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) { + slirp_smb_cleanup(s); + qemu_error("conflicting/invalid smbserver address\n"); + return -1; + } + return 0; +} + +/* automatic user mode samba server configuration (legacy interface) */ +int net_slirp_smb(const char *exported_dir) +{ + struct in_addr vserver_addr = { .s_addr = 0 }; + + if (legacy_smb_export) { + fprintf(stderr, "-smb given twice\n"); + return -1; + } + legacy_smb_export = exported_dir; + if (!QTAILQ_EMPTY(&slirp_stacks)) { + return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir, + vserver_addr); + } + return 0; +} + +#endif /* !defined(_WIN32) */ + +struct GuestFwd { + CharDriverState *hd; + struct in_addr server; + int port; + Slirp *slirp; +}; + +static int guestfwd_can_read(void *opaque) +{ + struct GuestFwd *fwd = opaque; + return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port); +} + +static void guestfwd_read(void *opaque, const uint8_t *buf, int size) +{ + struct GuestFwd *fwd = opaque; + slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size); +} + +static int slirp_guestfwd(SlirpState *s, const char *config_str, + int legacy_format) +{ + struct in_addr server = { .s_addr = 0 }; + struct GuestFwd *fwd; + const char *p; + char buf[128]; + char *end; + int port; + + p = config_str; + if (legacy_format) { + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + } else { + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (strcmp(buf, "tcp") && buf[0] != '\0') { + goto fail_syntax; + } + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &server)) { + goto fail_syntax; + } + if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { + goto fail_syntax; + } + } + port = strtol(buf, &end, 10); + if (*end != '\0' || port < 1 || port > 65535) { + goto fail_syntax; + } + + fwd = qemu_malloc(sizeof(struct GuestFwd)); + snprintf(buf, sizeof(buf), "guestfwd.tcp:%d", port); + fwd->hd = qemu_chr_open(buf, p, NULL); + if (!fwd->hd) { + qemu_error("could not open guest forwarding device '%s'\n", buf); + qemu_free(fwd); + return -1; + } + + if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) { + qemu_error("conflicting/invalid host:port in guest forwarding " + "rule '%s'\n", config_str); + qemu_free(fwd); + return -1; + } + fwd->server = server; + fwd->port = port; + fwd->slirp = s->slirp; + + qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read, + NULL, fwd); + return 0; + + fail_syntax: + qemu_error("invalid guest forwarding rule '%s'\n", config_str); + return -1; +} + +void do_info_usernet(Monitor *mon) +{ + SlirpState *s; + + QTAILQ_FOREACH(s, &slirp_stacks, entry) { + monitor_printf(mon, "VLAN %d (%s):\n", + s->nc.vlan ? s->nc.vlan->id : -1, + s->nc.name); + slirp_connection_info(s->slirp, mon); + } +} + +static int net_init_slirp_configs(const char *name, const char *value, void *opaque) +{ + struct slirp_config_str *config; + + if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) { + return 0; + } + + config = qemu_mallocz(sizeof(*config)); + + pstrcpy(config->str, sizeof(config->str), value); + + if (!strcmp(name, "hostfwd")) { + config->flags = SLIRP_CFG_HOSTFWD; + } + + config->next = slirp_configs; + slirp_configs = config; + + return 0; +} + +int net_init_slirp(QemuOpts *opts, + Monitor *mon, + const char *name, + VLANState *vlan) +{ + struct slirp_config_str *config; + const char *vhost; + const char *vhostname; + const char *vdhcp_start; + const char *vnamesrv; + const char *tftp_export; + const char *bootfile; + const char *smb_export; + const char *vsmbsrv; + char *vnet = NULL; + int restricted = 0; + int ret; + + vhost = qemu_opt_get(opts, "host"); + vhostname = qemu_opt_get(opts, "hostname"); + vdhcp_start = qemu_opt_get(opts, "dhcpstart"); + vnamesrv = qemu_opt_get(opts, "dns"); + tftp_export = qemu_opt_get(opts, "tftp"); + bootfile = qemu_opt_get(opts, "bootfile"); + smb_export = qemu_opt_get(opts, "smb"); + vsmbsrv = qemu_opt_get(opts, "smbserver"); + + if (qemu_opt_get(opts, "ip")) { + const char *ip = qemu_opt_get(opts, "ip"); + int l = strlen(ip) + strlen("/24") + 1; + + vnet = qemu_malloc(l); + + /* emulate legacy ip= parameter */ + pstrcpy(vnet, l, ip); + pstrcat(vnet, l, "/24"); + } + + if (qemu_opt_get(opts, "net")) { + if (vnet) { + qemu_free(vnet); + } + vnet = qemu_strdup(qemu_opt_get(opts, "net")); + } + + if (qemu_opt_get(opts, "restrict") && + qemu_opt_get(opts, "restrict")[0] == 'y') { + restricted = 1; + } + + qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0); + + ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost, + vhostname, tftp_export, bootfile, vdhcp_start, + vnamesrv, smb_export, vsmbsrv); + + while (slirp_configs) { + config = slirp_configs; + slirp_configs = config->next; + qemu_free(config); + } + + if (ret != -1 && vlan) { + vlan->nb_host_devs++; + } + + qemu_free(vnet); + + return ret; +} + +int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret) +{ + if (strcmp(opts_list->name, "net") != 0 || + strncmp(optarg, "channel,", strlen("channel,")) != 0) { + return 0; + } + + /* handle legacy -net channel,port:chr */ + optarg += strlen("channel,"); + + if (QTAILQ_EMPTY(&slirp_stacks)) { + struct slirp_config_str *config; + + config = qemu_malloc(sizeof(*config)); + pstrcpy(config->str, sizeof(config->str), optarg); + config->flags = SLIRP_CFG_LEGACY; + config->next = slirp_configs; + slirp_configs = config; + *ret = 0; + } else { + *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1); + } + + return 1; +} + diff --git a/net/slirp.h b/net/slirp.h new file mode 100644 index 000000000..c17de8ed4 --- /dev/null +++ b/net/slirp.h @@ -0,0 +1,51 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_SLIRP_H +#define QEMU_NET_SLIRP_H + +#include "qemu-common.h" +#include "qdict.h" +#include "qemu-option.h" + +#ifdef CONFIG_SLIRP + +int net_init_slirp(QemuOpts *opts, + Monitor *mon, + const char *name, + VLANState *vlan); + +void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict); +void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict); + +int net_slirp_redir(const char *redir_str); + +int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret); + +int net_slirp_smb(const char *exported_dir); + +void do_info_usernet(Monitor *mon); + +#endif + +#endif /* QEMU_NET_SLIRP_H */ diff --git a/net/socket.c b/net/socket.c new file mode 100644 index 000000000..7331d87c6 --- /dev/null +++ b/net/socket.c @@ -0,0 +1,577 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "net/socket.h" + +#include "config-host.h" + +#include "net.h" +#include "qemu-char.h" +#include "qemu-common.h" +#include "qemu-option.h" +#include "qemu_socket.h" +#include "sysemu.h" + +typedef struct NetSocketState { + VLANClientState nc; + int fd; + int state; /* 0 = getting length, 1 = getting data */ + unsigned int index; + unsigned int packet_len; + uint8_t buf[4096]; + struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */ +} NetSocketState; + +typedef struct NetSocketListenState { + VLANState *vlan; + char *model; + char *name; + int fd; +} NetSocketListenState; + +/* XXX: we consider we can send the whole packet without blocking */ +static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); + uint32_t len; + len = htonl(size); + + send_all(s->fd, (const uint8_t *)&len, sizeof(len)); + return send_all(s->fd, buf, size); +} + +static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); + + return sendto(s->fd, (const void *)buf, size, 0, + (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst)); +} + +static void net_socket_send(void *opaque) +{ + NetSocketState *s = opaque; + int size, err; + unsigned l; + uint8_t buf1[4096]; + const uint8_t *buf; + + size = recv(s->fd, (void *)buf1, sizeof(buf1), 0); + if (size < 0) { + err = socket_error(); + if (err != EWOULDBLOCK) + goto eoc; + } else if (size == 0) { + /* end of connection */ + eoc: + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + closesocket(s->fd); + return; + } + buf = buf1; + while (size > 0) { + /* reassemble a packet from the network */ + switch(s->state) { + case 0: + l = 4 - s->index; + if (l > size) + l = size; + memcpy(s->buf + s->index, buf, l); + buf += l; + size -= l; + s->index += l; + if (s->index == 4) { + /* got length */ + s->packet_len = ntohl(*(uint32_t *)s->buf); + s->index = 0; + s->state = 1; + } + break; + case 1: + l = s->packet_len - s->index; + if (l > size) + l = size; + if (s->index + l <= sizeof(s->buf)) { + memcpy(s->buf + s->index, buf, l); + } else { + fprintf(stderr, "serious error: oversized packet received," + "connection terminated.\n"); + s->state = 0; + goto eoc; + } + + s->index += l; + buf += l; + size -= l; + if (s->index >= s->packet_len) { + qemu_send_packet(&s->nc, s->buf, s->packet_len); + s->index = 0; + s->state = 0; + } + break; + } + } +} + +static void net_socket_send_dgram(void *opaque) +{ + NetSocketState *s = opaque; + int size; + + size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); + if (size < 0) + return; + if (size == 0) { + /* end of connection */ + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + return; + } + qemu_send_packet(&s->nc, s->buf, size); +} + +static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) +{ + struct ip_mreq imr; + int fd; + int val, ret; + if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) { + fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n", + inet_ntoa(mcastaddr->sin_addr), + (int)ntohl(mcastaddr->sin_addr.s_addr)); + return -1; + + } + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("socket(PF_INET, SOCK_DGRAM)"); + return -1; + } + + val = 1; + ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const char *)&val, sizeof(val)); + if (ret < 0) { + perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); + goto fail; + } + + ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr)); + if (ret < 0) { + perror("bind"); + goto fail; + } + + /* Add host to multicast group */ + imr.imr_multiaddr = mcastaddr->sin_addr; + imr.imr_interface.s_addr = htonl(INADDR_ANY); + + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char *)&imr, sizeof(struct ip_mreq)); + if (ret < 0) { + perror("setsockopt(IP_ADD_MEMBERSHIP)"); + goto fail; + } + + /* Force mcast msgs to loopback (eg. several QEMUs in same host */ + val = 1; + ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (const char *)&val, sizeof(val)); + if (ret < 0) { + perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)"); + goto fail; + } + + socket_set_nonblock(fd); + return fd; +fail: + if (fd >= 0) + closesocket(fd); + return -1; +} + +static void net_socket_cleanup(VLANClientState *nc) +{ + NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + close(s->fd); +} + +static NetClientInfo net_dgram_socket_info = { + .type = NET_CLIENT_TYPE_SOCKET, + .size = sizeof(NetSocketState), + .receive = net_socket_receive_dgram, + .cleanup = net_socket_cleanup, +}; + +static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, + const char *model, + const char *name, + int fd, int is_connected) +{ + struct sockaddr_in saddr; + int newfd; + socklen_t saddr_len; + VLANClientState *nc; + NetSocketState *s; + + /* fd passed: multicast: "learn" dgram_dst address from bound address and save it + * Because this may be "shared" socket from a "master" process, datagrams would be recv() + * by ONLY ONE process: we must "clone" this dgram socket --jjo + */ + + if (is_connected) { + if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) { + /* must be bound */ + if (saddr.sin_addr.s_addr==0) { + fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n", + fd); + return NULL; + } + /* clone dgram socket */ + newfd = net_socket_mcast_create(&saddr); + if (newfd < 0) { + /* error already reported by net_socket_mcast_create() */ + close(fd); + return NULL; + } + /* clone newfd to fd, close newfd */ + dup2(newfd, fd); + close(newfd); + + } else { + fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n", + fd, strerror(errno)); + return NULL; + } + } + + nc = qemu_new_net_client(&net_dgram_socket_info, vlan, NULL, model, name); + + snprintf(nc->info_str, sizeof(nc->info_str), + "socket: fd=%d (%s mcast=%s:%d)", + fd, is_connected ? "cloned" : "", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + + s = DO_UPCAST(NetSocketState, nc, nc); + + s->fd = fd; + + qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s); + + /* mcast: save bound address as dst */ + if (is_connected) s->dgram_dst=saddr; + + return s; +} + +static void net_socket_connect(void *opaque) +{ + NetSocketState *s = opaque; + qemu_set_fd_handler(s->fd, net_socket_send, NULL, s); +} + +static NetClientInfo net_socket_info = { + .type = NET_CLIENT_TYPE_SOCKET, + .size = sizeof(NetSocketState), + .receive = net_socket_receive, + .cleanup = net_socket_cleanup, +}; + +static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, + const char *model, + const char *name, + int fd, int is_connected) +{ + VLANClientState *nc; + NetSocketState *s; + + nc = qemu_new_net_client(&net_socket_info, vlan, NULL, model, name); + + snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd); + + s = DO_UPCAST(NetSocketState, nc, nc); + + s->fd = fd; + + if (is_connected) { + net_socket_connect(s); + } else { + qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s); + } + return s; +} + +static NetSocketState *net_socket_fd_init(VLANState *vlan, + const char *model, const char *name, + int fd, int is_connected) +{ + int so_type = -1, optlen=sizeof(so_type); + + if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, + (socklen_t *)&optlen)< 0) { + fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd); + return NULL; + } + switch(so_type) { + case SOCK_DGRAM: + return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected); + case SOCK_STREAM: + return net_socket_fd_init_stream(vlan, model, name, fd, is_connected); + default: + /* who knows ... this could be a eg. a pty, do warn and continue as stream */ + fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd); + return net_socket_fd_init_stream(vlan, model, name, fd, is_connected); + } + return NULL; +} + +static void net_socket_accept(void *opaque) +{ + NetSocketListenState *s = opaque; + NetSocketState *s1; + struct sockaddr_in saddr; + socklen_t len; + int fd; + + for(;;) { + len = sizeof(saddr); + fd = accept(s->fd, (struct sockaddr *)&saddr, &len); + if (fd < 0 && errno != EINTR) { + return; + } else if (fd >= 0) { + break; + } + } + s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1); + if (!s1) { + closesocket(fd); + } else { + snprintf(s1->nc.info_str, sizeof(s1->nc.info_str), + "socket: connection from %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + } +} + +static int net_socket_listen_init(VLANState *vlan, + const char *model, + const char *name, + const char *host_str) +{ + NetSocketListenState *s; + int fd, val, ret; + struct sockaddr_in saddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + s = qemu_mallocz(sizeof(NetSocketListenState)); + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + socket_set_nonblock(fd); + + /* allow fast reuse */ + val = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); + + ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0) { + perror("bind"); + return -1; + } + ret = listen(fd, 0); + if (ret < 0) { + perror("listen"); + return -1; + } + s->vlan = vlan; + s->model = qemu_strdup(model); + s->name = name ? qemu_strdup(name) : NULL; + s->fd = fd; + qemu_set_fd_handler(fd, net_socket_accept, NULL, s); + return 0; +} + +static int net_socket_connect_init(VLANState *vlan, + const char *model, + const char *name, + const char *host_str) +{ + NetSocketState *s; + int fd, connected, ret, err; + struct sockaddr_in saddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + socket_set_nonblock(fd); + + connected = 0; + for(;;) { + ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0) { + err = socket_error(); + if (err == EINTR || err == EWOULDBLOCK) { + } else if (err == EINPROGRESS) { + break; +#ifdef _WIN32 + } else if (err == WSAEALREADY) { + break; +#endif + } else { + perror("connect"); + closesocket(fd); + return -1; + } + } else { + connected = 1; + break; + } + } + s = net_socket_fd_init(vlan, model, name, fd, connected); + if (!s) + return -1; + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "socket: connect to %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + return 0; +} + +static int net_socket_mcast_init(VLANState *vlan, + const char *model, + const char *name, + const char *host_str) +{ + NetSocketState *s; + int fd; + struct sockaddr_in saddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + + fd = net_socket_mcast_create(&saddr); + if (fd < 0) + return -1; + + s = net_socket_fd_init(vlan, model, name, fd, 0); + if (!s) + return -1; + + s->dgram_dst = saddr; + + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "socket: mcast=%s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + return 0; + +} + +int net_init_socket(QemuOpts *opts, + Monitor *mon, + const char *name, + VLANState *vlan) +{ + if (qemu_opt_get(opts, "fd")) { + int fd; + + if (qemu_opt_get(opts, "listen") || + qemu_opt_get(opts, "connect") || + qemu_opt_get(opts, "mcast")) { + qemu_error("listen=, connect= and mcast= is invalid with fd=\n"); + return -1; + } + + fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); + if (fd == -1) { + return -1; + } + + if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) { + close(fd); + return -1; + } + } else if (qemu_opt_get(opts, "listen")) { + const char *listen; + + if (qemu_opt_get(opts, "fd") || + qemu_opt_get(opts, "connect") || + qemu_opt_get(opts, "mcast")) { + qemu_error("fd=, connect= and mcast= is invalid with listen=\n"); + return -1; + } + + listen = qemu_opt_get(opts, "listen"); + + if (net_socket_listen_init(vlan, "socket", name, listen) == -1) { + return -1; + } + } else if (qemu_opt_get(opts, "connect")) { + const char *connect; + + if (qemu_opt_get(opts, "fd") || + qemu_opt_get(opts, "listen") || + qemu_opt_get(opts, "mcast")) { + qemu_error("fd=, listen= and mcast= is invalid with connect=\n"); + return -1; + } + + connect = qemu_opt_get(opts, "connect"); + + if (net_socket_connect_init(vlan, "socket", name, connect) == -1) { + return -1; + } + } else if (qemu_opt_get(opts, "mcast")) { + const char *mcast; + + if (qemu_opt_get(opts, "fd") || + qemu_opt_get(opts, "connect") || + qemu_opt_get(opts, "listen")) { + qemu_error("fd=, connect= and listen= is invalid with mcast=\n"); + return -1; + } + + mcast = qemu_opt_get(opts, "mcast"); + + if (net_socket_mcast_init(vlan, "socket", name, mcast) == -1) { + return -1; + } + } else { + qemu_error("-socket requires fd=, listen=, connect= or mcast=\n"); + return -1; + } + + if (vlan) { + vlan->nb_host_devs++; + } + + return 0; +} diff --git a/net/socket.h b/net/socket.h new file mode 100644 index 000000000..ea46f02dd --- /dev/null +++ b/net/socket.h @@ -0,0 +1,33 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_SOCKET_H +#define QEMU_NET_SOCKET_H + +#include "net.h" +#include "qemu-common.h" + +int net_init_socket(QemuOpts *opts, Monitor *mon, + const char *name, VLANState *vlan); + +#endif /* QEMU_NET_SOCKET_H */ diff --git a/net/tap-linux.c b/net/tap-linux.c index 0f621a231..6af9e824d 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -52,6 +52,8 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required features & IFF_VNET_HDR) { *vnet_hdr = 1; ifr.ifr_flags |= IFF_VNET_HDR; + } else { + *vnet_hdr = 0; } if (vnet_hdr_required && !*vnet_hdr) { @@ -129,6 +131,11 @@ void tap_fd_set_offload(int fd, int csum, int tso4, { unsigned int offload = 0; + /* Check if our kernel supports TUNSETOFFLOAD */ + if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) { + return; + } + if (csum) { offload |= TUN_F_CSUM; if (tso4) diff --git a/net/tap-solaris.c b/net/tap-solaris.c index ef4e60c87..e14fe36fd 100644 --- a/net/tap-solaris.c +++ b/net/tap-solaris.c @@ -180,6 +180,17 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required return -1; } pstrcpy(ifname, ifname_size, dev); + if (*vnet_hdr) { + /* Solaris doesn't have IFF_VNET_HDR */ + *vnet_hdr = 0; + + if (vnet_hdr_required && !*vnet_hdr) { + qemu_error("vnet_hdr=1 requested, but no kernel " + "support for IFF_VNET_HDR available"); + close(fd); + return -1; + } + } fcntl(fd, F_SETFL, O_NONBLOCK); return fd; } diff --git a/net/tap-win32.c b/net/tap-win32.c index ea6647192..ef6378295 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -630,25 +630,24 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle, /********************************************/ typedef struct TAPState { - VLANClientState *vc; + VLANClientState nc; tap_win32_overlapped_t *handle; } TAPState; -static void tap_cleanup(VLANClientState *vc) +static void tap_cleanup(VLANClientState *nc) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL); /* FIXME: need to kill thread and close file handle: tap_win32_close(s); */ - qemu_free(s); } -static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); return tap_win32_write(s->handle, buf, size); } @@ -662,33 +661,41 @@ static void tap_win32_send(void *opaque) size = tap_win32_read(s->handle, &buf, max_size); if (size > 0) { - qemu_send_packet(s->vc, buf, size); + qemu_send_packet(&s->nc, buf, size); tap_win32_free_buffer(s->handle, buf); } } +static NetClientInfo net_tap_win32_info = { + .type = NET_CLIENT_TYPE_TAP, + .size = sizeof(TAPState), + .receive = tap_receive, + .cleanup = tap_cleanup, +}; + static int tap_win32_init(VLANState *vlan, const char *model, const char *name, const char *ifname) { + VLANClientState *nc; TAPState *s; + tap_win32_overlapped_t *handle; - s = qemu_mallocz(sizeof(TAPState)); - if (!s) - return -1; - if (tap_win32_open(&s->handle, ifname) < 0) { + if (tap_win32_open(&handle, ifname) < 0) { printf("tap: Could not open '%s'\n", ifname); return -1; } - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_TAP, - vlan, NULL, model, name, - NULL, tap_receive, - NULL, NULL, tap_cleanup, s); + nc = qemu_new_net_client(&net_tap_win32_info, vlan, NULL, model, name); - snprintf(s->vc->info_str, sizeof(s->vc->info_str), + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "tap: ifname=%s", ifname); + s = DO_UPCAST(TAPState, nc, nc); + + s->handle = handle; + qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s); + return 0; } @@ -47,7 +47,7 @@ #define TAP_BUFSIZE (4096 + 65536) typedef struct TAPState { - VLANClientState *vc; + VLANClientState nc; int fd; char down_script[1024]; char down_script_arg[128]; @@ -92,7 +92,7 @@ static void tap_writable(void *opaque) tap_write_poll(s, 0); - qemu_flush_queued_packets(s->vc); + qemu_flush_queued_packets(&s->nc); } static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt) @@ -111,10 +111,10 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt return len; } -static ssize_t tap_receive_iov(VLANClientState *vc, const struct iovec *iov, +static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov, int iovcnt) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); const struct iovec *iovp = iov; struct iovec iov_copy[iovcnt + 1]; struct virtio_net_hdr hdr = { 0, }; @@ -130,9 +130,9 @@ static ssize_t tap_receive_iov(VLANClientState *vc, const struct iovec *iov, return tap_write_packet(s, iovp, iovcnt); } -static ssize_t tap_receive_raw(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t size) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); struct iovec iov[2]; int iovcnt = 0; struct virtio_net_hdr hdr = { 0, }; @@ -150,13 +150,13 @@ static ssize_t tap_receive_raw(VLANClientState *vc, const uint8_t *buf, size_t s return tap_write_packet(s, iov, iovcnt); } -static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); struct iovec iov[1]; if (s->has_vnet_hdr && !s->using_vnet_hdr) { - return tap_receive_raw(vc, buf, size); + return tap_receive_raw(nc, buf, size); } iov[0].iov_base = (char *)buf; @@ -169,7 +169,7 @@ static int tap_can_send(void *opaque) { TAPState *s = opaque; - return qemu_can_send_packet(s->vc); + return qemu_can_send_packet(&s->nc); } #ifndef __sun__ @@ -179,9 +179,9 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) } #endif -static void tap_send_completed(VLANClientState *vc, ssize_t len) +static void tap_send_completed(VLANClientState *nc, ssize_t len) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); tap_read_poll(s, 1); } @@ -203,56 +203,56 @@ static void tap_send(void *opaque) size -= sizeof(struct virtio_net_hdr); } - size = qemu_send_packet_async(s->vc, buf, size, tap_send_completed); + size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed); if (size == 0) { tap_read_poll(s, 0); } - } while (size > 0 && qemu_can_send_packet(s->vc)); + } while (size > 0 && qemu_can_send_packet(&s->nc)); } -int tap_has_ufo(VLANClientState *vc) +int tap_has_ufo(VLANClientState *nc) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); - assert(vc->type == NET_CLIENT_TYPE_TAP); + assert(nc->info->type == NET_CLIENT_TYPE_TAP); return s->has_ufo; } -int tap_has_vnet_hdr(VLANClientState *vc) +int tap_has_vnet_hdr(VLANClientState *nc) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); - assert(vc->type == NET_CLIENT_TYPE_TAP); + assert(nc->info->type == NET_CLIENT_TYPE_TAP); return s->has_vnet_hdr; } -void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr) +void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); using_vnet_hdr = using_vnet_hdr != 0; - assert(vc->type == NET_CLIENT_TYPE_TAP); + assert(nc->info->type == NET_CLIENT_TYPE_TAP); assert(s->has_vnet_hdr == using_vnet_hdr); s->using_vnet_hdr = using_vnet_hdr; } -void tap_set_offload(VLANClientState *vc, int csum, int tso4, +void tap_set_offload(VLANClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); return tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo); } -static void tap_cleanup(VLANClientState *vc) +static void tap_cleanup(VLANClientState *nc) { - TAPState *s = vc->opaque; + TAPState *s = DO_UPCAST(TAPState, nc, nc); - qemu_purge_queued_packets(vc); + qemu_purge_queued_packets(nc); if (s->down_script[0]) launch_script(s->down_script, s->down_script_arg, s->fd); @@ -260,29 +260,37 @@ static void tap_cleanup(VLANClientState *vc) tap_read_poll(s, 0); tap_write_poll(s, 0); close(s->fd); - qemu_free(s); } /* fd support */ +static NetClientInfo net_tap_info = { + .type = NET_CLIENT_TYPE_TAP, + .size = sizeof(TAPState), + .receive = tap_receive, + .receive_raw = tap_receive_raw, + .receive_iov = tap_receive_iov, + .cleanup = tap_cleanup, +}; + static TAPState *net_tap_fd_init(VLANState *vlan, const char *model, const char *name, int fd, int vnet_hdr) { + VLANClientState *nc; TAPState *s; - s = qemu_mallocz(sizeof(TAPState)); + nc = qemu_new_net_client(&net_tap_info, vlan, NULL, model, name); + + s = DO_UPCAST(TAPState, nc, nc); + s->fd = fd; s->has_vnet_hdr = vnet_hdr != 0; s->using_vnet_hdr = 0; - s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_TAP, - vlan, NULL, model, name, NULL, - tap_receive, tap_receive_raw, - tap_receive_iov, tap_cleanup, s); s->has_ufo = tap_probe_has_ufo(s->fd); - tap_set_offload(s->vc, 0, 0, 0, 0, 0); + tap_set_offload(&s->nc, 0, 0, 0, 0, 0); tap_read_poll(s, 1); return s; } @@ -370,7 +378,7 @@ static int net_tap_init(QemuOpts *opts, int *vnet_hdr) int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) { TAPState *s; - int fd, vnet_hdr; + int fd, vnet_hdr = 0; if (qemu_opt_get(opts, "fd")) { if (qemu_opt_get(opts, "ifname") || @@ -415,7 +423,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan } if (qemu_opt_get(opts, "fd")) { - snprintf(s->vc->info_str, sizeof(s->vc->info_str), "fd=%d", fd); + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); } else { const char *ifname, *script, *downscript; @@ -423,7 +431,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan script = qemu_opt_get(opts, "script"); downscript = qemu_opt_get(opts, "downscript"); - snprintf(s->vc->info_str, sizeof(s->vc->info_str), + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "ifname=%s,script=%s,downscript=%s", ifname, script, downscript); diff --git a/net/util.c b/net/util.c new file mode 100644 index 000000000..1e9afbc1a --- /dev/null +++ b/net/util.c @@ -0,0 +1,60 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/util.h" +#include <errno.h> +#include <stdlib.h> + +int net_parse_macaddr(uint8_t *macaddr, const char *p) +{ + int i; + char *last_char; + long int offset; + + errno = 0; + offset = strtol(p, &last_char, 0); + if (errno == 0 && *last_char == '\0' && + offset >= 0 && offset <= 0xFFFFFF) { + macaddr[3] = (offset & 0xFF0000) >> 16; + macaddr[4] = (offset & 0xFF00) >> 8; + macaddr[5] = offset & 0xFF; + return 0; + } + + for (i = 0; i < 6; i++) { + macaddr[i] = strtol(p, (char **)&p, 16); + if (i == 5) { + if (*p != '\0') { + return -1; + } + } else { + if (*p != ':' && *p != '-') { + return -1; + } + p++; + } + } + + return 0; +} diff --git a/net/util.h b/net/util.h new file mode 100644 index 000000000..10c7da95f --- /dev/null +++ b/net/util.h @@ -0,0 +1,32 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_NET_UTIL_H +#define QEMU_NET_UTIL_H + +#include <stdint.h> + +int net_parse_macaddr(uint8_t *macaddr, const char *p); + +#endif /* QEMU_NET_UTIL_H */ diff --git a/net/vde.c b/net/vde.c new file mode 100644 index 000000000..42b463350 --- /dev/null +++ b/net/vde.c @@ -0,0 +1,135 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "net/vde.h" + +#include "config-host.h" + +#include <libvdeplug.h> + +#include "net.h" +#include "qemu-char.h" +#include "qemu-common.h" +#include "qemu-option.h" +#include "sysemu.h" + +typedef struct VDEState { + VLANClientState nc; + VDECONN *vde; +} VDEState; + +static void vde_to_qemu(void *opaque) +{ + VDEState *s = opaque; + uint8_t buf[4096]; + int size; + + size = vde_recv(s->vde, (char *)buf, sizeof(buf), 0); + if (size > 0) { + qemu_send_packet(&s->nc, buf, size); + } +} + +static ssize_t vde_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + VDEState *s = DO_UPCAST(VDEState, nc, nc); + ssize_t ret; + + do { + ret = vde_send(s->vde, (const char *)buf, size, 0); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +static void vde_cleanup(VLANClientState *nc) +{ + VDEState *s = DO_UPCAST(VDEState, nc, nc); + qemu_set_fd_handler(vde_datafd(s->vde), NULL, NULL, NULL); + vde_close(s->vde); +} + +static NetClientInfo net_vde_info = { + .type = NET_CLIENT_TYPE_VDE, + .size = sizeof(VDEState), + .receive = vde_receive, + .cleanup = vde_cleanup, +}; + +static int net_vde_init(VLANState *vlan, const char *model, + const char *name, const char *sock, + int port, const char *group, int mode) +{ + VLANClientState *nc; + VDEState *s; + VDECONN *vde; + char *init_group = (char *)group; + char *init_sock = (char *)sock; + + struct vde_open_args args = { + .port = port, + .group = init_group, + .mode = mode, + }; + + vde = vde_open(init_sock, (char *)"QEMU", &args); + if (!vde){ + return -1; + } + + nc = qemu_new_net_client(&net_vde_info, vlan, NULL, model, name); + + snprintf(nc->info_str, sizeof(nc->info_str), "sock=%s,fd=%d", + sock, vde_datafd(vde)); + + s = DO_UPCAST(VDEState, nc, nc); + + s->vde = vde; + + qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s); + + return 0; +} + +int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +{ + const char *sock; + const char *group; + int port, mode; + + sock = qemu_opt_get(opts, "sock"); + group = qemu_opt_get(opts, "group"); + + port = qemu_opt_get_number(opts, "port", 0); + mode = qemu_opt_get_number(opts, "mode", 0700); + + if (net_vde_init(vlan, "vde", name, sock, port, group, mode) == -1) { + return -1; + } + + if (vlan) { + vlan->nb_host_devs++; + } + + return 0; +} diff --git a/net/vde.h b/net/vde.h new file mode 100644 index 000000000..3e6ca3e87 --- /dev/null +++ b/net/vde.h @@ -0,0 +1,36 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_VDE_H +#define QEMU_NET_VDE_H + +#include "qemu-common.h" +#include "qemu-option.h" + +#ifdef CONFIG_VDE + +int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan); + +#endif /* CONFIG_VDE */ + +#endif /* QEMU_NET_VDE_H */ @@ -37,6 +37,16 @@ (type *) ((char *) __mptr - offsetof(type, member));}) #endif +/* Convert from a base type to a parent type, with compile time checking. */ +#ifdef __GNUC__ +#define DO_UPCAST(type, field, dev) ( __extension__ ( { \ + char __attribute__((unused)) offset_must_be_zero[ \ + -offsetof(type, field)]; \ + container_of(dev, type, field);})) +#else +#define DO_UPCAST(type, field, dev) container_of(dev, type, field) +#endif + #define typeof_field(type, field) typeof(((type *)0)->field) #define type_check(t1,t2) ((t1*)0 - (t2*)0) diff --git a/qemu-config.c b/qemu-config.c index e7eb020c0..6dd173120 100644 --- a/qemu-config.c +++ b/qemu-config.c @@ -65,6 +65,9 @@ QemuOptsList qemu_drive_opts = { .name = "serial", .type = QEMU_OPT_STRING, },{ + .name = "rerror", + .type = QEMU_OPT_STRING, + },{ .name = "werror", .type = QEMU_OPT_STRING, },{ diff --git a/qemu-option.c b/qemu-option.c index 49efd392d..b00910900 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -481,7 +481,7 @@ struct QemuOpt { }; struct QemuOpts { - const char *id; + char *id; QemuOptsList *list; QTAILQ_HEAD(QemuOptHead, QemuOpt) head; QTAILQ_ENTRY(QemuOpts) next; @@ -686,6 +686,7 @@ void qemu_opts_del(QemuOpts *opts) qemu_opt_del(opt); } QTAILQ_REMOVE(&opts->list->head, opts, next); + qemu_free(opts->id); qemu_free(opts); } diff --git a/qemu-queue.h b/qemu-queue.h index 8877efd7b..1d077458c 100644 --- a/qemu-queue.h +++ b/qemu-queue.h @@ -1,8 +1,9 @@ -/* $NetBSD: queue.h,v 1.45.14.1 2007/07/18 20:13:24 liamjfoy Exp $ */ +/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */ /* * Qemu version: Copy from netbsd, removed debug code, removed some of - * the implementations. Left in lists, tail queues and circular queues. + * the implementations. Left in lists, simple queues, tail queues and + * circular queues. */ /* @@ -40,8 +41,8 @@ #define QEMU_SYS_QUEUE_H_ /* - * This file defines three types of data structures: - * lists, tail queues, and circular queues. + * This file defines four types of data structures: + * lists, simple queues, tail queues, and circular queues. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked @@ -50,6 +51,13 @@ * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to @@ -140,6 +148,99 @@ struct { \ /* + * Simple queue definitions. + */ +#define QSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define QSIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define QSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define QSIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL)\ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + QSIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +#define QSIMPLEQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->sqh_first); \ + (var) && ((next = ((var)->field.sqe_next)), 1); \ + (var) = (next)) + +#define QSIMPLEQ_CONCAT(head1, head2) do { \ + if (!QSIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + QSIMPLEQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_LAST(head, type, field) \ + (QSIMPLEQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->sqh_last) - offsetof(struct type, field)))) + +/* + * Simple queue access methods. + */ +#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define QSIMPLEQ_FIRST(head) ((head)->sqh_first) +#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* * Tail queue definitions. */ #define Q_TAILQ_HEAD(name, type, qual) \ diff --git a/qemu-tool.c b/qemu-tool.c index b35ea8e1c..18b48af31 100644 --- a/qemu-tool.c +++ b/qemu-tool.c @@ -56,6 +56,10 @@ int get_async_context_id(void) return 0; } +void monitor_protocol_event(MonitorEvent event, QObject *data) +{ +} + QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) { QEMUBH *bh; @@ -120,24 +120,24 @@ static int announce_self_create(uint8_t *buf, return 60; /* len (FCS will be added by hardware) */ } -static void qemu_announce_self_once(void *opaque) +static void qemu_announce_self_iter(NICState *nic, void *opaque) { - int i, len; - VLANState *vlan; - VLANClientState *vc; uint8_t buf[60]; + int len; + + len = announce_self_create(buf, nic->conf->macaddr.a); + + qemu_send_packet_raw(&nic->nc, buf, len); +} + + +static void qemu_announce_self_once(void *opaque) +{ static int count = SELF_ANNOUNCE_ROUNDS; QEMUTimer *timer = *(QEMUTimer **)opaque; - for (i = 0; i < MAX_NICS; i++) { - if (!nd_table[i].used) - continue; - len = announce_self_create(buf, nd_table[i].macaddr); - vlan = nd_table[i].vlan; - QTAILQ_FOREACH(vc, &vlan->clients, next) { - qemu_send_packet_raw(vc, buf, len); - } - } + qemu_foreach_nic(qemu_announce_self_iter, NULL); + if (--count) { /* delay 50ms, 150ms, 250ms, ... */ qemu_mod_timer(timer, qemu_get_clock(rt_clock) + @@ -982,13 +982,27 @@ const VMStateInfo vmstate_info_buffer = { static int get_unused_buffer(QEMUFile *f, void *pv, size_t size) { - qemu_fseek(f, size, SEEK_CUR); - return 0; + uint8_t buf[1024]; + int block_len; + + while (size > 0) { + block_len = MIN(sizeof(buf), size); + size -= block_len; + qemu_get_buffer(f, buf, block_len); + } + return 0; } static void put_unused_buffer(QEMUFile *f, void *pv, size_t size) { - qemu_fseek(f, size, SEEK_CUR); + static const uint8_t buf[1024]; + int block_len; + + while (size > 0) { + block_len = MIN(sizeof(buf), size); + size -= block_len; + qemu_put_buffer(f, buf, block_len); + } } const VMStateInfo vmstate_info_unused_buffer = { @@ -1152,7 +1166,14 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, field->version_id <= version_id)) { void *base_addr = opaque + field->offset; int ret, i, n_elems = 1; + int size = field->size; + if (field->flags & VMS_VBUFFER) { + size = *(int32_t *)(opaque+field->size_offset); + if (field->flags & VMS_MULTIPLY) { + size *= field->size; + } + } if (field->flags & VMS_ARRAY) { n_elems = field->num; } else if (field->flags & VMS_VARRAY_INT32) { @@ -1161,10 +1182,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, n_elems = *(uint16_t *)(opaque+field->num_offset); } if (field->flags & VMS_POINTER) { - base_addr = *(void **)base_addr; + base_addr = *(void **)base_addr + field->start; } for (i = 0; i < n_elems; i++) { - void *addr = base_addr + field->size * i; + void *addr = base_addr + size * i; if (field->flags & VMS_ARRAY_OF_POINTER) { addr = *(void **)addr; @@ -1172,7 +1193,7 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, if (field->flags & VMS_STRUCT) { ret = vmstate_load_state(f, field->vmsd, addr, field->vmsd->version_id); } else { - ret = field->info->get(f, addr, field->size); + ret = field->info->get(f, addr, size); } if (ret < 0) { @@ -1201,7 +1222,14 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, field->field_exists(opaque, vmsd->version_id)) { void *base_addr = opaque + field->offset; int i, n_elems = 1; + int size = field->size; + if (field->flags & VMS_VBUFFER) { + size = *(int32_t *)(opaque+field->size_offset); + if (field->flags & VMS_MULTIPLY) { + size *= field->size; + } + } if (field->flags & VMS_ARRAY) { n_elems = field->num; } else if (field->flags & VMS_VARRAY_INT32) { @@ -1210,15 +1238,18 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, n_elems = *(uint16_t *)(opaque+field->num_offset); } if (field->flags & VMS_POINTER) { - base_addr = *(void **)base_addr; + base_addr = *(void **)base_addr + field->start; } for (i = 0; i < n_elems; i++) { - void *addr = base_addr + field->size * i; + void *addr = base_addr + size * i; + if (field->flags & VMS_ARRAY_OF_POINTER) { + addr = *(void **)addr; + } if (field->flags & VMS_STRUCT) { vmstate_save_state(f, field->vmsd, addr); } else { - field->info->put(f, addr, field->size); + field->info->put(f, addr, size); } } } @@ -1256,7 +1287,8 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se) #define QEMU_VM_SECTION_END 0x03 #define QEMU_VM_SECTION_FULL 0x04 -int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared) +int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, + int shared) { SaveStateEntry *se; @@ -1288,16 +1320,18 @@ int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared) qemu_put_be32(f, se->instance_id); qemu_put_be32(f, se->version_id); - se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque); + se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque); } - if (qemu_file_has_error(f)) + if (qemu_file_has_error(f)) { + qemu_savevm_state_cancel(mon, f); return -EIO; + } return 0; } -int qemu_savevm_state_iterate(QEMUFile *f) +int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; int ret = 1; @@ -1310,19 +1344,28 @@ int qemu_savevm_state_iterate(QEMUFile *f) qemu_put_byte(f, QEMU_VM_SECTION_PART); qemu_put_be32(f, se->section_id); - ret &= !!se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque); + ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque); + if (!ret) { + /* Do not proceed to the next vmstate before this one reported + completion of the current stage. This serializes the migration + and reduces the probability that a faster changing state is + synchronized over and over again. */ + break; + } } if (ret) return 1; - if (qemu_file_has_error(f)) + if (qemu_file_has_error(f)) { + qemu_savevm_state_cancel(mon, f); return -EIO; + } return 0; } -int qemu_savevm_state_complete(QEMUFile *f) +int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f) { SaveStateEntry *se; @@ -1334,7 +1377,7 @@ int qemu_savevm_state_complete(QEMUFile *f) qemu_put_byte(f, QEMU_VM_SECTION_END); qemu_put_be32(f, se->section_id); - se->save_live_state(f, QEMU_VM_SECTION_END, se->opaque); + se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque); } QTAILQ_FOREACH(se, &savevm_handlers, entry) { @@ -1366,7 +1409,18 @@ int qemu_savevm_state_complete(QEMUFile *f) return 0; } -int qemu_savevm_state(QEMUFile *f) +void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f) +{ + SaveStateEntry *se; + + QTAILQ_FOREACH(se, &savevm_handlers, entry) { + if (se->save_live_state) { + se->save_live_state(mon, f, -1, se->opaque); + } + } +} + +static int qemu_savevm_state(Monitor *mon, QEMUFile *f) { int saved_vm_running; int ret; @@ -1376,17 +1430,17 @@ int qemu_savevm_state(QEMUFile *f) bdrv_flush_all(); - ret = qemu_savevm_state_begin(f, 0, 0); + ret = qemu_savevm_state_begin(mon, f, 0, 0); if (ret < 0) goto out; do { - ret = qemu_savevm_state_iterate(f); + ret = qemu_savevm_state_iterate(mon, f); if (ret < 0) goto out; } while (ret == 0); - ret = qemu_savevm_state_complete(f); + ret = qemu_savevm_state_complete(mon, f); out: if (qemu_file_has_error(f)) @@ -1675,7 +1729,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Could not open VM state file\n"); goto the_end; } - ret = qemu_savevm_state(f); + ret = qemu_savevm_state(mon, f); vm_state_size = qemu_ftell(f); qemu_fclose(f); if (ret < 0) { @@ -63,10 +63,11 @@ void qemu_announce_self(void); void main_loop_wait(int timeout); -int qemu_savevm_state_begin(QEMUFile *f, int blk_enable, int shared); -int qemu_savevm_state_iterate(QEMUFile *f); -int qemu_savevm_state_complete(QEMUFile *f); -int qemu_savevm_state(QEMUFile *f); +int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, + int shared); +int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f); +int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f); +void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f); int qemu_loadvm_state(QEMUFile *f); void qemu_errors_to_file(FILE *fp); @@ -181,7 +182,8 @@ typedef struct DriveInfo { int bus; int unit; QemuOpts *opts; - BlockInterfaceErrorAction onerror; + BlockInterfaceErrorAction on_read_error; + BlockInterfaceErrorAction on_write_error; char serial[BLOCK_SERIAL_STRLEN + 1]; QTAILQ_ENTRY(DriveInfo) next; } DriveInfo; @@ -199,7 +201,9 @@ extern DriveInfo *drive_get_by_id(const char *id); extern int drive_get_max_bus(BlockInterfaceType type); extern void drive_uninit(DriveInfo *dinfo); extern const char *drive_get_serial(BlockDriverState *bdrv); -extern BlockInterfaceErrorAction drive_get_onerror(BlockDriverState *bdrv); + +extern BlockInterfaceErrorAction drive_get_on_error( + BlockDriverState *bdrv, int is_read); BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type); @@ -138,6 +138,7 @@ int main(int argc, char **argv) #include "hw/loader.h" #include "bt-host.h" #include "net.h" +#include "net/slirp.h" #include "monitor.h" #include "console.h" #include "sysemu.h" @@ -1972,16 +1973,17 @@ const char *drive_get_serial(BlockDriverState *bdrv) return "\0"; } -BlockInterfaceErrorAction drive_get_onerror(BlockDriverState *bdrv) +BlockInterfaceErrorAction drive_get_on_error( + BlockDriverState *bdrv, int is_read) { DriveInfo *dinfo; QTAILQ_FOREACH(dinfo, &drives, next) { if (dinfo->bdrv == bdrv) - return dinfo->onerror; + return is_read ? dinfo->on_read_error : dinfo->on_write_error; } - return BLOCK_ERR_STOP_ENOSPC; + return is_read ? BLOCK_ERR_REPORT : BLOCK_ERR_STOP_ENOSPC; } static void bdrv_format_print(void *opaque, const char *name) @@ -1997,6 +1999,23 @@ void drive_uninit(DriveInfo *dinfo) qemu_free(dinfo); } +static int parse_block_error_action(const char *buf, int is_read) +{ + if (!strcmp(buf, "ignore")) { + return BLOCK_ERR_IGNORE; + } else if (!is_read && !strcmp(buf, "enospc")) { + return BLOCK_ERR_STOP_ENOSPC; + } else if (!strcmp(buf, "stop")) { + return BLOCK_ERR_STOP_ANY; + } else if (!strcmp(buf, "report")) { + return BLOCK_ERR_REPORT; + } else { + fprintf(stderr, "qemu: '%s' invalid %s error action\n", + buf, is_read ? "read" : "write"); + return -1; + } +} + DriveInfo *drive_init(QemuOpts *opts, void *opaque, int *fatal_error) { @@ -2016,7 +2035,8 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque, int cache; int aio = 0; int ro = 0; - int bdrv_flags, onerror; + int bdrv_flags; + int on_read_error, on_write_error; const char *devaddr; DriveInfo *dinfo; int is_extboot = 0; @@ -2184,22 +2204,28 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque, return NULL; } - onerror = BLOCK_ERR_STOP_ENOSPC; + on_write_error = BLOCK_ERR_STOP_ENOSPC; if ((buf = qemu_opt_get(opts, "werror")) != NULL) { if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO) { fprintf(stderr, "werror is no supported by this format\n"); return NULL; } - if (!strcmp(buf, "ignore")) - onerror = BLOCK_ERR_IGNORE; - else if (!strcmp(buf, "enospc")) - onerror = BLOCK_ERR_STOP_ENOSPC; - else if (!strcmp(buf, "stop")) - onerror = BLOCK_ERR_STOP_ANY; - else if (!strcmp(buf, "report")) - onerror = BLOCK_ERR_REPORT; - else { - fprintf(stderr, "qemu: '%s' invalid write error action\n", buf); + + on_write_error = parse_block_error_action(buf, 0); + if (on_write_error < 0) { + return NULL; + } + } + + on_read_error = BLOCK_ERR_REPORT; + if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { + if (1) { + fprintf(stderr, "rerror is no supported by this format\n"); + return NULL; + } + + on_read_error = parse_block_error_action(buf, 1); + if (on_read_error < 0) { return NULL; } } @@ -2283,7 +2309,8 @@ DriveInfo *drive_init(QemuOpts *opts, void *opaque, dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; - dinfo->onerror = onerror; + dinfo->on_read_error = on_read_error; + dinfo->on_write_error = on_write_error; dinfo->opts = opts; if (serial) strncpy(dinfo->serial, serial, sizeof(serial)); @@ -2930,7 +2957,7 @@ static int ram_save_block(QEMUFile *f) return found; } -static uint64_t bytes_transferred = 0; +static uint64_t bytes_transferred; static ram_addr_t ram_save_remaining(void) { @@ -2960,19 +2987,26 @@ uint64_t ram_bytes_total(void) return last_ram_offset; } -static int ram_save_live(QEMUFile *f, int stage, void *opaque) +static int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) { ram_addr_t addr; uint64_t bytes_transferred_last; double bwidth = 0; uint64_t expected_time = 0; + if (stage < 0) { + cpu_physical_memory_set_dirty_tracking(0); + return 0; + } + if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) { qemu_file_set_error(f); return 0; } if (stage == 1) { + bytes_transferred = 0; + /* Make sure all dirty bits are set */ for (addr = 0; addr < last_ram_offset; addr += TARGET_PAGE_SIZE) { if (!cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) @@ -3049,8 +3083,12 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) madvise(qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE, MADV_DONTNEED); } #endif - } else if (flags & RAM_SAVE_FLAG_PAGE) + } else if (flags & RAM_SAVE_FLAG_PAGE) { qemu_get_buffer(f, qemu_get_ram_ptr(addr), TARGET_PAGE_SIZE); + } + if (qemu_file_has_error(f)) { + return -EIO; + } } while (!(flags & RAM_SAVE_FLAG_EOS)); return 0; @@ -4118,9 +4156,12 @@ static void main_loop(void) #endif } while (vm_can_run()); - if (qemu_debug_requested()) + if (qemu_debug_requested()) { + monitor_protocol_event(EVENT_DEBUG, NULL); vm_stop(EXCP_DEBUG); + } if (qemu_shutdown_requested()) { + monitor_protocol_event(EVENT_SHUTDOWN, NULL); if (no_shutdown) { vm_stop(0); no_shutdown = 0; @@ -4128,15 +4169,19 @@ static void main_loop(void) break; } if (qemu_reset_requested()) { + monitor_protocol_event(EVENT_RESET, NULL); pause_all_vcpus(); qemu_system_reset(); resume_all_vcpus(); } if (qemu_powerdown_requested()) { + monitor_protocol_event(EVENT_POWERDOWN, NULL); qemu_irq_raise(qemu_system_powerdown); } - if ((r = qemu_vmstop_requested())) + if ((r = qemu_vmstop_requested())) { + monitor_protocol_event(EVENT_STOP, NULL); vm_stop(r); + } } pause_all_vcpus(); } |