From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=37474 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OanOS-0000q2-Jk for qemu-devel@nongnu.org; Mon, 19 Jul 2010 06:16:17 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OanOR-0000hU-0e for qemu-devel@nongnu.org; Mon, 19 Jul 2010 06:16:16 -0400 Received: from mx1.redhat.com ([209.132.183.28]:9684) by eggs.gnu.org with esmtp (Exim 4.69) (envelope-from ) id 1OanOQ-0000gK-Ko for qemu-devel@nongnu.org; Mon, 19 Jul 2010 06:16:14 -0400 Received: from int-mx03.intmail.prod.int.phx2.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o6JAGEdH024174 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 19 Jul 2010 06:16:14 -0400 Received: from localhost (vpn1-5-94.ams2.redhat.com [10.36.5.94]) by int-mx03.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o6JAGDgh019698 for ; Mon, 19 Jul 2010 06:16:13 -0400 Date: Mon, 19 Jul 2010 11:16:12 +0100 From: "Richard W.M. Jones" Message-ID: <20100719101612.GC5216@amd.home.annexia.org> References: <20100717133930.GC19767@amd.home.annexia.org> <20100719101504.GA5216@amd.home.annexia.org> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="qlTNgmc+xy1dBmNv" Content-Disposition: inline In-Reply-To: <20100719101504.GA5216@amd.home.annexia.org> Subject: [Qemu-devel] [PATCH 2/2 version 2] fw_cfg: Allow guest to read kernel etc via fast, synchronous "DMA"-type operation. List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --qlTNgmc+xy1dBmNv Content-Type: text/plain; charset=us-ascii Content-Disposition: inline -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/ --qlTNgmc+xy1dBmNv Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="0002-fw_cfg-Allow-guest-to-read-kernel-etc-via-fast-synch.patch" >>From 55d4700262253da52aa403dc1ba68da2ae91b084 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Sat, 17 Jul 2010 14:30:46 +0100 Subject: [PATCH 2/2] fw_cfg: Allow guest to read kernel etc via fast, synchronous "DMA"-type operation. This adds a "DMA" operation for rapidly copying the kernel, initrd etc into the guest. The guest sets up a DMA address and size and then issues the usual read operation but with the FW_CFG_DMA bit set on the entry number. QEmu then just copies the whole config entry to the selected physical address synchronously. This saves some time when loading large images. This change is backwards compatible. ROMs using the old method will work unchanged. Signed-off-by: Richard W.M. Jones --- hw/fw_cfg.c | 22 +++++++++++++++++++++- hw/fw_cfg.h | 8 ++++++-- pc-bios/optionrom/linuxboot.S | 8 ++++---- pc-bios/optionrom/optionrom.h | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 37e6f1f..798a332 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -55,6 +55,13 @@ struct FWCfgState { uint32_t cur_offset; }; +/* Target address and size for DMA operations. This is only used + * during boot and across 32 and 64 bit architectures, so only writes + * to lower 4GB addresses are supported. + */ +static uint32_t dma_addr = 0; +static uint32_t dma_size = 0; + static void fw_cfg_write(FWCfgState *s, uint8_t value) { int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); @@ -98,7 +105,16 @@ static uint8_t fw_cfg_read(FWCfgState *s) if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len) ret = 0; - else + else if (s->cur_entry & FW_CFG_DMA) { + if (dma_size > e->len - s->cur_offset) + dma_size = e->len - s->cur_offset; + + cpu_physical_memory_write ((target_phys_addr_t) dma_addr, + &e->data[s->cur_offset], + dma_size); + s->cur_offset += e->len; + ret = 1; + } else ret = e->data[s->cur_offset++]; FW_CFG_DPRINTF("read %d\n", ret); @@ -351,6 +367,10 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); + fw_cfg_add_bytes(s, FW_CFG_DMA_ADDR | FW_CFG_WRITE_CHANNEL, + (uint8_t *)&dma_addr, sizeof dma_addr); + fw_cfg_add_bytes(s, FW_CFG_DMA_SIZE | FW_CFG_WRITE_CHANNEL, + (uint8_t *)&dma_size, sizeof dma_size); return s; } diff --git a/hw/fw_cfg.h b/hw/fw_cfg.h index 4d13a4f..44b2be5 100644 --- a/hw/fw_cfg.h +++ b/hw/fw_cfg.h @@ -30,11 +30,15 @@ #define FW_CFG_FILE_FIRST 0x20 #define FW_CFG_FILE_SLOTS 0x10 -#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS) +#define FW_CFG_DMA_ADDR 0x30 +#define FW_CFG_DMA_SIZE 0x31 +#define FW_CFG_MAX_ENTRY (FW_CFG_DMA_SIZE+1) + +#define FW_CFG_DMA 0x2000 #define FW_CFG_WRITE_CHANNEL 0x4000 #define FW_CFG_ARCH_LOCAL 0x8000 -#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) +#define FW_CFG_ENTRY_MASK ~(FW_CFG_DMA | FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) #define FW_CFG_INVALID 0xffff diff --git a/pc-bios/optionrom/linuxboot.S b/pc-bios/optionrom/linuxboot.S index c109363..dbf44cb 100644 --- a/pc-bios/optionrom/linuxboot.S +++ b/pc-bios/optionrom/linuxboot.S @@ -106,10 +106,10 @@ copy_kernel: /* We're now running in 16-bit CS, but 32-bit ES! */ /* Load kernel and initrd */ - read_fw_blob_addr32(FW_CFG_KERNEL) - read_fw_blob_addr32(FW_CFG_INITRD) - read_fw_blob_addr32(FW_CFG_CMDLINE) - read_fw_blob_addr32(FW_CFG_SETUP) + read_fw_blob_dma(FW_CFG_KERNEL) + read_fw_blob_dma(FW_CFG_INITRD) + read_fw_blob_dma(FW_CFG_CMDLINE) + read_fw_blob_dma(FW_CFG_SETUP) /* And now jump into Linux! */ mov $0, %eax diff --git a/pc-bios/optionrom/optionrom.h b/pc-bios/optionrom/optionrom.h index fbdd48a..7fffe2d 100644 --- a/pc-bios/optionrom/optionrom.h +++ b/pc-bios/optionrom/optionrom.h @@ -50,6 +50,27 @@ bswap %eax .endm +/* + * Write %eax to a variable in the fw_cfg device. + * In: %eax + * Clobbers: %edx + */ +.macro write_fw VAR + push %eax + mov $(\VAR|FW_CFG_WRITE_CHANNEL), %ax + mov $BIOS_CFG_IOPORT_CFG, %dx + outw %ax, (%dx) + pop %eax + mov $BIOS_CFG_IOPORT_DATA, %dx + outb %al, (%dx) + shr $8, %eax + outb %al, (%dx) + shr $8, %eax + outb %al, (%dx) + shr $8, %eax + outb %al, (%dx) +.endm + #define read_fw_blob_pre(var) \ read_fw var ## _ADDR; \ mov %eax, %edi; \ @@ -87,6 +108,22 @@ */ \ .dc.b 0x67,0xf3,0x6c +/* + * Fast DMA of data from fw_cfg device into physical memory. + * This should be a straight replacement for read_fw_blob and + * read_fw_blob_addr32. + */ +#define read_fw_blob_dma(var) \ + read_fw var ## _ADDR; \ + write_fw FW_CFG_DMA_ADDR; \ + read_fw var ## _SIZE; \ + write_fw FW_CFG_DMA_SIZE; \ + mov $(var ## _DATA|FW_CFG_DMA), %ax; \ + mov $BIOS_CFG_IOPORT_CFG, %edx; \ + outw %ax, (%dx); \ + mov $BIOS_CFG_IOPORT_DATA, %dx; \ + inb (%dx), %al + #define OPTION_ROM_START \ .code16; \ .text; \ -- 1.7.1 --qlTNgmc+xy1dBmNv--