lguest: Patches for 2.6.24 (and patchbomb test)
Subsystems (10)
cover lguest: Patches for 2.6.24 (and patchbomb test)
Hi all,
These are the patches I'm planning to submit for 2.6.24. Comments
gratefully accepted. Along with the usual cleanups and improvements are Jes' de-i386-ification patches, and a new "virtio" mechanism designed to be shared with KVM (and hopefully other hypervisors).
Cheers,
Rusty.
Documentation/lguest/Makefile | 30 Documentation/lguest/lguest.c | 1745 ++++++++++++----------- Documentation/lguest/lguest.txt | 67 a/drivers/block/lguest_blk.c | 423 ----- a/drivers/char/hvc_lguest.c | 177 -- a/drivers/lguest/io.c | 628 -------- a/drivers/lguest/lguest.c | 1101 -------------- a/drivers/lguest/lguest_asm.S | 94 - a/drivers/lguest/lguest_bus.c | 220 -- a/drivers/lguest/switcher.S | 351 ---- a/drivers/net/lguest_net.c | 552 ------- a/include/linux/lguest_bus.h | 51 arch/i386/Kconfig | 48 arch/i386/Makefile | 3 arch/i386/kernel/asm-offsets.c | 1 arch/i386/kernel/i8259.c | 3 arch/i386/kernel/io_apic.c | 13 arch/i386/kernel/traps.c | 10 arch/i386/lguest/Kconfig | 6 arch/i386/lguest/boot.c | 79 - arch/i386/lguest/head.S | 41 arch/i386/xen/Kconfig | 5 b/arch/i386/lguest/Kconfig | 8 b/arch/i386/lguest/Makefile | 1 b/arch/i386/lguest/boot.c | 1101 ++++++++++++++ b/arch/i386/lguest/head.S | 93 + b/drivers/block/virtio_blk.c | 327 ++++ b/drivers/char/virtio_console.c | 225 ++ b/drivers/lguest/i386_core.c | 476 ++++++ b/drivers/lguest/i386_switcher.S | 350 ++++ b/drivers/lguest/lguest_device.c | 373 ++++ b/drivers/net/virtio_net.c | 438 +++++ b/drivers/virtio/Kconfig | 3 b/drivers/virtio/Makefile | 1 b/drivers/virtio/config.c | 13 b/drivers/virtio/virtio.c | 171 ++ b/drivers/virtio/virtio_ring.c | 313 ++++ b/include/asm-i386/lguest.h | 88 + b/include/linux/virtio.h | 110 + b/include/linux/virtio_blk.h | 52 b/include/linux/virtio_config.h | 112 + b/include/linux/virtio_console.h | 13 b/include/linux/virtio_net.h | 37 b/include/linux/virtio_ring.h | 120 + drivers/Kconfig | 4 drivers/Makefile | 1 drivers/block/Kconfig | 6 drivers/block/Makefile | 2 drivers/char/Kconfig | 8 drivers/char/Makefile | 2 drivers/char/hvc_console.c | 67 drivers/kvm/Kconfig | 7 drivers/lguest/Kconfig | 17 drivers/lguest/Makefile | 20 drivers/lguest/core.c | 588 ------- drivers/lguest/hypercalls.c | 228 +-- drivers/lguest/i386_core.c | 127 + drivers/lguest/i386_switcher.S | 6 drivers/lguest/interrupts_and_traps.c | 131 + drivers/lguest/io.c | 28 drivers/lguest/lg.h | 198 -- drivers/lguest/lguest.c | 1 drivers/lguest/lguest_asm.S | 7 drivers/lguest/lguest_bus.c | 3 drivers/lguest/lguest_user.c | 162 -- drivers/lguest/page_tables.c | 288 ++- drivers/lguest/segments.c | 31 drivers/net/Kconfig | 6 drivers/net/Makefile | 3 drivers/net/lguest_net.c | 7 drivers/virtio/Kconfig | 5 drivers/virtio/Makefile | 1 drivers/virtio/virtio.c | 26 include/asm-i386/irq.h | 4 include/asm-i386/lguest.h | 4 include/asm-i386/lguest_hcall.h | 14 include/linux/Kbuild | 1 include/linux/lguest.h | 88 - include/linux/lguest_launcher.h | 134 - include/linux/mod_devicetable.h | 6 linux-2.6-lguest/include/asm-i386/lguest_hcall.h | 67 scripts/mod/file2alias.c | 19 82 files changed, 6455 insertions(+), 5935 deletions(-)
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 1/43 lguest: lguest example launcher truncates block device file to 0 length on problems
The function should also use ftruncate64() rather than ftruncate() to prevent files over 4GB (not uncommon for a root filesystem) being zeroed.
Documentation/lguest/lguest.c +1 -1
@@ -882,7 +882,7 @@ static u32 handle_block_output(int fd, c* of the block file (possibly extending it). */ if (off + len > device_len) {/* Trim it back to the correct length */ -ftruncate (dev->fd, device_len); +ftruncate64 (dev->fd, device_len);/* Die, bad Guest, die. */ errx (1 ,"Write past end %llu+%u" , off, len); }
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 2/43 lguest: fix modules oopsing in lguest guests
In reply to [email protected]The assembly templates for lguest guest patching are in the .init.text section. This means that modules get patched with "cc cc cc cc" or similar junk.
--- drivers/lguest/lguest_asm.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
===================================================================
drivers/lguest/lguest_asm.S +3 -3
@@ -22,8 +22,9 @@ jmp lguest_init /*G:055 We create a macro which puts the assembler code between lgstart_ and - * lgend_ markers. These templates end up in the .init.text section, so they - * are discarded after boot. */ + * lgend_ markers. These templates are put in the .text section: they can't be + * discarded after boot as we may need to patch modules, too. */ +.text #define LGUEST_PATCH(name, insns...) \ lgstart_##name: insns; lgend_##name:; \ .globl lgstart_##name; .globl lgend_##name
@@ -34,7 +35,6 @@ LGUEST_PATCH(pushf, movl lguest_data+LGU LGUEST_PATCH(pushf, movl lguest_data+LGUEST_DATA_irq_enabled, %eax) /*:*/ -.text /* These demark the EIP range where host should never deliver interrupts. */ .global lguest_noirq_start .global lguest_noirq_end
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 3/43 lguest: Normalize config options for guest support
In reply to [email protected]1) Group all the "guest OS" support options together, under a PARAVIRT_GUEST menu.
2) Make those options select CONFIG_PARAVIRT, as suggested by Andi.
3) Make kconfig help titles consistent.
--- arch/i386/Kconfig | 38 ++++++++++++++++++++++++++++---------- arch/i386/xen/Kconfig | 5 +++-- drivers/lguest/Kconfig | 3 +-- 3 files changed, 32 insertions(+), 14 deletions(-)
===================================================================
arch/i386/Kconfig +28 -10
@@ -215,27 +215,45 @@ endchoice endchoice config PARAVIRT - bool "Paravirtualization support (EXPERIMENTAL)" - depends on EXPERIMENTAL + bool depends on !(X86_VISWS || X86_VOYAGER) help - Paravirtualization is a way of running multiple instances of - Linux on the same machine, under a hypervisor. This option - changes the kernel so it can modify itself when it is run - under a hypervisor, improving performance significantly. - However, when run without a hypervisor the kernel is - theoretically slower. If in doubt, say N. + This changes the kernel so it can modify itself when it is run + under a hypervisor, potentially improving performance significantly + over full virtualization. However, when run without a hypervisor + the kernel is theoretically slower and slightly larger. + +menuconfig PARAVIRT_GUEST + bool "Paravirtualized guest support" + help + Say Y here to get to see options related to running Linux under + various hypervisors. This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if PARAVIRT_GUEST source "arch/i386/xen/Kconfig" config VMI - bool "VMI Paravirt-ops support" - depends on PARAVIRT + bool "VMI Guest support" + select PARAVIRT + depends on !(X86_VISWS || X86_VOYAGER) help VMI provides a paravirtualized interface to the VMware ESX server (it could be used by other hypervisors in theory too, but is not at the moment), by linking the kernel to a GPL-ed ROM module provided by the hypervisor. + +config LGUEST_GUEST + bool "Lguest guest support" + select PARAVIRT + depends on !X86_PAE + help + Lguest is a tiny in-kernel hypervisor. Selecting this will + allow your kernel to boot under lguest. This option will increase + your kernel size by about 6k. If in doubt, say N. +endif config ACPI_SRAT bool
===================================================================
arch/i386/xen/Kconfig +3 -2
@@ -3,8 +3,9 @@ # config XEN - bool "Enable support for Xen hypervisor" - depends on PARAVIRT && X86_CMPXCHG && X86_TSC && !NEED_MULTIPLE_NODES + bool "Xen guest support" + select PARAVIRT + depends on X86_CMPXCHG && X86_TSC && !NEED_MULTIPLE_NODES && !(X86_VISWS || X86_VOYAGER) help This is the Linux Xen port. Enabling this will allow the kernel to boot in a paravirtualized environment under the
===================================================================
drivers/lguest/Kconfig +1 -2
@@ -1,7 +1,6 @@ config LGUEST config LGUEST tristate "Linux hypervisor example code" - depends on X86 && PARAVIRT && EXPERIMENTAL && !X86_PAE && FUTEX - select LGUEST_GUEST + depends on X86 && EXPERIMENTAL && !X86_PAE && FUTEX && !(X86_VISWS || X86_VOYAGER) select HVC_DRIVER ---help--- This is a very simple module which allows you to run
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 4/43 lguest: Consolidate host virtualization support under Virtualization menu
In reply to [email protected]Move lguest under the virtualization menu.
--- drivers/Kconfig | 2 -- drivers/kvm/Kconfig | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-)
===================================================================
drivers/Kconfig +0 -2
@@ -87,6 +87,4 @@ source "drivers/kvm/Kconfig" source "drivers/kvm/Kconfig" source "drivers/uio/Kconfig" - -source "drivers/lguest/Kconfig" endmenu
===================================================================
drivers/kvm/Kconfig +5 -1
@@ -45,4 +36,8 @@ config KVM_AMD Provides support for KVM on AMD processors equipped with the AMD-V (SVM) extensions. -endif # VIRTUALIZATION +# OK, it's a little counter-intuitive to do this, but it puts it neatly under +# the virtualization menu. +source drivers/lguest/Kconfig + +endif # VIRTUALIZATION
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 5/43 lguest: Example launcher should include asm/e820.h instead of asm-i386/
In reply to [email protected]--- Documentation/lguest/lguest.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
===================================================================
Documentation/lguest/lguest.c +1 -1
@@ -46,7 +46,7 @@ typedef uint32_t u32;typedef uint16_t u16 ;typedef uint8_t u8 ;#include "../../include/linux/lguest_launcher.h" -#include "../../include/asm-i386/e820.h" +#include "../../include/asm/e820.h" /*:*/ #define PAGE_PRESENT 0x7/* Present, RW, Execute */
Signature · 7 lines
1.4.4.4 -- -- there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 6/43 lguest: turn err into errx in lguest call sites
In reply to [email protected]These two callsites should really be errx instead of err, since there is no errno associated with them in the moment they are issued.
--- Documentation/lguest/lguest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
===================================================================
Documentation/lguest/lguest.c +2 -2
@@ -159,7 +159,7 @@ static unsigned long entry_point(void *sif (memcmp (p,"GenuineLguest" ,strlen ("GenuineLguest" )) ==0 )return (long )p +strlen ("GenuineLguest" ) + page_offset; -err (1 ,"Is this image a genuine lguest?" ); +errx (1 ,"Is this image a genuine lguest?" ); }/* This routine takes an open vmlinux image, which is in ELF, and maps it into
@@ -858,7 +858,7 @@ static u32 handle_block_output(int fd, c/* We first check that the read or write is within the length of the * block file. */ if (off >= device_len) -err (1 ,"Bad offset %llu vs %llu" , off, device_len); +errx (1 ,"Bad offset %llu vs %llu" , off, device_len);/* Move to the right location in the block file. This shouldn't fail, * but best to check. */ if (lseek64 (dev->fd, off,SEEK_SET ) != off)
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 7/43 lguest: Use copy_to_user() not put_user for struct timespec
In reply to [email protected]Use copy_to_user() when copying a struct timespec to the guest - put_user() cannot handle two long's in one go on a 64bit arch.
--- drivers/lguest/hypercalls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
===================================================================
drivers/lguest/hypercalls.c +1 -1
@@ -243,6 +243,6 @@ void write_timestamp(struct lguest *lg) {struct timespec now;ktime_get_real_ts (&now); -if (put_user (now, &lg->lguest_data->time)) +if (copy_to_user (&lg->lguest_data->time, &now,sizeof (struct timespec )))kill_guest (lg,"Writing timestamp" ); }
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 8/43 lguest: Lguest currently depends on 32-bit x86, not just x86.
In reply to [email protected]--- drivers/lguest/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
===================================================================
drivers/lguest/Kconfig +1 -1
@@ -1,6 +1,6 @@ config LGUEST config LGUEST tristate "Linux hypervisor example code" - depends on X86 && EXPERIMENTAL && !X86_PAE && FUTEX && !(X86_VISWS || X86_VOYAGER) + depends on X86_32 && EXPERIMENTAL && !X86_PAE && FUTEX && !(X86_VISWS || X86_VOYAGER) select HVC_DRIVER ---help--- This is a very simple module which allows you to run
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 9/43 lguest: lguest.txt update
In reply to [email protected]o Describe the new split configurations
o Highlight code documentation in drivers/lguest/README
o Point out necessity of having a getty on /dev/hvc0
o Remove gratuitous "m" in example
o Don't discuss I/O model here, stick to user documentation.
--- Documentation/lguest/lguest.txt | 66 ++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 38 deletions(-)
===================================================================
Documentation/lguest/lguest.txt +28 -38
@@ -6,7 +6,7 @@ Linux developers and users to experiment Linux developers and users to experiment with virtualization with the minimum of complexity. Nonetheless, it should have sufficient features to make it useful for specific tasks, and, of course, you are -encouraged to fork and enhance it. +encouraged to fork and enhance it (see drivers/lguest/README). Features:
@@ -23,19 +23,30 @@ Developer features: Running Lguest: -- Lguest runs the same kernel as guest and host. You can configure - them differently, but usually it's easiest not to. +- The easiest way to run lguest is to use same kernel as guest and host. + You can configure them differently, but usually it's easiest not to. You will need to configure your kernel with the following options: - CONFIG_HIGHMEM64G=n ("High Memory Support" "64GB")[1] - CONFIG_TUN=y/m ("Universal TUN/TAP device driver support") - CONFIG_EXPERIMENTAL=y ("Prompt for development and/or incomplete code/drivers") - CONFIG_PARAVIRT=y ("Paravirtualization support (EXPERIMENTAL)") - CONFIG_LGUEST=y/m ("Linux hypervisor example code") + "General setup": + "Prompt for development and/or incomplete code/drivers" = Y + (CONFIG_EXPERIMENTAL=y) - and I recommend: - CONFIG_HZ=100 ("Timer frequency")[2] + "Processor type and features": + "Paravirtualized guest support" = Y + "Lguest guest support" = Y + "High Memory Support" = off/4GB + "Alignment value to which kernel should be aligned" = 0x100000 + (CONFIG_PARAVIRT=y, CONFIG_LGUEST_GUEST=y, CONFIG_HIGHMEM64G=n and + CONFIG_PHYSICAL_ALIGN=0x100000) + + "Device Drivers": + "Network device support" + "Universal TUN/TAP device driver support" = M/Y + (CONFIG_TUN=m) + "Virtualization" + "Linux hypervisor example code" = M/Y + (CONFIG_LGUEST=m) - A tool called "lguest" is available in this directory: type "make" to build it. If you didn't build your kernel in-tree, use "make
@@ -51,14 +61,17 @@ Running Lguest: dd if=/dev/zero of=rootfile bs=1M count=2048 qemu -cdrom image.iso -hda rootfile -net user -net nic -boot d + Make sure that you install a getty on /dev/hvc0 if you want to log in on the + console! + - "modprobe lg" if you built it as a module. - Run an lguest as root: - Documentation/lguest/lguest 64m vmlinux --tunnet=192.168.19.1 --block=rootfile root=/dev/lgba + Documentation/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 --block=rootfile root=/dev/lgba Explanation: - 64m: the amount of memory to use. + 64: the amount of memory to use, in MB. vmlinux: the kernel image found in the top of your build directory. You can also use a standard bzImage.
@@ -99,31 +112,7 @@ Running Lguest: "--sharenet=<filename>": any two guests using the same file are on the same network. This file is created if it does not exist. -Lguest I/O model: +There is a helpful mailing list at http://ozlabs.org/mailman/listinfo/lguest -Lguest uses a simplified DMA model plus shared memory for I/O. Guests -can communicate with each other if they share underlying memory -(usually by the lguest program mmaping the same file), but they can -use any non-shared memory to communicate with the lguest process. - -Guests can register DMA buffers at any key (must be a valid physical -address) using the LHCALL_BIND_DMA(key, dmabufs, num<<8|irq) -hypercall. "dmabufs" is the physical address of an array of "num" -"struct lguest_dma": each contains a used_len, and an array of -physical addresses and lengths. When a transfer occurs, the -"used_len" field of one of the buffers which has used_len 0 will be -set to the length transferred and the irq will fire. - -Using an irq value of 0 unbinds the dma buffers. - -To send DMA, the LHCALL_SEND_DMA(key, dma_physaddr) hypercall is used, -and the bytes used is written to the used_len field. This can be 0 if -noone else has bound a DMA buffer to that key or some other error. -DMA buffers bound by the same guest are ignored. - -Cheers! +Good luck! Rusty Russell [email protected]. - -[1] These are on various places on the TODO list, waiting for you to - get annoyed enough at the limitation to fix it. -[2] Lguest is not yet tickless when idle. See [1].
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 10/43 lguest: Make lguest_launcher.h types userspace-friendly
In reply to [email protected]lguest_launcher.h uses "u32" not "__u32", which sets a bad example. Fix that, and include <linux/types.h>.
This means we need to use -I on the Launcher build line so types.h is found.
(Note: the example launcher also includes asm/e820.h which uses u32/u64, but at least this cleans up the lguest code).
--- Documentation/lguest/Makefile | 2 +- Documentation/lguest/lguest.c | 4 ++-- include/linux/lguest_launcher.h | 21 +++++++++++---------- 3 files changed, 14 insertions(+), 13 deletions(-)
===================================================================
Documentation/lguest/Makefile +1 -1
@@ -11,7 +11,7 @@ include $(KBUILD_OUTPUT)/.config include $(KBUILD_OUTPUT)/.config LGUEST_GUEST_TOP := ($(CONFIG_PAGE_OFFSET) - 0x08000000) -CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -Wl,-T,lguest.lds +CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include -Wl,-T,lguest.lds LDLIBS:=-lz # Removing this works for some versions of ld.so (eg. Ubuntu Feisty) and # not others (eg. FC7).
===================================================================
Documentation/lguest/lguest.c +2 -2
@@ -45,8 +45,8 @@ typedef uint32_t u32;typedef uint32_t u32 ;typedef uint16_t u16 ;typedef uint8_t u8 ; -#include "../../include/linux/lguest_launcher.h" -#include "../../include/asm/e820.h" +#include "linux/lguest_launcher.h" +#include "asm/e820.h" /*:*/ #define PAGE_PRESENT 0x7/* Present, RW, Execute */
===================================================================
include/linux/lguest_launcher.h +11 -10
@@ -1,6 +1,7 @@#ifndef _ASM_LGUEST_USER#define _ASM_LGUEST_USER/* Everything the "lguest" userspace program needs to know. */ +#include <linux/types.h> /* They can register up to 32 arrays of lguest_dma. */ #define LGUEST_MAX_DMA 32/* At most we can dma 16 lguest_dma in one op. */
@@ -37,9 +38,9 @@ struct lguest_dmastruct lguest_dma {/* 0 if free to be used, filled by the Host. */ -u32 used_len; +__u32 used_len; +__u16 len[LGUEST_MAX_DMA_SECTIONS ];unsigned long addr[LGUEST_MAX_DMA_SECTIONS ]; -u16 len[LGUEST_MAX_DMA_SECTIONS ]; };/*:*/
@@ -52,11 +53,11 @@ struct lguest_block_page {/* 0 is a read, 1 is a write. */ int type; -u32 sector;/* Offset in device = sector * 512. */ -u32 bytes;/* Length expected to be read/written in bytes */ +__u32 sector;/* Offset in device = sector * 512. */ +__u32 bytes;/* Length expected to be read/written in bytes */ /* 0 = pending, 1 = done, 2 = done, error */ int result; -u32 num_sectors;/* Disk length = num_sectors * 512 */ +__u32 num_sectors;/* Disk length = num_sectors * 512 */ };/*D:520 The network device is basically a memory page where all the Guests on
@@ -86,21 +87,21 @@ struct lguest_net*/ struct lguest_device_desc {/* The device type: console, network, disk etc. */ -u16 type; +__u16 type;#define LGUEST_DEVICE_T_CONSOLE 1#define LGUEST_DEVICE_T_NET 2#define LGUEST_DEVICE_T_BLOCK 3/* The specific features of this device: these depends on device type * except for LGUEST_DEVICE_F_RANDOMNESS. */ -u16 features; +__u16 features;#define LGUEST_NET_F_NOCSUM 0x4000/* Don't bother checksumming */ #define LGUEST_DEVICE_F_RANDOMNESS 0x8000/* IRQ is fairly random */ /* This is how the Guest reports status of the device: the Host can set * LGUEST_DEVICE_S_REMOVED to indicate removal, but the rest are only * ever manipulated by the Guest, and only ever set. */ -u16 status; +__u16 status;/* 256 and above are device specific. */ #define LGUEST_DEVICE_S_ACKNOWLEDGE 1/* We have seen device. */ #define LGUEST_DEVICE_S_DRIVER 2/* We have found a driver */
@@ -111,8 +112,8 @@ struct lguest_device_desc {/* Each device exists somewhere in Guest physical memory, over some * number of pages. */ -u16 num_pages; -u32 pfn; +__u16 num_pages; +__u32 pfn; };/*:*/
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 11/43 lguest: lguest_devices belongs in lguest_bus.c: its not i386-specific.
In reply to [email protected]--- drivers/lguest/lguest.c | 1 - drivers/lguest/lguest_bus.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-)
===================================================================
drivers/lguest/lguest.c +0 -1
@@ -87,7 +87,6 @@ struct lguest_data lguest_data = { .noirq_end = (u32)lguest_noirq_end, .blocked_interrupts = { 1 }, /* Block timer interrupts */ }; -struct lguest_device_desc *lguest_devices;static cycle_t clock_base;/*G:035 Notice the lazy_hcall() above, rather than hcall(). This is our first
===================================================================
drivers/lguest/lguest_bus.c +2 -0
@@ -6,6 +6,8 @@#include <linux/lguest_bus.h> #include <asm/io.h> #include <asm/paravirt.h> + +struct lguest_device_desc *lguest_devices;static ssize_t type_show (struct device *_dev,struct device_attribute *attr,char *buf)
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 12/43 lguest: Only start khvcd when someone uses hvc_console driver.
In reply to [email protected]This changes hvc_init() to be called only when someone actually uses the hvc_console driver.
hvc_console used to only be for pSeries and iSeries: now lguest and Xen both want it built-in in case the kernel is a guest under one of those, even though usually it will be a native boot.
--- drivers/char/Kconfig | 4 +- drivers/char/hvc_console.c | 66 ++++++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 23 deletions(-)
drivers/char/Kconfig +2 -2
@@ -568,8 +568,8 @@ config HVC_DRIVER config HVC_DRIVER bool help - Users of pSeries machines that want to utilize the hvc console front-end - module for their backend console driver should select this option. + Generic "hypervisor virtual console" infrastructure for various + hypervisors (pSeries, iSeries, Xen, lguest). It will automatically be selected if one of the back-end console drivers is selected.
drivers/char/hvc_console.c +45 -21
103 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 13/43 lguest: Move lguest hcalls to arch-specific header
In reply to [email protected]Move architecture specific portion of lg_hcall code to asm-i386/lg_hcall.h and have it included from linux/lguest.h.
[Changed to asm-i386/lguest_hcall.h so documentation finds it -RR]
--- include/asm-i386/lguest_hcall.h | 67 +++++++++++++++++++++++++++++++++++++ include/linux/lguest.h | 69 ++------------------------------------- 2 files changed, 71 insertions(+), 65 deletions(-)
===================================================================
include/asm-i386/lguest_hcall.h new file +67 -0
@@ -0,0 +1,67 @@ +/* Architecture specific portion of the lguest hypercalls */ +#ifndef _I386_LGUEST_HCALL_H +#define _I386_LGUEST_HCALL_H + +#define LHCALL_FLUSH_ASYNC 0 +#define LHCALL_LGUEST_INIT 1 +#define LHCALL_CRASH 2 +#define LHCALL_LOAD_GDT 3 +#define LHCALL_NEW_PGTABLE 4 +#define LHCALL_FLUSH_TLB 5 +#define LHCALL_LOAD_IDT_ENTRY 6 +#define LHCALL_SET_STACK 7 +#define LHCALL_TS 8 +#define LHCALL_SET_CLOCKEVENT 9 +#define LHCALL_HALT 10 +#define LHCALL_BIND_DMA 12 +#define LHCALL_SEND_DMA 13 +#define LHCALL_SET_PTE 14 +#define LHCALL_SET_PMD 15 +#define LHCALL_LOAD_TLS 16 + +/*G:031 First, how does our Guest contact the Host to ask for privileged +* operations? There are two ways: the direct way is to make a "hypercall", +* to make requests of the Host Itself. +* +* Our hypercall mechanism uses the highest unused trap code (traps 32 and +* above are used by real hardware interrupts). Seventeen hypercalls are +* available: the hypercall number is put in the %eax register, and the +* arguments (when required) are placed in %edx, %ebx and %ecx. If a return +* value makes sense, it's returned in %eax. +* +* Grossly invalid calls result in Sudden Death at the hands of the vengeful +* Host, rather than returning failure. This reflects Winston Churchill's +* definition of a gentleman: "someone who is only rude intentionally". */ +#define LGUEST_TRAP_ENTRY 0x1F + +static inline unsigned long +hcall (unsigned long call, +unsigned long arg1,unsigned long arg2,unsigned long arg3) +{ +/* "int" is the Intel instruction to trigger a trap. */ + asm volatile("int $" __stringify(LGUEST_TRAP_ENTRY) + /* The call is in %eax (aka "a"), and can be replaced */ + : "=a"(call) + /* The other arguments are in %eax, %edx, %ebx & %ecx */ + : "a"(call), "d"(arg1), "b"(arg2), "c"(arg3) + /* "memory" means this might write somewhere in memory. + * This isn't true for all calls, but it's safe to tell + * gcc that it might happen so it doesn't get clever. */ + : "memory"); +return call; +} +/*:*/ + +void async_hcall (unsigned long call, +unsigned long arg1,unsigned long arg2,unsigned long arg3); + +/* Can't use our min() macro here: needs to be a constant */ +#define LGUEST_IRQS (NR_IRQS < 32 ? NR_IRQS: 32) + +#define LHCALL_RING_SIZE 64 +struct hcall_ring +{ +u32 eax, edx, ebx, ecx; +}; + +#endif /* _I386_LGUEST_HCALL_H */
===================================================================
include/linux/lguest.h +4 -65
84 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 14/43 lguest: Move lguest guest support to arch/i386 where it logically belongs.
In reply to [email protected]--- arch/i386/Kconfig | 10 arch/i386/Makefile | 3 arch/i386/lguest/Kconfig | 8 arch/i386/lguest/Makefile | 1 arch/i386/lguest/boot.c | 1101 +++++++++++++++++++++++++++++++++++++++++++ arch/i386/lguest/head.S | 93 +++ drivers/lguest/Makefile | 4 drivers/lguest/lguest.c | 1101 ------------------------------------------- drivers/lguest/lguest_asm.S | 93 --- 9 files changed, 1210 insertions(+), 1204 deletions(-)
===================================================================
arch/i386/Kconfig +2 -8
@@ -245,14 +245,8 @@ config VMI at the moment), by linking the kernel to a GPL-ed ROM module provided by the hypervisor. -config LGUEST_GUEST - bool "Lguest guest support" - select PARAVIRT - depends on !X86_PAE - help - Lguest is a tiny in-kernel hypervisor. Selecting this will - allow your kernel to boot under lguest. This option will increase - your kernel size by about 6k. If in doubt, say N. +source "arch/i386/lguest/Kconfig" + endif config ACPI_SRAT
===================================================================
arch/i386/Makefile +3 -0
@@ -96,6 +96,9 @@ core-$(CONFIG_X86_ES7000) := arch/i386/m # Xen paravirtualization support core-$(CONFIG_XEN) += arch/i386/xen/ +# lguest paravirtualization support +core-$(CONFIG_LGUEST_GUEST) += arch/i386/lguest/ + # default subarch .h files mflags-y += -Iinclude/asm-i386/mach-default
===================================================================
arch/i386/lguest/Kconfig new file +8 -0
@@ -0,0 +1,8 @@ +config LGUEST_GUEST + bool "Lguest guest support" + select PARAVIRT + depends on !X86_PAE + help + Lguest is a tiny in-kernel hypervisor. Selecting this will + allow your kernel to boot under lguest. This option will increase + your kernel size by about 6k. If in doubt, say N.
===================================================================
arch/i386/lguest/Makefile new file +1 -0
@@ -0,0 +1,1 @@ +obj-y := head.o boot.o
===================================================================
arch/i386/lguest/boot.c new file +1101 -0
1,101 lines not on this page · raw message
===================================================================
arch/i386/lguest/head.S new file +93 -0
93 lines not on this page · raw message
===================================================================
drivers/lguest/Makefile +2 -2
@@ -1,5 +1,5 @@ -# Guest requires the paravirt_ops replacement and the bus driver. -obj-$(CONFIG_LGUEST_GUEST) += lguest.o lguest_asm.o lguest_bus.o +# Guest requires the bus driver. +obj-$(CONFIG_LGUEST_GUEST) += lguest_bus.o # Host requires the other files, which can be a module. obj-$(CONFIG_LGUEST) += lg.o
===================================================================
drivers/lguest/lguest.c deleted +0 -1101
1,101 lines not on this page · raw message
===================================================================
drivers/lguest/lguest_asm.S deleted +0 -93
93 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 15/43 lguest: Rename switcher.S to i386_switcher.S, since its very i386-specific.
In reply to [email protected]--- drivers/lguest/Makefile | 6 drivers/lguest/i386_switcher.S | 350 ++++++++++++++++++++++++++++++++++++++++ drivers/lguest/switcher.S | 350 ---------------------------------------- 3 files changed, 354 insertions(+), 352 deletions(-)
===================================================================
drivers/lguest/Makefile +4 -2
@@ -1,8 +1,10 @@ # Host requires the other files, which can be a module. obj-$(CONFIG_LGUEST) += lg.o -lg-y := core.o hypercalls.o page_tables.o interrupts_and_traps.o \ - segments.o io.o lguest_user.o switcher.o +lg-y = core.o hypercalls.o page_tables.o interrupts_and_traps.o \ + segments.o io.o lguest_user.o + +lg-$(CONFIG_X86_32) += i386_switcher.o Preparation Preparation!: PREFIX=P Guest: PREFIX=G
===================================================================
drivers/lguest/i386_switcher.S new file +350 -0
350 lines not on this page · raw message
===================================================================
drivers/lguest/switcher.S deleted +0 -350
350 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 16/43 lguest: Accept elf files that are valid but have sections that can not be mmaped for some reason.
In reply to [email protected]--- Documentation/lguest/lguest.c | 61 +++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 29 deletions(-)
===================================================================
Documentation/lguest/lguest.c +32 -29
@@ -15,6 +15,7 @@#include <stdlib.h> #include <elf.h> #include <sys/mman.h> +#include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h>
@@ -162,6 +163,30 @@ static unsigned long entry_point(void *serrx (1 ,"Is this image a genuine lguest?" ); } +/* This routine is used to load the kernel or initrd. It tries mmap, but if +* that fails (Plan 9's kernel file isn't nicely aligned on page boundaries), +* it falls back to reading the memory in. */ +static void map_at (int fd,void *addr,unsigned long offset,unsigned long len) +{ +ssize_t r; + +/* We map writable even though for some segments are marked read-only. +* The kernel really wants to be writable: it patches its own +* instructions. +* +* MAP_PRIVATE means that the page won't be copied until a write is +* done to it. This allows us to share untouched memory between +* Guests. */ +if (mmap (addr, len,PROT_READ |PROT_WRITE |PROT_EXEC , +MAP_FIXED |MAP_PRIVATE , fd, offset) !=MAP_FAILED ) +return ; + +/* pread does a seek and a read in one shot: saves a few lines. */ + r =pread (fd, addr, len, offset); +if (r != len) +err (1 ,"Reading offset %lu len %lu gave %zi" , offset, len, r); +} +/* This routine takes an open vmlinux image, which is in ELF, and maps it into * the Guest memory. ELF = Embedded Linking Format, which is the format used * by all modern binaries on Linux including the kernel.
@@ -176,7 +201,6 @@ static unsigned long map_elf(int elf_fd,static unsigned long map_elf (int elf_fd,const Elf32_Ehdr *ehdr,unsigned long *page_offset) { -void *addr;Elf32_Phdr phdr[ehdr->e_phnum];unsigned int i;unsigned long start =-1UL , end =0 ;
@@ -227,23 +251,9 @@ static unsigned long map_elf(int elf_fd,if (phdr[i].p_paddr + phdr[i].p_filesz > end) end = phdr[i].p_paddr + phdr[i].p_filesz; -/* We map this section of the file at its physical address. We -* map it read & write even if the header says this segment is -* read-only. The kernel really wants to be writable: it -* patches its own instructions which would normally be -* read-only. -* -* MAP_PRIVATE means that the page won't be copied until a -* write is done to it. This allows us to share much of the -* kernel memory between Guests. */ - addr =mmap ((void *)phdr[i].p_paddr, - phdr[i].p_filesz, -PROT_READ |PROT_WRITE |PROT_EXEC , -MAP_FIXED |MAP_PRIVATE , - elf_fd, phdr[i].p_offset); -if (addr != (void *)phdr[i].p_paddr) -err (1 ,"Mmaping vmlinux seg %i gave %p not %p" , - i, addr, (void *)phdr[i].p_paddr); +/* We map this section of the file at its physical address. */ +map_at (elf_fd, (void *)phdr[i].p_paddr, + phdr[i].p_offset, phdr[i].p_filesz); }return entry_point ((void *)start, (void *)end, *page_offset);
@@ -402,27 +412,20 @@ static unsigned long load_initrd(const cint ifd;struct stat st;unsigned long len; -void *iaddr; ifd =open_or_die (name,O_RDONLY );/* fstat() is needed to get the file size. */ if (fstat (ifd, &st) <0 )err (1 ,"fstat() on initrd '%s'" , name); -/* The length needs to be rounded up to a page size: mmap needs the -* address to be page aligned. */ +/* We map the initrd at the top of memory, but mmap wants it to be +* page-aligned, so we round the size up for that. */ len =page_align (st.st_size); -/* We map the initrd at the top of memory. */ - iaddr =mmap ((void *)mem - len, st.st_size, -PROT_READ |PROT_EXEC |PROT_WRITE , -MAP_FIXED |MAP_PRIVATE , ifd,0 ); -if (iaddr != (void *)mem - len) -err (1 ,"Mmaping initrd '%s' returned %p not %p" , - name, iaddr, (void *)mem - len); +map_at (ifd, (void *)mem - len,0 , st.st_size);/* Once a file is mapped, you can close the file descriptor. It's a * little odd, but quite useful. */ close (ifd); -verbose ("mapped initrd %s size=%lu @ %p\n" , name, st.st_size, iaddr); +verbose ("mapped initrd %s size=%lu @ %p\n" , name, len, (void *)mem-len);/* We return the initrd size. */ return len;
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 17/43 lguest: Introduce guest mem offset, static link example launcher
In reply to [email protected]In order to avoid problematic special linking of the Launcher, we give the Host an offset: this means we can use any memory region in the Launcher as Guest memory rather than insisting on mmap() at 0.
The result is quite pleasing: a number of casts are replaced with simple additions.
--- Documentation/lguest/Makefile | 28 ----- Documentation/lguest/lguest.c | 195 +++++++++++++++++++++++------------------ drivers/lguest/core.c | 22 ++-- drivers/lguest/hypercalls.c | 15 +-- drivers/lguest/io.c | 18 ++- drivers/lguest/lg.h | 3 drivers/lguest/lguest_user.c | 23 ++-- drivers/lguest/page_tables.c | 7 + 8 files changed, 167 insertions(+), 144 deletions(-)
===================================================================
Documentation/lguest/Makefile +4 -24
@@ -1,28 +1,8 @@ # This creates the demonstration utility "lguest" which runs a Linux guest. +CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include +LDLIBS:=-lz -# For those people that have a separate object dir, look there for .config -KBUILD_OUTPUT := ../.. -ifdef O - ifeq ("$(origin O)", "command line") - KBUILD_OUTPUT := $(O) - endif -endif -# We rely on CONFIG_PAGE_OFFSET to know where to put lguest binary. -include $(KBUILD_OUTPUT)/.config -LGUEST_GUEST_TOP := ($(CONFIG_PAGE_OFFSET) - 0x08000000) - -CFLAGS:=-Wall -Wmissing-declarations -Wmissing-prototypes -O3 -I../../include -Wl,-T,lguest.lds -LDLIBS:=-lz -# Removing this works for some versions of ld.so (eg. Ubuntu Feisty) and -# not others (eg. FC7). -LDFLAGS+=-static -all: lguest.lds lguest - -# The linker script on x86 is so complex the only way of creating one -# which will link our binary in the right place is to mangle the -# default one. -lguest.lds: - $(LD) --verbose | awk '/^==========/ { PRINT=1; next; } /SIZEOF_HEADERS/ { gsub(/0x[0-9A-F]*/, "$(LGUEST_GUEST_TOP)") } { if (PRINT) print $$0; }' > $@ +all: lguest clean: - rm -f lguest.lds lguest + rm -f lguest
===================================================================
Documentation/lguest/lguest.c +113 -82
368 lines not on this page · raw message
===================================================================
drivers/lguest/core.c +10 -12
@@ -325,8 +325,8 @@ static int emulate_insn(struct lguest *l* Dealing With Guest Memory. * * When the Guest gives us (what it thinks is) a physical address, we can use -* the normal copy_from_user() & copy_to_user() on that address: remember, -* Guest physical == Launcher virtual. +* the normal copy_from_user() & copy_to_user() on the corresponding place in +* the memory region allocated by the Launcher. * * But we can't trust the Guest: it might be trying to access the Launcher * code. We have to check that the range is below the pfn_limit the Launcher
@@ -348,8 +348,8 @@ u32 lgread_u32(struct lguest *lg, unsign/* Don't let them access lguest binary. */ if (!lguest_address_ok (lg, addr,sizeof (val)) - ||get_user (val, (u32 __user *)addr) !=0 ) -kill_guest (lg,"bad read address %#lx" , addr); + ||get_user (val, (u32 *)(lg->mem_base + addr)) !=0 ) +kill_guest (lg,"bad read address %#lx: pfn_limit=%u membase=%p" , addr, lg->pfn_limit, lg->mem_base);return val; }
@@ -357,7 +357,7 @@ void lgwrite_u32(struct lguest *lg, unsivoid lgwrite_u32 (struct lguest *lg,unsigned long addr,u32 val) {if (!lguest_address_ok (lg, addr,sizeof (val)) - ||put_user (val, (u32 __user *)addr) !=0 ) + ||put_user (val, (u32 *)(lg->mem_base + addr)) !=0 )kill_guest (lg,"bad write address %#lx" , addr); }
@@ -367,7 +367,7 @@ void lgread(struct lguest *lg, void *b,void lgread (struct lguest *lg,void *b,unsigned long addr,unsigned bytes) {if (!lguest_address_ok (lg, addr, bytes) - ||copy_from_user (b, (void __user *)addr, bytes) !=0 ) { + ||copy_from_user (b, lg->mem_base + addr, bytes) !=0 ) {/* copy_from_user should do this, but as we rely on it... */ memset (b,0 , bytes);kill_guest (lg,"bad read address %#lx len %u" , addr, bytes);
@@ -379,7 +379,7 @@ void lgwrite(struct lguest *lg, unsigned unsigned bytes) {if (!lguest_address_ok (lg, addr, bytes) - ||copy_to_user ((void __user *)addr, b, bytes) !=0 ) + ||copy_to_user (lg->mem_base + addr, b, bytes) !=0 )kill_guest (lg,"bad write address %#lx len %u" , addr, bytes); }/* (end of memory access helper routines) :*/
@@ -616,11 +616,9 @@ int run_guest(struct lguest *lg, unsigne* * Note that if the Guest were really messed up, this * could happen before it's done the INITIALIZE -* hypercall, so lg->lguest_data will be NULL, so -* &lg->lguest_data->cr2 will be address 8. Writing -* into that address won't hurt the Host at all, -* though. */ -if (put_user (cr2, &lg->lguest_data->cr2)) +* hypercall, so lg->lguest_data will be NULL */ +if (lg->lguest_data + &&put_user (cr2, &lg->lguest_data->cr2))kill_guest (lg,"Writing cr2" );break ;case 7 :/* We've intercepted a Device Not Available fault. */
===================================================================
drivers/lguest/hypercalls.c +9 -6
@@ -205,16 +205,19 @@ static void initialize(struct lguest *lg tsc_speed =0 ;/* The pointer to the Guest's "struct lguest_data" is the only -* argument. */ - lg->lguest_data = (struct lguest_data __user *)lg->regs->edx; -/* If we check the address they gave is OK now, we can simply -* copy_to_user/from_user from now on rather than using lgread/lgwrite. -* I put this in to show that I'm not immune to writing stupid -* optimizations. */ +* argument. We check that address now. */ if (!lguest_address_ok (lg, lg->regs->edx,sizeof (*lg->lguest_data))) {kill_guest (lg,"bad guest page %p" , lg->lguest_data);return ; } + +/* Having checked it, we simply set lg->lguest_data to point straight +* into the Launcher's memory at the right place and then use +* copy_to_user/from_user from now on, instead of lgread/write. I put +* this in to show that I'm not immune to writing stupid +* optimizations. */ + lg->lguest_data = lg->mem_base + lg->regs->edx; +/* The Guest tells us where we're not to deliver interrupts by putting * the range of addresses into "struct lguest_data". */ if (get_user(lg->noirq_start, &lg->lguest_data->noirq_start)
===================================================================
drivers/lguest/io.c +10 -8
@@ -186,7 +186,7 @@ int bind_dma(struct lguest *lg,* we're doing this. */ mutex_lock (&lguest_lock);down_read (fshared); -if (get_futex_key ((u32 __user *)ukey, fshared, &key) !=0 ) { +if (get_futex_key (lg->mem_base + ukey, fshared, &key) !=0 ) {kill_guest (lg,"bad dma key %#lx" , ukey); goto unlock; }
@@ -247,7 +247,8 @@ static int lgread_other(struct lguest *l void *buf, u32 addr, unsigned bytes) { if (!lguest_address_ok(lg, addr, bytes) - || access_process_vm(lg->tsk, addr, buf, bytes, 0) != bytes) { + || access_process_vm(lg->tsk, (unsigned long)lg->mem_base + addr, + buf, bytes, 0) != bytes) { memset(buf, 0, bytes);kill_guest (lg,"bad address in registered DMA struct" );return 0 ;
@@ -261,8 +262,8 @@ static int lgwrite_other(struct lguest * const void *buf, unsigned bytes) { if (!lguest_address_ok(lg, addr, bytes) - || (access_process_vm(lg->tsk, addr, (void *)buf, bytes, 1) - != bytes)) { + || access_process_vm(lg->tsk, (unsigned long)lg->mem_base + addr, + (void *)buf, bytes, 1) != bytes) { kill_guest(lg, "bad address writing to registered DMA");return 0 ; }
@@ -318,7 +319,7 @@ static u32 copy_data(struct lguest *srcl* copy_to_user_page(), and some arch's seem to need special * flushes. x86 is fine. */ if (copy_from_user (maddr + (dst->addr[di] + dstoff)%PAGE_SIZE , - (void __user *)src->addr[si], len) !=0 ) { + srclg->mem_base+src->addr[si], len) !=0 ) {/* If a copy failed, it's the source's fault. */ kill_guest (srclg,"bad address in sending DMA" ); totlen =0 ;
@@ -377,7 +378,8 @@ static u32 do_dma(struct lguest *srclg,* number of pages. Note that we're holding the destination's * mmap_sem, as get_user_pages() requires. */ if (get_user_pages (dstlg->tsk, dstlg->mm, - dst->addr[i],1 ,1 ,1 , pages+i,NULL ) + (unsigned long )dstlg->mem_base+dst->addr[i], +1 ,1 ,1 , pages+i,NULL ) !=1 ) {/* This means the destination gave us a bogus buffer */ kill_guest (dstlg,"Error mapping DMA pages" );
@@ -493,7 +495,7 @@ again:mutex_lock (&lguest_lock);down_read (fshared);/* Get the futex key for the key the Guest gave us */ -if (get_futex_key ((u32 __user *)ukey, fshared, &key) !=0 ) { +if (get_futex_key (lg->mem_base + ukey, fshared, &key) !=0 ) {kill_guest (lg,"bad sending DMA key" ); goto unlock; }
@@ -584,7 +586,7 @@ unsigned long get_dma_buffer(struct lgue/* This can fail if it's not a valid address, or if the address is not * divisible by 4 (the futex code needs that, we don't really). */ -if (get_futex_key ((u32 __user *)ukey, fshared, &key) !=0 ) { +if (get_futex_key (lg->mem_base + ukey, fshared, &key) !=0 ) {kill_guest (lg,"bad registered DMA buffer" ); goto unlock; }
===================================================================
drivers/lguest/lg.h +3 -0
@@ -143,6 +143,9 @@ struct lgueststruct mm_struct *mm;/* == tsk->mm, but that becomes NULL on exit */ u16 guestid;u32 pfn_limit; +/* This provides the offset to the base of guest-physical +* memory in the Launcher. */ +void __user *mem_base;u32 page_offset;u32 cr2;int halted;
===================================================================
drivers/lguest/lguest_user.c +13 -10
@@ -1,9 +1,9 @@/*P:200 This contains all the /dev/lguest code, whereby the userspace launcher * controls and communicates with the Guest. For example, the first write will -* tell us the memory size, pagetable, entry point and kernel address offset. -* A read will run the Guest until a signal is pending (-EINTR), or the Guest -* does a DMA out to the Launcher. Writes are also used to get a DMA buffer -* registered by the Guest and to send the Guest an interrupt. :*/ +* tell us the Guest's memory layout, pagetable, entry point and kernel address +* offset. A read will run the Guest until something happens, such as a signal +* or the Guest doing a DMA out to the Launcher. Writes are also used to get a +* DMA buffer registered by the Guest and to send the Guest an interrupt. :*/ #include <linux/uaccess.h> #include <linux/miscdevice.h> #include <linux/fs.h>
@@ -142,8 +142,10 @@ static ssize_t read(struct file *file, creturn run_guest (lg, (unsigned long __user *)user); } -/*L:020 The initialization write supplies 4 32-bit values (in addition to the +/*L:020 The initialization write supplies 5 32-bit values (in addition to the * 32-bit LHREQ_INITIALIZE value). These are: +* +* base: The start of the Guest-physical memory inside the Launcher memory. * * pfnlimit: The highest (Guest-physical) page number the Guest should be * allowed to access. The Launcher has to live in Guest memory, so it sets
@@ -166,7 +168,7 @@ static int initialize(struct file *file,* Guest. */ struct lguest *lg;int err, i; -u32 args[4 ]; +u32 args[5 ];/* We grab the Big Lguest lock, which protects the global array * "lguests" and multiple simultaneous initializations. */
@@ -194,8 +196,9 @@ static int initialize(struct file *file,/* Populate the easy fields of our "struct lguest" */ lg->guestid = i; - lg->pfn_limit = args[0 ]; - lg->page_offset = args[3 ]; + lg->mem_base = (void __user *)(long )args[0 ]; + lg->pfn_limit = args[1 ]; + lg->page_offset = args[4 ];/* We need a complete page for the Guest registers: they are accessible * to the Guest and we can only grant it access to whole pages. */
@@ -210,13 +213,13 @@ static int initialize(struct file *file,/* Initialize the Guest's shadow page tables, using the toplevel * address the Launcher gave us. This allocates memory, so can * fail. */ - err =init_guest_pagetable (lg, args[1 ]); + err =init_guest_pagetable (lg, args[2 ]);if (err) goto free_regs;/* Now we initialize the Guest's registers, handing it the start * address. */ -setup_regs (lg->regs, args[2 ]); +setup_regs (lg->regs, args[3 ]);/* There are a couple of GDT entries the Guest expects when first * booting. */
===================================================================
drivers/lguest/page_tables.c +5 -2
@@ -152,7 +152,7 @@ static spte_t gpte_to_spte(struct lgueststatic spte_t gpte_to_spte (struct lguest *lg,gpte_t gpte,int write) {spte_t spte; -unsigned long pfn; +unsigned long pfn, base;/* The Guest sets the global flag, because it thinks that it is using * PGE. We only told it to use PGE so it would tell us whether it was
@@ -160,11 +160,14 @@ static spte_t gpte_to_spte(struct lguest* use the global bit, so throw it away. */ spte.flags = (gpte.flags & ~_PAGE_GLOBAL); +/* The Guest's pages are offset inside the Launcher. */ + base = (unsigned long )lg->mem_base /PAGE_SIZE ; +/* We need a temporary "unsigned long" variable to hold the answer from * get_pfn(), because it returns 0xFFFFFFFF on failure, which wouldn't * fit in spte.pfn. get_pfn() finds the real physical number of the * page, given the virtual number. */ - pfn =get_pfn (gpte.pfn, write); + pfn =get_pfn (base + gpte.pfn, write);if (pfn ==-1UL ) {kill_guest (lg,"failed to get page %u" , gpte.pfn);/* When we destroy the Guest, we'll go through the shadow page
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 18/43 lguest: Remove fixed limit on number of guests, and lguests array.
In reply to [email protected]Back when we had all the Guest state in the switcher, we had a fixed array of them. This is no longer necessary.
If we switch the network code to using random_ether_addr (46 bits is enough to avoid clashes), we can get rid of the concept of "guest id" altogether.
--- drivers/lguest/core.c | 14 -------------- drivers/lguest/hypercalls.c | 4 +--- drivers/lguest/io.c | 10 +++++----- drivers/lguest/lg.h | 5 +---- drivers/lguest/lguest_user.c | 17 ++++++----------- drivers/net/lguest_net.c | 7 +------ include/linux/lguest.h | 2 -- 7 files changed, 14 insertions(+), 45 deletions(-)
===================================================================
drivers/lguest/core.c +0 -14
@@ -46,10 +46,6 @@ static struct {/* This One Big lock protects all inter-guest data structures. */ DEFINE_MUTEX (lguest_lock); static DEFINE_PER_CPU(struct lguest *, last_guest); - -/* FIXME: Make dynamic. */ -#define MAX_LGUEST_GUESTS 16 -struct lguest lguests[MAX_LGUEST_GUESTS ];/* Offset from where switcher.S was compiled to where we've copied it */ static unsigned long switcher_offset (void )
@@ -660,16 +656,6 @@ int run_guest(struct lguest *lg, unsigne* deliver_trap() and demand_page(). After all those, we'll be ready to * examine the Switcher, and our philosophical understanding of the Host/Guest * duality will be complete. :*/ - -int find_free_guest (void ) -{ -unsigned int i; -for (i =0 ; i <MAX_LGUEST_GUESTS ; i++) -if (!lguests[i].tsk) -return i; -return -1 ; -} -static void adjust_pge (void *on) {if (on)
===================================================================
drivers/lguest/hypercalls.c +1 -3
@@ -225,9 +225,7 @@ static void initialize(struct lguest *lg/* We tell the Guest that it can't use the top 4MB of virtual * addresses used by the Switcher. */ || put_user(4U*1024*1024, &lg->lguest_data->reserve_mem) - || put_user(tsc_speed, &lg->lguest_data->tsc_khz) - /* We also give the Guest a unique id, as used in lguest_net.c. */ - || put_user(lg->guestid, &lg->lguest_data->guestid)) + || put_user(tsc_speed, &lg->lguest_data->tsc_khz))kill_guest (lg,"bad guest page %p" , lg->lguest_data);/* We write the current time into the Guest's data page once now. */
===================================================================
drivers/lguest/io.c +5 -5
@@ -212,7 +212,7 @@ int bind_dma(struct lguest *lg, lg->dma[i].num_dmas = numdmas; lg->dma[i].next_dma =0 ; lg->dma[i].key = key; - lg->dma[i].guestid = lg->guestid; + lg->dma[i].owner = lg; lg->dma[i].interrupt = interrupt;/* Now we add it to the hash table: the position
@@ -412,7 +412,7 @@ static int dma_transfer(struct lguest *s/* From the "struct lguest_dma_info" we found in the hash, grab the * Guest. */ - dstlg = &lguests[dst->guestid]; + dstlg = dst->owner;/* Read in the source "struct lguest_dma" handed to SEND_DMA. */ lgread (srclg, &src_dma, udma,sizeof (src_dma));
@@ -506,8 +506,8 @@ again:struct lguest_dma_info *i;/* Look through the hash for other Guests. */ list_for_each_entry (i, &dma_hash[hash (&key)], list) { -/* Don't send to ourselves. */ -if (i->guestid == lg->guestid) +/* Don't send to ourselves (would deadlock). */ +if (i->owner->mm == lg->mm)continue ;if (!key_eq (&key, &i->key))continue ;
@@ -594,7 +594,7 @@ unsigned long get_dma_buffer(struct lgue* send to its own Guest for the moment, so the entry must be for this * Guest) */ list_for_each_entry (i, &dma_hash[hash (&key)], list) { -if (key_eq (&key, &i->key) && i->guestid == lg->guestid) { +if (key_eq (&key, &i->key) && i->owner == lg) {unsigned int j;/* Look through the registered DMA array for an * available buffer. */
===================================================================
drivers/lguest/lg.h +1 -4
@@ -52,9 +52,9 @@ struct lguest_dma_infostruct list_head list;union futex_key key;unsigned long dmas; +struct lguest *owner;u16 next_dma;u16 num_dmas; -u16 guestid;u8 interrupt;/* 0 when not registered */ };
@@ -141,7 +141,6 @@ struct lgueststruct lguest_data __user *lguest_data;struct task_struct *tsk;struct mm_struct *mm;/* == tsk->mm, but that becomes NULL on exit */ -u16 guestid;u32 pfn_limit;/* This provides the offset to the base of guest-physical * memory in the Launcher. */
@@ -196,7 +195,6 @@ struct lguestDECLARE_BITMAP (irqs_pending,LGUEST_IRQS ); }; -extern struct lguest lguests[];extern struct mutex lguest_lock;/* core.c: */
@@ -204,7 +202,6 @@ void lgwrite_u32(struct lguest *lg, unsivoid lgwrite_u32 (struct lguest *lg,unsigned long addr,u32 val);void lgread (struct lguest *lg,void *buf,unsigned long addr,unsigned len);void lgwrite (struct lguest *lg,unsigned long ,const void *buf,unsigned len); -int find_free_guest (void );int lguest_address_ok (const struct lguest *lg,unsigned long addr,unsigned long len);int run_guest (struct lguest *lg,unsigned long __user *user);
===================================================================
drivers/lguest/lguest_user.c +6 -11
@@ -167,11 +167,11 @@ static int initialize(struct file *file,/* "struct lguest" contains everything we (the Host) know about a * Guest. */ struct lguest *lg; -int err, i; +int err;u32 args[5 ]; -/* We grab the Big Lguest lock, which protects the global array -* "lguests" and multiple simultaneous initializations. */ +/* We grab the Big Lguest lock, which protects against multiple +* simultaneous initializations. */ mutex_lock (&lguest_lock);/* You can't initialize twice! Close the device and start again... */ if (file->private_data) {
@@ -184,18 +184,13 @@ static int initialize(struct file *file, goto unlock; } -/* Find an unused guest. */ - i =find_free_guest (); -if (i <0 ) { - err = -ENOSPC ; + lg =kzalloc (sizeof (*lg),GFP_KERNEL ); +if (!lg) { + err = -ENOMEM ; goto unlock; } -/* OK, we have an index into the "lguest" array: "lg" is a convenient -* pointer. */ - lg = &lguests[i];/* Populate the easy fields of our "struct lguest" */ - lg->guestid = i; lg->mem_base = (void __user *)(long )args[0 ]; lg->pfn_limit = args[1 ]; lg->page_offset = args[4 ];
===================================================================
drivers/net/lguest_net.c +1 -6
@@ -465,12 +465,7 @@ static int lguestnet_probe(struct lguest/* Ethernet defaults with some changes */ ether_setup (dev); dev->set_mac_address =NULL ; - - dev->dev_addr[0 ] =0x02 ;/* set local assignment bit (IEEE802) */ - dev->dev_addr[1 ] =0x00 ; -memcpy (&dev->dev_addr[2 ], &lguest_data.guestid,2 ); - dev->dev_addr[4 ] =0x00 ; - dev->dev_addr[5 ] =0x00 ; +random_ether_addr (dev->dev_addr); dev->open = lguestnet_open; dev->stop = lguestnet_close;
===================================================================
include/linux/lguest.h +0 -2
@@ -41,8 +41,6 @@ struct lguest_data/* Fields initialized by the Host at boot: */ /* Memory not to try to access */ unsigned long reserve_mem; -/* ID of this Guest (used by network driver to set ethernet address) */ -u16 guestid;/* KHz for the TSC clock. */ u32 tsc_khz;
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 19/43 lguest: Make shadow IDT a complete IDT with 256 entries.
In reply to [email protected]This simplifies the code a little, in preparation for allowing alternate system call vectors in guests (Plan 9 uses 0x40).
--- drivers/lguest/interrupts_and_traps.c | 49 ++++++++++++--------------------- drivers/lguest/lg.h | 3 -- 2 files changed, 20 insertions(+), 32 deletions(-)
===================================================================
drivers/lguest/interrupts_and_traps.c +19 -30
@@ -215,10 +215,9 @@ int deliver_trap(struct lguest *lg, unsi* system calls down from 1750ns to 270ns. Plus, if lguest didn't do it, all * the other hypervisors would tease it. * -* This routine determines if a trap can be delivered directly. */ -static int direct_trap (const struct lguest *lg, -const struct desc_struct *trap, -unsigned int num) +* This routine indicates if a particular trap number could be delivered +* directly. */ +static int direct_trap (unsigned int num) {/* Hardware interrupts don't go to the Guest at all (except system * call). */
@@ -229,14 +228,7 @@ static int direct_trap(const struct lgue* fault address), general protection faults (in/out emulation) and * device not available (TS handling), and of course, the hypercall * trap. */ -if (num ==14 || num ==13 || num ==7 || num ==LGUEST_TRAP_ENTRY ) -return 0 ; - -/* Only trap gates (type 15) can go direct to the Guest. Interrupt -* gates (type 14) disable interrupts as they are entered, which we -* never let the Guest do. Not present entries (type 0x0) also can't -* go direct, of course 8) */ -return idt_type (trap->a, trap->b) ==0xF ; +return num !=14 && num !=13 && num !=7 && num !=LGUEST_TRAP_ENTRY ; }/*:*/
@@ -342,15 +334,11 @@ void load_guest_idt_entry(struct lguest* to copy this again. */ lg->changed |=CHANGED_IDT ; -/* The IDT which we keep in "struct lguest" only contains 32 entries -* for the traps and LGUEST_IRQS (32) entries for interrupts. We -* ignore attempts to set handlers for higher interrupt numbers, except -* for the system call "interrupt" at 128: we have a special IDT entry -* for that. */ -if (num <ARRAY_SIZE (lg->idt)) +/* Check that the Guest doesn't try to step outside the bounds. */ +if (num >=ARRAY_SIZE (lg->idt)) +kill_guest (lg,"Setting idt entry %u" , num); +else set_trap (lg, &lg->idt[num], num, lo, hi); -else if (num ==SYSCALL_VECTOR ) -set_trap (lg, &lg->syscall_idt, num, lo, hi); }/* The default entry for each interrupt points into the Switcher routines which
@@ -393,20 +381,21 @@ void copy_traps(const struct lguest *lg,/* We can simply copy the direct traps, otherwise we use the default * ones in the Switcher: they will return to the Host. */ -for (i =0 ; i <FIRST_EXTERNAL_VECTOR ; i++) { -if (direct_trap (lg, &lg->idt[i], i)) +for (i =0 ; i <ARRAY_SIZE (lg->idt); i++) { +/* If no Guest can ever override this trap, leave it alone. */ +if (!direct_trap (i)) +continue ; + +/* Only trap gates (type 15) can go direct to the Guest. +* Interrupt gates (type 14) disable interrupts as they are +* entered, which we never let the Guest do. Not present +* entries (type 0x0) also can't go direct, of course. */ +if (idt_type (lg->idt[i].a, lg->idt[i].b) ==0xF ) idt[i] = lg->idt[i];else +/* Reset it to the default. */ default_idt_entry (&idt[i], i, def[i]); } - -/* Don't forget the system call trap! The IDT entries for other -* interupts never change, so no need to copy them. */ - i =SYSCALL_VECTOR ; -if (direct_trap (lg, &lg->syscall_idt, i)) - idt[i] = lg->syscall_idt; -else -default_idt_entry (&idt[i], i, def[i]); }void guest_set_clockevent (struct lguest *lg,unsigned long delta)
===================================================================
drivers/lguest/lg.h +1 -2
@@ -183,8 +183,7 @@ struct lgueststruct desc_struct gdt[GDT_ENTRIES ];/* The IDT entries: some copied into lguest_ro_state when running. */ -struct desc_struct idt[FIRST_EXTERNAL_VECTOR +LGUEST_IRQS ]; -struct desc_struct syscall_idt; +struct desc_struct idt[IDT_ENTRIES ];/* Virtual clock device */ struct hrtimer hrt;
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 20/43 lguest: Move i386 part of core.c to i386_core.c.
In reply to [email protected]Separate i386 architecture specific from core.c and move it to i386_core.c and add asm-i386/lguest.h header file to match.
arch/i386/lguest/boot.c +1 -0
@@ -65,6 +65,7 @@#include <asm/e820.h> #include <asm/mce.h> #include <asm/io.h> +#include <asm/i387.h> /*G:010 Welcome to the Guest! *
drivers/lguest/Makefile +1 -1
@@ -6,7 +6,7 @@ lg-y = core.o hypercalls.o page_tables.o lg-y = core.o hypercalls.o page_tables.o interrupts_and_traps.o \ segments.o io.o lguest_user.o -lg-$(CONFIG_X86_32) += i386_switcher.o +lg-$(CONFIG_X86_32) += i386_switcher.o i386_core.o Preparation Preparation!: PREFIX=P Guest: PREFIX=G
drivers/lguest/core.c +15 -448
538 lines not on this page · raw message
drivers/lguest/i386_core.c new file +476 -0
476 lines not on this page · raw message
drivers/lguest/i386_switcher.S +2 -0
@@ -48,6 +48,8 @@ #include <linux/linkage.h> #include <asm/asm-offsets.h> #include <asm/page.h> +#include <asm/segment.h> +#include <asm/lguest.h> #include "lg.h" // We mark the start of the code to copy
drivers/lguest/interrupts_and_traps.c +9 -9
@@ -165,7 +165,7 @@ void maybe_do_interrupt(struct lguest *l/* Look at the IDT entry the Guest gave us for this interrupt. The * first 32 (FIRST_EXTERNAL_VECTOR) entries are for traps, so we skip * over them. */ - idt = &lg->idt[FIRST_EXTERNAL_VECTOR +irq]; + idt = &lg->arch.idt[FIRST_EXTERNAL_VECTOR +irq];/* If they don't have a handler (yet?), we just ignore it */ if (idt_present (idt->a, idt->b)) {/* OK, mark it no longer pending and deliver it. */
@@ -197,14 +197,14 @@ int deliver_trap(struct lguest *lg, unsi {/* Trap numbers are always 8 bit, but we set an impossible trap number * for traps inside the Switcher, so check that here. */ -if (num >=ARRAY_SIZE (lg->idt)) +if (num >=ARRAY_SIZE (lg->arch.idt))return 0 ;/* Early on the Guest hasn't set the IDT entries (or maybe it put a * bogus one in): if we fail here, the Guest will be killed. */ -if (!idt_present (lg->idt[num].a, lg->idt[num].b)) +if (!idt_present (lg->arch.idt[num].a, lg->arch.idt[num].b))return 0 ; -set_guest_interrupt (lg, lg->idt[num].a, lg->idt[num].b,has_err (num)); +set_guest_interrupt (lg, lg->arch.idt[num].a, lg->arch.idt[num].b,has_err (num));return 1 ; }
@@ -341,10 +341,10 @@ void load_guest_idt_entry(struct lguest lg->changed |=CHANGED_IDT ;/* Check that the Guest doesn't try to step outside the bounds. */ -if (num >=ARRAY_SIZE (lg->idt)) +if (num >=ARRAY_SIZE (lg->arch.idt))kill_guest (lg,"Setting idt entry %u" , num);else -set_trap (lg, &lg->idt[num], num, lo, hi); +set_trap (lg, &lg->arch.idt[num], num, lo, hi); }/* The default entry for each interrupt points into the Switcher routines which
@@ -387,7 +387,7 @@ void copy_traps(const struct lguest *lg,/* We can simply copy the direct traps, otherwise we use the default * ones in the Switcher: they will return to the Host. */ -for (i =0 ; i <ARRAY_SIZE (lg->idt); i++) { +for (i =0 ; i <ARRAY_SIZE (lg->arch.idt); i++) {/* If no Guest can ever override this trap, leave it alone. */ if (!direct_trap (i))continue ;
@@ -396,8 +396,8 @@ void copy_traps(const struct lguest *lg,* Interrupt gates (type 14) disable interrupts as they are * entered, which we never let the Guest do. Not present * entries (type 0x0) also can't go direct, of course. */ -if (idt_type (lg->idt[i].a, lg->idt[i].b) ==0xF ) - idt[i] = lg->idt[i]; +if (idt_type (lg->arch.idt[i].a, lg->arch.idt[i].b) ==0xF ) + idt[i] = lg->arch.idt[i];else /* Reset it to the default. */ default_idt_entry (&idt[i], i, def[i]);
drivers/lguest/lg.h +12 -53
97 lines not on this page · raw message
drivers/lguest/segments.c +13 -13
@@ -73,14 +73,14 @@ static void fixup_gdt_table(struct lgues/* Segment descriptors contain a privilege level: the Guest is * sometimes careless and leaves this as 0, even though it's * running at privilege level 1. If so, we fix it here. */ -if ((lg->gdt[i].b &0x00006000 ) ==0 ) - lg->gdt[i].b |= (GUEST_PL <<13 ); +if ((lg->arch.gdt[i].b &0x00006000 ) ==0 ) + lg->arch.gdt[i].b |= (GUEST_PL <<13 );/* Each descriptor has an "accessed" bit. If we don't set it * now, the CPU will try to set it when the Guest first loads * that entry into a segment register. But the GDT isn't * writable by the Guest, so bad things can happen. */ - lg->gdt[i].b |=0x00000100 ; + lg->arch.gdt[i].b |=0x00000100 ; } }
@@ -106,12 +106,12 @@ void setup_guest_gdt(struct lguest *lg)void setup_guest_gdt (struct lguest *lg) {/* Start with full 0-4G segments... */ - lg->gdt[GDT_ENTRY_KERNEL_CS ] =FULL_EXEC_SEGMENT ; - lg->gdt[GDT_ENTRY_KERNEL_DS ] =FULL_SEGMENT ; + lg->arch.gdt[GDT_ENTRY_KERNEL_CS ] =FULL_EXEC_SEGMENT ; + lg->arch.gdt[GDT_ENTRY_KERNEL_DS ] =FULL_SEGMENT ;/* ...except the Guest is allowed to use them, so set the privilege * level appropriately in the flags. */ - lg->gdt[GDT_ENTRY_KERNEL_CS ].b |= (GUEST_PL <<13 ); - lg->gdt[GDT_ENTRY_KERNEL_DS ].b |= (GUEST_PL <<13 ); + lg->arch.gdt[GDT_ENTRY_KERNEL_CS ].b |= (GUEST_PL <<13 ); + lg->arch.gdt[GDT_ENTRY_KERNEL_DS ].b |= (GUEST_PL <<13 ); }/* Like the IDT, we never simply use the GDT the Guest gives us. We set up the
@@ -126,7 +126,7 @@ void copy_gdt_tls(const struct lguest *lunsigned int i;for (i =GDT_ENTRY_TLS_MIN ; i <=GDT_ENTRY_TLS_MAX ; i++) - gdt[i] = lg->gdt[i]; + gdt[i] = lg->arch.gdt[i]; }/* This is the full version */
@@ -138,7 +138,7 @@ void copy_gdt(const struct lguest *lg, s* replaced. See ignored_gdt() above. */ for (i =0 ; i <GDT_ENTRIES ; i++)if (!ignored_gdt (i)) - gdt[i] = lg->gdt[i]; + gdt[i] = lg->arch.gdt[i]; }/* This is where the Guest asks us to load a new GDT (LHCALL_LOAD_GDT). */
@@ -146,12 +146,12 @@ void load_guest_gdt(struct lguest *lg, u {/* We assume the Guest has the same number of GDT entries as the * Host, otherwise we'd have to dynamically allocate the Guest GDT. */ -if (num >ARRAY_SIZE (lg->gdt)) +if (num >ARRAY_SIZE (lg->arch.gdt))kill_guest (lg,"too many gdt entries %i" , num);/* We read the whole thing in, then fix it up. */ -lgread (lg, lg->gdt, table, num *sizeof (lg->gdt[0 ])); -fixup_gdt_table (lg,0 ,ARRAY_SIZE (lg->gdt)); +lgread (lg, lg->arch.gdt, table, num *sizeof (lg->arch.gdt[0 ])); +fixup_gdt_table (lg,0 ,ARRAY_SIZE (lg->arch.gdt));/* Mark that the GDT changed so the core knows it has to copy it again, * even if the Guest is run on the same CPU. */ lg->changed |=CHANGED_GDT ;
@@ -159,7 +159,7 @@ void load_guest_gdt(struct lguest *lg, uvoid guest_load_tls (struct lguest *lg,unsigned long gtls) { -struct desc_struct *tls = &lg->gdt[GDT_ENTRY_TLS_MIN ]; +struct desc_struct *tls = &lg->arch.gdt[GDT_ENTRY_TLS_MIN ];lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES );fixup_gdt_table (lg,GDT_ENTRY_TLS_MIN ,GDT_ENTRY_TLS_MAX +1 );
include/asm-i386/lguest.h new file +87 -0
87 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 21/43 lguest: Reorder guest saved regs to match hyperall order
In reply to [email protected]Move eax next to ebx/ecx/edx in struct lguest_regs on i386, so they will be located together and allow it to map directly to a struct hcall_ring entry (which will be renamed struct hcall_args as in a subsequent patch).
This is in preparation for making the code hcall code architecture independent.
--- drivers/lguest/i386_switcher.S | 4 ++-- include/asm-i386/lguest.h | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-)
===================================================================
drivers/lguest/i386_switcher.S +2 -2
@@ -134,6 +134,7 @@ ENTRY(switch_to_guest) // The Guest's register page has been mapped // Writable onto our %esp (stack) -- // We can simply pop off all Guest regs. + popl %eax popl %ebx popl %ecx popl %edx
@@ -141,7 +142,6 @@ ENTRY(switch_to_guest) popl %edi popl %ebp popl %gs - popl %eax popl %fs popl %ds popl %es
@@ -169,7 +169,6 @@ ENTRY(switch_to_guest) pushl %es; \ pushl %ds; \ pushl %fs; \ - pushl %eax; \ pushl %gs; \ pushl %ebp; \ pushl %edi; \
@@ -177,6 +176,7 @@ ENTRY(switch_to_guest) pushl %edx; \ pushl %ecx; \ pushl %ebx; \ + pushl %eax; \ /* Our stack and our code are using segments \ * Set in the TSS and IDT \ * Yet if we were to touch data we'd use \
===================================================================
include/asm-i386/lguest.h +1 -2
@@ -26,10 +26,9 @@ extern unsigned long default_idt_entriesstruct lguest_regs {/* Manually saved part. */ -unsigned long ebx, ecx, edx; +unsigned long eax, ebx, ecx, edx;unsigned long esi, edi, ebp;unsigned long gs; -unsigned long eax;unsigned long fs, ds, es;unsigned long trapnum, errcode;/* Trap pushed part */
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 22/43 lguest: Introduce "hcall" pointer to indicate pending hypercall.
In reply to [email protected]Currently we look at the "trapnum" to see if the Guest wants a hypercall. But once the hypercall is done we have to reset trapnum to a bogus value, otherwise if we exit to userspace and return, we'd run the same hypercall twice (that was a nasty bug to find!).
This has two main effects:
1) When Jes's patch changes the hypercall args to be a generic "struct hcall_args" we simply change the type of "lg->hcall". It's set by arch code, so if it has to copy args or something it can do so, and point "hcall" into lg->arch somewhere.
2) Async hypercalls only get run when an actual hypercall is pending. This simplfies the code a little and is a more logical semantic.
--- drivers/lguest/core.c | 8 +++---- drivers/lguest/hypercalls.c | 48 +++++++++++++++++-------------------------- drivers/lguest/i386_core.c | 13 ++++++----- drivers/lguest/lg.h | 3 ++ 4 files changed, 34 insertions(+), 38 deletions(-)
===================================================================
drivers/lguest/core.c +4 -4
@@ -198,10 +198,10 @@ int run_guest(struct lguest *lg, unsigne {/* We stop running once the Guest is dead. */ while (!lg->dead) { -/* First we run any hypercalls the Guest wants done: either in -* the hypercall ring in "struct lguest_data", or directly by -* using int 31 (LGUEST_TRAP_ENTRY). */ -do_hypercalls (lg); +/* First we run any hypercalls the Guest wants done. */ +if (lg->hcall) +do_hypercalls (lg); +/* It's possible the Guest did a SEND_DMA hypercall to the * Launcher, in which case we return from the read() now. */ if (lg->dma_is_pending) {
===================================================================
drivers/lguest/hypercalls.c +20 -28
67 lines not on this page · raw message
===================================================================
drivers/lguest/i386_core.c +7 -6
@@ -316,13 +316,14 @@ void lguest_arch_handle_trap(struct lguereturn ;break ; case 32 ... 255: -/* These values mean a real interrupt occurred, in -* which case the Host handler has already been run. -* We just do a friendly check if another process -* should now be run, then fall through to loop -* around: */ +/* These values mean a real interrupt occurred, in which case +* the Host handler has already been run. We just do a +* friendly check if another process should now be run, then +* return to run the Guest again */ cond_resched (); -case LGUEST_TRAP_ENTRY :/* Handled before re-entering Guest */ +return ; +case LGUEST_TRAP_ENTRY : + lg->hcall = lg->regs;return ; }
===================================================================
drivers/lguest/lg.h +3 -0
@@ -111,6 +111,9 @@ struct lguestu32 esp1;u8 ss1; +/* If a hypercall was asked for, this points to the arguments. */ +struct lguest_regs *hcall; +/* Do we need to stop what we're doing and return to userspace? */ int break_out;wait_queue_head_t break_wq;
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 23/43 lguest: Make hypercalls arch-independent.
In reply to [email protected]Clean up the hypercall code to make the code in hypercalls.c architecture independent. First process the common hypercalls and then call lguest_arch_do_hcall() if the call hasn't been handled. Rename struct hcall_ring to hcall_args.
This patch requires the previous patch which reorganize the layout of struct lguest_regs on i386 so they match the layout of struct hcall_args.
--- arch/i386/lguest/boot.c | 8 +- drivers/lguest/hypercalls.c | 120 ++++++++++++--------------------------- drivers/lguest/i386_core.c | 62 +++++++++++++++++++- drivers/lguest/lg.h | 4 - include/asm-i386/lguest_hcall.h | 8 +- include/linux/lguest.h | 2 6 files changed, 112 insertions(+), 92 deletions(-)
arch/i386/lguest/boot.c +4 -4
@@ -158,10 +158,10 @@ void async_hcall(unsigned long call,/* Table full, so do normal hcall which will flush table. */ hcall (call, arg1, arg2, arg3); }else { - lguest_data.hcalls[next_call].eax = call; - lguest_data.hcalls[next_call].edx = arg1; - lguest_data.hcalls[next_call].ebx = arg2; - lguest_data.hcalls[next_call].ecx = arg3; + lguest_data.hcalls[next_call].arg0 = call; + lguest_data.hcalls[next_call].arg1 = arg1; + lguest_data.hcalls[next_call].arg2 = arg2; + lguest_data.hcalls[next_call].arg3 = arg3;/* Arguments must all be written before we mark it to go */ wmb (); lguest_data.hcall_status[next_call] =0 ;
drivers/lguest/hypercalls.c +38 -82
210 lines not on this page · raw message
drivers/lguest/i386_core.c +61 -1
71 lines not on this page · raw message
drivers/lguest/lg.h +3 -1
@@ -108,7 +108,7 @@ struct lguestu8 ss1;/* If a hypercall was asked for, this points to the arguments. */ -struct lguest_regs *hcall; +struct hcall_args *hcall;/* Do we need to stop what we're doing and return to userspace? */ int break_out;
@@ -198,6 +198,8 @@ void lguest_arch_host_fini(void);void lguest_arch_host_fini (void );void lguest_arch_run_guest (struct lguest *lg);void lguest_arch_handle_trap (struct lguest *lg); +int lguest_arch_init_hypercalls (struct lguest *lg); +int lguest_arch_do_hcall (struct lguest *lg,struct hcall_args *args);/* <arch>_switcher.S: */ extern char start_switcher_text[], end_switcher_text[], switch_to_guest[];
include/asm-i386/lguest_hcall.h +5 -3
@@ -1,6 +1,8 @@/* Architecture specific portion of the lguest hypercalls */ #ifndef _I386_LGUEST_HCALL_H#define _I386_LGUEST_HCALL_H + +#include <irq_vectors.h> #define LHCALL_FLUSH_ASYNC 0#define LHCALL_LGUEST_INIT 1
@@ -59,9 +61,9 @@ void async_hcall(unsigned long call,#define LGUEST_IRQS (NR_IRQS < 32 ? NR_IRQS: 32)#define LHCALL_RING_SIZE 64 -struct hcall_ring +struct hcall_args { -u32 eax, edx, ebx, ecx; +/* These map directly onto eax, ebx, ecx, edx in struct lguest_regs */ +unsigned long arg0, arg2, arg3, arg1; }; -#endif /* _I386_LGUEST_HCALL_H */
include/linux/lguest.h +1 -1
@@ -36,7 +36,7 @@ struct lguest_data/* 0xFF == done (set by Host), 0 == pending (set by Guest). */ u8 hcall_status[LHCALL_RING_SIZE ];/* The actual registers for the hypercalls. */ -struct hcall_ring hcalls[LHCALL_RING_SIZE ]; +struct hcall_args hcalls[LHCALL_RING_SIZE ];/* Fields initialized by the Host at boot: */ /* Memory not to try to access */
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 24/43 lguest: Change example launcher to use unsigned long not u32
In reply to [email protected]Apply Clue 2x4 to lguest userland<->kernel handling code and the lguest launcher. Pointers are not to be passed in u32's!
Basic rule of thumb: Anything passing u32's back and forth should be passing unsigned longs to be portable to 64 bit archs.
For those who forgotten already, I repeat: NO POINTERS IN u32!
--- Documentation/lguest/lguest.c | 29 +++++++++++++++-------------- drivers/lguest/lguest_user.c | 33 +++++++++++++++++---------------- 2 files changed, 32 insertions(+), 30 deletions(-)
===================================================================
Documentation/lguest/lguest.c +15 -14
@@ -473,9 +473,9 @@ static unsigned long setup_pagetables(ununsigned long initrd_size, unsigned long page_offset) { - u32 *pgdir, *linear; + unsigned long *pgdir, *linear;unsigned int mapped_pages, i, linear_pages; -unsigned int ptes_per_page =getpagesize ()/sizeof (u32); +unsigned int ptes_per_page =getpagesize ()/sizeof (void *);/* Ideally we map all physical memory starting at page_offset. * However, if page_offset is 0xC0000000 we can only map 1G of physical
@@ -505,7 +505,7 @@ static unsigned long setup_pagetables(un* continue from there. */ for (i =0 ; i < mapped_pages; i += ptes_per_page) { pgdir[(i + page_offset/getpagesize ())/ptes_per_page] - = ((to_guest_phys (linear) + i*sizeof (u32)) + = ((to_guest_phys (linear) + i*sizeof (void *)) |PAGE_PRESENT ); }
@@ -537,12 +537,13 @@ static void concat(char *dst, char *args* the base of guest "physical" memory, the top physical page to allow, the * top level pagetable, the entry point and the page_offset constant for the * Guest. */ -static int tell_kernel (u32 pgdir,u32 start,u32 page_offset) -{ -u32 args[] = {LHREQ_INITIALIZE , - (unsigned long )guest_base, - guest_limit /getpagesize (), - pgdir, start, page_offset }; +static int tell_kernel (unsigned long pgdir,unsigned long start, +unsigned long page_offset) +{ +unsigned long args[] = {LHREQ_INITIALIZE , + (unsigned long )guest_base, + guest_limit /getpagesize (), + pgdir, start, page_offset };int fd; verbose("Guest: %p - %p (%#lx)\n",
@@ -586,7 +587,7 @@ static void wake_parent(int pipefd, intfor (;;) {fd_set rfds = devices->infds; -u32 args[] = {LHREQ_BREAK ,1 }; +unsigned long args[] = {LHREQ_BREAK ,1 };/* Wait until input is ready from one of the devices. */ select (devices->max_infd+1 , &rfds,NULL ,NULL ,NULL );
@@ -684,7 +685,7 @@ static u32 *get_dma_buffer(int fd, voidstatic u32 *get_dma_buffer (int fd,void *key,struct iovec iov[],unsigned int *num,u32 *irq) { -u32 buf[] = {LHREQ_GETDMA ,to_guest_phys (key) }; +unsigned long buf[] = {LHREQ_GETDMA ,to_guest_phys (key) };unsigned long udma;u32 *res;
@@ -705,7 +706,7 @@ static u32 *get_dma_buffer(int fd, void/* This is a convenient routine to send the Guest an interrupt. */ static void trigger_irq (int fd,u32 irq) { -u32 buf[] = {LHREQ_IRQ , irq }; +unsigned long buf[] = {LHREQ_IRQ , irq };if (write (fd, buf,sizeof (buf)) !=0 )err (1 ,"Triggering irq %i" , irq); }
@@ -787,7 +788,7 @@ static bool handle_console_input(int fd,struct timeval now;gettimeofday (&now,NULL );if (now.tv_sec <= abort->start.tv_sec+1 ) { -u32 args[] = {LHREQ_BREAK ,0 }; +unsigned long args[] = {LHREQ_BREAK ,0 };/* Close the fd so Waker will know it has to * exit. */ close (waker_fd);
@@ -1365,7 +1366,7 @@ run_guest(int lguest_fd, struct device_l run_guest(int lguest_fd, struct device_list *device_list) {for (;;) { -u32 args[] = {LHREQ_BREAK ,0 }; +unsigned long args[] = {LHREQ_BREAK ,0 };unsigned long arr[2 ];int readval;
===================================================================
drivers/lguest/lguest_user.c +17 -16
@@ -43,7 +43,7 @@ static void setup_regs(struct lguest_reg/*L:310 To send DMA into the Guest, the Launcher needs to be able to ask for a * DMA buffer. This is done by writing LHREQ_GETDMA and the key to * /dev/lguest. */ -static long user_get_dma (struct lguest *lg,const u32 __user *input) +static long user_get_dma (struct lguest *lg,const unsigned long __user *input) {unsigned long key, udma, irq;
@@ -67,7 +67,7 @@ static long user_get_dma(struct lguest */*L:315 To force the Guest to stop running and return to the Launcher, the * Waker sets writes LHREQ_BREAK and the value "1" to /dev/lguest. The * Launcher then writes LHREQ_BREAK and "0" to release the Waker. */ -static int break_guest_out (struct lguest *lg,const u32 __user *input) +static int break_guest_out (struct lguest *lg,const unsigned long __user *input) {unsigned long on;
@@ -90,9 +90,9 @@ static int break_guest_out(struct lguest/*L:050 Sending an interrupt is done by writing LHREQ_IRQ and an interrupt * number to /dev/lguest. */ -static int user_send_irq (struct lguest *lg,const u32 __user *input) -{ -u32 irq; +static int user_send_irq (struct lguest *lg,const unsigned long __user *input) +{ +unsigned long irq;if (get_user (irq, input) !=0 )return -EFAULT ;
@@ -142,8 +142,8 @@ static ssize_t read(struct file *file, creturn run_guest (lg, (unsigned long __user *)user); } -/*L:020 The initialization write supplies 5 32-bit values (in addition to the -* 32-bit LHREQ_INITIALIZE value). These are: +/*L:020 The initialization write supplies 5 pointer sized (32 or 64 bit) +* values (in addition to the LHREQ_INITIALIZE value). These are: * * base: The start of the Guest-physical memory inside the Launcher memory. *
@@ -162,13 +162,13 @@ static ssize_t read(struct file *file, c* quickly converted from physical to virtual by adding PAGE_OFFSET. It's * 0xC0000000 (3G) by default, but it's configurable at kernel build time. */ -static int initialize (struct file *file,const u32 __user *input) +static int initialize (struct file *file,const unsigned long __user *input) {/* "struct lguest" contains everything we (the Host) know about a * Guest. */ struct lguest *lg;int err; -u32 args[5 ]; +unsigned long args[5 ];/* We grab the Big Lguest lock, which protects against multiple * simultaneous initializations. */
@@ -259,17 +259,18 @@ unlock:* start with a 32 bit number: for the first write this must be * LHREQ_INITIALIZE to set up the Guest. After that the Launcher can use * writes of other values to get DMA buffers and send interrupts. */ -static ssize_t write (struct file *file,const char __user *input, +static ssize_t write (struct file *file,const char __user *in,size_t size,loff_t *off) {/* Once the guest is initialized, we hold the "struct lguest" in the * file private data. */ struct lguest *lg = file->private_data; -u32 req; +const unsigned long __user *input = (const unsigned long __user *)in; +unsigned long req;if (get_user (req, input) !=0 )return -EFAULT ; - input +=sizeof (req); + input++;/* If you haven't initialized, you must do that first. */ if (req !=LHREQ_INITIALIZE && !lg)
@@ -285,13 +286,13 @@ static ssize_t write(struct file *file,switch (req) {case LHREQ_INITIALIZE : -return initialize (file, (const u32 __user *)input); +return initialize (file, input);case LHREQ_GETDMA : -return user_get_dma (lg, (const u32 __user *)input); +return user_get_dma (lg, input);case LHREQ_IRQ : -return user_send_irq (lg, (const u32 __user *)input); +return user_send_irq (lg, input);case LHREQ_BREAK : -return break_guest_out (lg, (const u32 __user *)input); +return break_guest_out (lg, input);default :return -EINVAL ; }
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 25/43 lguest: Move register setup into i386_core.c
In reply to [email protected]Move setup_regs() to lguest_arch_setup_regs() in i386_core.c given that this is very architecture specific.
--- drivers/lguest/i386_core.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/lguest/lg.h | 1 + drivers/lguest/lguest_user.c | 37 +------------------------------------ 3 files changed, 38 insertions(+), 36 deletions(-)
===================================================================
drivers/lguest/i386_core.c +36 -0
@@ -535,3 +535,39 @@ int lguest_arch_init_hypercalls(struct l/* Now we've examined the hypercall code; our Guest can make requests. There * is one other way we can do things for the Guest, as we see in * emulate_insn(). :*/ + +/*L:030 lguest_arch_setup_regs() +* +* Most of the Guest's registers are left alone: we used get_zeroed_page() to +* allocate the structure, so they will be 0. */ +void lguest_arch_setup_regs (struct lguest *lg,unsigned long start) +{ +struct lguest_regs *regs = lg->regs; + +/* There are four "segment" registers which the Guest needs to boot: +* The "code segment" register (cs) refers to the kernel code segment +* __KERNEL_CS, and the "data", "extra" and "stack" segment registers +* refer to the kernel data segment __KERNEL_DS. +* +* The privilege level is packed into the lower bits. The Guest runs +* at privilege level 1 (GUEST_PL).*/ + regs->ds = regs->es = regs->ss = __KERNEL_DS|GUEST_PL ; + regs->cs = __KERNEL_CS|GUEST_PL ; + +/* The "eflags" register contains miscellaneous flags. Bit 1 (0x002) +* is supposed to always be "1". Bit 9 (0x200) controls whether +* interrupts are enabled. We always leave interrupts enabled while +* running the Guest. */ + regs->eflags =0x202 ; + +/* The "Extended Instruction Pointer" register says where the Guest is +* running. */ + regs->eip = start; + +/* %esi points to our boot information, at physical address 0, so don't +* touch it. */ +/* There are a couple of GDT entries the Guest expects when first +* booting. */ + +setup_guest_gdt (lg); +}
===================================================================
drivers/lguest/lg.h +1 -0
@@ -204,6 +204,7 @@ void lguest_arch_handle_trap(struct lguevoid lguest_arch_handle_trap (struct lguest *lg);int lguest_arch_init_hypercalls (struct lguest *lg);int lguest_arch_do_hcall (struct lguest *lg,struct hcall_args *args); +void lguest_arch_setup_regs (struct lguest *lg,unsigned long start);/* <arch>_switcher.S: */ extern char start_switcher_text[], end_switcher_text[], switch_to_guest[];
===================================================================
drivers/lguest/lguest_user.c +1 -36
@@ -9,37 +9,6 @@#include <linux/fs.h> #include "lg.h" -/*L:030 setup_regs() doesn't really belong in this file, but it gives us an -* early glimpse deeper into the Host so it's worth having here. -* -* Most of the Guest's registers are left alone: we used get_zeroed_page() to -* allocate the structure, so they will be 0. */ -static void setup_regs (struct lguest_regs *regs,unsigned long start) -{ -/* There are four "segment" registers which the Guest needs to boot: -* The "code segment" register (cs) refers to the kernel code segment -* __KERNEL_CS, and the "data", "extra" and "stack" segment registers -* refer to the kernel data segment __KERNEL_DS. -* -* The privilege level is packed into the lower bits. The Guest runs -* at privilege level 1 (GUEST_PL).*/ - regs->ds = regs->es = regs->ss = __KERNEL_DS|GUEST_PL ; - regs->cs = __KERNEL_CS|GUEST_PL ; - -/* The "eflags" register contains miscellaneous flags. Bit 1 (0x002) -* is supposed to always be "1". Bit 9 (0x200) controls whether -* interrupts are enabled. We always leave interrupts enabled while -* running the Guest. */ - regs->eflags =0x202 ; - -/* The "Extended Instruction Pointer" register says where the Guest is -* running. */ - regs->eip = start; - -/* %esi points to our boot information, at physical address 0, so don't -* touch it. */ -} -/*L:310 To send DMA into the Guest, the Launcher needs to be able to ask for a * DMA buffer. This is done by writing LHREQ_GETDMA and the key to * /dev/lguest. */
@@ -214,11 +183,7 @@ static int initialize(struct file *file,/* Now we initialize the Guest's registers, handing it the start * address. */ -setup_regs (lg->regs, args[3 ]); - -/* There are a couple of GDT entries the Guest expects when first -* booting. */ -setup_guest_gdt (lg); +lguest_arch_setup_regs (lg, args[3 ]);/* The timer for lguest's clock needs initialization. */ init_clockdev (lg);
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 26/43 lguest: guest.h declares a struct timespec, make it include linux/time.h
In reply to [email protected]--- include/linux/lguest.h | 1 + 1 file changed, 1 insertion(+)
===================================================================
include/linux/lguest.h +1 -0
@@ -4,6 +4,7 @@#define _LINUX_LGUEST_H#ifndef __ASSEMBLY__ +#include <linux/time.h> #include <asm/irq.h> #include <asm/lguest_hcall.h>
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 27/43 lguest: Pagetables to use normal kernel types
In reply to [email protected]This is my first step in the migration of page_tables.c to the kernel types and functions/macros (2.6.23-rc3). Seems to be working OK.
--- drivers/lguest/hypercalls.c | 2 drivers/lguest/lg.h | 45 +-------- drivers/lguest/page_tables.c | 203 +++++++++++++++++++----------------------- 3 files changed, 105 insertions(+), 145 deletions(-)
===================================================================
drivers/lguest/hypercalls.c +1 -1
@@ -83,7 +83,7 @@ static void do_hcall(struct lguest *lg,guest_set_stack (lg, args->arg1, args->arg2, args->arg3);break ;case LHCALL_SET_PTE : -guest_set_pte (lg, args->arg1, args->arg2,mkgpte (args->arg3)); +guest_set_pte (lg, args->arg1, args->arg2,__pte (args->arg3));break ;case LHCALL_SET_PMD :guest_set_pmd (lg, args->arg1, args->arg2);
===================================================================
drivers/lguest/lg.h +8 -37
66 lines not on this page · raw message
===================================================================
drivers/lguest/page_tables.c +96 -107
454 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 28/43 lguest: Rename "cr3" to "gpgdir" to avoid x86-specific naming.
In reply to [email protected]--- drivers/lguest/lg.h | 6 +++--- drivers/lguest/page_tables.c | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-)
===================================================================
drivers/lguest/lg.h +3 -3
@@ -35,7 +35,7 @@ struct lguest_dma_infostruct pgdir { -unsigned long cr3; +unsigned long gpgdir;pgd_t *pgdir; };
@@ -159,10 +159,10 @@ int init_guest_pagetable(struct lguest *int init_guest_pagetable (struct lguest *lg,unsigned long pgtable);void free_guest_pagetable (struct lguest *lg);void guest_new_pagetable (struct lguest *lg,unsigned long pgtable); -void guest_set_pmd (struct lguest *lg,unsigned long cr3,u32 i); +void guest_set_pmd (struct lguest *lg,unsigned long gpgdir,u32 i);void guest_pagetable_clear_all (struct lguest *lg);void guest_pagetable_flush_user (struct lguest *lg); -void guest_set_pte (struct lguest *lg,unsigned long cr3, +void guest_set_pte (struct lguest *lg,unsigned long gpgdir,unsigned long vaddr,pte_t val);void map_switcher_in_guest (struct lguest *lg,struct lguest_pages *pages);int demand_page (struct lguest *info,unsigned long cr2,int errcode);
===================================================================
drivers/lguest/page_tables.c +9 -9
@@ -96,7 +96,7 @@ static unsigned long gpgd_addr(struct lgstatic unsigned long gpgd_addr (struct lguest *lg,unsigned long vaddr) {unsigned int index = vaddr >> (PGDIR_SHIFT ); -return lg->pgdirs[lg->pgdidx].cr3 + index *sizeof (pgd_t); +return lg->pgdirs[lg->pgdidx].gpgdir + index *sizeof (pgd_t); } static unsigned long gpte_addr(struct lguest *lg,
@@ -369,7 +369,7 @@ static unsigned int find_pgdir(struct lg {unsigned int i;for (i =0 ; i <ARRAY_SIZE (lg->pgdirs); i++) -if (lg->pgdirs[i].cr3 == pgtable) +if (lg->pgdirs[i].gpgdir == pgtable)break ;return i; }
@@ -378,7 +378,7 @@ static unsigned int find_pgdir(struct lg* allocate a new one (and so the kernel parts are not there), we set * blank_pgdir. */ static unsigned int new_pgdir (struct lguest *lg, -unsigned long cr3, +unsigned long gpgdir,int *blank_pgdir) {unsigned int next;
@@ -398,7 +398,7 @@ static unsigned int new_pgdir(struct lgu *blank_pgdir =1 ; }/* Record which Guest toplevel this shadows. */ - lg->pgdirs[next].cr3 = cr3; + lg->pgdirs[next].gpgdir = gpgdir;/* Release all the non-kernel mappings. */ flush_user_mappings (lg, next);
@@ -499,7 +499,7 @@ static void do_set_pte(struct lguest *lg* The benefit is that when we have to track a new page table, we can copy keep * all the kernel mappings. This speeds up context switch immensely. */ void guest_set_pte (struct lguest *lg, -unsigned long cr3,unsigned long vaddr,pte_t gpte) +unsigned long gpgdir,unsigned long vaddr,pte_t gpte) {/* Kernel mappings must be changed on all top levels. Slow, but * doesn't happen often. */
@@ -510,7 +510,7 @@ void guest_set_pte(struct lguest *lg,do_set_pte (lg, i, vaddr, gpte); }else {/* Is this page table one we have a shadow for? */ -int pgdir =find_pgdir (lg, cr3); +int pgdir =find_pgdir (lg, gpgdir);if (pgdir !=ARRAY_SIZE (lg->pgdirs))/* If so, do the update. */ do_set_pte (lg, pgdir, vaddr, gpte);
@@ -531,7 +531,7 @@ void guest_set_pte(struct lguest *lg,* * So with that in mind here's our code to to update a (top-level) PGD entry: */ -void guest_set_pmd (struct lguest *lg,unsigned long cr3,u32 idx) +void guest_set_pmd (struct lguest *lg,unsigned long gpgdir,u32 idx) {int pgdir;
@@ -541,7 +541,7 @@ void guest_set_pmd(struct lguest *lg, unreturn ;/* If they're talking about a page table we have a shadow for... */ - pgdir =find_pgdir (lg, cr3); + pgdir =find_pgdir (lg, gpgdir);if (pgdir <ARRAY_SIZE (lg->pgdirs))/* ... throw it away. */ release_pgd (lg, lg->pgdirs[pgdir].pgdir + idx);
@@ -561,7 +561,7 @@ int init_guest_pagetable(struct lguest */* We start on the first shadow page table, and give it a blank PGD * page. */ lg->pgdidx =0 ; - lg->pgdirs[lg->pgdidx].cr3 = pgtable; + lg->pgdirs[lg->pgdidx].gpgdir = pgtable; lg->pgdirs[lg->pgdidx].pgdir = (pgd_t *)get_zeroed_page (GFP_KERNEL );if (!lg->pgdirs[lg->pgdidx].pgdir)return -ENOMEM ;
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 29/43 lguest: Introduce "used_vectors" bitmap which can be used to reserve vectors.
In reply to [email protected]This simplifies the io_apic.c __assign_irq_vector() logic and removes the explicit SYSCALL_VECTOR check, and also allows for vectors to be reserved by other mechanisms (ie. lguest).
--- arch/i386/kernel/i8259.c | 3 ++- arch/i386/kernel/io_apic.c | 13 ++++++++----- arch/i386/kernel/traps.c | 10 ++++++++++ include/asm-i386/irq.h | 3 +++ 4 files changed, 23 insertions(+), 6 deletions(-)
===================================================================
arch/i386/kernel/i8259.c +2 -1
@@ -400,7 +400,8 @@ void __init native_init_IRQ(void)int vector =FIRST_EXTERNAL_VECTOR + i;if (i >=NR_IRQS )break ; -if (vector !=SYSCALL_VECTOR ) +/* SYSCALL_VECTOR was reserved in trap_init. */ +if (!test_bit (vector, used_vectors))set_intr_gate (vector, interrupt[i]); }
===================================================================
arch/i386/kernel/io_apic.c +8 -5
@@ -1198,7 +1198,7 @@ static int __assign_irq_vector(int irq)static int __assign_irq_vector (int irq) {static int current_vector =FIRST_DEVICE_VECTOR , current_offset =0 ; -int vector, offset, i; +int vector, offset;BUG_ON ((unsigned )irq >=NR_IRQ_VECTORS );
@@ -1215,11 +1215,8 @@ next: }if (vector == current_vector)return -ENOSPC ; -if (vector ==SYSCALL_VECTOR ) +if (test_and_set_bit (vector, used_vectors)) goto next; -for (i =0 ; i <NR_IRQ_VECTORS ; i++) -if (irq_vector[i] == vector) - goto next; current_vector = vector; current_offset = offset;
@@ -2290,6 +2287,12 @@ static inline void __init check_timer(vovoid __init setup_IO_APIC (void ) { +int i; + +/* Reserve all the system vectors. */ +for (i =FIRST_SYSTEM_VECTOR ; i <NR_VECTORS ; i++) +set_bit (i, used_vectors); +enable_IO_APIC ();if (acpi_ioapic)
===================================================================
arch/i386/kernel/traps.c +10 -0
@@ -64,6 +64,9 @@#include "mach_traps.h" int panic_on_unrecovered_nmi; + +DECLARE_BITMAP (used_vectors,NR_VECTORS ); +EXPORT_SYMBOL_GPL (used_vectors); asmlinkage int system_call(void);
@@ -1156,6 +1159,8 @@ static void __init set_task_gate(unsignevoid __init trap_init (void ) { +int i; +#ifdef CONFIG_EISA void __iomem *p =ioremap (0x0FFFD9 ,4 );if (readl (p) =='E' +('I' <<8 )+('S' <<16 )+('A' <<24 )) {
@@ -1215,6 +1220,11 @@ void __init trap_init(void)set_system_gate (SYSCALL_VECTOR ,&system_call); +/* Reserve all the builtin and the syscall vector. */ +for (i =0 ; i <FIRST_EXTERNAL_VECTOR ; i++) +set_bit (i, used_vectors); +set_bit (SYSCALL_VECTOR , used_vectors); +/* * Should be a barrier for any external CPU state. */
===================================================================
include/asm-i386/irq.h +3 -0
@@ -45,4 +45,7 @@ void init_IRQ(void);void init_IRQ (void );void __init native_init_IRQ (void ); +/* Interrupt vector management */ +extern DECLARE_BITMAP(used_vectors, NR_VECTORS); +#endif /* _ASM_IRQ_H */
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 30/43 lguest: Allow guest to specify syscall vector to use.
In reply to [email protected](Based on Ron Minnich's LGUEST_PLAN9_SYSCALL patch).
This patch allows Guests to specify what system call vector they want, and we try to reserve it. We only allow one non-Linux system call vector, to try to avoid DoS on the Host.
--- arch/i386/lguest/boot.c | 1 drivers/lguest/core.c | 30 +++++++++++++------- drivers/lguest/i386_core.c | 4 ++ drivers/lguest/interrupts_and_traps.c | 49 ++++++++++++++++++++++++++++++++- drivers/lguest/lg.h | 3 ++ include/linux/lguest.h | 3 ++ 6 files changed, 79 insertions(+), 11 deletions(-)
===================================================================
drivers/lguest/core.c +20 -10
@@ -281,37 +281,47 @@ static int __init init(void)/* First we put the Switcher up in very high virtual memory. */ err =map_switcher ();if (err) -return err; + goto out;/* Now we set up the pagetable implementation for the Guests. */ err =init_pagetables (switcher_page,SHARED_SWITCHER_PAGES ); -if (err) { -unmap_switcher (); -return err; - } +if (err) + goto unmap;/* The I/O subsystem needs some things initialized. */ lguest_io_init (); +/* We might need to reserve an interrupt vector. */ + err =init_interrupts (); +if (err) + goto free_pgtables; +/* /dev/lguest needs to be registered. */ err =lguest_device_init (); -if (err) { -free_pagetables (); -unmap_switcher (); -return err; - } +if (err) + goto free_interrupts;/* Finally we do some architecture-specific setup. */ lguest_arch_host_init ();/* All good! */ return 0 ; + +free_interrupts: +free_interrupts (); +free_pgtables: +free_pagetables (); +unmap: +unmap_switcher (); +out: +return err; }/* Cleaning up is just the same code, backwards. With a little French. */ static void __exit fini(void) {lguest_device_remove (); +free_interrupts ();free_pagetables ();unmap_switcher ();
===================================================================
drivers/lguest/i386_core.c +4 -0
@@ -530,6 +530,10 @@ int lguest_arch_init_hypercalls(struct lif (put_user (tsc_speed, &lg->lguest_data->tsc_khz))return -EFAULT ; +/* The interrupt code might not like the system call vector. */ +if (!check_syscall_vector (lg)) +kill_guest (lg,"bad syscall vector" ); +return 0 ; }/* Now we've examined the hypercall code; our Guest can make requests. There
===================================================================
arch/i386/lguest/boot.c +1 -0
@@ -87,6 +87,7 @@ struct lguest_data lguest_data = { .noirq_start = (u32)lguest_noirq_start, .noirq_end = (u32)lguest_noirq_end, .blocked_interrupts = { 1 }, /* Block timer interrupts */ + .syscall_vec = SYSCALL_VECTOR, };static cycle_t clock_base;
===================================================================
drivers/lguest/interrupts_and_traps.c +48 -1
68 lines not on this page · raw message
===================================================================
drivers/lguest/lg.h +3 -0
@@ -142,6 +142,9 @@ void copy_traps(const struct lguest *lg, const unsigned long *def);void guest_set_clockevent (struct lguest *lg,unsigned long delta);void init_clockdev (struct lguest *lg); +bool check_syscall_vector (struct lguest *lg); +int init_interrupts (void ); +void free_interrupts (void );/* segments.c: */ void setup_default_gdt_entries (struct lguest_ro_state *state);
===================================================================
include/linux/lguest.h +3 -0
@@ -48,6 +48,9 @@ struct lguest_data/* Fields initialized by the Guest at boot: */ /* Instruction range to suppress interrupts even if enabled */ unsigned long noirq_start, noirq_end; + +/* The vector to try to use for system calls (0x40 or 0x80). */ + unsigned int syscall_vec; };extern struct lguest_data lguest_data;#endif /* __ASSEMBLY__ */
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 31/43 lguest: Boot with virtual == physical to get closer to native Linux.
In reply to [email protected]1) This allows us to get alot closer to booting bzImages.
2) It means we don't have to know page_offset.
3) The Guest needs to modify the boot pagetables to create the PAGE_OFFSET mapping before jumping to C code.
4) guest_pa() walks the page tables rather than using page_offset.
5) We don't use page_offset to figure out whether to emulate: it was always kinda quesationable, and won't work for instructions done before remapping (bzImage unpacking in particular).
6) We still want the kernel address for tlb flushing: have the initial hypercall give us that, too.
--- Documentation/lguest/lguest.c | 134 +++++++-------------------------- arch/i386/kernel/asm-offsets.c | 1 arch/i386/lguest/boot.c | 7 - arch/i386/lguest/head.S | 41 ++++++++-- drivers/lguest/hypercalls.c | 8 - drivers/lguest/i386_core.c | 7 - drivers/lguest/interrupts_and_traps.c | 13 ++- drivers/lguest/lg.h | 8 - drivers/lguest/lguest_user.c | 11 -- drivers/lguest/page_tables.c | 47 +++++++++-- include/asm-i386/lguest_hcall.h | 3 include/linux/lguest.h | 5 - 12 files changed, 139 insertions(+), 146 deletions(-)
===================================================================
Documentation/lguest/lguest.c +31 -103
@@ -178,19 +178,16 @@ static void *get_pages(unsigned int num)/* To find out where to start we look for the magic Guest string, which marks * the code we see in lguest_asm.S. This is a hack which we are currently * plotting to replace with the normal Linux entry point. */ -static unsigned long entry_point (const void *start,const void *end, -unsigned long page_offset) +static unsigned long entry_point (const void *start,const void *end) {const void *p; -/* The scan gives us the physical starting address. We want the -* virtual address in this case, and fortunately, we already figured -* out the physical-virtual difference and passed it here in -* "page_offset". */ +/* The scan gives us the physical starting address. We boot with +* pagetables set up with virtual and physical the same, so that's +* OK. */ for (p = start; p < end; p++)if (memcmp (p,"GenuineLguest" ,strlen ("GenuineLguest" )) ==0 ) -return to_guest_phys (p +strlen ("GenuineLguest" )) - + page_offset; +return to_guest_phys (p +strlen ("GenuineLguest" ));errx (1 ,"Is this image a genuine lguest?" ); }
@@ -224,14 +221,11 @@ static void map_at(int fd, void *addr, u* by all modern binaries on Linux including the kernel. * * The ELF headers give *two* addresses: a physical address, and a virtual -* address. The Guest kernel expects to be placed in memory at the physical -* address, and the page tables set up so it will correspond to that virtual -* address. We return the difference between the virtual and physical -* addresses in the "page_offset" pointer. +* address. We use the physical address; the Guest will map itself to the +* virtual address. * * We return the starting address. */ -static unsigned long map_elf (int elf_fd,const Elf32_Ehdr *ehdr, -unsigned long *page_offset) +static unsigned long map_elf (int elf_fd,const Elf32_Ehdr *ehdr) {void *start = (void *)-1 , *end =NULL ;Elf32_Phdr phdr[ehdr->e_phnum];
@@ -255,9 +249,6 @@ static unsigned long map_elf(int elf_fd,if (read (elf_fd, phdr,sizeof (phdr)) !=sizeof (phdr))err (1 ,"Reading program headers" ); -/* We don't know page_offset yet. */ - *page_offset =0 ; -/* Try all the headers: there are usually only three. A read-only one, * a read-write one, and a "note" section which isn't loadable. */ for (i =0 ; i < ehdr->e_phnum; i++) {
@@ -268,14 +259,6 @@ static unsigned long map_elf(int elf_fd,verbose ("Section %i: size %i addr %p\n" , i, phdr[i].p_memsz, (void *)phdr[i].p_paddr); -/* We expect a simple linear address space: every segment must -* have the same difference between virtual (p_vaddr) and -* physical (p_paddr) address. */ -if (!*page_offset) - *page_offset = phdr[i].p_vaddr - phdr[i].p_paddr; -else if (*page_offset != phdr[i].p_vaddr - phdr[i].p_paddr) -errx (1 ,"Page offset of section %i different" , i); -/* We track the first and last address we mapped, so we can * tell entry_point() where to scan. */ if (from_guest_phys (phdr[i].p_paddr) < start)
@@ -288,50 +271,13 @@ static unsigned long map_elf(int elf_fd, phdr[i].p_offset, phdr[i].p_filesz); } -return entry_point (start, end, *page_offset); -} - -/*L:170 Prepare to be SHOCKED and AMAZED. And possibly a trifle nauseated. -* -* We know that CONFIG_PAGE_OFFSET sets what virtual address the kernel expects -* to be. We don't know what that option was, but we can figure it out -* approximately by looking at the addresses in the code. I chose the common -* case of reading a memory location into the %eax register: -* -* movl <some-address>, %eax -* -* This gets encoded as five bytes: "0xA1 <4-byte-address>". For example, -* "0xA1 0x18 0x60 0x47 0xC0" reads the address 0xC0476018 into %eax. -* -* In this example can guess that the kernel was compiled with -* CONFIG_PAGE_OFFSET set to 0xC0000000 (it's always a round number). If the -* kernel were larger than 16MB, we might see 0xC1 addresses show up, but our -* kernel isn't that bloated yet. -* -* Unfortunately, x86 has variable-length instructions, so finding this -* particular instruction properly involves writing a disassembler. Instead, -* we rely on statistics. We look for "0xA1" and tally the different bytes -* which occur 4 bytes later (the "0xC0" in our example above). When one of -* those bytes appears three times, we can be reasonably confident that it -* forms the start of CONFIG_PAGE_OFFSET. -* -* This is amazingly reliable. */ -static unsigned long intuit_page_offset (unsigned char *img,unsigned long len) -{ -unsigned int i, possibilities[256 ] = {0 }; - -for (i =0 ; i +4 < len; i++) { -/* mov 0xXXXXXXXX,%eax */ -if (img[i] ==0xA1 && ++possibilities[img[i+4 ]] >3 ) -return (unsigned long )img[i+4 ] <<24 ; - } -errx (1 ,"could not determine page offset" ); +return entry_point (start, end); }/*L:160 Unfortunately the entire ELF image isn't compressed: the segments * which need loading are extracted and compressed raw. This denies us the * information we need to make a fully-general loader. */ -static unsigned long unpack_bzimage (int fd,unsigned long *page_offset) +static unsigned long unpack_bzimage (int fd) {gzFile f;int ret, len =0 ;
@@ -352,12 +298,7 @@ static unsigned long unpack_bzimage(intverbose ("Unpacked size %i addr %p\n" , len, img); -/* Without the ELF header, we can't tell virtual-physical gap. This is -* CONFIG_PAGE_OFFSET, and people do actually change it. Fortunately, -* I have a clever way of figuring it out from the code itself. */ - *page_offset =intuit_page_offset (img, len); - -return entry_point (img, img + len, *page_offset); +return entry_point (img, img + len); }/*L:150 A bzImage, unlike an ELF file, is not meant to be loaded. You're
@@ -368,7 +309,7 @@ static unsigned long unpack_bzimage(int* The bzImage is formed by putting the decompressing code in front of the * compressed kernel code. So we can simple scan through it looking for the * first "gzip" header, and start decompressing from there. */ -static unsigned long load_bzimage (int fd,unsigned long *page_offset) +static unsigned long load_bzimage (int fd) {unsigned char c;int state =0 ;
@@ -396,7 +337,7 @@ static unsigned long load_bzimage(int fdif (c !=0x03 ) state =-1 ;else -return unpack_bzimage (fd, page_offset); +return unpack_bzimage (fd); } }errx (1 ,"Could not find kernel in bzImage" );
@@ -405,7 +346,7 @@ static unsigned long load_bzimage(int fd/*L:140 Loading the kernel is easy when it's a "vmlinux", but most kernels * come wrapped up in the self-decompressing "bzImage" format. With some funky * coding, we can load those, too. */ -static unsigned long load_kernel (int fd,unsigned long *page_offset) +static unsigned long load_kernel (int fd) {Elf32_Ehdr hdr;
@@ -415,10 +356,10 @@ static unsigned long load_kernel(int fd,/* If it's an ELF file, it starts with "\177ELF" */ if (memcmp (hdr.e_ident,ELFMAG ,SELFMAG ) ==0 ) -return map_elf (fd, &hdr, page_offset); +return map_elf (fd, &hdr);/* Otherwise we assume it's a bzImage, and try to unpack it */ -return load_bzimage (fd, page_offset); +return load_bzimage (fd); }/* This is a trivial little helper to align pages. Andi Kleen hated it because
@@ -463,27 +404,20 @@ static unsigned long load_initrd(const creturn len; } -/* Once we know the address the Guest kernel expects, we can construct simple -* linear page tables for all of memory which will get the Guest far enough +/* Once we know how much memory we have, we can construct simple linear page +* tables which set virtual == physical which will get the Guest far enough * into the boot to create its own. * * We lay them out of the way, just below the initrd (which is why we need to * know its size). */ static unsigned long setup_pagetables (unsigned long mem, -unsigned long initrd_size, -unsigned long page_offset) +unsigned long initrd_size) {unsigned long *pgdir, *linear;unsigned int mapped_pages, i, linear_pages;unsigned int ptes_per_page =getpagesize ()/sizeof (void *); -/* Ideally we map all physical memory starting at page_offset. -* However, if page_offset is 0xC0000000 we can only map 1G of physical -* (0xC0000000 + 1G overflows). */ -if (mem <= -page_offset) - mapped_pages = mem/getpagesize (); -else - mapped_pages = -page_offset/getpagesize (); + mapped_pages = mem/getpagesize ();/* Each PTE page can map ptes_per_page pages: how many do we need? */ linear_pages = (mapped_pages + ptes_per_page-1 )/ptes_per_page;
@@ -500,11 +434,9 @@ static unsigned long setup_pagetables(unfor (i =0 ; i < mapped_pages; i++) linear[i] = ((i *getpagesize ()) |PAGE_PRESENT ); -/* The top level points to the linear page table pages above. The -* entry representing page_offset points to the first one, and they -* continue from there. */ +/* The top level points to the linear page table pages above. */ for (i =0 ; i < mapped_pages; i += ptes_per_page) { - pgdir[(i + page_offset/getpagesize ())/ptes_per_page] + pgdir[i/ptes_per_page] = ((to_guest_phys (linear) + i*sizeof (void *)) |PAGE_PRESENT ); }
@@ -535,15 +467,12 @@ static void concat(char *dst, char *args/* This is where we actually tell the kernel to initialize the Guest. We saw * the arguments it expects when we looked at initialize() in lguest_user.c: * the base of guest "physical" memory, the top physical page to allow, the -* top level pagetable, the entry point and the page_offset constant for the -* Guest. */ -static int tell_kernel (unsigned long pgdir,unsigned long start, -unsigned long page_offset) +* top level pagetable and the entry point for the Guest. */ +static int tell_kernel (unsigned long pgdir,unsigned long start) {unsigned long args[] = {LHREQ_INITIALIZE , (unsigned long )guest_base, - guest_limit /getpagesize (), - pgdir, start, page_offset }; + guest_limit /getpagesize (), pgdir, start };int fd; verbose("Guest: %p - %p (%#lx)\n",
@@ -1424,9 +1353,9 @@ static void usage(void)/*L:105 The main routine is where the real work begins: */ int main (int argc,char *argv[]) { -/* Memory, top-level pagetable, code startpoint, PAGE_OFFSET and size -* of the (optional) initrd. */ -unsigned long mem =0 , pgdir, start, page_offset, initrd_size =0 ; +/* Memory, top-level pagetable, code startpoint and size of the +* (optional) initrd. */ +unsigned long mem =0 , pgdir, start, initrd_size =0 ;/* A temporary and the /dev/lguest file descriptor. */ int i, c, lguest_fd;/* The list of Guest devices, based on command line arguments. */
@@ -1500,8 +1429,7 @@ int main(int argc, char *argv[])setup_console (&device_list);/* Now we load the kernel */ - start =load_kernel (open_or_die (argv[optind+1 ],O_RDONLY ), - &page_offset); + start =load_kernel (open_or_die (argv[optind+1 ],O_RDONLY ));/* Boot information is stashed at physical address 0 */ boot =from_guest_phys (0 );
@@ -1518,7 +1446,7 @@ int main(int argc, char *argv[]) }/* Set up the initial linear pagetables, starting below the initrd. */ - pgdir =setup_pagetables (mem, initrd_size, page_offset); + pgdir =setup_pagetables (mem, initrd_size);/* The Linux boot header contains an "E820" memory map: ours is a * simple, single region. */
@@ -1535,7 +1463,7 @@ int main(int argc, char *argv[])/* We tell the kernel to initialize the Guest: this returns the open * /dev/lguest file descriptor. */ - lguest_fd =tell_kernel (pgdir, start, page_offset); + lguest_fd =tell_kernel (pgdir, start);/* We fork off a child process, which wakes the Launcher whenever one * of the input file descriptors needs attention. Otherwise we would
===================================================================
arch/i386/kernel/asm-offsets.c +1 -0
@@ -133,6 +133,7 @@ void foo(void)#ifdef CONFIG_LGUEST_GUEST BLANK ();OFFSET (LGUEST_DATA_irq_enabled, lguest_data, irq_enabled); +OFFSET (LGUEST_DATA_pgdir, lguest_data, pgdir);OFFSET (LGUEST_PAGES_host_gdt_desc, lguest_pages, state.host_gdt_desc);OFFSET (LGUEST_PAGES_host_idt_desc, lguest_pages, state.host_idt_desc);OFFSET (LGUEST_PAGES_host_cr3, lguest_pages, state.host_cr3);
===================================================================
arch/i386/lguest/boot.c +2 -5
@@ -86,6 +86,7 @@ struct lguest_data lguest_data = { .hcall_status = { [0 ... LHCALL_RING_SIZE-1] = 0xFF }, .noirq_start = (u32)lguest_noirq_start, .noirq_end = (u32)lguest_noirq_end, + .kernel_address = PAGE_OFFSET, .blocked_interrupts = { 1 }, /* Block timer interrupts */ .syscall_vec = SYSCALL_VECTOR, };
@@ -988,11 +989,7 @@ __init void lguest_init(void *boot)/*G:070 Now we've seen all the paravirt_ops, we return to * lguest_init() where the rest of the fairly chaotic boot setup -* occurs. -* -* The Host expects our first hypercall to tell it where our "struct -* lguest_data" is, so we do that first. */ -hcall (LHCALL_LGUEST_INIT ,__pa (&lguest_data),0 ,0 ); +* occurs. */ /* The native boot code sets up initial page tables immediately after * the kernel itself, and sets init_pg_tables_end so they're not
===================================================================
arch/i386/lguest/head.S +36 -5
59 lines not on this page · raw message
===================================================================
drivers/lguest/hypercalls.c +4 -4
@@ -181,14 +181,14 @@ static void initialize(struct lguest *lg/* The Guest tells us where we're not to deliver interrupts by putting * the range of addresses into "struct lguest_data". */ if (get_user (lg->noirq_start, &lg->lguest_data->noirq_start) - ||get_user (lg->noirq_end, &lg->lguest_data->noirq_end) -/* We tell the Guest that it can't use the top 4MB of virtual -* addresses used by the Switcher. */ - ||put_user (4U *1024 *1024 , &lg->lguest_data->reserve_mem)) + ||get_user (lg->noirq_end, &lg->lguest_data->noirq_end))kill_guest (lg,"bad guest page %p" , lg->lguest_data);/* We write the current time into the Guest's data page once now. */ write_timestamp (lg); + +/* page_tables.c will also do some setup. */ +page_table_guest_data_init (lg);/* This is the one case where the above accesses might have been the * first write to a Guest page. This may have caused a copy-on-write
===================================================================
drivers/lguest/i386_core.c +4 -3
@@ -216,9 +216,10 @@ static int emulate_insn(struct lguest *l* guest_pa just subtracts the Guest's page_offset. */ unsigned long physaddr =guest_pa (lg, lg->regs->eip); -/* The guest_pa() function only works for Guest kernel addresses, but -* that's all we're trying to do anyway. */ -if (lg->regs->eip < lg->page_offset) +/* This must be the Guest kernel trying to do something, not userspace! +* The bottom two bits of the CS segment register are the privilege +* level. */ +if ((lg->regs->cs &3 ) !=GUEST_PL )return 0 ;/* Decoding x86 instructions is icky. */
===================================================================
drivers/lguest/interrupts_and_traps.c +9 -4
@@ -62,8 +62,9 @@ static void push_guest_stack(struct lgue* it). */ static void set_guest_interrupt (struct lguest *lg,u32 lo,u32 hi,int has_err) { -unsigned long gstack; +unsigned long gstack, origstack;u32 eflags, ss, irq_enable; +unsigned long virtstack;/* There are two cases for interrupts: one where the Guest is already * in the kernel, and a more complex one where the Guest is in
@@ -71,8 +72,10 @@ static void set_guest_interrupt(struct lif ((lg->regs->ss&0x3 ) !=GUEST_PL ) {/* The Guest told us their kernel stack with the SET_STACK * hypercall: both the virtual address and the segment */ - gstack =guest_pa (lg, lg->esp1); + virtstack = lg->esp1; ss = lg->ss1; + + origstack = gstack =guest_pa (lg, virtstack);/* We push the old stack segment and pointer onto the new * stack: when the Guest does an "iret" back from the interrupt * handler the CPU will notice they're dropping privilege
@@ -81,8 +84,10 @@ static void set_guest_interrupt(struct lpush_guest_stack (lg, &gstack, lg->regs->esp); }else {/* We're staying on the same Guest (kernel) stack. */ - gstack =guest_pa (lg, lg->regs->esp); + virtstack = lg->regs->esp; ss = lg->regs->ss; + + origstack = gstack =guest_pa (lg, virtstack); }/* Remember that we never let the Guest actually disable interrupts, so
@@ -108,7 +113,7 @@ static void set_guest_interrupt(struct l/* Now we've pushed all the old state, we change the stack, the code * segment and the address to execute. */ lg->regs->ss = ss; - lg->regs->esp = gstack + lg->page_offset; + lg->regs->esp = virtstack + (gstack - origstack); lg->regs->cs = (__KERNEL_CS|GUEST_PL ); lg->regs->eip =idt_address (lo, hi);
===================================================================
drivers/lguest/lg.h +3 -5
@@ -64,7 +64,7 @@ struct lguest/* This provides the offset to the base of guest-physical * memory in the Launcher. */ void __user *mem_base; -u32 page_offset; +unsigned long kernel_address;u32 cr2;int halted;int ts;
@@ -166,6 +166,8 @@ void map_switcher_in_guest(struct lguestvoid map_switcher_in_guest (struct lguest *lg,struct lguest_pages *pages);int demand_page (struct lguest *info,unsigned long cr2,int errcode);void pin_page (struct lguest *lg,unsigned long vaddr); +unsigned long guest_pa (struct lguest *lg,unsigned long vaddr); +void page_table_guest_data_init (struct lguest *lg);/* <arch>_core.c: */ void lguest_arch_host_init (void );
@@ -230,9 +232,5 @@ do { \ } while(0)/* (End of aside) :*/ -static inline unsigned long guest_pa(struct lguest *lg, unsigned long vaddr) -{ -return vaddr - lg->page_offset; -}#endif /* __ASSEMBLY__ */ #endif /* _LGUEST_H */
===================================================================
drivers/lguest/lguest_user.c +2 -9
@@ -111,7 +111,7 @@ static ssize_t read(struct file *file, creturn run_guest (lg, (unsigned long __user *)user); } -/*L:020 The initialization write supplies 5 pointer sized (32 or 64 bit) +/*L:020 The initialization write supplies 4 pointer sized (32 or 64 bit) * values (in addition to the LHREQ_INITIALIZE value). These are: * * base: The start of the Guest-physical memory inside the Launcher memory.
@@ -124,12 +124,6 @@ static ssize_t read(struct file *file, c* pagetables (which are set up by the Launcher). * * start: The first instruction to execute ("eip" in x86-speak). -* -* page_offset: The PAGE_OFFSET constant in the Guest kernel. We should -* probably wean the code off this, but it's a very useful constant! Any -* address above this is within the Guest kernel, and any kernel address can -* quickly converted from physical to virtual by adding PAGE_OFFSET. It's -* 0xC0000000 (3G) by default, but it's configurable at kernel build time. */ static int initialize (struct file *file,const unsigned long __user *input) {
@@ -137,7 +131,7 @@ static int initialize(struct file *file,* Guest. */ struct lguest *lg;int err; -unsigned long args[5 ]; +unsigned long args[4 ];/* We grab the Big Lguest lock, which protects against multiple * simultaneous initializations. */
@@ -162,7 +156,6 @@ static int initialize(struct file *file,/* Populate the easy fields of our "struct lguest" */ lg->mem_base = (void __user *)(long )args[0 ]; lg->pfn_limit = args[1 ]; - lg->page_offset = args[4 ];/* We need a complete page for the Guest registers: they are accessible * to the Guest and we can only grant it access to whole pages. */
===================================================================
drivers/lguest/page_tables.c +40 -7
83 lines not on this page · raw message
===================================================================
include/asm-i386/lguest_hcall.h +3 -0
@@ -36,6 +36,7 @@* definition of a gentleman: "someone who is only rude intentionally". */ #define LGUEST_TRAP_ENTRY 0x1F +#ifndef __ASSEMBLY__static inline unsigned long hcall (unsigned long call,unsigned long arg1,unsigned long arg2,unsigned long arg3)
@@ -71,4 +72,6 @@ struct hcall_args/* These map directly onto eax, ebx, ecx, edx in struct lguest_regs */ unsigned long arg0, arg2, arg3, arg1; }; + +#endif /* !__ASSEMBLY__ */ #endif /* _I386_LGUEST_HCALL_H */
===================================================================
include/linux/lguest.h +4 -1
@@ -44,11 +44,14 @@ struct lguest_dataunsigned long reserve_mem;/* KHz for the TSC clock. */ u32 tsc_khz; +/* Page where the top-level pagetable is */ +unsigned long pgdir;/* Fields initialized by the Guest at boot: */ /* Instruction range to suppress interrupts even if enabled */ unsigned long noirq_start, noirq_end; - +/* Address above which page tables are all identical. */ +unsigned long kernel_address;/* The vector to try to use for system calls (0x40 or 0x80). */ unsigned int syscall_vec; };
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
Re: [patch 31/43] lguest: Boot with virtual == physical to get closer to native Linux.
Quoted 8 lines · [email protected] wrote:
drivers/lguest/page_tables.c +8
-0+/* We walk down the guest page tables to get a guest-physical address */ +unsigned long guest_pa (struct lguest *lg,unsigned long vaddr) +{ +pgd_t gpgd; +pte_t gpte; + +/* First step: get the top-level Guest page table entry. */ + gpgd =__pgd (lgread_u32 (lg,gpgd_addr (lg, vaddr)));
drivers/lguest/page_tables.c void guest_pagetable_flush_user(struct l · line 382
Quoted 10 lines
drivers/lguest/page_tables.c +10
-0+/* Toplevel not present? We can't map it in. */ +if (!(pgd_flags (gpgd) & _PAGE_PRESENT)) +kill_guest (lg,"Bad address %#lx" , vaddr); + + gpte =__pte (lgread_u32 (lg,gpte_addr (lg, gpgd, vaddr))); +if (!(pte_flags (gpte) & _PAGE_PRESENT)) +kill_guest (lg,"Bad address %#lx" , vaddr); + +return pte_pfn (gpte) *PAGE_SIZE | (vaddr & ~PAGE_MASK ); +}
That's nice and general, but is it necessary? Is this just to handle the two cases of booting with P=V, then switching to the normal kernel offset?
J
Re: [Lguest] [patch 31/43] lguest: Boot with virtual == physical to get closer to native Linux.
It would actually simplify a nasty problem I have had on Plan 9. I can use this change!
Thanks
ron
Re: [patch 31/43] lguest: Boot with virtual == physical to get closer to native Linux.
In reply to Jeremy FitzhardingeQuoted 15 lines · Jeremy Fitzhardinge
Quoted 14 lines · [email protected] wrote:
drivers/lguest/page_tables.c +14
-0+/* We walk down the guest page tables to get a guest-physical address */ +unsigned long guest_pa (struct lguest *lg,unsigned long vaddr) +{ +pgd_t gpgd; +pte_t gpte; + +/* First step: get the top-level Guest page table entry. */ + gpgd =__pgd (lgread_u32 (lg,gpgd_addr (lg, vaddr))); +/* Toplevel not present? We can't map it in. */ +if (!(pgd_flags (gpgd) & _PAGE_PRESENT)) +kill_guest (lg,"Bad address %#lx" , vaddr); + + gpte =__pte (lgread_u32 (lg,gpte_addr (lg, gpgd, vaddr))); +if (!(pte_flags (gpte) & _PAGE_PRESENT))
Quoted 8 lines
drivers/lguest/page_tables.c void guest_pagetable_flush_user(struct l · line 382
Quoted 4 lines
drivers/lguest/page_tables.c +4
-0+kill_guest (lg,"Bad address %#lx" , vaddr); + +return pte_pfn (gpte) *PAGE_SIZE | (vaddr & ~PAGE_MASK ); +}That's nice and general, but is it necessary? Is this just to handle the two cases of booting with P=V, then switching to the normal kernel offset?
Hi Jeremy,
It's probably overkill, but it has the advantage of being
straightforward and futureproof.
Cheers,
Rusty.
PATCH 32/43 lguest: Virtio interface
In reply to [email protected]This attempts to implement a "virtual I/O" layer which should allow common drivers to be efficiently used across most virtual I/O mechanisms. It will no-doubt need further enhancement.
The virtio drivers add buffers to virtio queues; as the buffers are consumed the driver "interrupt" callbacks are invoked.
There is also a generic implementation of config space which drivers can query to get setup information from the host.
--- drivers/Kconfig | 2 drivers/Makefile | 1 drivers/virtio/Kconfig | 3 drivers/virtio/Makefile | 1 drivers/virtio/config.c | 13 ++ drivers/virtio/virtio.c | 171 +++++++++++++++++++++++++++++++++++++++ include/linux/mod_devicetable.h | 6 + include/linux/virtio.h | 110 +++++++++++++++++++++++++ include/linux/virtio_config.h | 111 +++++++++++++++++++++++++ 9 files changed, 418 insertions(+)
===================================================================
drivers/Kconfig +2 -0
@@ -87,4 +87,6 @@ source "drivers/kvm/Kconfig" source "drivers/kvm/Kconfig" source "drivers/uio/Kconfig" + +source "drivers/virtio/Kconfig" endmenu
===================================================================
drivers/Makefile +1 -0
@@ -88,3 +88,4 @@ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ +obj-$(CONFIG_VIRTIO) += virtio/
===================================================================
drivers/virtio/Kconfig new file +3 -0
@@ -0,0 +1,3 @@ +# Virtio always gets selected by whoever wants it. +config VIRTIO + bool
===================================================================
drivers/virtio/Makefile new file +1 -0
@@ -0,0 +1,1 @@ +obj-$(CONFIG_VIRTIO) += virtio.o
===================================================================
drivers/virtio/config.c new file +13 -0
@@ -0,0 +1,13 @@ +/* Configuration space parsing helpers for virtio. +* +* The configuration is [type][len][... len bytes ...] fields. +* +* Copyright 2007 Rusty Russell, IBM Corporation. +* GPL v2 or later. +*/ +#include <linux/err.h> +#include <linux/virtio.h> +#include <linux/virtio_config.h> +#include <linux/bug.h> +#include <asm/system.h> +
===================================================================
drivers/virtio/virtio.c new file +171 -0
171 lines not on this page · raw message
===================================================================
include/linux/mod_devicetable.h +6 -0
@@ -340,4 +340,10 @@ struct parisc_device_id {#define PA_HVERSION_ANY_ID 0xffff#define PA_SVERSION_ANY_ID 0xffffffff +struct virtio_device_id { +__u32 device; +__u32 vendor; +}; +#define VIRTIO_DEV_ANY_ID 0xffffffff +#endif /* LINUX_MOD_DEVICETABLE_H */
===================================================================
include/linux/virtio.h new file +110 -0
110 lines not on this page · raw message
===================================================================
include/linux/virtio_config.h new file +111 -0
111 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
Re: [patch 32/43] lguest: Virtio interface
Am Mittwoch, 26. September 2007 schrieb [email protected]:
Quoted 1 line
include/linux/virtio_config.h | 111 +++++++++++++++++++++++++
I get the following warnings on s390:
/space/linux-2.6/drivers/block/virtio_blk.c: In function 'virtblk_probe':
/space/linux-2.6/drivers/block/virtio_blk.c:240: warning: passing argument 1 of '__swab16s' from incompatible pointer type /space/linux-2.6/drivers/block/virtio_blk.c:240: warning: passing argument 1 of '__swab32s' from incompatible pointer type /space/linux-2.6/drivers/block/virtio_blk.c:254: warning: passing argument 1 of '__swab16s' from incompatible pointer type /space/linux-2.6/drivers/block/virtio_blk.c:254: warning: passing argument 1 of '__swab64s' from incompatible pointer type /space/linux-2.6/drivers/block/virtio_blk.c:262: warning: passing argument 1 of '__swab16s' from incompatible pointer type /space/linux-2.6/drivers/block/virtio_blk.c:262: warning: passing argument 1 of '__swab64s' from incompatible pointer type
So what about:
--- include/linux/virtio_config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
include/linux/virtio_config.h +3 -3
@@ -85,9 +85,9 @@ struct virtio_config_ops && sizeof(*(v)) != 4 && sizeof(*(v)) != 8); \if (!_err) { \switch (sizeof (*(v))) { \ -case 2 :le16_to_cpus (v);break ; \ -case 4 :le32_to_cpus (v);break ; \ -case 8 :le64_to_cpus (v);break ; \ +case 2 :le16_to_cpus ((__u16 *) v);break ; \ +case 4 :le32_to_cpus ((__u32 *) v);break ; \ +case 8 :le64_to_cpus ((__u64 *) v);break ; \ } \ } \ _err; \
Re: [patch 32/43] lguest: Virtio interface
Quoted 7 lines · Christian Borntraeger
Am Mittwoch, 26. September 2007 schrieb [email protected]:
Quoted 1 line
include/linux/virtio_config.h | 111 +++++++++++++++++++++++++I get the following warnings on s390:
/space/linux-2.6/drivers/block/virtio_blk.c: In function 'virtblk_probe':
/space/linux-2.6/drivers/block/virtio_blk.c:240: warning: passing argument 1 of '__swab16s' from incompatible pointer type
Hi Christian,
Oh! Took me a while to figure out what the problem was. I must be
particularly slow this evening.
I've rolled your fix in.
Thanks!
Rusty.
Re: [patch 32/43] lguest: Virtio interface
In reply to [email protected]Hi Rusty,
I have a late question :-)
Quoted 2 lines
There is also a generic implementation of config space which drivers can query to get setup information from the host.
After thinking about that for a while, is there a special reason the config field is defined as little endian? The only reason I can come up with, is to use virtio for cross platform emulators, lets say a ppc emulates an x86 system with virtio devices instead of real ones.
Do you have other reasons for a fixed endianess?
Christian
Re: [patch 32/43] lguest: Virtio interface
Quoted 11 lines · Christian Borntraeger
Hi Rusty,
I have a late question :-)
Quoted 2 lines
There is also a generic implementation of config space which drivers can query to get setup information from the host.
After thinking about that for a while, is there a special reason the config field is defined as little endian? The only reason I can come up with, is to use virtio for cross platform emulators, lets say a ppc emulates an x86 system with virtio devices instead of real ones.
Not really.
Some platforms have multi-endianess capabilities. So you may want a virtual machine running in a different endianess than the host machine.
Signature · 5 lines
Glauber de Oliveira Costa. "Free as in Freedom" http://glommer.net "The less confident you are, the more serious you have to act."
Arnd Bergmann
maintainerRe: [patch 32/43] lguest: Virtio interface
Quoted 8 lines · Glauber de Oliveira Costa
Quoted 5 lines · Christian Borntraeger
After thinking about that for a while, is there a special reason the config field is defined as little endian? The only reason I can come up with, is to use virtio for cross platform emulators, lets say a ppc emulates an x86 system with virtio devices instead of real ones.
Quoted 5 lines
Not really.
Some platforms have multi-endianess capabilities. So you may want a virtual machine running in a different endianess than the host machine.
Since virtio is a rather generic concept, there are other things you might want to do with them, like routing virtio data over the network to a machine with different endianess, or actually building hardware that implements some sort of virtio compatible interface.
Arnd <><
Re: [patch 32/43] lguest: Virtio interface
Quoted 6 lines · Arnd Bergmann
Since virtio is a rather generic concept, there are other things you might want to do with them, like routing virtio data over the network to a machine with different endianess, or actually building hardware that implements some sort of virtio compatible interface.
That being the case, why not just use network byte order (big endian)?
-eric
Arnd Bergmann
maintainerRe: [patch 32/43] lguest: Virtio interface
Quoted 1 line · Eric Van Hensbergen
That being the case, why not just use network byte order (big endian)?
PCI hardware is by convention little-endian, so I'd vote for that, but it doesn't really matter as long as it's fixed endian.
The advantage of big-endian would be that it's not what x86 uses, so endianess bugs are more likely to hit early.
Arnd <><
Re: [patch 32/43] lguest: Virtio interface
In reply to Christian BorntraegerQuoted 7 lines · On Wednesday 10 October 2007 18:50:48 Christian Borntraeger wrote:
Hi Rusty,
I have a late question :-)
Quoted 2 lines
There is also a generic implementation of config space which drivers can query to get setup information from the host.
Quoted 6 lines
After thinking about that for a while, is there a special reason the config field is defined as little endian? The only reason I can come up with, is to use virtio for cross platform emulators, lets say a ppc emulates an x86 system with virtio devices instead of real ones.
Do you have other reasons for a fixed endianess?
Other people came up with really good reasons, but in fact it was because PCI is little-endian. If someone found a compelling counter-argument, I'd change it.
Cheers,
Rusty.
PATCH 33/43 lguest: Net driver using virtio
In reply to [email protected]The network driver uses two virtqueues: one for input packets and one for output packets. This has nice locking properties (ie. we don't do any for recv vs send).
TODO:
1) Big packets.
2) Multi-client devices (maybe separate driver?).
3) Resolve freeing of old xmit skbs (Christian Borntraeger)
--- drivers/net/Kconfig | 6 drivers/net/Makefile | 2 drivers/net/virtio_net.c | 438 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/virtio_net.h | 36 +++ 4 files changed, 481 insertions(+), 1 deletion(-)
===================================================================
drivers/net/Kconfig +6 -0
@@ -2998,4 +2998,10 @@ config NET_POLL_CONTROLLER config NET_POLL_CONTROLLER def_bool NETPOLL +config VIRTIO_NET + tristate "Virtio network driver (EXPERIMENTAL)" + depends on EXPERIMENTAL && VIRTIO + ---help--- + This is the virtual network driver for lguest. Say Y or M. + endif # NETDEVICES
===================================================================
drivers/net/Makefile +1 -1
@@ -38,7 +38,7 @@ obj-$(CONFIG_SUNVNET) += sunvnet.o obj-$(CONFIG_MACE) += mace.o obj-$(CONFIG_BMAC) += bmac.o - +obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_DGRS) += dgrs.o obj-$(CONFIG_VORTEX) += 3c59x.o obj-$(CONFIG_TYPHOON) += typhoon.o
===================================================================
drivers/net/virtio_net.c new file +438 -0
438 lines not on this page · raw message
===================================================================
include/linux/virtio_net.h new file +36 -0
@@ -0,0 +1,36 @@ +#ifndef _LINUX_VIRTIO_NET_H +#define _LINUX_VIRTIO_NET_H +#include <linux/virtio_config.h> + +/* The ID for virtio_net */ +#define VIRTIO_ID_NET 1 + +/* The bitmap of config for virtio net */ +#define VIRTIO_CONFIG_NET_F 0x40 +#define VIRTIO_NET_F_NO_CSUM 0 +#define VIRTIO_NET_F_TSO4 1 +#define VIRTIO_NET_F_UFO 2 +#define VIRTIO_NET_F_TSO4_ECN 3 +#define VIRTIO_NET_F_TSO6 4 + +/* The config defining mac address. */ +#define VIRTIO_CONFIG_NET_MAC_F 0x41 + +/* This is the first element of the scatter-gather list. If you don't +* specify GSO or CSUM features, you can simply ignore the header. */ +struct virtio_net_hdr +{ +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset +__u8 flags; +#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame +#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) +/* FIXME: Do we need this? If they said they can handle ECN, do they care? */ +#define VIRTIO_NET_HDR_GSO_TCPV4_ECN 2 // GSO frame, IPv4 TCP w/ ECN +#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) +#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP +__u8 gso_type; +__u16 gso_size; +__u16 csum_start; +__u16 csum_offset; +}; +#endif /* _LINUX_VIRTIO_NET_H */
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 33/43 lguest: Net driver using virtio
In reply to [email protected]The network driver uses two virtqueues: one for input packets and one for output packets. This has nice locking properties (ie. we don't do any for recv vs send).
TODO:
1) Big packets.
2) Multi-client devices (maybe separate driver?).
3) Resolve freeing of old xmit skbs (Christian Borntraeger)
--- drivers/net/Kconfig | 6 drivers/net/Makefile | 2 drivers/net/virtio_net.c | 438 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/virtio_net.h | 36 +++ 4 files changed, 481 insertions(+), 1 deletion(-)
===================================================================
drivers/net/Kconfig +6 -0
@@ -2998,4 +2998,10 @@ config NET_POLL_CONTROLLER config NET_POLL_CONTROLLER def_bool NETPOLL +config VIRTIO_NET + tristate "Virtio network driver (EXPERIMENTAL)" + depends on EXPERIMENTAL && VIRTIO + ---help--- + This is the virtual network driver for lguest. Say Y or M. + endif # NETDEVICES
===================================================================
drivers/net/Makefile +1 -1
@@ -38,7 +38,7 @@ obj-$(CONFIG_SUNVNET) += sunvnet.o obj-$(CONFIG_MACE) += mace.o obj-$(CONFIG_BMAC) += bmac.o - +obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_DGRS) += dgrs.o obj-$(CONFIG_VORTEX) += 3c59x.o obj-$(CONFIG_TYPHOON) += typhoon.o
===================================================================
drivers/net/virtio_net.c new file +438 -0
438 lines not on this page · raw message
===================================================================
include/linux/virtio_net.h new file +36 -0
@@ -0,0 +1,36 @@ +#ifndef _LINUX_VIRTIO_NET_H +#define _LINUX_VIRTIO_NET_H +#include <linux/virtio_config.h> + +/* The ID for virtio_net */ +#define VIRTIO_ID_NET 1 + +/* The bitmap of config for virtio net */ +#define VIRTIO_CONFIG_NET_F 0x40 +#define VIRTIO_NET_F_NO_CSUM 0 +#define VIRTIO_NET_F_TSO4 1 +#define VIRTIO_NET_F_UFO 2 +#define VIRTIO_NET_F_TSO4_ECN 3 +#define VIRTIO_NET_F_TSO6 4 + +/* The config defining mac address. */ +#define VIRTIO_CONFIG_NET_MAC_F 0x41 + +/* This is the first element of the scatter-gather list. If you don't +* specify GSO or CSUM features, you can simply ignore the header. */ +struct virtio_net_hdr +{ +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset +__u8 flags; +#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame +#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) +/* FIXME: Do we need this? If they said they can handle ECN, do they care? */ +#define VIRTIO_NET_HDR_GSO_TCPV4_ECN 2 // GSO frame, IPv4 TCP w/ ECN +#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) +#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP +__u8 gso_type; +__u16 gso_size; +__u16 csum_start; +__u16 csum_offset; +}; +#endif /* _LINUX_VIRTIO_NET_H */
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 34/43 lguest: Block driver using virtio.
In reply to [email protected]The block driver uses scatter-gather lists with sg[0] being the request information (struct virtio_blk_outhdr) with the type, sector and inbuf id. The next N sg entries are the bio itself, then the last sg is the status byte. Whether the N entries are in or out depends on whether it's a read or a write.
We accept the normal (SCSI) ioctls: they get handed through to the other side which can then handle it or reply that it's unsupported. It's not clear that this actually works in general, since I don't know if blk_pc_request() requests have an accurate rq_data_dir().
Although we try to reply -ENOTTY on unsupported commands, ioctl(fd, CDROMEJECT) returns success to userspace. This needs a separate patch.
--- drivers/block/Kconfig | 6 drivers/block/Makefile | 1 drivers/block/virtio_blk.c | 327 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/Kbuild | 1 include/linux/virtio_blk.h | 51 ++++++ 5 files changed, 386 insertions(+)
===================================================================
drivers/block/Kconfig +6 -0
@@ -443,4 +443,10 @@ config XEN_BLKDEV_FRONTEND block device driver. It communicates with a back-end driver in another domain which drives the actual block device. +config VIRTIO_BLK + tristate "Virtio block driver (EXPERIMENTAL)" + depends on EXPERIMENTAL && VIRTIO + ---help--- + This is the virtual block driver for lguest. Say Y or M. + endif # BLK_DEV
===================================================================
drivers/block/Makefile +1 -0
@@ -25,6 +25,7 @@ obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryptoloop.o +obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o obj-$(CONFIG_VIODASD) += viodasd.o obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
===================================================================
drivers/block/virtio_blk.c new file +327 -0
327 lines not on this page · raw message
===================================================================
include/linux/Kbuild +1 -0
@@ -343,6 +343,7 @@ unifdef-y += utsname.h unifdef-y += utsname.h unifdef-y += videodev2.h unifdef-y += videodev.h +unifdef-y += virtio_blk.h unifdef-y += wait.h unifdef-y += wanrouter.h unifdef-y += watchdog.h
===================================================================
include/linux/virtio_blk.h new file +51 -0
51 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
Re: [Lguest] [patch 34/43] lguest: Block driver using virtio.
Review
Quoted 1 line · [email protected] wrote:
(quoted diff) +1
-0+ sprintf(vblk->disk->disk_name, "vd%c", virtblk_index++);
Update lguest documentation to reflect the new virtual block device name.
---
lguest/lguest.txt renamed from lguest/lguest.txt.orig +3 -3
@@ -69,7 +69,7 @@ - Run an lguest as root: - Documentation/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 --block=rootfile root=/dev/lgba + Documentation/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 --block=rootfile root=/dev/vda Explanation: 64: the amount of memory to use, in MB.
@@ -80,10 +80,10 @@ --tunnet=192.168.19.1: configures a "tap" device for networking with this IP address. - --block=rootfile: a file or block device which becomes /dev/lgba + --block=rootfile: a file or block device which becomes /dev/vda inside the guest. - root=/dev/lgba: this (and anything else on the command line) are + root=/dev/vda: this (and anything else on the command line) are kernel boot parameters. - Configuring networking. I usually have the host masquerade, using
Rusty Russell
AppliedRe: [Lguest] [patch 34/43] lguest: Block driver using virtio.
Quoted 6 lines · Chris Malley
Review
Quoted 1 line · [email protected] wrote:
(quoted diff) +1
-0+ sprintf(vblk->disk->disk_name, "vd%c", virtblk_index++);Update lguest documentation to reflect the new virtual block device name.
Signed-off-by Chris Malley <[email protected]>
Thanks, applied!
Rusty.
PATCH 35/43 lguest: Virtio console driver
In reply to [email protected]This is an hvc-based virtio console driver. It's suboptimal becuase hvc expects to have raw access to interrupts and virtio doesn't assume that, so it currently polls.
There are two solutions: expose hvc's "kick" interface, or wean off hvc.
--- drivers/char/Kconfig | 4 drivers/char/Makefile | 1 drivers/char/virtio_console.c | 225 ++++++++++++++++++++++++++++++++++++++++ include/linux/virtio_console.h | 12 ++ 4 files changed, 242 insertions(+)
===================================================================
drivers/char/Kconfig +4 -0
@@ -613,6 +613,10 @@ config HVC_XEN help Xen virtual console device driver +config VIRTIO_CONSOLE + bool + select HVC_DRIVER + config HVCS tristate "IBM Hypervisor Virtual Console Server support" depends on PPC_PSERIES
===================================================================
drivers/char/Makefile +1 -0
@@ -50,6 +50,7 @@ obj-$(CONFIG_HVC_BEAT) += hvc_beat.o obj-$(CONFIG_HVC_BEAT) += hvc_beat.o obj-$(CONFIG_HVC_DRIVER) += hvc_console.o obj-$(CONFIG_HVC_XEN) += hvc_xen.o +obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o
===================================================================
drivers/char/virtio_console.c new file +225 -0
225 lines not on this page · raw message
===================================================================
include/linux/virtio_console.h new file +12 -0
@@ -0,0 +1,12 @@ +#ifndef _LINUX_VIRTIO_CONSOLE_H +#define _LINUX_VIRTIO_CONSOLE_H +#include <linux/virtio_config.h> + +/* The ID for virtio console */ +#define VIRTIO_ID_CONSOLE 3 + +#ifdef __KERNEL__ +int __init virtio_cons_early_init (int (*put_chars)(u32 ,const char *,int )); +#endif /* __KERNEL__ */ + +#endif /* _LINUX_VIRTIO_CONSOLE_H */
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 36/43 lguest: Module autoprobing support for virtio drivers.
In reply to [email protected]This adds the logic to convert the virtio ids into module aliases, and includes a modalias entry in sysfs and the env var to make probing work.
--- drivers/virtio/virtio.c | 26 ++++++++++++++++++++++++++ scripts/mod/file2alias.c | 18 ++++++++++++++++++ 2 files changed, 44 insertions(+)
===================================================================
drivers/virtio/virtio.c +26 -0
@@ -20,10 +20,19 @@ static ssize_t status_show(struct device struct virtio_device *dev = container_of(_d,struct virtio_device,dev);return sprintf (buf,"0x%08x" , dev->config->get_status (dev)); } +static ssize_t modalias_show (struct device *_d, +struct device_attribute *attr,char *buf) +{ + struct virtio_device *dev = container_of(_d,struct virtio_device,dev); + +return sprintf (buf,"virtio:d%08Xv%08X\n" , + dev->id.device, dev->id.vendor); +}static struct device_attribute virtio_dev_attrs[] = {__ATTR_RO (device),__ATTR_RO (vendor),__ATTR_RO (status), +__ATTR_RO (modalias), __ATTR_NULL };
@@ -51,10 +60,27 @@ static int virtio_dev_match(struct devicreturn 0 ; } +static int virtio_uevent (struct device *_dv,char **envp, +int num_envp,char *buffer,int buffer_size) +{ + struct virtio_device *dev = container_of(_dv,struct virtio_device,dev); +int i =0 , err; +int length =0 ; + + err =add_uevent_var (envp, num_envp, &i, buffer, buffer_size, &length, +"MODALIAS=virtio:d%08Xv%08X" , + dev->id.device, dev->id.vendor); +if (err) +return err; + envp[i] =NULL ; +return 0 ; +} +static struct bus_type virtio_bus = { .name ="virtio" , .match = virtio_dev_match, .dev_attrs = virtio_dev_attrs, + .uevent = virtio_uevent, };static void add_status (struct virtio_device *dev,unsigned status)
===================================================================
scripts/mod/file2alias.c +18 -0
@@ -484,6 +484,20 @@ static int do_parisc_entry(const char *freturn 1 ; } +/* Looks like: virtio:dNvN */ +static int do_virtio_entry (const char *filename,struct virtio_device_id *id, +char *alias) +{ + id->device =TO_NATIVE (id->device); + id->vendor =TO_NATIVE (id->vendor); + +strcpy (alias,"virtio:" ); +ADD (alias,"d" ,1 , id->device); +ADD (alias,"v" , id->vendor !=VIRTIO_DEV_ANY_ID , id->vendor); + +return 1 ; +} +/* Ignore any prefix, eg. v850 prepends _ */ static inline int sym_is (const char *symbol,const char *name) {
@@ -599,6 +613,10 @@ void handle_moddevtable(struct module *mdo_table (symval, sym->st_size,sizeof (struct parisc_device_id ),"parisc" , do_parisc_entry, mod); + else if (sym_is(symname, "__mod_virtio_device_table")) + do_table(symval, sym->st_size, + sizeof(struct virtio_device_id), "virtio", + do_virtio_entry, mod); }/* Now add out buffered information to the generated C source */
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 37/43 lguest: Virtio helper routines for a descriptor ringbuffer implementation
In reply to [email protected]These helper routines supply most of the virtqueue_ops for hypervisors which want to use a ring for virtio. Unlike the previous lguest implementation:
1) The rings are variable sized (2^n-1 elements).
2) They have an unfortunate limit of 65535 bytes per sg element.
3) The page numbers are always 64 bit (PAE anyone?)
4) They no longer place used[] on a separate page, just a separate cacheline.
5) We do a modulo on a variable. We could be tricky if we cared.
6) Interrupts and notifies are suppressed using flags within the rings.
Users need only get the ring pages and provide a notify hook (KVM wants the guest to allocate the rings, lguest does it sanely).
--- arch/i386/lguest/Kconfig | 1 drivers/virtio/Kconfig | 5 drivers/virtio/Makefile | 1 drivers/virtio/virtio_ring.c | 313 ++++++++++++++++++++++++++++++++++++++++++ include/linux/virtio_ring.h | 119 +++++++++++++++ 5 files changed, 439 insertions(+)
===================================================================
arch/i386/lguest/Kconfig +1 -0
@@ -2,6 +2,7 @@ config LGUEST_GUEST bool "Lguest guest support" select PARAVIRT depends on !X86_PAE + select VIRTIO_RING help Lguest is a tiny in-kernel hypervisor. Selecting this will allow your kernel to boot under lguest. This option will increase
===================================================================
drivers/virtio/Kconfig +5 -0
@@ -1,3 +1,8 @@ # Virtio always gets selected by whoever wants it. config VIRTIO bool + +# Similarly the virtio ring implementation. +config VIRTIO_RING + bool + depends on VIRTIO
===================================================================
drivers/virtio/Makefile +1 -0
@@ -1,1 +1,2 @@ obj-$(CONFIG_VIRTIO) += virtio.o obj-$(CONFIG_VIRTIO) += virtio.o +obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
===================================================================
drivers/virtio/virtio_ring.c new file +313 -0
313 lines not on this page · raw message
===================================================================
include/linux/virtio_ring.h new file +119 -0
119 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
Re: [patch 37/43] lguest: Virtio helper routines for a descriptor ringbuffer implementation
Quoted 12 lines · [email protected] wrote:
These helper routines supply most of the
virtqueue_opsfor hypervisors which want to use a ring for virtio. Unlike the previous lguest implementation:1) The rings are variable sized (2^n-1 elements).
2) They have an unfortunate limit of 65535 bytes per sg element.
3) The page numbers are always 64 bit (PAE anyone?)
4) They no longer place used[] on a separate page, just a separate cacheline.
5) We do a modulo on a variable. We could be tricky if we cared.
6) Interrupts and notifies are suppressed using flags within the rings.
Quoted 3 lines
Users need only get the ring pages and provide a notify hook (KVM wants the guest to allocate the rings, lguest does it sanely).
This fixes an ABI. I don't think we have enough experience with this to set an ABI for 2.6.24. I also owe you a patch for scatter/gather indirection instead of chaining.
It is okay to put this in as long as 2.6.24 drivers won't try to talk to 2.6.25+ hosts (which presumably will have a stable ABI). We could do this by having "stable API" feature bit which the new drivers will require, and an "2.6.24 ABI" feature bit which the current drivers will require, but the whole config interface itself is too new for this in my opinion.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 37/43] lguest: Virtio helper routines for a descriptor ringbuffer implementation
Quoted 21 lines · Avi Kivity
Quoted 12 lines · [email protected] wrote:
These helper routines supply most of the
virtqueue_opsfor hypervisors which want to use a ring for virtio. Unlike the previous lguest implementation:1) The rings are variable sized (2^n-1 elements).
2) They have an unfortunate limit of 65535 bytes per sg element.
3) The page numbers are always 64 bit (PAE anyone?)
4) They no longer place used[] on a separate page, just a separate cacheline.
5) We do a modulo on a variable. We could be tricky if we cared.
6) Interrupts and notifies are suppressed using flags within the rings.Quoted 2 lines
Users need only get the ring pages and provide a notify hook (KVM wants the guest to allocate the rings, lguest does it sanely).
This fixes an ABI. I don't think we have enough experience with this to set an ABI for 2.6.24. I also owe you a patch for scatter/gather indirection instead of chaining.
Quoted 6 lines
It is okay to put this in as long as 2.6.24 drivers won't try to talk to 2.6.25+ hosts (which presumably will have a stable ABI). We could do this by having "stable API" feature bit which the new drivers will require, and an "2.6.24 ABI" feature bit which the current drivers will require, but the whole config interface itself is too new for this in my opinion.
Yes, but this is what lguest is for, to play without fixing an ABI. I fully expect incompatible tweaks before KVM causes the ABI to nail down.
And it's going to be far easier to manage lguest-kvm cooperation with this in tree than outside.
Cheers,
Rusty.
Re: [patch 37/43] lguest: Virtio helper routines for a descriptor ringbuffer implementation
Quoted 32 lines · Rusty Russell
Quoted 22 lines · Avi Kivity
Quoted 12 lines · [email protected] wrote:
These helper routines supply most of the
virtqueue_opsfor hypervisors which want to use a ring for virtio. Unlike the previous lguest implementation:1) The rings are variable sized (2^n-1 elements).
2) They have an unfortunate limit of 65535 bytes per sg element.
3) The page numbers are always 64 bit (PAE anyone?)
4) They no longer place used[] on a separate page, just a separate cacheline.
5) We do a modulo on a variable. We could be tricky if we cared.
6) Interrupts and notifies are suppressed using flags within the rings.Quoted 3 lines
Users need only get the ring pages and provide a notify hook (KVM wants the guest to allocate the rings, lguest does it sanely).
This fixes an ABI. I don't think we have enough experience with this to set an ABI for 2.6.24. I also owe you a patch for scatter/gather indirection instead of chaining.
Quoted 7 lines
It is okay to put this in as long as 2.6.24 drivers won't try to talk to 2.6.25+ hosts (which presumably will have a stable ABI). We could do this by having "stable API" feature bit which the new drivers will require, and an "2.6.24 ABI" feature bit which the current drivers will require, but the whole config interface itself is too new for this in my opinion.
Quoted 4 lines
Yes, but this is what lguest is for, to play without fixing an ABI. I fully expect incompatible tweaks before KVM causes the ABI to nail down.
I'm worried about users tearing down the "caution, experimental ABI, will invalidate warranty" sticker from the package, selecting CONFIG_EXPERIMENTAL, CONFIG_I_REALLY_MEAN_ITS_EXPERIMENTAL, and CONFIG_I_DONT_CARE_IF_THIS_BLOWS_UP, not reading your blog, and running 2.6.24 guests on top of 2.6.25 or vice versa.
I want the module not to load instead of the guest blowing up.
Quoted 3 lines
And it's going to be far easier to manage lguest-kvm cooperation with this in tree than outside.
True.
Signature · 1 line
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
Re: [patch 37/43] lguest: Virtio helper routines for a descriptor ringbuffer implementation
Quoted 11 lines · Avi Kivity
Quoted 2 lines · Rusty Russell
Yes, but this is what lguest is for, to play without fixing an ABI. I fully expect incompatible tweaks before KVM causes the ABI to nail down.
I'm worried about users tearing down the "caution, experimental ABI, will invalidate warranty" sticker from the package, selecting
CONFIG_EXPERIMENTAL,CONFIG_I_REALLY_MEAN_ITS_EXPERIMENTAL, andCONFIG_I_DONT_CARE_IF_THIS_BLOWS_UP, not reading your blog, and running 2.6.24 guests on top of 2.6.25 or vice versa.I want the module not to load instead of the guest blowing up.
Well, this is the guest side we're talking about. We can use some bogus vendor ID for the experimental devices so that they simply won't be found by the final drivers.
Cheers,
Rusty.
Re: [patch 37/43] lguest: Virtio helper routines for a descriptor ringbuffer implementation
Quoted 13 lines · Rusty Russell
Quoted 11 lines · Avi Kivity
Quoted 3 lines · Rusty Russell
Yes, but this is what lguest is for, to play without fixing an ABI. I fully expect incompatible tweaks before KVM causes the ABI to nail down.
I'm worried about users tearing down the "caution, experimental ABI, will invalidate warranty" sticker from the package, selecting
CONFIG_EXPERIMENTAL,CONFIG_I_REALLY_MEAN_ITS_EXPERIMENTAL, andCONFIG_I_DONT_CARE_IF_THIS_BLOWS_UP, not reading your blog, and running 2.6.24 guests on top of 2.6.25 or vice versa.
Quoted 8 lines
Quoted 2 lines
I want the module not to load instead of the guest blowing up.
Well, this is the guest side we're talking about. We can use some bogus vendor ID for the experimental devices so that they simply won't be found by the final drivers.
That works. Thanks!
Signature · 1 line
Any sufficiently difficult bug is indistinguishable from a feature.
PATCH 38/43 lguest: This gets rid of the lguest bus, drivers and DMA mechanism, to make way for a generic virtio mechanism.
In reply to [email protected]--- arch/i386/lguest/boot.c | 41 -- drivers/block/Makefile | 1 drivers/block/lguest_blk.c | 423 ----------------------------- drivers/char/Makefile | 1 drivers/char/hvc_lguest.c | 177 ------------ drivers/lguest/Kconfig | 10 drivers/lguest/Makefile | 3 drivers/lguest/i386_core.c | 1 drivers/lguest/lguest_bus.c | 220 --------------- drivers/net/Makefile | 1 drivers/net/lguest_net.c | 552 --------------------------------------- include/linux/lguest_bus.h | 51 --- include/linux/lguest_launcher.h | 26 - 13 files changed, 1507 deletions(-)
===================================================================
arch/i386/lguest/boot.c +0 -41
@@ -55,7 +55,6 @@#include <linux/clockchips.h> #include <linux/lguest.h> #include <linux/lguest_launcher.h> -#include <linux/lguest_bus.h> #include <asm/paravirt.h> #include <asm/param.h> #include <asm/page.h>
@@ -173,46 +172,6 @@ void async_hcall(unsigned long call,local_irq_restore (flags); }/*:*/ - -/* Wrappers for the SEND_DMA and BIND_DMA hypercalls. This is mainly because -* Jeff Garzik complained that __pa() should never appear in drivers, and this -* helps remove most of them. But also, it wraps some ugliness. */ -void lguest_send_dma (unsigned long key,struct lguest_dma *dma) -{ -/* The hcall might not write this if something goes wrong */ - dma->used_len =0 ; -hcall (LHCALL_SEND_DMA , key,__pa (dma),0 ); -} - -int lguest_bind_dma (unsigned long key,struct lguest_dma *dmas, -unsigned int num,u8 irq) -{ -/* This is the only hypercall which actually wants 5 arguments, and we -* only support 4. Fortunately the interrupt number is always less -* than 256, so we can pack it with the number of dmas in the final -* argument. */ -if (!hcall (LHCALL_BIND_DMA , key,__pa (dmas), (num <<8 ) | irq)) -return -ENOMEM ; -return 0 ; -} - -/* Unbinding is the same hypercall as binding, but with 0 num & irq. */ -void lguest_unbind_dma (unsigned long key,struct lguest_dma *dmas) -{ -hcall (LHCALL_BIND_DMA , key,__pa (dmas),0 ); -} - -/* For guests, device memory can be used as normal memory, so we cast away the -* __iomem to quieten sparse. */ -void *lguest_map (unsigned long phys_addr,unsigned long pages) -{ -return (__force void *)ioremap (phys_addr,PAGE_SIZE *pages); -} - -void lguest_unmap (void *addr) -{ -iounmap ((__force void __iomem *)addr); -}/*G:033 * Here are our first native-instruction replacements: four functions for
===================================================================
drivers/block/Makefile +0 -1
@@ -32,4 +32,3 @@ obj-$(CONFIG_BLK_DEV_UB) += ub.o obj-$(CONFIG_BLK_DEV_UB) += ub.o obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o -obj-$(CONFIG_LGUEST_BLOCK) += lguest_blk.o
===================================================================
drivers/block/lguest_blk.c deleted +0 -423
423 lines not on this page · raw message
===================================================================
drivers/char/Makefile +0 -1
@@ -42,7 +42,6 @@ obj-$(CONFIG_N_HDLC) += n_hdlc.o obj-$(CONFIG_N_HDLC) += n_hdlc.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o -obj-$(CONFIG_LGUEST_GUEST) += hvc_lguest.o obj-$(CONFIG_RIO) += rio/ generic_serial.o obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
===================================================================
drivers/char/hvc_lguest.c deleted +0 -177
177 lines not on this page · raw message
===================================================================
drivers/lguest/Kconfig +0 -10
@@ -17,13 +17,3 @@ config LGUEST_GUEST The guest needs code built-in, even if the host has lguest support as a module. The drivers are tiny, so we build them in too. - -config LGUEST_NET - tristate - default y - depends on LGUEST_GUEST && NET - -config LGUEST_BLOCK - tristate - default y - depends on LGUEST_GUEST && BLOCK
===================================================================
drivers/lguest/Makefile +0 -3
@@ -1,6 +1,3 @@ -# Guest requires the bus driver. -obj-$(CONFIG_LGUEST_GUEST) += lguest_bus.o - # Host requires the other files, which can be a module. obj-$(CONFIG_LGUEST) += lg.o lg-y = core.o hypercalls.o page_tables.o interrupts_and_traps.o \
===================================================================
drivers/lguest/i386_core.c +0 -1
@@ -29,7 +29,6 @@#include <linux/cpu.h> #include <linux/lguest.h> #include <linux/lguest_launcher.h> -#include <linux/lguest_bus.h> #include <asm/paravirt.h> #include <asm/param.h> #include <asm/page.h>
===================================================================
drivers/lguest/lguest_bus.c deleted +0 -220
220 lines not on this page · raw message
===================================================================
drivers/net/Makefile +0 -1
@@ -177,7 +177,6 @@ obj-$(CONFIG_HPLANCE) += hplance.o 7990. obj-$(CONFIG_HPLANCE) += hplance.o 7990.o obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o obj-$(CONFIG_EQUALIZER) += eql.o -obj-$(CONFIG_LGUEST_NET) += lguest_net.o obj-$(CONFIG_MIPS_JAZZ_SONIC) += jazzsonic.o obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o obj-$(CONFIG_MIPS_SIM_NET) += mipsnet.o
===================================================================
drivers/net/lguest_net.c deleted +0 -552
552 lines not on this page · raw message
===================================================================
include/linux/lguest_bus.h deleted +0 -51
51 lines not on this page · raw message
===================================================================
include/linux/lguest_launcher.h +0 -26
@@ -41,32 +41,6 @@ struct lguest_dma__u32 used_len;__u16 len[LGUEST_MAX_DMA_SECTIONS ];unsigned long addr[LGUEST_MAX_DMA_SECTIONS ]; -}; -/*:*/ - -/*D:460 This is the layout of a block device memory page. The Launcher sets up -* the num_sectors initially to tell the Guest the size of the disk. The Guest -* puts the type, sector and length of the request in the first three fields, -* then DMAs to the Host. The Host processes the request, sets up the result, -* then DMAs back to the Guest. */ -struct lguest_block_page -{ -/* 0 is a read, 1 is a write. */ -int type; -__u32 sector;/* Offset in device = sector * 512. */ -__u32 bytes;/* Length expected to be read/written in bytes */ -/* 0 = pending, 1 = done, 2 = done, error */ -int result; -__u32 num_sectors;/* Disk length = num_sectors * 512 */ -}; - -/*D:520 The network device is basically a memory page where all the Guests on -* the network publish their MAC (ethernet) addresses: it's an array of "struct -* lguest_net": */ -struct lguest_net -{ -/* Simply the mac address (with multicast bit meaning promisc). */ -unsigned char mac[6 ]; };/*:*/
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 39/43 lguest: This patch gets rid of the old lguest host I/O infrastructure and replaces it with a single hypercall "LHCALL_NOTIFY" which takes an address.
In reply to [email protected]The main change is the removal of io.c: that mainly did inter-guest I/O, which virtio doesn't yet support.
--- drivers/lguest/Makefile | 2 drivers/lguest/core.c | 12 drivers/lguest/hypercalls.c | 26 - drivers/lguest/io.c | 628 --------------------------------------- drivers/lguest/lg.h | 27 - drivers/lguest/lguest_user.c | 39 -- include/asm-i386/lguest_hcall.h | 3 include/linux/lguest_launcher.h | 36 -- 8 files changed, 21 insertions(+), 752 deletions(-)
===================================================================
drivers/lguest/Makefile +1 -1
@@ -1,7 +1,7 @@ # Host requires the other files, which can be a module. obj-$(CONFIG_LGUEST) += lg.o lg-y = core.o hypercalls.o page_tables.o interrupts_and_traps.o \ - segments.o io.o lguest_user.o + segments.o lguest_user.o lg-$(CONFIG_X86_32) += i386_switcher.o i386_core.o
===================================================================
drivers/lguest/core.c +4 -8
@@ -202,13 +202,12 @@ int run_guest(struct lguest *lg, unsigneif (lg->hcall)do_hypercalls (lg); -/* It's possible the Guest did a SEND_DMA hypercall to the +/* It's possible the Guest did a NOTIFY hypercall to the * Launcher, in which case we return from the read() now. */ -if (lg->dma_is_pending) { -if (put_user (lg->pending_dma, user) || -put_user (lg->pending_key, user+1 )) +if (lg->pending_notify) { +if (put_user (lg->pending_notify, user))return -EFAULT ; -return sizeof (unsigned long )*2 ; +return sizeof (lg->pending_notify); }/* Check for signals */
@@ -287,9 +286,6 @@ static int __init init(void) err =init_pagetables (switcher_page,SHARED_SWITCHER_PAGES );if (err) goto unmap; - -/* The I/O subsystem needs some things initialized. */ -lguest_io_init ();/* We might need to reserve an interrupt vector. */ err =init_interrupts ();
===================================================================
drivers/lguest/hypercalls.c +8 -18
@@ -60,22 +60,9 @@ static void do_hcall(struct lguest *lg,else guest_pagetable_flush_user (lg);break ; -case LHCALL_BIND_DMA : -/* BIND_DMA really wants four arguments, but it's the only call -* which does. So the Guest packs the number of buffers and -* the interrupt number into the final argument, and we decode -* it here. This can legitimately fail, since we currently -* place a limit on the number of DMA pools a Guest can have. -* So we return true or false from this call. */ - args->arg0 =bind_dma (lg, args->arg1, args->arg2, - args->arg3 >>8 , args->arg3 &0xFF ); -break ;/* All these calls simply pass the arguments through to the right * routines. */ -case LHCALL_SEND_DMA : -send_dma (lg, args->arg1, args->arg2); -break ;case LHCALL_NEW_PGTABLE :guest_new_pagetable (lg, args->arg1);break ;
@@ -98,6 +85,9 @@ static void do_hcall(struct lguest *lg,case LHCALL_HALT :/* Similarly, this sets the halted flag for run_guest(). */ lg->halted =1 ; +break ; +case LHCALL_NOTIFY : + lg->pending_notify = args->arg1;break ;default :if (lguest_arch_do_hcall (lg, args))
@@ -156,9 +146,9 @@ static void do_async_hcalls(struct lguesbreak ; } -/* Stop doing hypercalls if we've just done a DMA to the -* Launcher: it needs to service this first. */ -if (lg->dma_is_pending) +/* Stop doing hypercalls if they want to notify the Launcher: +* it needs to service this first. */ +if (lg->pending_notify)break ; } }
@@ -220,9 +210,9 @@ void do_hypercalls(struct lguest *lg)do_async_hcalls (lg);/* If we stopped reading the hypercall ring because the Guest did a -* SEND_DMA to the Launcher, we want to return now. Otherwise we do +* NOTIFY to the Launcher, we want to return now. Otherwise we do * the hypercall. */ -if (!lg->dma_is_pending) { +if (!lg->pending_notify) {do_hcall (lg, lg->hcall);/* Tricky point: we reset the hcall pointer to mark the * hypercall as "done". We use the hcall pointer rather than
===================================================================
drivers/lguest/io.c deleted +0 -628
628 lines not on this page · raw message
===================================================================
drivers/lguest/lg.h +1 -26
@@ -6,7 +6,6 @@#include <linux/init.h> #include <linux/stringify.h> #include <linux/binfmts.h> -#include <linux/futex.h> #include <linux/lguest.h> #include <linux/lguest_launcher.h> #include <linux/wait.h>
@@ -17,17 +16,6 @@void free_pagetables (void );int init_pagetables (struct page **switcher_page,unsigned int pages); - -struct lguest_dma_info -{ -struct list_head list; -union futex_key key; -unsigned long dmas; -struct lguest *owner; -u16 next_dma; -u16 num_dmas; -u8 interrupt;/* 0 when not registered */ -};struct pgdir {
@@ -91,14 +79,10 @@ struct lgueststruct task_struct *wake;unsigned long noirq_start, noirq_end; -int dma_is_pending; -unsigned long pending_dma;/* struct lguest_dma */ -unsigned long pending_key;/* address they're sending to */ +unsigned long pending_notify;/* pfn from LHCALL_NOTIFY */ unsigned int stack_pages;u32 tsc_khz; - -struct lguest_dma_info dma[LGUEST_MAX_DMA ];/* Dead? */ const char *dead;
@@ -185,15 +169,6 @@ int lguest_device_init(void);int lguest_device_init (void );void lguest_device_remove (void ); -/* io.c: */ -void lguest_io_init (void ); -int bind_dma (struct lguest *lg, -unsigned long key,unsigned long udma,u16 numdmas,u8 interrupt); -void send_dma (struct lguest *info,unsigned long key,unsigned long udma); -void release_all_dma (struct lguest *lg); -unsigned long get_dma_buffer (struct lguest *lg,unsigned long key, -unsigned long *interrupt); -/* hypercalls.c: */ void do_hypercalls (struct lguest *lg);void write_timestamp (struct lguest *lg);
===================================================================
drivers/lguest/lguest_user.c +5 -34
@@ -2,36 +2,11 @@* controls and communicates with the Guest. For example, the first write will * tell us the Guest's memory layout, pagetable, entry point and kernel address * offset. A read will run the Guest until something happens, such as a signal -* or the Guest doing a DMA out to the Launcher. Writes are also used to get a -* DMA buffer registered by the Guest and to send the Guest an interrupt. :*/ +* or the Guest doing a NOTIFY out to the Launcher. :*/ #include <linux/uaccess.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include "lg.h" - -/*L:310 To send DMA into the Guest, the Launcher needs to be able to ask for a -* DMA buffer. This is done by writing LHREQ_GETDMA and the key to -* /dev/lguest. */ -static long user_get_dma (struct lguest *lg,const unsigned long __user *input) -{ -unsigned long key, udma, irq; - -/* Fetch the key they wrote to us. */ -if (get_user (key, input) !=0 ) -return -EFAULT ; -/* Look for a free Guest DMA buffer bound to that key. */ - udma =get_dma_buffer (lg, key, &irq); -if (!udma) -return -ENOENT ; - -/* We need to tell the Launcher what interrupt the Guest expects after -* the buffer is filled. We stash it in udma->used_len. */ -lgwrite_u32 (lg, udma + offsetof(struct lguest_dma , used_len), irq); - -/* The (guest-physical) address of the DMA buffer is returned from -* the write(). */ -return udma; -}/*L:315 To force the Guest to stop running and return to the Launcher, the * Waker sets writes LHREQ_BREAK and the value "1" to /dev/lguest. The
@@ -102,10 +77,10 @@ static ssize_t read(struct file *file, creturn len; } -/* If we returned from read() last time because the Guest sent DMA, +/* If we returned from read() last time because the Guest notified, * clear the flag. */ -if (lg->dma_is_pending) - lg->dma_is_pending =0 ; +if (lg->pending_notify) + lg->pending_notify =0 ;/* Run the Guest until something interesting happens. */ return run_guest (lg, (unsigned long __user *)user);
@@ -216,7 +191,7 @@ unlock:/*L:010 The first operation the Launcher does must be a write. All writes * start with a 32 bit number: for the first write this must be * LHREQ_INITIALIZE to set up the Guest. After that the Launcher can use -* writes of other values to get DMA buffers and send interrupts. */ +* writes of other values to send interrupts. */ static ssize_t write (struct file *file,const char __user *in,size_t size,loff_t *off) {
@@ -245,8 +220,6 @@ static ssize_t write(struct file *file,switch (req) {case LHREQ_INITIALIZE :return initialize (file, input); -case LHREQ_GETDMA : -return user_get_dma (lg, input);case LHREQ_IRQ :return user_send_irq (lg, input);case LHREQ_BREAK :
@@ -276,8 +249,6 @@ static int close(struct inode *inode, stmutex_lock (&lguest_lock);/* Cancels the hrtimer set via LHCALL_SET_CLOCKEVENT. */ hrtimer_cancel (&lg->hrt); -/* Free any DMA buffers the Guest had bound. */ -release_all_dma (lg);/* Free up the shadow page tables for the Guest. */ free_guest_pagetable (lg);/* Now all the memory cleanups are done, it's safe to release the
===================================================================
include/asm-i386/lguest_hcall.h +1 -2
@@ -15,11 +15,10 @@#define LHCALL_TS 8#define LHCALL_SET_CLOCKEVENT 9#define LHCALL_HALT 10 -#define LHCALL_BIND_DMA 12 -#define LHCALL_SEND_DMA 13#define LHCALL_SET_PTE 14#define LHCALL_SET_PMD 15#define LHCALL_LOAD_TLS 16 +#define LHCALL_NOTIFY 17/*G:031 First, how does our Guest contact the Host to ask for privileged * operations? There are two ways: the direct way is to make a "hypercall",
===================================================================
include/linux/lguest_launcher.h +1 -35
@@ -9,40 +9,6 @@/* How many devices? Assume each one wants up to two dma arrays per device. */ #define LGUEST_MAX_DEVICES (LGUEST_MAX_DMA/2) - -/*D:200 -* Lguest I/O -* -* The lguest I/O mechanism is the only way Guests can talk to devices. There -* are two hypercalls involved: SEND_DMA for output and BIND_DMA for input. In -* each case, "struct lguest_dma" describes the buffer: this contains 16 -* addr/len pairs, and if there are fewer buffer elements the len array is -* terminated with a 0. -* -* I/O is organized by keys: BIND_DMA attaches buffers to a particular key, and -* SEND_DMA transfers to buffers bound to particular key. By convention, keys -* correspond to a physical address within the device's page. This means that -* devices will never accidentally end up with the same keys, and allows the -* Host use The Futex Trick (as we'll see later in our journey). -* -* SEND_DMA simply indicates a key to send to, and the physical address of the -* "struct lguest_dma" to send. The Host will write the number of bytes -* transferred into the "struct lguest_dma"'s used_len member. -* -* BIND_DMA indicates a key to bind to, a pointer to an array of "struct -* lguest_dma"s ready for receiving, the size of that array, and an interrupt -* to trigger when data is received. The Host will only allow transfers into -* buffers with a used_len of zero: it then sets used_len to the number of -* bytes transferred and triggers the interrupt for the Guest to process the -* new input. */ -struct lguest_dma -{ -/* 0 if free to be used, filled by the Host. */ -__u32 used_len; -__u16 len[LGUEST_MAX_DMA_SECTIONS ]; -unsigned long addr[LGUEST_MAX_DMA_SECTIONS ]; -}; -/*:*/ /* Where the Host expects the Guest to SEND_DMA console output to. */ #define LGUEST_CONSOLE_DMA_KEY 0
@@ -95,7 +61,7 @@ enum lguest_reqenum lguest_req {LHREQ_INITIALIZE ,/* + pfnlimit, pgdir, start, pageoffset */ -LHREQ_GETDMA ,/* + addr (returns &lguest_dma, irq in ->used_len) */ +LHREQ_GETDMA ,/* No longer used */ LHREQ_IRQ ,/* + irq */ LHREQ_BREAK ,/* + on/off flag (on blocks until someone does off) */ };
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 40/43 lguest: Lguest support for Virtio
In reply to [email protected]This makes lguest able to use the virtio devices.
We change the device descriptor page from a simple array to a variable length "type, config_len, status, config data..." format, and implement virtio_config_ops to read from that config data.
We use the virtio ring implementation for an efficient Guest <-> Host virtqueue mechanism, and the new LHCALL_NOTIFY hypercall to kick the host when it changes.
We also use LHCALL_NOTIFY on kernel addresses for very very early console output. We could have another hypercall, but this hack works quite well.
--- arch/i386/lguest/Kconfig | 5 arch/i386/lguest/boot.c | 21 ++ drivers/lguest/Makefile | 3 drivers/lguest/lguest_device.c | 373 +++++++++++++++++++++++++++++++++++++++ include/linux/lguest_launcher.h | 47 +--- 5 files changed, 421 insertions(+), 28 deletions(-)
===================================================================
arch/i386/lguest/Kconfig +5 -0
@@ -2,8 +2,13 @@ config LGUEST_GUEST bool "Lguest guest support" select PARAVIRT depends on !X86_PAE + select VIRTIO select VIRTIO_RING + select VIRTIO_CONSOLE help Lguest is a tiny in-kernel hypervisor. Selecting this will allow your kernel to boot under lguest. This option will increase your kernel size by about 6k. If in doubt, say N. + + If you say Y here, make sure you say Y (or M) to the virtio block + and net drivers which lguest needs.
===================================================================
arch/i386/lguest/boot.c +21 -0
@@ -55,6 +55,7 @@#include <linux/clockchips.h> #include <linux/lguest.h> #include <linux/lguest_launcher.h> +#include <linux/virtio_console.h> #include <asm/paravirt.h> #include <asm/param.h> #include <asm/page.h>
@@ -858,6 +859,23 @@ static __init char *lguest_memory_setup(/* This string is for the boot messages. */ return "LGUEST" ; +} + +/* Before virtqueues are set up, we use LHCALL_NOTIFY on normal memory to +* produce console output. */ +static __init int early_put_chars (u32 vtermno,const char *buf,int count) +{ +char scratch[17 ]; +unsigned int len = count; + +if (len >sizeof (scratch) -1 ) + len =sizeof (scratch) -1 ; + scratch[len] ='\0' ; +memcpy (scratch, buf, len); +hcall (LHCALL_NOTIFY ,__pa (scratch),0 ,0 ); + +/* This routine returns the number of bytes actually written. */ +return len; }/*G:050
@@ -1043,6 +1061,9 @@ __init void lguest_init(void *boot)* adapted for lguest's use. */ add_preferred_console ("hvc" ,0 ,NULL ); +/* Register our very early console. */ +virtio_cons_early_init (early_put_chars); +/* Last of all, we set the power management poweroff hook to point to * the Guest routine to power off. */ pm_power_off = lguest_power_off;
===================================================================
drivers/lguest/Makefile +3 -0
@@ -1,3 +1,6 @@ +# Guest requires the device configuration and probing code. +obj-$(CONFIG_LGUEST_GUEST) += lguest_device.o + # Host requires the other files, which can be a module. obj-$(CONFIG_LGUEST) += lg.o lg-y = core.o hypercalls.o page_tables.o interrupts_and_traps.o \
===================================================================
drivers/lguest/lguest_device.c new file +373 -0
373 lines not on this page · raw message
===================================================================
include/linux/lguest_launcher.h +19 -28
56 lines not on this page · raw message
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 41/43 lguest: Update example launcher for virtio
In reply to [email protected]Implements virtio-based console, network and block servers. The block server uses a thread so it's async, which is an improvement over the old synchronous implementation (but a little more complex).
--- Documentation/lguest/lguest.c | 1201 ++++++++++++++++++++++++----------------- 1 file changed, 711 insertions(+), 490 deletions(-)
Documentation/lguest/lguest.c +709 -490
@@ -32,7 +32,9 @@#include <termios.h> #include <getopt.h> #include <zlib.h> -/*L:110 We can ignore the 28 include files we need for this program, but I do +#include <assert.h> +#include <sched.h> +/*L:110 We can ignore the 30 include files we need for this program, but I do * want to draw attention to the use of kernel-style types. * * As Linus said, "C is a Spartan language, and so should your naming be." I
@@ -44,6 +46,12 @@ typedef uint16_t u16;typedef uint16_t u16 ;typedef uint8_t u8 ;#include "linux/lguest_launcher.h" +#include "linux/pci_ids.h" +#include "linux/virtio_config.h" +#include "linux/virtio_net.h" +#include "linux/virtio_blk.h" +#include "linux/virtio_console.h" +#include "linux/virtio_ring.h" #include "asm/e820.h" /*:*/
@@ -55,6 +63,8 @@ typedef uint8_t u8;#endif /* We can have up to 256 pages for devices. */ #define DEVICE_PAGES 256 +/* This fits nicely in a single 4096-byte page. */ +#define VIRTQUEUE_NUM 127/*L:120 verbose is both a global flag and a macro. The C preprocessor allows * this, and although I wouldn't recommend it, it works quite nicely here. */
@@ -78,8 +88,17 @@ struct device_listfd_set infds;int max_infd; +/* Counter to assign interrupt numbers. */ +unsigned int next_irq; + +/* Counter to print out convenient device numbers. */ +unsigned int device_num; +/* The descriptor page for the devices. */ -struct lguest_device_desc *descs; +u8 *descpage; + +/* The tail of the last descriptor. */ +unsigned int desc_used;/* A single linked list of devices. */ struct device *dev;
@@ -87,30 +106,87 @@ struct device_liststruct device **lastdev; }; +/* The list of Guest devices, based on command line arguments. */ +static struct device_list devices; +/* The device structure describes a single device. */ struct device {/* The linked-list pointer. */ struct device *next; -/* The descriptor for this device, as mapped into the Guest. */ + +/* The this device's descriptor, as mapped into the Guest. */ struct lguest_device_desc *desc; -/* The memory page(s) of this device, if any. Also mapped in Guest. */ -void *mem; + +/* The name of this device, for --verbose. */ +const char *name;/* If handle_input is set, it wants to be called when this file * descriptor is ready. */ int fd;bool (*handle_input)(int fd,struct device *me); -/* If handle_output is set, it wants to be called when the Guest sends -* DMA to this key. */ -unsigned long watch_key; -u32 (*handle_output)(int fd,const struct iovec *iov, -unsigned int num,struct device *me); +/* Any queues attached to this device */ +struct virtqueue *vq;/* Device-specific data. */ void *priv; }; + +/* The virtqueue structure describes a queue attached to a device. */ +struct virtqueue +{ +struct virtqueue *next; + +/* Which device owns me. */ +struct device *dev; + +/* The configuration for this queue. */ +struct lguest_vqconfig config; + +/* The actual ring of buffers. */ +struct vring vring; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* The routine to call when the Guest pings us. */ +void (*handle_output)(int fd,struct virtqueue *me); +}; + +/* Since guest is UP and we don't run at the same time, we don't need barriers. +* But I include them in the code in case others copy it. */ +#define wmb () + +/* Convert an iovec element to the given type. +* +* This is a fairly ugly trick: we need to know the size of the type and +* alignment requirement to check the pointer is kosher. It's also nice to +* have the name of the type in case we report failure. +* +* Typing those three things all the time is cumbersome and error prone, so we +* have a macro which sets them all up and passes to the real function. */ +#define convert (iov, type) \ + ((type *)_convert((iov), sizeof(type), __alignof__(type), #type)) + +static void *_convert (struct iovec *iov,size_t size,size_t align, +const char *name) +{ +if (iov->iov_len != size) +errx (1 ,"Bad iovec size %zu for %s" , iov->iov_len, name); +if ((unsigned long )iov->iov_base % align !=0 ) +errx (1 ,"Bad alignment %p for %s" , iov->iov_base, name); +return iov->iov_base; +} + +/* The virtio configuration space is defined to be little-endian. x86 is +* little-endian too, but it's nice to be explicit so we have these helpers. */ +#define cpu_to_le16 (v16) (v16) +#define cpu_to_le32 (v32) (v32) +#define cpu_to_le64 (v64) (v64) +#define le16_to_cpu (v16) (v16) +#define le32_to_cpu (v32) (v32) +#define le64_to_cpu (v32) (v64)/*L:100 The Launcher code itself takes us out into userspace, that scary place * where pointers run wild and free! Unfortunately, like most userspace
@@ -486,11 +562,11 @@ static int tell_kernel(unsigned long pgd }/*:*/ -static void set_fd (int fd,struct device_list *devices) -{ -FD_SET (fd, &devices->infds); -if (fd > devices->max_infd) - devices->max_infd = fd; +static void add_device_fd (int fd) +{ +FD_SET (fd, &devices.infds); +if (fd > devices.max_infd) + devices.max_infd = fd; }/*L:200
@@ -508,18 +584,18 @@ static void set_fd(int fd, struct device* * This, of course, is merely a different *kind* of icky. */ -static void wake_parent (int pipefd,int lguest_fd,struct device_list *devices) +static void wake_parent (int pipefd,int lguest_fd) {/* Add the pipe from the Launcher to the fdset in the device_list, so * we watch it, too. */ -set_fd (pipefd, devices); +add_device_fd (pipefd);for (;;) { -fd_set rfds = devices->infds; +fd_set rfds = devices.infds;unsigned long args[] = {LHREQ_BREAK ,1 };/* Wait until input is ready from one of the devices. */ -select (devices->max_infd+1 , &rfds,NULL ,NULL ,NULL ); +select (devices.max_infd+1 , &rfds,NULL ,NULL ,NULL );/* Is it a message from the Launcher? */ if (FD_ISSET (pipefd, &rfds)) {int ignorefd;
@@ -530,14 +606,14 @@ static void wake_parent(int pipefd, int/* Otherwise it's telling us there's a problem with one * of the devices, and we should ignore that file * descriptor from now on. */ -FD_CLR (ignorefd, &devices->infds); +FD_CLR (ignorefd, &devices.infds); }else /* Send LHREQ_BREAK command. */ write (lguest_fd, args,sizeof (args)); } }/* This routine just sets up a pipe to the Waker process. */ -static int setup_waker (int lguest_fd,struct device_list *device_list) +static int setup_waker (int lguest_fd) {int pipefd[2 ], child;
@@ -551,7 +627,7 @@ static int setup_waker(int lguest_fd, stif (child ==0 ) {/* Close the "writing" end of our copy of the pipe */ close (pipefd[1 ]); -wake_parent (pipefd[0 ], lguest_fd, device_list); +wake_parent (pipefd[0 ], lguest_fd); }/* Close the reading end of our copy of the pipe. */ close (pipefd[0 ]);
@@ -574,71 +650,13 @@ static void *_check_pointer(unsigned lon/* We have to separately check addr and addr+size, because size could * be huge and addr + size might wrap around. */ if (addr >= guest_limit || addr + size >= guest_limit) -errx (1 ,"%s:%i: Invalid address %li" , __FILE__, line, addr); +errx (1 ,"%s:%i: Invalid address %#lx" , __FILE__, line, addr);/* We return a pointer for the caller's convenience, now we know it's * safe to use. */ return from_guest_phys (addr); }/* A macro which transparently hands the line number to the real function. */ #define check_pointer (addr,size) _check_pointer(addr, size, __LINE__) - -/* The Guest has given us the address of a "struct lguest_dma". We check it's -* OK and convert it to an iovec (which is a simple array of ptr/size -* pairs). */ -static u32 *dma2iov (unsigned long dma,struct iovec iov[],unsigned *num) -{ -unsigned int i; -struct lguest_dma *udma; - -/* First we make sure that the array memory itself is valid. */ - udma =check_pointer (dma,sizeof (*udma)); -/* Now we check each element */ -for (i =0 ; i <LGUEST_MAX_DMA_SECTIONS ; i++) { -/* A zero length ends the array. */ -if (!udma->len[i]) -break ; - - iov[i].iov_base =check_pointer (udma->addr[i], udma->len[i]); - iov[i].iov_len = udma->len[i]; - } - *num = i; - -/* We return the pointer to where the caller should write the amount of -* the buffer used. */ -return &udma->used_len; -} - -/* This routine gets a DMA buffer from the Guest for a given key, and converts -* it to an iovec array. It returns the interrupt the Guest wants when we're -* finished, and a pointer to the "used_len" field to fill in. */ -static u32 *get_dma_buffer (int fd,void *key, -struct iovec iov[],unsigned int *num,u32 *irq) -{ -unsigned long buf[] = {LHREQ_GETDMA ,to_guest_phys (key) }; -unsigned long udma; -u32 *res; - -/* Ask the kernel for a DMA buffer corresponding to this key. */ - udma =write (fd, buf,sizeof (buf)); -/* They haven't registered any, or they're all used? */ -if (udma == (unsigned long )-1 ) -return NULL ; - -/* Convert it into our iovec array */ - res =dma2iov (udma, iov, num); -/* The kernel stashes irq in ->used_len to get it out to us. */ - *irq = *res; -/* Return a pointer to ((struct lguest_dma *)udma)->used_len. */ -return res; -} - -/* This is a convenient routine to send the Guest an interrupt. */ -static void trigger_irq (int fd,u32 irq) -{ -unsigned long buf[] = {LHREQ_IRQ , irq }; -if (write (fd, buf,sizeof (buf)) !=0 ) -err (1 ,"Triggering irq %i" , irq); -}/* This simply sets up an iovec array where we can put data to be discarded. * This happens when the Guest doesn't want or can't handle the input: we have
@@ -652,6 +670,121 @@ static void discard_iovec(struct iovec * iov->iov_len =sizeof (discard_buf); } +/* This function returns the next descriptor in the chain, or vq->vring.num. */ +static unsigned next_desc (struct virtqueue *vq,unsigned int i) +{ +unsigned int next; + +/* If this descriptor says it doesn't chain, we're done. */ +if (!(vq->vring.desc[i].flags &VRING_DESC_F_NEXT )) +return vq->vring.num; + +/* Check they're not leading us off end of descriptors. */ + next = vq->vring.desc[i].next; +/* Make sure compiler knows to grab that: we don't want it changing! */ +wmb (); + +if (next >= vq->vring.num) +errx (1 ,"Desc next is %u" , next); + +return next; +} + +/* This looks in the virtqueue and for the first available buffer, and converts +* it to an iovec for convenient access. Since descriptors consist of some +* number of output then some number of input descriptors, it's actually two +* iovecs, but we pack them into one and note how many of each there were. +* +* This function returns the descriptor number found, or vq->vring.num (which +* is never a valid descriptor number) if none was found. */ +static unsigned get_vq_desc (struct virtqueue *vq, +struct iovec iov[], +unsigned int *out_num,unsigned int *in_num) +{ +unsigned int i, head; + +/* Check it isn't doing very strange things with descriptor numbers. */ +if ((u16)(vq->vring.avail->idx - vq->last_avail_idx) > vq->vring.num) +errx (1 ,"Guest moved used index from %u to %u" , + vq->last_avail_idx, vq->vring.avail->idx); + +/* If there's nothing new since last we looked, return invalid. */ +if (vq->vring.avail->idx == vq->last_avail_idx) +return vq->vring.num; + +/* Grab the next descriptor number they're advertising, and increment +* the index we've seen. */ + head = vq->vring.avail->ring[vq->last_avail_idx++ % vq->vring.num]; + +/* If their number is silly, that's a fatal mistake. */ +if (head >= vq->vring.num) +errx (1 ,"Guest says index %u is available" , head); + +/* When we start there are none of either input nor output. */ + *out_num = *in_num =0 ; + + i = head; +do { +/* Grab the first descriptor, and check it's OK. */ + iov[*out_num + *in_num].iov_len = vq->vring.desc[i].len; + iov[*out_num + *in_num].iov_base + =check_pointer (vq->vring.desc[i].addr, + vq->vring.desc[i].len); +/* If this is an input descriptor, increment that count. */ +if (vq->vring.desc[i].flags &VRING_DESC_F_WRITE ) + (*in_num)++; +else { +/* If it's an output descriptor, they're all supposed +* to come before any input descriptors. */ +if (*in_num) +errx (1 ,"Descriptor has out after in" ); + (*out_num)++; + } + +/* If we've got too many, that implies a descriptor loop. */ +if (*out_num + *in_num > vq->vring.num) +errx (1 ,"Looped descriptor" ); + }while ((i =next_desc (vq, i)) != vq->vring.num); + +return head; +} + +/* Once we've used one of their buffers, we tell them about it. We'll then +* want to send them an interrupt, using trigger_irq(). */ +static void add_used (struct virtqueue *vq,unsigned int head,int len) +{ +struct vring_used_elem *used; + +/* Get a pointer to the next entry in the used ring. */ + used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num]; + used->id = head; + used->len = len; +/* Make sure buffer is written before we update index. */ +wmb (); + vq->vring.used->idx++; +} + +/* This actually sends the interrupt for this virtqueue */ +static void trigger_irq (int fd,struct virtqueue *vq) +{ +unsigned long buf[] = {LHREQ_IRQ , vq->config.irq }; + +if (vq->vring.avail->flags &VRING_AVAIL_F_NO_INTERRUPT ) +return ; + +/* Send the Guest an interrupt tell them we used something up. */ +if (write (fd, buf,sizeof (buf)) !=0 ) +err (1 ,"Triggering irq %i" , vq->config.irq); +} + +/* And here's the combo meal deal. Supersize me! */ +static void add_used_and_trigger (int fd,struct virtqueue *vq, +unsigned int head,int len) +{ +add_used (vq, head, len); +trigger_irq (fd, vq); +} +/* Here is the input terminal setting we save, and the routine to restore them * on exit so the user can see what they type next. */ static struct termios orig_term;
@@ -672,38 +805,37 @@ struct console_abort/* This is the routine which handles console input (ie. stdin). */ static bool handle_console_input (int fd,struct device *dev) { -u32 irq =0 , *lenp;int len; -unsigned int num; -struct iovec iov[LGUEST_MAX_DMA_SECTIONS ]; +unsigned int head, in_num, out_num; +struct iovec iov[dev->vq->vring.num];struct console_abort *abort = dev->priv; -/* First we get the console buffer from the Guest. The key is dev->mem -* which was set to 0 in setup_console(). */ - lenp =get_dma_buffer (fd, dev->mem, iov, &num, &irq); -if (!lenp) { -/* If it's not ready for input, warn and set up to discard. */ -warn ("console: no dma buffer!" ); -discard_iovec (iov, &num); - } +/* First we need a console buffer from the Guests's input virtqueue. */ + head =get_vq_desc (dev->vq, iov, &out_num, &in_num); +if (head == dev->vq->vring.num) { +/* If they're not ready for input, we warn and set up to +* discard. */ +warnx ("console: no dma buffer!" ); +discard_iovec (iov, &in_num); + }else if (out_num) +errx (1 ,"Output buffers in console in queue?" );/* This is why we convert to iovecs: the readv() call uses them, and so * it reads straight into the Guest's buffer. */ - len =readv (dev->fd, iov, num); + len =readv (dev->fd, iov, in_num);if (len <=0 ) {/* This implies that the console is closed, is /dev/null, or -* something went terribly wrong. We still go through the rest -* of the logic, though, especially the exit handling below. */ +* something went terribly wrong. */ warnx ("Failed to get console input, ignoring console." ); - len =0 ; - } - -/* If we read the data into the Guest, fill in the length and send the -* interrupt. */ -if (lenp) { - *lenp = len; -trigger_irq (fd, irq); - } +/* Put the input terminal back and return failure (meaning, +* don't call us again). */ +restore_term (); +return false; + } + +/* If we actually read the data into the Guest, tell them about it. */ +if (head != dev->vq->vring.num) +add_used_and_trigger (fd, dev->vq, head, len);/* Three ^C within one second? Exit. *
@@ -732,202 +864,137 @@ static bool handle_console_input(int fd,/* Any other key resets the abort counter. */ abort->count =0 ; -/* Now, if we didn't read anything, put the input terminal back and -* return failure (meaning, don't call us again). */ -if (!len) { -restore_term (); -return false; - }/* Everything went OK! */ return true; } -/* Handling console output is much simpler than input. */ -static u32 handle_console_output (int fd,const struct iovec *iov, -unsigned num,struct device *dev) -{ -/* Whatever the Guest sends, write it to standard output. Return the -* number of bytes written. */ -return writev (STDOUT_FILENO , iov, num); -} - -/* Guest->Host network output is also pretty easy. */ -static u32 handle_tun_output (int fd,const struct iovec *iov, -unsigned num,struct device *dev) -{ -/* We put a flag in the "priv" pointer of the network device, and set -* it as soon as we see output. We'll see why in handle_tun_input() */ - *(bool *)dev->priv = true; -/* Whatever packet the Guest sent us, write it out to the tun -* device. */ -return writev (dev->fd, iov, num); -} - -/* This matches the peer_key() in lguest_net.c. The key for any given slot -* is the address of the network device's page plus 4 * the slot number. */ -static unsigned long peer_offset (unsigned int peernum) -{ -return 4 * peernum; -} - -/* This is where we handle a packet coming in from the tun device */ +/* Handling output for console is simple: we just get all the output buffers +* and write them to stdout. */ +static void handle_console_output (int fd,struct virtqueue *vq) +{ +unsigned int head, out, in; +int len; +struct iovec iov[vq->vring.num]; + +/* Keep getting output buffers from the Guest until we run out. */ +while ((head =get_vq_desc (vq, iov, &out, &in)) != vq->vring.num) { +if (in) +errx (1 ,"Input buffers in output queue?" ); + len =writev (STDOUT_FILENO , iov, out); +add_used_and_trigger (fd, vq, head, len); + } +} + +/* Handling output for network is also simple: we get all the output buffers +* and write them (ignoring the first element) to this device's file descriptor +* (stdout). */ +static void handle_net_output (int fd,struct virtqueue *vq) +{ +unsigned int head, out, in; +int len; +struct iovec iov[vq->vring.num]; + +/* Keep getting output buffers from the Guest until we run out. */ +while ((head =get_vq_desc (vq, iov, &out, &in)) != vq->vring.num) { +if (in) +errx (1 ,"Input buffers in output queue?" ); +/* Check header, but otherwise ignore it (we said we supported +* no features). */ + (void)convert(&iov[0], struct virtio_net_hdr); + len =writev (vq->dev->fd, iov+1 , out-1 ); +add_used_and_trigger (fd, vq, head, len); + } +} + +/* This is where we handle a packet coming in from the tun device to our +* Guest. */ static bool handle_tun_input (int fd,struct device *dev) { -u32 irq =0 , *lenp; +unsigned int head, in_num, out_num;int len; -unsigned num; -struct iovec iov[LGUEST_MAX_DMA_SECTIONS ]; - -/* First we get a buffer the Guest has bound to its key. */ - lenp =get_dma_buffer (fd, dev->mem+peer_offset (NET_PEERNUM ), iov, &num, - &irq); -if (!lenp) { +struct iovec iov[dev->vq->vring.num]; +struct virtio_net_hdr *hdr; + +/* First we need a network buffer from the Guests's recv virtqueue. */ + head =get_vq_desc (dev->vq, iov, &out_num, &in_num); +if (head == dev->vq->vring.num) {/* Now, it's expected that if we try to send a packet too -* early, the Guest won't be ready yet. This is why we set a -* flag when the Guest sends its first packet. If it's sent a -* packet we assume it should be ready to receive them. -* -* Actually, this is what the status bits in the descriptor are -* for: we should *use* them. FIXME! */ -if (*(bool *)dev->priv) +* early, the Guest won't be ready yet. Wait until the device +* status says it's ready. */ +/* FIXME: Actually want DRIVER_ACTIVE here. */ +if (dev->desc->status &VIRTIO_CONFIG_S_DRIVER_OK )warn ("network: no dma buffer!" ); -discard_iovec (iov, &num); - } +discard_iovec (iov, &in_num); + }else if (out_num) +errx (1 ,"Output buffers in network recv queue?" ); + +/* First element is the header: we set it to 0 (no features). */ + hdr = convert(&iov[0], struct virtio_net_hdr); + hdr->flags =0 ; + hdr->gso_type =VIRTIO_NET_HDR_GSO_NONE ;/* Read the packet from the device directly into the Guest's buffer. */ - len =readv (dev->fd, iov, num); + len =readv (dev->fd, iov+1 , in_num-1 );if (len <=0 )err (1 ,"reading network" ); -/* Write the used_len, and trigger the interrupt for the Guest */ -if (lenp) { - *lenp = len; -trigger_irq (fd, irq); - } +/* If we actually read the data into the Guest, tell them about it. */ +if (head != dev->vq->vring.num) +add_used_and_trigger (fd, dev->vq, head,sizeof (*hdr) + len); +verbose ("tun input packet len %i [%02x %02x] (%s)\n" , len, - ((u8 *)iov[0 ].iov_base)[0 ], ((u8 *)iov[0 ].iov_base)[1 ], - lenp ?"sent" :"discarded" ); + ((u8 *)iov[1 ].iov_base)[0 ], ((u8 *)iov[1 ].iov_base)[1 ], + head != dev->vq->vring.num ?"sent" :"discarded" ); +/* All good. */ return true; } -/* The last device handling routine is block output: the Guest has sent a DMA -* to the block device. It will have placed the command it wants in the -* "struct lguest_block_page". */ -static u32 handle_block_output (int fd,const struct iovec *iov, -unsigned num,struct device *dev) -{ -struct lguest_block_page *p = dev->mem; -u32 irq, *lenp; -unsigned int len, reply_num; -struct iovec reply[LGUEST_MAX_DMA_SECTIONS ]; -off64_t device_len, off = (off64_t )p->sector *512 ; - -/* First we extract the device length from the dev->priv pointer. */ - device_len = *(off64_t *)dev->priv; - -/* We first check that the read or write is within the length of the -* block file. */ -if (off >= device_len) -errx (1 ,"Bad offset %llu vs %llu" , off, device_len); -/* Move to the right location in the block file. This shouldn't fail, -* but best to check. */ -if (lseek64 (dev->fd, off,SEEK_SET ) != off) -err (1 ,"Bad seek to sector %i" , p->sector); - -verbose ("Block: %s at offset %llu\n" , p->type ?"WRITE" :"READ" , off); - -/* They were supposed to bind a reply buffer at key equal to the start -* of the block device memory. We need this to tell them when the -* request is finished. */ - lenp =get_dma_buffer (fd, dev->mem, reply, &reply_num, &irq); -if (!lenp) -err (1 ,"Block request didn't give us a dma buffer" ); - -if (p->type) { -/* A write request. The DMA they sent contained the data, so -* write it out. */ - len =writev (dev->fd, iov, num); -/* Grr... Now we know how long the "struct lguest_dma" they -* sent was, we make sure they didn't try to write over the end -* of the block file (possibly extending it). */ -if (off + len > device_len) { -/* Trim it back to the correct length */ -ftruncate64 (dev->fd, device_len); -/* Die, bad Guest, die. */ -errx (1 ,"Write past end %llu+%u" , off, len); +/* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */ +static void handle_output (int fd,unsigned long addr) +{ +struct device *i; +struct virtqueue *vq; + +/* Check each virtqueue. */ +for (i = devices.dev; i; i = i->next) { +for (vq = i->vq; vq; vq = vq->next) { +if (vq->config.pfn == addr/getpagesize () + && vq->handle_output) { +verbose ("Output to %s\n" , vq->dev->name); + vq->handle_output (fd, vq); +return ; + } } -/* The reply length is 0: we just send back an empty DMA to -* interrupt them and tell them the write is finished. */ - *lenp =0 ; - }else { -/* A read request. They sent an empty DMA to start the -* request, and we put the read contents into the reply -* buffer. */ - len =readv (dev->fd, reply, reply_num); - *lenp = len; - } - -/* The result is 1 (done), 2 if there was an error (short read or -* write). */ - p->result =1 + (p->bytes != len); -/* Now tell them we've used their reply buffer. */ -trigger_irq (fd, irq); - -/* We're supposed to return the number of bytes of the output buffer we -* used. But the block device uses the "result" field instead, so we -* don't bother. */ -return 0 ; -} - -/* This is the generic routine we call when the Guest sends some DMA out. */ -static void handle_output (int fd,unsigned long dma,unsigned long key, -struct device_list *devices) -{ -struct device *i; -u32 *lenp; -struct iovec iov[LGUEST_MAX_DMA_SECTIONS ]; -unsigned num =0 ; - -/* Convert the "struct lguest_dma" they're sending to a "struct -* iovec". */ - lenp =dma2iov (dma, iov, &num); - -/* Check each device: if they expect output to this key, tell them to -* handle it. */ -for (i = devices->dev; i; i = i->next) { -if (i->handle_output && key == i->watch_key) { -/* We write the result straight into the used_len field -* for them. */ - *lenp = i->handle_output (fd, iov, num, i); -return ; - } - } - -/* This can happen: the kernel sends any SEND_DMA which doesn't match -* another Guest to us. It could be that another Guest just left a -* network, for example. But it's unusual. */ -warnx ("Pending dma %p, key %p" , (void *)dma, (void *)key); + } + +/* Early console write is done using notify on a nul-terminated string +* in Guest memory. */ +if (addr >= guest_limit) +errx (1 ,"Bad NOTIFY %#lx" , addr); + +write (STDOUT_FILENO ,from_guest_phys (addr), +strnlen (from_guest_phys (addr), guest_limit - addr)); }/* This is called when the waker wakes us up: check for incoming file * descriptors. */ -static void handle_input (int fd,struct device_list *devices) +static void handle_input (int fd) {/* select() wants a zeroed timeval to mean "don't wait". */ struct timeval poll = { .tv_sec =0 , .tv_usec =0 };for (;;) {struct device *i; -fd_set fds = devices->infds; +fd_set fds = devices.infds;/* If nothing is ready, we're done. */ -if (select (devices->max_infd+1 , &fds,NULL ,NULL , &poll) ==0 ) +if (select (devices.max_infd+1 , &fds,NULL ,NULL , &poll) ==0 )break ;/* Otherwise, call the device(s) which have readable * file descriptors and a method of handling them. */ -for (i = devices->dev; i; i = i->next) { +for (i = devices.dev; i; i = i->next) {if (i->handle_input &&FD_ISSET (i->fd, &fds)) {/* If handle_input() returns false, it means we * should no longer service it.
@@ -936,7 +1003,7 @@ static void handle_input(int fd, struct/* Clear it from the set of input file * descriptors kept at the head of the * device list. */ -FD_CLR (i->fd, &devices->infds); +FD_CLR (i->fd, &devices.infds);/* Tell waker to ignore it too... */ write (waker_fd, &i->fd,sizeof (i->fd)); }
@@ -953,43 +1020,93 @@ static void handle_input(int fd, struct* routines to allocate them. * * This routine allocates a new "struct lguest_device_desc" from descriptor -* table in the devices array just above the Guest's normal memory. */ -static struct lguest_device_desc * -new_dev_desc (struct lguest_device_desc *descs, -u16 type,u16 features,u16 num_pages) -{ -unsigned int i; - -for (i =0 ; i <LGUEST_MAX_DEVICES ; i++) { -if (!descs[i].type) { - descs[i].type = type; - descs[i].features = features; - descs[i].num_pages = num_pages; -/* If they said the device needs memory, we allocate -* that now. */ -if (num_pages) { -unsigned long pa; - pa =to_guest_phys (get_pages (num_pages)); - descs[i].pfn = pa /getpagesize (); - } -return &descs[i]; - } - } -errx (1 ,"too many devices" ); -} - -/* This monster routine does all the creation and setup of a new device, -* including caling new_dev_desc() to allocate the descriptor and device -* memory. */ -static struct device *new_device (struct device_list *devices, -u16 type,u16 num_pages,u16 features, -int fd, -bool (*handle_input)(int ,struct device *), -unsigned long watch_off, -u32 (*handle_output)(int , -const struct iovec *, -unsigned , -struct device *)) +* table just above the Guest's normal memory. It returns a pointer to that +* descriptor. */ +static struct lguest_device_desc *new_dev_desc (u16 type) +{ +struct lguest_device_desc *d; + +/* We only have one page for all the descriptors. */ +if (devices.desc_used +sizeof (*d) >getpagesize ()) +errx (1 ,"Too many devices" ); + +/* We don't need to set config_len or status: page is 0 already. */ + d = (void *)devices.descpage + devices.desc_used; + d->type = type; + devices.desc_used +=sizeof (*d); + +return d; +} + +/* Each device descriptor is followed by some configuration information. +* The first byte is a "status" byte for the Guest to report what's happening. +* After that are fields: u8 type, u8 len, [... len bytes...]. +* +* This routine adds a new field to an existing device's descriptor. It only +* works for the last device, but that's OK because that's how we use it. */ +static void add_desc_field (struct device *dev,u8 type,u8 len,const void *c) +{ +/* This is the last descriptor, right? */ +assert (devices.descpage + devices.desc_used + == (u8 *)(dev->desc +1 ) + dev->desc->config_len); + +/* We only have one page of device descriptions. */ +if (devices.desc_used +2 + len >getpagesize ()) +errx (1 ,"Too many devices" ); + +/* Copy in the new config header: type then length. */ + devices.descpage[devices.desc_used++] = type; + devices.descpage[devices.desc_used++] = len; +memcpy (devices.descpage + devices.desc_used, c, len); + devices.desc_used += len; + +/* Update the device descriptor length: two byte head then data. */ + dev->desc->config_len +=2 + len; +} + +/* This routine adds a virtqueue to a device. We specify how many descriptors +* the virtqueue is to have. */ +static void add_virtqueue (struct device *dev,unsigned int num_descs, +void (*handle_output)(int fd,struct virtqueue *me)) +{ +unsigned int pages; +struct virtqueue **i, *vq =malloc (sizeof (*vq)); +void *p; + +/* First we need some pages for this virtqueue. */ + pages = (vring_size (num_descs) +getpagesize () -1 ) /getpagesize (); + p =get_pages (pages); + +/* Initialize the configuration. */ + vq->config.num = num_descs; + vq->config.irq = devices.next_irq++; + vq->config.pfn =to_guest_phys (p) /getpagesize (); + +/* Initialize the vring. */ +vring_init (&vq->vring, num_descs, p); + +/* Add the configuration information to this device's descriptor. */ +add_desc_field (dev,VIRTIO_CONFIG_F_VIRTQUEUE , +sizeof (vq->config), &vq->config); + +/* Add to tail of list, so dev->vq is first vq, dev->vq->next is +* second. */ +for (i = &dev->vq; *i; i = &(*i)->next); + *i = vq; + +/* Link virtqueue back to device. */ + vq->dev = dev; + +/* Set up handler. */ + vq->handle_output = handle_output; +if (!handle_output) + vq->vring.used->flags =VRING_USED_F_NO_NOTIFY ; +} + +/* This routine does all the creation and setup of a new device, including +* caling new_dev_desc() to allocate the descriptor and device memory. */ +static struct device *new_device (const char *name,u16 type,int fd, +bool (*handle_input)(int ,struct device *)) {struct device *dev =malloc (sizeof (*dev));
@@ -997,27 +1114,25 @@ static struct device *new_device(struct* easier, but the user expects the devices to be arranged on the bus * in command-line order. The first network device on the command line * is eth0, the first block device /dev/lgba, etc. */ - *devices->lastdev = dev; + *devices.lastdev = dev; dev->next =NULL ; - devices->lastdev = &dev->next; + devices.lastdev = &dev->next;/* Now we populate the fields one at a time. */ dev->fd = fd;/* If we have an input handler for this file descriptor, then we add it * to the device_list's fdset and maxfd. */ if (handle_input) -set_fd (dev->fd, devices); - dev->desc =new_dev_desc (devices->descs, type, features, num_pages); - dev->mem =from_guest_phys (dev->desc->pfn *getpagesize ()); +add_device_fd (dev->fd); + dev->desc =new_dev_desc (type); dev->handle_input = handle_input; - dev->watch_key =to_guest_phys (dev->mem) + watch_off; - dev->handle_output = handle_output; + dev->name = name;return dev; }/* Our first setup routine is the console. It's a fairly simple device, but * UNIX tty handling makes it uglier than it could be. */ -static void setup_console (struct device_list *devices) +static void setup_console (void ) {struct device *dev;
@@ -1033,127 +1148,38 @@ static void setup_console(struct device_atexit (restore_term); } -/* We don't currently require any memory for the console, so we ask for -* 0 pages. */ - dev =new_device (devices,LGUEST_DEVICE_T_CONSOLE ,0 ,0 , -STDIN_FILENO , handle_console_input, -LGUEST_CONSOLE_DMA_KEY , handle_console_output); + dev =new_device ("console" ,VIRTIO_ID_CONSOLE , +STDIN_FILENO , handle_console_input);/* We store the console state in dev->priv, and initialize it. */ dev->priv =malloc (sizeof (struct console_abort )); ((struct console_abort *)dev->priv)->count =0 ; -verbose ("device %p: console\n" , - (void *)(dev->desc->pfn *getpagesize ())); -} - -/* Setting up a block file is also fairly straightforward. */ -static void setup_block_file (const char *filename,struct device_list *devices) -{ -int fd; -struct device *dev; -off64_t *device_len; -struct lguest_block_page *p; - -/* We open with O_LARGEFILE because otherwise we get stuck at 2G. We -* open with O_DIRECT because otherwise our benchmarks go much too -* fast. */ - fd =open_or_die (filename,O_RDWR |O_LARGEFILE |O_DIRECT ); - -/* We want one page, and have no input handler (the block file never -* has anything interesting to say to us). Our timing will be quite -* random, so it should be a reasonable randomness source. */ - dev =new_device (devices,LGUEST_DEVICE_T_BLOCK ,1 , -LGUEST_DEVICE_F_RANDOMNESS , - fd,NULL ,0 , handle_block_output); - -/* We store the device size in the private area */ - device_len = dev->priv =malloc (sizeof (*device_len)); -/* This is the safe way of establishing the size of our device: it -* might be a normal file or an actual block device like /dev/hdb. */ - *device_len =lseek64 (fd,0 ,SEEK_END ); - -/* The device memory is a "struct lguest_block_page". It's zeroed -* already, we just need to put in the device size. Block devices -* think in sectors (ie. 512 byte chunks), so we translate here. */ - p = dev->mem; - p->num_sectors = *device_len/512 ; -verbose ("device %p: block %i sectors\n" , - (void *)(dev->desc->pfn *getpagesize ()), p->num_sectors); -} - -/* -* Network Devices. -* -* Setting up network devices is quite a pain, because we have three types. -* First, we have the inter-Guest network. This is a file which is mapped into -* the address space of the Guests who are on the network. Because it is a -* shared mapping, the same page underlies all the devices, and they can send -* DMA to each other. -* -* Remember from our network driver, the Guest is told what slot in the page it -* is to use. We use exclusive fnctl locks to reserve a slot. If another -* Guest is using a slot, the lock will fail and we try another. Because fnctl -* locks are cleaned up automatically when we die, this cleverly means that our -* reservation on the slot will vanish if we crash. */ -static unsigned int find_slot (int netfd,const char *filename) -{ -struct flock fl; - - fl.l_type =F_WRLCK ; - fl.l_whence =SEEK_SET ; - fl.l_len =1 ; -/* Try a 1 byte lock in each possible position number */ -for (fl.l_start =0 ; - fl.l_start <getpagesize ()/sizeof (struct lguest_net ); - fl.l_start++) { -/* If we succeed, return the slot number. */ -if (fcntl (netfd,F_SETLK , &fl) ==0 ) -return fl.l_start; - } -errx (1 ,"No free slots in network file %s" , filename); -} - -/* This function sets up the network file */ -static void setup_net_file (const char *filename, -struct device_list *devices) -{ -int netfd; -struct device *dev; - -/* We don't use open_or_die() here: for friendliness we create the file -* if it doesn't already exist. */ - netfd =open (filename,O_RDWR ,0 ); -if (netfd <0 ) { -if (errno ==ENOENT ) { - netfd =open (filename,O_RDWR |O_CREAT ,0600 ); -if (netfd >=0 ) { -/* If we succeeded, initialize the file with a -* blank page. */ -char page[getpagesize ()]; -memset (page,0 ,sizeof (page)); -write (netfd, page,sizeof (page)); - } - } -if (netfd <0 ) -err (1 ,"cannot open net file '%s'" , filename); - } - -/* We need 1 page, and the features indicate the slot to use and that -* no checksum is needed. We never touch this device again; it's -* between the Guests on the network, so we don't register input or -* output handlers. */ - dev =new_device (devices,LGUEST_DEVICE_T_NET ,1 , -find_slot (netfd, filename)|LGUEST_NET_F_NOCSUM , --1 ,NULL ,0 ,NULL ); - -/* Map the shared file. */ -if (mmap (dev->mem,getpagesize (),PROT_READ |PROT_WRITE , -MAP_FIXED |MAP_SHARED , netfd,0 ) != dev->mem) -err (1 ,"could not mmap '%s'" , filename); -verbose ("device %p: shared net %s, peer %i\n" , - (void *)(dev->desc->pfn *getpagesize ()), filename, - dev->desc->features & ~LGUEST_NET_F_NOCSUM ); + +/* The console needs two virtqueues: the input then the output. We +* don't care when they refill the input queue, since we don't hold +* data waiting for them. That's why the input queue's callback is +* NULL. */ +add_virtqueue (dev,VIRTQUEUE_NUM ,NULL ); +add_virtqueue (dev,VIRTQUEUE_NUM , handle_console_output); + +verbose ("device %u: console\n" , devices.device_num++); }/*:*/ + +/*M:010 Inter-guest networking is an interesting area. Simplest is to have a +* --sharenet=<name> option which opens or creates a named pipe. This can be +* used to send packets to another guest in a 1:1 manner. +* +* More sopisticated is to use one of the tools developed for project like UML +* to do networking. +* +* Faster is to do virtio bonding in kernel. Doing this 1:1 would be +* completely generic ("here's my vring, attach to your vring") and would work +* for any traffic. Of course, namespace and permissions issues need to be +* dealt with. A more sophisticated "multi-channel" virtio_net.c could hide +* multiple inter-guest channels behind one interface, although it would +* require some manner of hotplugging new virtio channels. +* +* Finally, we could implement a virtio network switch in the kernel. :*/ static u32 str2ip (const char *ipaddr) {
@@ -1188,7 +1214,7 @@ static void add_to_bridge(int fd, const/* This sets up the Host end of the network device with an IP address, brings * it up so packets will flow, the copies the MAC address into the hwaddr -* pointer (in practice, the Host's slot in the network device's memory). */ +* pointer. */ static void configure_device (int fd,const char *devname,u32 ipaddr,unsigned char hwaddr[6 ]) {
@@ -1214,18 +1240,18 @@ static void configure_device(int fd, conmemcpy (hwaddr, ifr.ifr_hwaddr.sa_data,6 ); } -/*L:195 The other kind of network is a Host<->Guest network. This can either -* use briding or routing, but the principle is the same: it uses the "tun" -* device to inject packets into the Host as if they came in from a normal -* network card. We just shunt packets between the Guest and the tun -* device. */ -static void setup_tun_net (const char *arg,struct device_list *devices) +/*L:195 Our network is a Host<->Guest network. This can either use bridging or +* routing, but the principle is the same: it uses the "tun" device to inject +* packets into the Host as if they came in from a normal network card. We +* just shunt packets between the Guest and the tun device. */ +static void setup_tun_net (const char *arg) {struct device *dev;struct ifreq ifr;int netfd, ipfd;u32 ip;const char *br_name =NULL ; +u8 hwaddr[6 ];/* We open the /dev/net/tun device and tell it we want a tap device. A * tap device is like a tun device, only somehow different. To tell
@@ -1241,21 +1267,12 @@ static void setup_tun_net(const char *ar* device: trust us! */ ioctl (netfd,TUNSETNOCSUM ,1 ); -/* We create the net device with 1 page, using the features field of -* the descriptor to tell the Guest it is in slot 1 (NET_PEERNUM), and -* that the device has fairly random timing. We do *not* specify -* LGUEST_NET_F_NOCSUM: these packets can reach the real world. -* -* We will put our MAC address is slot 0 for the Guest to see, so -* it will send packets to us using the key "peer_offset(0)": */ - dev =new_device (devices,LGUEST_DEVICE_T_NET ,1 , -NET_PEERNUM |LGUEST_DEVICE_F_RANDOMNESS , netfd, - handle_tun_input,peer_offset (0 ), handle_tun_output); - -/* We keep a flag which says whether we've seen packets come out from -* this network device. */ - dev->priv =malloc (sizeof (bool )); - *(bool *)dev->priv = false; +/* First we create a new network device. */ + dev =new_device ("net" ,VIRTIO_ID_NET , netfd, handle_tun_input); + +/* Network devices need a receive and a send queue. */ +add_virtqueue (dev,VIRTQUEUE_NUM ,NULL ); +add_virtqueue (dev,VIRTQUEUE_NUM , handle_net_output);/* We need a socket to perform the magic network ioctls to bring up the * tap interface, connect to the bridge etc. Any socket will do! */
@@ -1271,44 +1288,251 @@ static void setup_tun_net(const char *ar }else /* It is an IP address to set up the device with */ ip =str2ip (arg); -/* We are peer 0, ie. first slot, so we hand dev->mem to this routine -* to write the MAC address at the start of the device memory. */ -configure_device (ipfd, ifr.ifr_name, ip, dev->mem); - -/* Set "promisc" bit: we want every single packet if we're going to -* bridge to other machines (and otherwise it doesn't matter). */ - *((u8 *)dev->mem) |=0x1 ; - +/* Set up the tun device, and get the mac address for the interface. */ +configure_device (ipfd, ifr.ifr_name, ip, hwaddr); + +/* Tell Guest what MAC address to use. */ +add_desc_field (dev,VIRTIO_CONFIG_NET_MAC_F ,sizeof (hwaddr), hwaddr); + +/* We don't seed the socket any more; setup is done. */ close (ipfd); -verbose ("device %p: tun net %u.%u.%u.%u\n" , - (void *)(dev->desc->pfn *getpagesize ()), - (u8)(ip>>24 ), (u8)(ip>>16 ), (u8)(ip>>8 ), (u8 )ip); +verbose ("device %u: tun net %u.%u.%u.%u\n" , + devices.device_num++, + (u8)(ip>>24 ),(u8)(ip>>16 ),(u8)(ip>>8 ),(u8 )ip);if (br_name)verbose ("attached to bridge: %s\n" , br_name); } + + +/* +* Block device. +* +* Serving a block device is really easy: the Guest asks for a block number and +* we read or write that position in the file. +* +* Unfortunately, this is amazingly slow: the Guest waits until the read is +* finished before running anything else, even if it could be doing useful +* work. We could use async I/O, except it's reputed to suck so hard that +* characters actually go missing from your code when you try to use it. +* +* So we farm the I/O out to thread, and communicate with it via a pipe. */ + +/* This hangs off device->priv, with the data. */ +struct vblk_info +{ +/* The size of the file. */ +off64_t len; + +/* The file descriptor for the file. */ +int fd; + +/* IO thread listens on this file descriptor [0]. */ +int workpipe[2 ]; + +/* IO thread writes to this file descriptor to mark it done, then +* Launcher triggers interrupt to Guest. */ +int done_fd; +}; + +/* This is the core of the I/O thread. It returns true if it did something. */ +static bool service_io (struct device *dev) +{ +struct vblk_info *vblk = dev->priv; +unsigned int head, out_num, in_num, wlen; +int ret; +struct virtio_blk_inhdr *in; +struct virtio_blk_outhdr *out; +struct iovec iov[dev->vq->vring.num]; +off64_t off; + + head =get_vq_desc (dev->vq, iov, &out_num, &in_num); +if (head == dev->vq->vring.num) +return false; + +if (out_num ==0 || in_num ==0 ) +errx (1 ,"Bad virtblk cmd %u out=%u in=%u" , + head, out_num, in_num); + + out = convert(&iov[0], struct virtio_blk_outhdr); + in = convert(&iov[out_num+in_num-1], struct virtio_blk_inhdr); + off = out->sector *512 ; + +/* This is how we implement barriers. Pretty poor, no? */ +if (out->type &VIRTIO_BLK_T_BARRIER ) +fdatasync (vblk->fd); + +if (out->type &VIRTIO_BLK_T_SCSI_CMD ) { +fprintf (stderr,"Scsi commands unsupported\n" ); + in->status =VIRTIO_BLK_S_UNSUPP ; + wlen =sizeof (in); + }else if (out->type &VIRTIO_BLK_T_OUT ) { +/* Write */ + +/* Move to the right location in the block file. This can fail +* if they try to write past end. */ +if (lseek64 (vblk->fd, off,SEEK_SET ) != off) +err (1 ,"Bad seek to sector %llu" , out->sector); + + ret =writev (vblk->fd, iov+1 , out_num-1 ); +verbose ("WRITE to sector %llu: %i\n" , out->sector, ret); + +/* Grr... Now we know how long the descriptor they sent was, we +* make sure they didn't try to write over the end of the block +* file (possibly extending it). */ +if (ret >0 && off + ret > vblk->len) { +/* Trim it back to the correct length */ +ftruncate64 (vblk->fd, vblk->len); +/* Die, bad Guest, die. */ +errx (1 ,"Write past end %llu+%u" , off, ret); + } + wlen =sizeof (in); + in->status = (ret >=0 ?VIRTIO_BLK_S_OK :VIRTIO_BLK_S_IOERR ); + }else { +/* Read */ + +/* Move to the right location in the block file. This can fail +* if they try to read past end. */ +if (lseek64 (vblk->fd, off,SEEK_SET ) != off) +err (1 ,"Bad seek to sector %llu" , out->sector); + + ret =readv (vblk->fd, iov+1 , in_num-1 ); +verbose ("READ from sector %llu: %i\n" , out->sector, ret); +if (ret >=0 ) { + wlen =sizeof (in) + ret; + in->status =VIRTIO_BLK_S_OK ; + }else { + wlen =sizeof (in); + in->status =VIRTIO_BLK_S_IOERR ; + } + } + +/* We can't trigger an IRQ, because we're not the Launcher. It does +* that when we tell it we're done. */ +add_used (dev->vq, head, wlen); +return true; +} + +/* This is the thread which actually services the I/O. */ +static int io_thread (void *_dev) +{ +struct device *dev = _dev; +struct vblk_info *vblk = dev->priv; +char c; + +/* Close other side of workpipe so we get 0 read when main dies. */ +close (vblk->workpipe[1 ]); +/* Close the other side of the done_fd pipe. */ +close (dev->fd); + +/* When this read fails, it means Launcher died, so we follow. */ +while (read (vblk->workpipe[0 ], &c,1 ) ==1 ) { +/* We acknowledge each request immediately, to reduce latency, +* rather than waiting until we've done them all. I haven't +* measured to see if it makes any difference. */ +while (service_io (dev)) +write (vblk->done_fd, &c,1 ); + } +return 0 ; +} + +/* When the thread says some I/O is done, we interrupt the Guest. */ +static bool handle_io_finish (int fd,struct device *dev) +{ +char c; + +/* If child died, presumably it printed message. */ +if (read (dev->fd, &c,1 ) !=1 ) +exit (1 ); + +/* It did some work, so trigger the irq. */ +trigger_irq (fd, dev->vq); +return true; +} + +/* When the Guest submits some I/O, we wake the I/O thread. */ +static void handle_virtblk_output (int fd,struct virtqueue *vq) +{ +struct vblk_info *vblk = vq->dev->priv; +char c =0 ; + +/* Wake up I/O thread and tell it to go to work! */ +if (write (vblk->workpipe[1 ], &c,1 ) !=1 ) +/* Presumably it indicated why it died. */ +exit (1 ); +} + +/* This creates a virtual block device. */ +static void setup_block_file (const char *filename) +{ +int p[2 ]; +struct device *dev; +struct vblk_info *vblk; +void *stack; +u64 cap; +unsigned int val; + +/* This is the pipe the I/O thread will use to tell us I/O is done. */ +pipe (p); + +/* The device responds to return from I/O thread. */ + dev =new_device ("block" ,VIRTIO_ID_BLOCK , p[0 ], handle_io_finish); + +/* The device has a virtqueue. */ +add_virtqueue (dev,VIRTQUEUE_NUM , handle_virtblk_output); + +/* Allocate the room for our own bookkeeping */ + vblk = dev->priv =malloc (sizeof (*vblk)); + +/* First we open the file and store the length. */ + vblk->fd =open_or_die (filename,O_RDWR |O_LARGEFILE ); + vblk->len =lseek64 (vblk->fd,0 ,SEEK_END ); + +/* Tell Guest how many sectors this device has. */ + cap =cpu_to_le64 (vblk->len /512 ); +add_desc_field (dev,VIRTIO_CONFIG_BLK_F_CAPACITY ,sizeof (cap), &cap); + +/* Tell Guest not to put in too many descriptors at once: two are used +* for the in and out elements. */ + val =cpu_to_le32 (VIRTQUEUE_NUM -2 ); +add_desc_field (dev,VIRTIO_CONFIG_BLK_F_SEG_MAX ,sizeof (val), &val); + +/* The I/O thread writes to this end of the pipe when done. */ + vblk->done_fd = p[1 ]; + +/* This is how we tell the I/O thread about more work. */ +pipe (vblk->workpipe); + +/* Create stack for thread and run it */ + stack =malloc (32768 ); +if (clone (io_thread, stack +32768 ,CLONE_VM , dev) ==-1 ) +err (1 ,"Creating clone" ); + +/* We don't need to keep the I/O thread's end of the pipes open. */ +close (vblk->done_fd); +close (vblk->workpipe[0 ]); + +verbose ("device %u: virtblock %llu sectors\n" , + devices.device_num, cap); +}/* That's the end of device setup. */ /*L:220 Finally we reach the core of the Launcher, which runs the Guest, serves * its input and output, and finally, lays it to rest. */ -static void __attribute__((noreturn)) -run_guest (int lguest_fd,struct device_list *device_list) +static void __attribute__((noreturn))run_guest (int lguest_fd) {for (;;) {unsigned long args[] = {LHREQ_BREAK ,0 }; -unsigned long arr[2 ]; +unsigned long notify_addr;int readval;/* We read from the /dev/lguest device to run the Guest. */ - readval =read (lguest_fd, arr,sizeof (arr)); - -/* The read can only really return sizeof(arr) (the Guest did a -* SEND_DMA to us), or an error. */ - -/* For a successful read, arr[0] is the address of the "struct -* lguest_dma", and arr[1] is the key the Guest sent to. */ -if (readval ==sizeof (arr)) { -handle_output (lguest_fd, arr[0 ], arr[1 ], device_list); + readval =read (lguest_fd, ¬ify_addr,sizeof (notify_addr)); + +/* One unsigned long means the Guest did HCALL_NOTIFY */ +if (readval ==sizeof (notify_addr)) { +verbose ("Notify on address %#lx\n" , notify_addr); +handle_output (lguest_fd, notify_addr);continue ;/* ENOENT means the Guest died. Reading tells us why. */ }else if (errno ==ENOENT ) {
@@ -1322,7 +1546,7 @@ run_guest(int lguest_fd, struct device_l/* Service input, then unset the BREAK which releases * the Waker. */ -handle_input (lguest_fd, device_list); +handle_input (lguest_fd);if (write (lguest_fd, args,sizeof (args)) <0 )err (1 ,"Resetting break" ); }
@@ -1336,7 +1560,6 @@ run_guest(int lguest_fd, struct device_lstatic struct option opts[] = { {"verbose" ,0 ,NULL ,'v' }, - {"sharenet" ,1 ,NULL ,'s' }, {"tunnet" ,1 ,NULL ,'t' }, {"block" ,1 ,NULL ,'b' }, {"initrd" ,1 ,NULL ,'i' },
@@ -1345,7 +1568,7 @@ static void usage(void)static void usage (void ) {errx (1 ,"Usage: lguest [--verbose] " -"[--sharenet=<filename>|--tunnet=(<ipaddr>|bridge:<bridgename>)\n" +"[--tunnet=(<ipaddr>|bridge:<bridgename>)\n" "|--block=<filename>|--initrd=<filename>]...\n" "<mem-in-mb> vmlinux [args...]" ); }
@@ -1358,8 +1581,6 @@ int main(int argc, char *argv[])unsigned long mem =0 , pgdir, start, initrd_size =0 ;/* A temporary and the /dev/lguest file descriptor. */ int i, c, lguest_fd; -/* The list of Guest devices, based on command line arguments. */ -struct device_list device_list;/* The boot information for the Guest. */ void *boot;/* If they specify an initrd file to load. */
@@ -1369,11 +1590,12 @@ int main(int argc, char *argv[])* device receive input from a file descriptor, we keep an fdset * (infds) and the maximum fd number (max_infd) with the head of the * list. We also keep a pointer to the last device, for easy appending -* to the list. */ - device_list.max_infd =-1 ; - device_list.dev =NULL ; - device_list.lastdev = &device_list.dev; -FD_ZERO (&device_list.infds); +* to the list. Finally, we keep the next interrupt number to hand out +* (1: remember that 0 is used by the timer). */ +FD_ZERO (&devices.infds); + devices.max_infd =-1 ; + devices.lastdev = &devices.dev; + devices.next_irq =1 ;/* We need to know how much memory so we can set up the device * descriptor and memory pages for the devices as we parse the command
@@ -1390,7 +1612,7 @@ int main(int argc, char *argv[]) + DEVICE_PAGES); guest_limit = mem; guest_max = mem +DEVICE_PAGES *getpagesize (); - device_list.descs =get_pages (1 ); + devices.descpage =get_pages (1 );break ; } }
@@ -1401,14 +1623,11 @@ int main(int argc, char *argv[])case 'v' : verbose = true;break ; -case 's' : -setup_net_file (optarg, &device_list); -break ;case 't' : -setup_tun_net (optarg, &device_list); +setup_tun_net (optarg);break ;case 'b' : -setup_block_file (optarg, &device_list); +setup_block_file (optarg);break ;case 'i' : initrd_name = optarg;
@@ -1426,7 +1645,7 @@ int main(int argc, char *argv[])verbose ("Guest base is at %p\n" , guest_base);/* We always have a console device */ -setup_console (&device_list); +setup_console ();/* Now we load the kernel */ start =load_kernel (open_or_die (argv[optind+1 ],O_RDONLY ));
@@ -1468,10 +1687,10 @@ int main(int argc, char *argv[])/* We fork off a child process, which wakes the Launcher whenever one * of the input file descriptors needs attention. Otherwise we would * run the Guest until it tries to output something. */ - waker_fd =setup_waker (lguest_fd, &device_list); + waker_fd =setup_waker (lguest_fd);/* Finally, run the Guest. This doesn't return. */ -run_guest (lguest_fd, &device_list); +run_guest (lguest_fd); }/*:*/
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 42/43 lguest: Example launcher handle guests not being ready for input
In reply to [email protected]We currently discard console and network input when the guest has no input buffers. This patch changes that, so that we simply stop listening to that fd until the guest refills its input buffers.
This is particularly important because hvc_console without interrupts does backoff polling and so often lose characters if we discard.
--- Documentation/lguest/lguest.c | 107 +++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 50 deletions(-)
Documentation/lguest/lguest.c +57 -50
@@ -598,15 +598,17 @@ static void wake_parent(int pipefd, intselect (devices.max_infd+1 , &rfds,NULL ,NULL ,NULL );/* Is it a message from the Launcher? */ if (FD_ISSET (pipefd, &rfds)) { -int ignorefd; +int fd;/* If read() returns 0, it means the Launcher has * exited. We silently follow. */ -if (read (pipefd, &ignorefd,sizeof (ignorefd)) ==0 ) +if (read (pipefd, &fd,sizeof (fd)) ==0 )exit (0 ); -/* Otherwise it's telling us there's a problem with one -* of the devices, and we should ignore that file -* descriptor from now on. */ -FD_CLR (ignorefd, &devices.infds); +/* Otherwise it's telling us to change what file +* descriptors we're to listen to. */ +if (fd >=0 ) +FD_SET (fd, &devices.infds); +else +FD_CLR (-fd -1 , &devices.infds); }else /* Send LHREQ_BREAK command. */ write (lguest_fd, args,sizeof (args)); }
@@ -657,18 +659,6 @@ static void *_check_pointer(unsigned lon }/* A macro which transparently hands the line number to the real function. */ #define check_pointer (addr,size) _check_pointer(addr, size, __LINE__) - -/* This simply sets up an iovec array where we can put data to be discarded. -* This happens when the Guest doesn't want or can't handle the input: we have -* to get rid of it somewhere, and if we bury it in the ceiling space it will -* start to smell after a week. */ -static void discard_iovec (struct iovec *iov,unsigned int *num) -{ -static char discard_buf[1024 ]; - *num =1 ; - iov->iov_base = discard_buf; - iov->iov_len =sizeof (discard_buf); -}/* This function returns the next descriptor in the chain, or vq->vring.num. */ static unsigned next_desc (struct virtqueue *vq,unsigned int i)
@@ -812,12 +802,13 @@ static bool handle_console_input(int fd,/* First we need a console buffer from the Guests's input virtqueue. */ head =get_vq_desc (dev->vq, iov, &out_num, &in_num); -if (head == dev->vq->vring.num) { -/* If they're not ready for input, we warn and set up to -* discard. */ -warnx ("console: no dma buffer!" ); -discard_iovec (iov, &in_num); - }else if (out_num) + +/* If they're not ready for input, stop listening to this file +* descriptor. We'll start again once they add an input buffer. */ +if (head == dev->vq->vring.num) +return false; + +if (out_num)errx (1 ,"Output buffers in console in queue?" );/* This is why we convert to iovecs: the readv() call uses them, and so
@@ -827,15 +818,16 @@ static bool handle_console_input(int fd,/* This implies that the console is closed, is /dev/null, or * something went terribly wrong. */ warnx ("Failed to get console input, ignoring console." ); -/* Put the input terminal back and return failure (meaning, -* don't call us again). */ +/* Put the input terminal back. */ restore_term (); +/* Remove callback from input vq, so it doesn't restart us. */ + dev->vq->handle_output =NULL ; +/* Stop listening to this fd: don't call us again. */ return false; } -/* If we actually read the data into the Guest, tell them about it. */ -if (head != dev->vq->vring.num) -add_used_and_trigger (fd, dev->vq, head, len); +/* Tell the Guest about the new input. */ +add_used_and_trigger (fd, dev->vq, head, len);/* Three ^C within one second? Exit. *
@@ -924,7 +916,8 @@ static bool handle_tun_input(int fd, str/* FIXME: Actually want DRIVER_ACTIVE here. */ if (dev->desc->status &VIRTIO_CONFIG_S_DRIVER_OK )warn ("network: no dma buffer!" ); -discard_iovec (iov, &in_num); +/* We'll turn this back on if input buffers are registered. */ +return false; }else if (out_num)errx (1 ,"Output buffers in network recv queue?" );
@@ -938,9 +931,8 @@ static bool handle_tun_input(int fd, strif (len <=0 )err (1 ,"reading network" ); -/* If we actually read the data into the Guest, tell them about it. */ -if (head != dev->vq->vring.num) -add_used_and_trigger (fd, dev->vq, head,sizeof (*hdr) + len); +/* Tell the Guest about the new packet. */ +add_used_and_trigger (fd, dev->vq, head,sizeof (*hdr) + len); verbose("tun input packet len %i [%02x %02x] (%s)\n", len, ((u8 *)iov[1].iov_base)[0], ((u8 *)iov[1].iov_base)[1],
@@ -948,6 +940,15 @@ static bool handle_tun_input(int fd, str/* All good. */ return true; +} + +/* This callback ensures we try again, in case we stopped console or net +* delivery because Guest didn't have any buffers. */ +static void enable_fd (int fd,struct virtqueue *vq) +{ +add_device_fd (vq->dev->fd); +/* Tell waker to listen to it again */ +write (waker_fd, &vq->dev->fd,sizeof (vq->dev->fd)); }/* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */
@@ -996,17 +997,22 @@ static void handle_input(int fd)* file descriptors and a method of handling them. */ for (i = devices.dev; i; i = i->next) {if (i->handle_input &&FD_ISSET (i->fd, &fds)) { +int dev_fd; +if (i->handle_input (fd, i)) +continue ; +/* If handle_input() returns false, it means we -* should no longer service it. -* handle_console_input() does this. */ -if (!i->handle_input (fd, i)) { -/* Clear it from the set of input file -* descriptors kept at the head of the -* device list. */ -FD_CLR (i->fd, &devices.infds); -/* Tell waker to ignore it too... */ -write (waker_fd, &i->fd,sizeof (i->fd)); - } +* should no longer service it. Networking and +* console do this when there's no input +* buffers to deliver into. Console also uses +* it when it discovers that stdin is +* closed. */ +FD_CLR (i->fd, &devices.infds); +/* Tell waker to ignore it too, by sending a +* negative fd number (-1, since 0 is a valid +* FD number). */ + dev_fd = -i->fd -1 ; +write (waker_fd, &dev_fd,sizeof (dev_fd)); } } }
@@ -1154,11 +1160,11 @@ static void setup_console(void) dev->priv =malloc (sizeof (struct console_abort )); ((struct console_abort *)dev->priv)->count =0 ; -/* The console needs two virtqueues: the input then the output. We -* don't care when they refill the input queue, since we don't hold -* data waiting for them. That's why the input queue's callback is -* NULL. */ -add_virtqueue (dev,VIRTQUEUE_NUM ,NULL ); +/* The console needs two virtqueues: the input then the output. When +* they put something the input queue, we make sure we're listening to +* stdin. When they put something in the output queue, we write it to +* stdout. */ +add_virtqueue (dev,VIRTQUEUE_NUM , enable_fd);add_virtqueue (dev,VIRTQUEUE_NUM , handle_console_output);verbose ("device %u: console\n" , devices.device_num++);
@@ -1270,8 +1276,9 @@ static void setup_tun_net(const char *ar/* First we create a new network device. */ dev =new_device ("net" ,VIRTIO_ID_NET , netfd, handle_tun_input); -/* Network devices need a receive and a send queue. */ -add_virtqueue (dev,VIRTQUEUE_NUM ,NULL ); +/* Network devices need a receive and a send queue, just like +* console. */ +add_virtqueue (dev,VIRTQUEUE_NUM , enable_fd);add_virtqueue (dev,VIRTQUEUE_NUM , handle_net_output);/* We need a socket to perform the magic network ioctls to bring up the
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
PATCH 43/43 lguest: generalize lgread_u32/lgwrite_u32.
In reply to [email protected]Jes complains that page table code still uses lgread_u32 even though it now uses general kernel pte types. The best thing to do is to generalize lgread_u32 and lgwrite_u32.
This means we lose the efficiency of getuser(). We could potentially regain it if we used __copy_from_user instead of copy_from_user, but I'm not certain that our range check is equivalent to access_ok() on all platforms.
--- drivers/lguest/core.c | 39 ++++++--------------------------- drivers/lguest/hypercalls.c | 2 - drivers/lguest/i386_core.c | 4 +-- drivers/lguest/interrupts_and_traps.c | 2 - drivers/lguest/lg.h | 23 ++++++++++++++++--- drivers/lguest/page_tables.c | 10 ++++---- drivers/lguest/segments.c | 4 +-- 7 files changed, 38 insertions(+), 46 deletions(-)
===================================================================
drivers/lguest/core.c +8 -31
@@ -145,33 +145,10 @@ int lguest_address_ok(const struct lguesreturn (addr+len) /PAGE_SIZE < lg->pfn_limit && (addr+len >= addr); } -/* This is a convenient routine to get a 32-bit value from the Guest (a very -* common operation). Here we can see how useful the kill_lguest() routine we -* met in the Launcher can be: we return a random value (0) instead of needing -* to return an error. */ -u32 lgread_u32 (struct lguest *lg,unsigned long addr) -{ -u32 val =0 ; - -/* Don't let them access lguest binary. */ -if (!lguest_address_ok (lg, addr,sizeof (val)) - ||get_user (val, (u32 *)(lg->mem_base + addr)) !=0 ) -kill_guest (lg,"bad read address %#lx: pfn_limit=%u membase=%p" , addr, lg->pfn_limit, lg->mem_base); -return val; -} - -/* Same thing for writing a value. */ -void lgwrite_u32 (struct lguest *lg,unsigned long addr,u32 val) -{ -if (!lguest_address_ok (lg, addr,sizeof (val)) - ||put_user (val, (u32 *)(lg->mem_base + addr)) !=0 ) -kill_guest (lg,"bad write address %#lx" , addr); -} - -/* This routine is more generic, and copies a range of Guest bytes into a -* buffer. If the copy_from_user() fails, we fill the buffer with zeroes, so -* the caller doesn't end up using uninitialized kernel memory. */ -void lgread (struct lguest *lg,void *b,unsigned long addr,unsigned bytes) +/* This routine copies memory from the Guest. Here we can see how useful the +* kill_lguest() routine we met in the Launcher can be: we return a random +* value (all zeroes) instead of needing to return an error. */ +void __lgread (struct lguest *lg,void *b,unsigned long addr,unsigned bytes) {if (!lguest_address_ok (lg, addr, bytes) ||copy_from_user (b, lg->mem_base + addr, bytes) !=0 ) {
@@ -181,15 +158,15 @@ void lgread(struct lguest *lg, void *b, } } -/* Similarly, our generic routine to copy into a range of Guest bytes. */ -void lgwrite (struct lguest *lg,unsigned long addr,const void *b, -unsigned bytes) +/* This is the write (copy into guest) version. */ +void __lgwrite (struct lguest *lg,unsigned long addr,const void *b, +unsigned bytes) {if (!lguest_address_ok (lg, addr, bytes) ||copy_to_user (lg->mem_base + addr, b, bytes) !=0 )kill_guest (lg,"bad write address %#lx len %u" , addr, bytes); } -/* (end of memory access helper routines) :*/ +/*:*/ /*H:030 Let's jump straight to the the main loop which runs the Guest. * Remember, this is called by the Launcher reading /dev/lguest, and we keep
===================================================================
drivers/lguest/hypercalls.c +1 -1
@@ -47,7 +47,7 @@ static void do_hcall(struct lguest *lg,char msg[128 ];/* If the lgread fails, it will call kill_guest() itself; the * kill_guest() with the message will be ignored. */ -lgread (lg, msg, args->arg1,sizeof (msg)); +__lgread (lg, msg, args->arg1,sizeof (msg)); msg[sizeof (msg)-1 ] ='\0' ;kill_guest (lg,"CRASH: %s" , msg);break ;
===================================================================
drivers/lguest/i386_core.c +2 -2
@@ -222,7 +222,7 @@ static int emulate_insn(struct lguest *lreturn 0 ;/* Decoding x86 instructions is icky. */ -lgread (lg, &insn, physaddr,1 ); + insn =lgread (lg, &insn, u8);/* 0x66 is an "operand prefix". It means it's using the upper 16 bits of the eax register. */
@@ -230,7 +230,7 @@ static int emulate_insn(struct lguest *l shift =16 ;/* The instruction is 1 byte so far, read the next byte. */ insnlen =1 ; -lgread (lg, &insn, physaddr + insnlen,1 ); + insn =lgread (lg, physaddr + insnlen, u8); }/* We can ignore the lower bit for the moment and decode the 4 opcodes
===================================================================
drivers/lguest/interrupts_and_traps.c +1 -1
@@ -45,7 +45,7 @@ static void push_guest_stack(struct lgue {/* Stack grows upwards: move stack then write value. */ *gstack -=4 ; -lgwrite_u32 (lg, *gstack, val); +lgwrite (lg, *gstack, u32, val); }/*H:210 The set_guest_interrupt() routine actually delivers the interrupt or
===================================================================
drivers/lguest/lg.h +19 -4
@@ -99,12 +99,27 @@ extern struct mutex lguest_lock;extern struct mutex lguest_lock;/* core.c: */ -u32 lgread_u32 (struct lguest *lg,unsigned long addr); -void lgwrite_u32 (struct lguest *lg,unsigned long addr,u32 val); -void lgread (struct lguest *lg,void *buf,unsigned long addr,unsigned len); -void lgwrite (struct lguest *lg,unsigned long ,const void *buf,unsigned len);int lguest_address_ok (const struct lguest *lg,unsigned long addr,unsigned long len); +void __lgread (struct lguest *,void *,unsigned long ,unsigned ); +void __lgwrite (struct lguest *,unsigned long ,const void *,unsigned ); + +/*L:306 Using memory-copy operations like that is usually inconvient, so we +* have the following helper macros which read and write a specific type (often +* an unsigned long). +* +* This reads into a variable of the given type then returns that. */ +#define lgread (lg, addr, type) \ + {( type _v; __lgread((lg), &_v, (addr), sizeof(_v)); _v; )} + +/* This checks that the variable is of the given type, then writes it out. */ +#define lgwrite (lg, addr, type, val) \ + do { \ + typecheck(type, v); \ + __lgwrite((lg), &(v), (addr), sizeof(v)); \ + } while(0) +/* (end of memory access helper routines) :*/ +int run_guest (struct lguest *lg,unsigned long __user *user);/* Helper macros to obtain the first 12 or the last 20 bits, this is only the
===================================================================
drivers/lguest/page_tables.c +5 -5
@@ -209,7 +209,7 @@ int demand_page(struct lguest *lg, unsigpte_t *spte;/* First step: get the top-level Guest page table entry. */ - gpgd =__pgd (lgread_u32 (lg,gpgd_addr (lg, vaddr))); + gpgd =lgread (lg,gpgd_addr (lg, vaddr), pgd_t);/* Toplevel not present? We can't map it in. */ if (!(pgd_flags (gpgd) & _PAGE_PRESENT))return 0 ;
@@ -235,7 +235,7 @@ int demand_page(struct lguest *lg, unsig/* OK, now we look at the lower level in the Guest page table: keep its * address, because we might update it later. */ gpte_ptr =gpte_addr (lg, gpgd, vaddr); - gpte =__pte (lgread_u32 (lg, gpte_ptr)); + gpte =lgread (lg, gpte_ptr, pte_t);/* If this page isn't in the Guest page tables, we can't page it in. */ if (!(pte_flags (gpte) & _PAGE_PRESENT))
@@ -281,7 +281,7 @@ int demand_page(struct lguest *lg, unsig/* Finally, we write the Guest PTE entry back: we've set the * _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags. */ -lgwrite_u32 (lg, gpte_ptr,pte_val (gpte)); +lgwrite (lg, gpte_ptr, pte_t, gpte);/* We succeeded in mapping the page! */ return 1 ;
@@ -369,12 +369,12 @@ unsigned long guest_pa(struct lguest *lgpte_t gpte;/* First step: get the top-level Guest page table entry. */ - gpgd =__pgd (lgread_u32 (lg,gpgd_addr (lg, vaddr))); + gpgd =lgread (lg,gpgd_addr (lg, vaddr), pgd_t);/* Toplevel not present? We can't map it in. */ if (!(pgd_flags (gpgd) & _PAGE_PRESENT))kill_guest (lg,"Bad address %#lx" , vaddr); - gpte =__pte (lgread_u32 (lg,gpte_addr (lg, gpgd, vaddr))); + gpte =lgread (lg,gpte_addr (lg, gpgd, vaddr), pte_t);if (!(pte_flags (gpte) & _PAGE_PRESENT))kill_guest (lg,"Bad address %#lx" , vaddr);
===================================================================
drivers/lguest/segments.c +2 -2
@@ -150,7 +150,7 @@ void load_guest_gdt(struct lguest *lg, ukill_guest (lg,"too many gdt entries %i" , num);/* We read the whole thing in, then fix it up. */ -lgread (lg, lg->arch.gdt, table, num *sizeof (lg->arch.gdt[0 ])); +__lgread (lg, lg->arch.gdt, table, num *sizeof (lg->arch.gdt[0 ]));fixup_gdt_table (lg,0 ,ARRAY_SIZE (lg->arch.gdt));/* Mark that the GDT changed so the core knows it has to copy it again, * even if the Guest is run on the same CPU. */
@@ -161,7 +161,7 @@ void guest_load_tls(struct lguest *lg, u {struct desc_struct *tls = &lg->arch.gdt[GDT_ENTRY_TLS_MIN ]; -lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES ); +__lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES );fixup_gdt_table (lg,GDT_ENTRY_TLS_MIN ,GDT_ENTRY_TLS_MAX +1 ); lg->changed |=CHANGED_GDT_TLS ; }
--
there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy
Re: [Lguest] [patch 43/43] lguest: generalize lgread_u32/lgwrite_u32.
Hi Rusty, Jes et al
This last patch causes page_tables.c to fail compilation on my system thus:
....
CC [M] drivers/kvm/vmx.o CC [M] drivers/kvm/kvm_main.o CC [M] drivers/kvm/mmu.o CC [M] drivers/kvm/x86_emulate.o LD [M] drivers/kvm/kvm.o LD [M] drivers/kvm/kvm-intel.o CC drivers/leds/led-core.o LD drivers/leds/built-in.o CC [M] drivers/leds/led-class.o CC drivers/lguest/lguest_device.o LD drivers/lguest/built-in.o CC [M] drivers/lguest/core.o CC [M] drivers/lguest/hypercalls.o CC [M] drivers/lguest/page_tables.o
drivers/lguest/page_tables.c: In function ‘demand_page’:drivers/lguest/page_tables.c:212: error: expected expression before ‘{’ token drivers/lguest/page_tables.c:238: error: expected expression before ‘{’ token drivers/lguest/page_tables.c:284: error: ‘v’ undeclared (first use in this function) drivers/lguest/page_tables.c:284: error: (Each undeclared identifier is reported only once drivers/lguest/page_tables.c:284: error: for each function it appears in.) drivers/lguest/page_tables.c:284: warning: type defaults to ‘int’ in declaration of ‘__dummy2’ drivers/lguest/page_tables.c:284: warning: comparison of distinct pointer types lacks a cast drivers/lguest/page_tables.c:284: warning: passing argument 3 of ‘__lgwrite’ makes pointer from integer without a cast drivers/lguest/page_tables.c: In function ‘guest_pa’:drivers/lguest/page_tables.c:372: error: expected expression before ‘{’ token drivers/lguest/page_tables.c:377: error: expected expression before ‘{’ token
make[2]: *** [drivers/lguest/page_tables.o] Error 1
make[1]: *** [drivers/lguest] Error 2
make: *** [drivers] Error 2
It compiles fine with all the previous patches applied, just doesn't seem to like the new lgread/lgwrite macros.
(GCC 4.1.2 on i686 in case that makes any difference, patched against v2.6.23-rc8)
--
Chris
Quoted 237 lines · On Wed, 2007-09-26 at 16:37 +1000, [email protected] wrote:
Jes complains that page table code still uses
lgread_u32even though it now uses general kernel pte types. The best thing to do is to generalizelgread_u32andlgwrite_u32.This means we lose the efficiency of
getuser(). We could potentially regain it if we used__copy_from_userinstead ofcopy_from_user, but I'm not certain that our range check is equivalent toaccess_ok()on all platforms.Signed-off-by Rusty Russell <[email protected]>Cc Jes Sorensen <[email protected]>--- drivers/lguest/core.c | 39 ++++++--------------------------- drivers/lguest/hypercalls.c | 2 - drivers/lguest/i386_core.c | 4 +-- drivers/lguest/interrupts_and_traps.c | 2 - drivers/lguest/lg.h | 23 ++++++++++++++++--- drivers/lguest/page_tables.c | 10 ++++---- drivers/lguest/segments.c | 4 +-- 7 files changed, 38 insertions(+), 46 deletions(-)===================================================================
drivers/lguest/core.c +8
-31@@ -145,33 +145,10 @@ int lguest_address_ok(const struct lguesreturn (addr+len) /PAGE_SIZE < lg->pfn_limit && (addr+len >= addr); } -/* This is a convenient routine to get a 32-bit value from the Guest (a very -* common operation). Here we can see how useful the kill_lguest() routine we -* met in the Launcher can be: we return a random value (0) instead of needing -* to return an error. */ -u32 lgread_u32 (struct lguest *lg,unsigned long addr) -{ -u32 val =0 ; - -/* Don't let them access lguest binary. */ -if (!lguest_address_ok (lg, addr,sizeof (val)) - ||get_user (val, (u32 *)(lg->mem_base + addr)) !=0 ) -kill_guest (lg,"bad read address %#lx: pfn_limit=%u membase=%p" , addr, lg->pfn_limit, lg->mem_base); -return val; -} - -/* Same thing for writing a value. */ -void lgwrite_u32 (struct lguest *lg,unsigned long addr,u32 val) -{ -if (!lguest_address_ok (lg, addr,sizeof (val)) - ||put_user (val, (u32 *)(lg->mem_base + addr)) !=0 ) -kill_guest (lg,"bad write address %#lx" , addr); -} - -/* This routine is more generic, and copies a range of Guest bytes into a -* buffer. If the copy_from_user() fails, we fill the buffer with zeroes, so -* the caller doesn't end up using uninitialized kernel memory. */ -void lgread (struct lguest *lg,void *b,unsigned long addr,unsigned bytes) +/* This routine copies memory from the Guest. Here we can see how useful the +* kill_lguest() routine we met in the Launcher can be: we return a random +* value (all zeroes) instead of needing to return an error. */ +void __lgread (struct lguest *lg,void *b,unsigned long addr,unsigned bytes) {if (!lguest_address_ok (lg, addr, bytes) ||copy_from_user (b, lg->mem_base + addr, bytes) !=0 ) {@@ -181,15 +158,15 @@ void lgread(struct lguest *lg, void *b, } } -/* Similarly, our generic routine to copy into a range of Guest bytes. */ -void lgwrite (struct lguest *lg,unsigned long addr,const void *b, -unsigned bytes) +/* This is the write (copy into guest) version. */ +void __lgwrite (struct lguest *lg,unsigned long addr,const void *b, +unsigned bytes) {if (!lguest_address_ok (lg, addr, bytes) ||copy_to_user (lg->mem_base + addr, b, bytes) !=0 )kill_guest (lg,"bad write address %#lx len %u" , addr, bytes); } -/* (end of memory access helper routines) :*/ +/*:*/ /*H:030 Let's jump straight to the the main loop which runs the Guest. * Remember, this is called by the Launcher reading /dev/lguest, and we keep ===================================================================
drivers/lguest/hypercalls.c +1
-1@@ -47,7 +47,7 @@ static void do_hcall(struct lguest *lg,char msg[128 ];/* If the lgread fails, it will call kill_guest() itself; the * kill_guest() with the message will be ignored. */ -lgread (lg, msg, args->arg1,sizeof (msg)); +__lgread (lg, msg, args->arg1,sizeof (msg)); msg[sizeof (msg)-1 ] ='\0' ;kill_guest (lg,"CRASH: %s" , msg);break ;===================================================================
drivers/lguest/i386_core.c +2
-2@@ -222,7 +222,7 @@ static int emulate_insn(struct lguest *lreturn 0 ;/* Decoding x86 instructions is icky. */ -lgread (lg, &insn, physaddr,1 ); + insn =lgread (lg, &insn, u8);/* 0x66 is an "operand prefix". It means it's using the upper 16 bits of the eax register. */ @@ -230,7 +230,7 @@ static int emulate_insn(struct lguest *l shift =16 ;/* The instruction is 1 byte so far, read the next byte. */ insnlen =1 ; -lgread (lg, &insn, physaddr + insnlen,1 ); + insn =lgread (lg, physaddr + insnlen, u8); }/* We can ignore the lower bit for the moment and decode the 4 opcodes ===================================================================
drivers/lguest/interrupts_and_traps.c +1
-1@@ -45,7 +45,7 @@ static void push_guest_stack(struct lgue {/* Stack grows upwards: move stack then write value. */ *gstack -=4 ; -lgwrite_u32 (lg, *gstack, val); +lgwrite (lg, *gstack, u32, val); }/*H:210 The set_guest_interrupt() routine actually delivers the interrupt or ===================================================================
drivers/lguest/lg.h +19
-4@@ -99,12 +99,27 @@ extern struct mutex lguest_lock;extern struct mutex lguest_lock;/* core.c: */ -u32 lgread_u32 (struct lguest *lg,unsigned long addr); -void lgwrite_u32 (struct lguest *lg,unsigned long addr,u32 val); -void lgread (struct lguest *lg,void *buf,unsigned long addr,unsigned len); -void lgwrite (struct lguest *lg,unsigned long ,const void *buf,unsigned len);int lguest_address_ok (const struct lguest *lg,unsigned long addr,unsigned long len); +void __lgread (struct lguest *,void *,unsigned long ,unsigned ); +void __lgwrite (struct lguest *,unsigned long ,const void *,unsigned ); + +/*L:306 Using memory-copy operations like that is usually inconvient, so we +* have the following helper macros which read and write a specific type (often +* an unsigned long). +* +* This reads into a variable of the given type then returns that. */ +#define lgread (lg, addr, type) \ + {( type _v; __lgread((lg), &_v, (addr), sizeof(_v)); _v; )} + +/* This checks that the variable is of the given type, then writes it out. */ +#define lgwrite (lg, addr, type, val) \ + do { \ + typecheck(type, v); \ + __lgwrite((lg), &(v), (addr), sizeof(v)); \ + } while(0) +/* (end of memory access helper routines) :*/ +int run_guest (struct lguest *lg,unsigned long __user *user);/* Helper macros to obtain the first 12 or the last 20 bits, this is only the ===================================================================
drivers/lguest/page_tables.c +5
-5@@ -209,7 +209,7 @@ int demand_page(struct lguest *lg, unsigpte_t *spte;/* First step: get the top-level Guest page table entry. */ - gpgd =__pgd (lgread_u32 (lg,gpgd_addr (lg, vaddr))); + gpgd =lgread (lg,gpgd_addr (lg, vaddr), pgd_t);/* Toplevel not present? We can't map it in. */ if (!(pgd_flags (gpgd) & _PAGE_PRESENT))return 0 ;@@ -235,7 +235,7 @@ int demand_page(struct lguest *lg, unsig/* OK, now we look at the lower level in the Guest page table: keep its * address, because we might update it later. */ gpte_ptr =gpte_addr (lg, gpgd, vaddr); - gpte =__pte (lgread_u32 (lg, gpte_ptr)); + gpte =lgread (lg, gpte_ptr, pte_t);/* If this page isn't in the Guest page tables, we can't page it in. */ if (!(pte_flags (gpte) & _PAGE_PRESENT))@@ -281,7 +281,7 @@ int demand_page(struct lguest *lg, unsig/* Finally, we write the Guest PTE entry back: we've set the * _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags. */ -lgwrite_u32 (lg, gpte_ptr,pte_val (gpte)); +lgwrite (lg, gpte_ptr, pte_t, gpte);/* We succeeded in mapping the page! */ return 1 ;@@ -369,12 +369,12 @@ unsigned long guest_pa(struct lguest *lgpte_t gpte;/* First step: get the top-level Guest page table entry. */ - gpgd =__pgd (lgread_u32 (lg,gpgd_addr (lg, vaddr))); + gpgd =lgread (lg,gpgd_addr (lg, vaddr), pgd_t);/* Toplevel not present? We can't map it in. */ if (!(pgd_flags (gpgd) & _PAGE_PRESENT))kill_guest (lg,"Bad address %#lx" , vaddr); - gpte =__pte (lgread_u32 (lg,gpte_addr (lg, gpgd, vaddr))); + gpte =lgread (lg,gpte_addr (lg, gpgd, vaddr), pte_t);if (!(pte_flags (gpte) & _PAGE_PRESENT))kill_guest (lg,"Bad address %#lx" , vaddr);===================================================================
drivers/lguest/segments.c +2
-3@@ -150,7 +150,7 @@ void load_guest_gdt(struct lguest *lg, ukill_guest (lg,"too many gdt entries %i" , num);/* We read the whole thing in, then fix it up. */ -lgread (lg, lg->arch.gdt, table, num *sizeof (lg->arch.gdt[0 ])); +__lgread (lg, lg->arch.gdt, table, num *sizeof (lg->arch.gdt[0 ]));fixup_gdt_table (lg,0 ,ARRAY_SIZE (lg->arch.gdt));/* Mark that the GDT changed so the core knows it has to copy it again, * even if the Guest is run on the same CPU. */ @@ -161,7 +161,7 @@ void guest_load_tls(struct lguest *lg, u {struct desc_struct *tls = &lg->arch.gdt[GDT_ENTRY_TLS_MIN ]; -lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES ); +__lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES );fixup_gdt_table (lg,GDT_ENTRY_TLS_MIN ,GDT_ENTRY_TLS_MAX +1 ); lg->changed |=CHANGED_GDT_TLS ; } -- there are those who do and those who hang on and you don't see too many doers quoting their contemporaries. -- Larry McVoy_______________________________________________
Lguest mailing list
[email protected]
https://ozlabs.org/mailman/listinfo/lguest
Rusty Russell
Acked-by Jes SorensenRe: [Lguest] [patch 43/43] lguest: generalize lgread_u32/lgwrite_u32.
Quoted 3 lines · Chris Malley
Hi Rusty, Jes et al
This last patch causes
page_tables.cto fail compilation on my system thus:
Erk, thanks. That one wasn't cooked yet.
This should be better:
Subject: generalize lgread_u32/lgwrite_u32.
Jes complains that page table code still uses lgread_u32 even though it now uses general kernel pte types. The best thing to do is to generalize lgread_u32 and lgwrite_u32.
This means we lose the efficiency of getuser(). We could potentially regain it if we used __copy_from_user instead of copy_from_user, but I'm not certain that our range check is equivalent to access_ok() on all platforms.
--- drivers/lguest/core.c | 39 ++++++--------------------------- drivers/lguest/hypercalls.c | 2 - drivers/lguest/i386_core.c | 4 +-- drivers/lguest/interrupts_and_traps.c | 2 - drivers/lguest/lg.h | 23 ++++++++++++++++--- drivers/lguest/page_tables.c | 10 ++++---- drivers/lguest/segments.c | 4 +-- 7 files changed, 38 insertions(+), 46 deletions(-)
===================================================================
drivers/lguest/core.c +8 -31
@@ -145,33 +145,10 @@ int lguest_address_ok(const struct lguesreturn (addr+len) /PAGE_SIZE < lg->pfn_limit && (addr+len >= addr); } -/* This is a convenient routine to get a 32-bit value from the Guest (a very -* common operation). Here we can see how useful the kill_lguest() routine we -* met in the Launcher can be: we return a random value (0) instead of needing -* to return an error. */ -u32 lgread_u32 (struct lguest *lg,unsigned long addr) -{ -u32 val =0 ; - -/* Don't let them access lguest binary. */ -if (!lguest_address_ok (lg, addr,sizeof (val)) - ||get_user (val, (u32 *)(lg->mem_base + addr)) !=0 ) -kill_guest (lg,"bad read address %#lx: pfn_limit=%u membase=%p" , addr, lg->pfn_limit, lg->mem_base); -return val; -} - -/* Same thing for writing a value. */ -void lgwrite_u32 (struct lguest *lg,unsigned long addr,u32 val) -{ -if (!lguest_address_ok (lg, addr,sizeof (val)) - ||put_user (val, (u32 *)(lg->mem_base + addr)) !=0 ) -kill_guest (lg,"bad write address %#lx" , addr); -} - -/* This routine is more generic, and copies a range of Guest bytes into a -* buffer. If the copy_from_user() fails, we fill the buffer with zeroes, so -* the caller doesn't end up using uninitialized kernel memory. */ -void lgread (struct lguest *lg,void *b,unsigned long addr,unsigned bytes) +/* This routine copies memory from the Guest. Here we can see how useful the +* kill_lguest() routine we met in the Launcher can be: we return a random +* value (all zeroes) instead of needing to return an error. */ +void __lgread (struct lguest *lg,void *b,unsigned long addr,unsigned bytes) {if (!lguest_address_ok (lg, addr, bytes) ||copy_from_user (b, lg->mem_base + addr, bytes) !=0 ) {
@@ -181,15 +158,15 @@ void lgread(struct lguest *lg, void *b, } } -/* Similarly, our generic routine to copy into a range of Guest bytes. */ -void lgwrite (struct lguest *lg,unsigned long addr,const void *b, -unsigned bytes) +/* This is the write (copy into guest) version. */ +void __lgwrite (struct lguest *lg,unsigned long addr,const void *b, +unsigned bytes) {if (!lguest_address_ok (lg, addr, bytes) ||copy_to_user (lg->mem_base + addr, b, bytes) !=0 )kill_guest (lg,"bad write address %#lx len %u" , addr, bytes); } -/* (end of memory access helper routines) :*/ +/*:*/ /*H:030 Let's jump straight to the the main loop which runs the Guest. * Remember, this is called by the Launcher reading /dev/lguest, and we keep
===================================================================
drivers/lguest/hypercalls.c +1 -1
@@ -47,7 +47,7 @@ static void do_hcall(struct lguest *lg,char msg[128 ];/* If the lgread fails, it will call kill_guest() itself; the * kill_guest() with the message will be ignored. */ -lgread (lg, msg, args->arg1,sizeof (msg)); +__lgread (lg, msg, args->arg1,sizeof (msg)); msg[sizeof (msg)-1 ] ='\0' ;kill_guest (lg,"CRASH: %s" , msg);break ;
===================================================================
drivers/lguest/i386_core.c +2 -2
@@ -222,7 +222,7 @@ static int emulate_insn(struct lguest *lreturn 0 ;/* Decoding x86 instructions is icky. */ -lgread (lg, &insn, physaddr,1 ); + insn =lgread (lg, physaddr, u8);/* 0x66 is an "operand prefix". It means it's using the upper 16 bits of the eax register. */
@@ -230,7 +230,7 @@ static int emulate_insn(struct lguest *l shift =16 ;/* The instruction is 1 byte so far, read the next byte. */ insnlen =1 ; -lgread (lg, &insn, physaddr + insnlen,1 ); + insn =lgread (lg, physaddr + insnlen, u8); }/* We can ignore the lower bit for the moment and decode the 4 opcodes
===================================================================
drivers/lguest/interrupts_and_traps.c +1 -1
@@ -45,7 +45,7 @@ static void push_guest_stack(struct lgue {/* Stack grows upwards: move stack then write value. */ *gstack -=4 ; -lgwrite_u32 (lg, *gstack, val); +lgwrite (lg, *gstack, u32, val); }/*H:210 The set_guest_interrupt() routine actually delivers the interrupt or
===================================================================
drivers/lguest/lg.h +19 -4
@@ -99,12 +99,27 @@ extern struct mutex lguest_lock;extern struct mutex lguest_lock;/* core.c: */ -u32 lgread_u32 (struct lguest *lg,unsigned long addr); -void lgwrite_u32 (struct lguest *lg,unsigned long addr,u32 val); -void lgread (struct lguest *lg,void *buf,unsigned long addr,unsigned len); -void lgwrite (struct lguest *lg,unsigned long ,const void *buf,unsigned len);int lguest_address_ok (const struct lguest *lg,unsigned long addr,unsigned long len); +void __lgread (struct lguest *,void *,unsigned long ,unsigned ); +void __lgwrite (struct lguest *,unsigned long ,const void *,unsigned ); + +/*L:306 Using memory-copy operations like that is usually inconvient, so we +* have the following helper macros which read and write a specific type (often +* an unsigned long). +* +* This reads into a variable of the given type then returns that. */ +#define lgread (lg, addr, type) \ + ({ type _v; __lgread((lg), &_v, (addr), sizeof(_v)); _v; }) + +/* This checks that the variable is of the given type, then writes it out. */ +#define lgwrite (lg, addr, type, val) \ + do { \ + typecheck(type, val); \ + __lgwrite((lg), (addr), &(val), sizeof(val)); \ + } while(0) +/* (end of memory access helper routines) :*/ +int run_guest (struct lguest *lg,unsigned long __user *user);/* Helper macros to obtain the first 12 or the last 20 bits, this is only the
===================================================================
drivers/lguest/page_tables.c +5 -5
@@ -209,7 +209,7 @@ int demand_page(struct lguest *lg, unsigpte_t *spte;/* First step: get the top-level Guest page table entry. */ - gpgd =__pgd (lgread_u32 (lg,gpgd_addr (lg, vaddr))); + gpgd =lgread (lg,gpgd_addr (lg, vaddr), pgd_t);/* Toplevel not present? We can't map it in. */ if (!(pgd_flags (gpgd) & _PAGE_PRESENT))return 0 ;
@@ -235,7 +235,7 @@ int demand_page(struct lguest *lg, unsig/* OK, now we look at the lower level in the Guest page table: keep its * address, because we might update it later. */ gpte_ptr =gpte_addr (lg, gpgd, vaddr); - gpte =__pte (lgread_u32 (lg, gpte_ptr)); + gpte =lgread (lg, gpte_ptr, pte_t);/* If this page isn't in the Guest page tables, we can't page it in. */ if (!(pte_flags (gpte) & _PAGE_PRESENT))
@@ -281,7 +281,7 @@ int demand_page(struct lguest *lg, unsig/* Finally, we write the Guest PTE entry back: we've set the * _PAGE_ACCESSED and maybe the _PAGE_DIRTY flags. */ -lgwrite_u32 (lg, gpte_ptr,pte_val (gpte)); +lgwrite (lg, gpte_ptr, pte_t, gpte);/* We succeeded in mapping the page! */ return 1 ;
@@ -369,12 +369,12 @@ unsigned long guest_pa(struct lguest *lgpte_t gpte;/* First step: get the top-level Guest page table entry. */ - gpgd =__pgd (lgread_u32 (lg,gpgd_addr (lg, vaddr))); + gpgd =lgread (lg,gpgd_addr (lg, vaddr), pgd_t);/* Toplevel not present? We can't map it in. */ if (!(pgd_flags (gpgd) & _PAGE_PRESENT))kill_guest (lg,"Bad address %#lx" , vaddr); - gpte =__pte (lgread_u32 (lg,gpte_addr (lg, gpgd, vaddr))); + gpte =lgread (lg,gpte_addr (lg, gpgd, vaddr), pte_t);if (!(pte_flags (gpte) & _PAGE_PRESENT))kill_guest (lg,"Bad address %#lx" , vaddr);
===================================================================
drivers/lguest/segments.c +2 -2
@@ -150,7 +150,7 @@ void load_guest_gdt(struct lguest *lg, ukill_guest (lg,"too many gdt entries %i" , num);/* We read the whole thing in, then fix it up. */ -lgread (lg, lg->arch.gdt, table, num *sizeof (lg->arch.gdt[0 ])); +__lgread (lg, lg->arch.gdt, table, num *sizeof (lg->arch.gdt[0 ]));fixup_gdt_table (lg,0 ,ARRAY_SIZE (lg->arch.gdt));/* Mark that the GDT changed so the core knows it has to copy it again, * even if the Guest is run on the same CPU. */
@@ -161,7 +161,7 @@ void guest_load_tls(struct lguest *lg, u {struct desc_struct *tls = &lg->arch.gdt[GDT_ENTRY_TLS_MIN ]; -lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES ); +__lgread (lg, tls, gtls,sizeof (*tls)*GDT_ENTRY_TLS_ENTRIES );fixup_gdt_table (lg,GDT_ENTRY_TLS_MIN ,GDT_ENTRY_TLS_MAX +1 ); lg->changed |=CHANGED_GDT_TLS ; }
Quoted 7 lines · On 9/26/07, [email protected] <[email protected]> wrote:
Hi all,
These are the patches I'm planning to submit for 2.6.24. Comments
gratefully accepted. Along with the usual cleanups and improvements are Jes' de-i386-ification patches, and a new "virtio" mechanism designed to be shared with KVM (and hopefully other hypervisors).
I applied the patchbomb to a branch in the v9fs.git tree in prep to write the virtio transport for 9p (it's under lguest-devel branch of
http://www.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs.git -- although for some reason guilt put my name as the Author in place of Rusty everywhere...not sure why...anyways).
It built clean, but when I tried to make beer to read the updated docs for virtio, it didn't seem to pick up the documentation for the console, network, or block devices (just the core device support code)....
Something stupid I'm doing wrong?
-eric