Il 28/08/2013 22:58, Chris Metcalf ha scritto: > This change enables support for a virtio-based console, > network support, and block driver support. > > We remove some debug code in relocate_kernel_64.S that made raw > calls to the hv_console_putc Tilera hypervisor API, since everything > now should funnel through the early_hv_write() API. > > Signed-off-by: Chris Metcalf <cmetc...@tilera.com>
Why couldn't this use the "regular" virtio-mmio interface? > diff --git a/arch/tile/include/asm/kvm_virtio.h > b/arch/tile/include/asm/kvm_virtio.h > new file mode 100644 > index 0000000..8faa959 > --- /dev/null > +++ b/arch/tile/include/asm/kvm_virtio.h > @@ -0,0 +1,26 @@ > +/* > + * Copyright 2013 Tilera Corporation. All Rights Reserved. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation, version 2. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or > + * NON INFRINGEMENT. See the GNU General Public License for > + * more details. > + */ > +#ifndef _ASM_TILE_KVM_VIRTIO_H > +#define _ASM_TILE_KVM_VIRTIO_H > + > +#include <uapi/asm/kvm_virtio.h> > + > + > +struct kvm_device { > + struct virtio_device vdev; > + struct kvm_device_desc *desc; > + unsigned long desc_pa; > +}; > + > +#endif /* _ASM_TILE_KVM_VIRTIO_H */ > diff --git a/arch/tile/include/uapi/asm/Kbuild > b/arch/tile/include/uapi/asm/Kbuild > index 89022a5..f07cc24 100644 > --- a/arch/tile/include/uapi/asm/Kbuild > +++ b/arch/tile/include/uapi/asm/Kbuild > @@ -8,6 +8,7 @@ header-y += cachectl.h > header-y += hardwall.h > header-y += kvm.h > header-y += kvm_para.h > +header-y += kvm_virtio.h > header-y += mman.h > header-y += ptrace.h > header-y += setup.h > diff --git a/arch/tile/include/uapi/asm/kvm.h > b/arch/tile/include/uapi/asm/kvm.h > index aa7b97f..4346520 100644 > --- a/arch/tile/include/uapi/asm/kvm.h > +++ b/arch/tile/include/uapi/asm/kvm.h > @@ -149,6 +149,9 @@ > */ > #define KVM_OTHER_HCALL 128 > > +/* Hypercall index for virtio. */ > +#define KVM_HCALL_virtio 128 > + > /* One greater than the maximum hypercall number. */ > #define KVM_NUM_HCALLS 256 > > @@ -256,6 +259,8 @@ struct kvm_sync_regs { > KVM_EMULATE(get_ipi_pte) \ > KVM_EMULATE(set_pte_super_shift) \ > KVM_EMULATE(set_speed) \ > + /* For others */ \ > + USER_HCALL(virtio) Ah, here it is. :) > > #endif > > diff --git a/arch/tile/include/uapi/asm/kvm_virtio.h > b/arch/tile/include/uapi/asm/kvm_virtio.h > new file mode 100644 > index 0000000..d94f535 > --- /dev/null > +++ b/arch/tile/include/uapi/asm/kvm_virtio.h > @@ -0,0 +1,60 @@ > +/* > + * Copyright 2013 Tilera Corporation. All Rights Reserved. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation, version 2. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or > + * NON INFRINGEMENT. See the GNU General Public License for > + * more details. > + */ > + > +#ifndef _UAPI_ASM_TILE_KVM_VIRTIO_H > +#define _UAPI_ASM_TILE_KVM_VIRTIO_H > + > +#include <linux/types.h> > + > +#define KVM_VIRTIO_UNKNOWN 0 > +#define KVM_VIRTIO_NOTIFY 1 > +#define KVM_VIRTIO_RESET 2 > +#define KVM_VIRTIO_SET_STATUS 3 > + > +struct kvm_device_desc { > + /* The device type: console, network, disk etc. Type 0 terminates. */ > + __u8 type; > + /* The number of virtqueues (first in config array) */ > + __u8 num_vq; > + /* > + * The number of bytes of feature bits. Multiply by 2: one for host > + * features and one for Guest acknowledgements. > + */ > + __u8 feature_len; > + /* The number of bytes of the config array after virtqueues. */ > + __u8 config_len; > + /* A status byte, written by the Guest. */ > + __u8 status; > + __u64 config[0]; > +}; > + > +struct kvm_vqinfo { > + /* Pointer to the information contained in the device config. */ > + struct kvm_vqconfig *config; > + /* The address where we mapped the virtio ring, so we can unmap it. */ > + void *pages; > +}; > + > +struct kvm_vqconfig { > + /* The physical address of the virtio ring */ > + __u64 pa; > + /* The number of entries in the virtio_ring */ > + __u64 num; > + /* The interrupt we get when something happens. Set by the guest. */ > + __u32 irq; > + > +}; > + > + > +#endif /* _UAPI_ASM_TILE_KVM_VIRTIO_H */ > diff --git a/arch/tile/kernel/Makefile b/arch/tile/kernel/Makefile > index b7c8b5e..b638d3e 100644 > --- a/arch/tile/kernel/Makefile > +++ b/arch/tile/kernel/Makefile > @@ -29,5 +29,6 @@ obj-$(CONFIG_TILE_USB) += usb.o > obj-$(CONFIG_TILE_HVGLUE_TRACE) += hvglue_trace.o > obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount_64.o > obj-$(CONFIG_KPROBES) += kprobes.o > +obj-$(CONFIG_KVM_GUEST) += kvm_virtio.o > > obj-y += vdso/ > diff --git a/arch/tile/kernel/early_printk.c b/arch/tile/kernel/early_printk.c > index b608e00..53f2be4 100644 > --- a/arch/tile/kernel/early_printk.c > +++ b/arch/tile/kernel/early_printk.c > @@ -18,11 +18,26 @@ > #include <linux/string.h> > #include <linux/irqflags.h> > #include <linux/printk.h> > +#ifdef CONFIG_KVM_GUEST > +#include <linux/virtio_console.h> > +#include <linux/kvm_para.h> > +#include <asm/kvm_virtio.h> > +#endif > #include <asm/setup.h> > #include <hv/hypervisor.h> > > static void early_hv_write(struct console *con, const char *s, unsigned n) > { > +#ifdef CONFIG_KVM_GUEST > + char buf[512]; > + > + if (n > sizeof(buf) - 1) > + n = sizeof(buf) - 1; > + memcpy(buf, s, n); > + buf[n] = '\0'; > + > + hcall_virtio(KVM_VIRTIO_NOTIFY, __pa(buf)); How can userspace know the difference between KVM_VIRTIO_NOTIFY with a string buffer, and KVM_VIRTIO_NOTIFY with a config space pointer? In fact, this looks like a completely separate hypercall, why not keep hv_console_putc? > index 0000000..c6b6c6a > --- /dev/null > +++ b/arch/tile/kernel/kvm_virtio.c > @@ -0,0 +1,430 @@ > +/* > + * Copyright 2013 Tilera Corporation. All Rights Reserved. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation, version 2. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or > + * NON INFRINGEMENT. See the GNU General Public License for > + * more details. > + */ > + > +/* Referred lguest & s390 implemenation */ > +/* > + * kvm_virtio.c - virtio for kvm on s390 > + * > + * Copyright IBM Corp. 2008 > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License (version 2 only) > + * as published by the Free Software Foundation. > + * > + * Author(s): Christian Borntraeger <borntrae...@de.ibm.com> > + */ This has the same problem as the old s390 implementation (there is a new one that emulates the usual s390 I/O instead of using paravirtualization); it doesn't raise an interrupt on config space writes. Apart from this it looks good, but I'm not sure why it is necessary. > +#include <linux/bootmem.h> > +#include <linux/io.h> > +#include <linux/vmalloc.h> > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/export.h> > +#include <linux/virtio.h> > +#include <linux/virtio_config.h> > +#include <linux/virtio_console.h> > +#include <linux/virtio_ring.h> > +#include <linux/virtio_pci.h> > + > +#include <linux/kvm_para.h> > +#include <asm/kvm_virtio.h> > + > +static void *kvm_devices; > + > +/* > + * TODO: We actually does not use PCI virtio here. We use this > + * because qemu: virtqueue_init() uses VIRTIO_PCI_VRING_ALIGN. > + * Maybe we should change them to generic definitions in both qemu & Linux. > + * Besides, Let's check whether the alignment value (4096, i.e. default > + * x86 page size) affects performance later. > + */ > +#define KVM_TILE_VIRTIO_RING_ALIGN VIRTIO_PCI_VRING_ALIGN > +#define to_kvmdev(vd) container_of(vd, struct kvm_device, vdev) > + > +/* > + * memory layout: (Total: PAGE_SIZE) > + * <device 0> > + * - kvm device descriptor > + * struct kvm_device_desc > + * - vqueue configuration (totally desc->num_vq) > + * struct kvm_vqconfig > + * ...... > + * struct kvm_vqconfig > + * - feature bits (size: desc->feature_len * 2) > + * - config space (size: desc->config_len) > + * <device 1> > + * ...... > + */ > +static struct kvm_vqconfig *kvm_vq_config(const struct kvm_device_desc *desc) > +{ > + return (struct kvm_vqconfig *)(desc + 1); > +} > + > +static u8 *kvm_vq_features(const struct kvm_device_desc *desc) > +{ > + return (u8 *)(kvm_vq_config(desc) + desc->num_vq); > +} > + > +static u8 *kvm_vq_configspace(const struct kvm_device_desc *desc) > +{ > + return kvm_vq_features(desc) + desc->feature_len * 2; > +} > + > +/* > + * The total size of the config page used by this device (incl. desc) > + */ > +static unsigned desc_size(const struct kvm_device_desc *desc) > +{ > + return sizeof(*desc) > + + desc->num_vq * sizeof(struct kvm_vqconfig) > + + desc->feature_len * 2 > + + desc->config_len; > +} > + > +/* This gets the device's feature bits. */ > +static u32 kvm_get_features(struct virtio_device *vdev) > +{ > + unsigned int i; > + u32 features = 0; > + struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; > + u8 *in_features = kvm_vq_features(desc); > + > + for (i = 0; i < min(desc->feature_len * 8, 32); i++) > + if (in_features[i / 8] & (1 << (i % 8))) > + features |= (1 << i); > + return features; > +} > + > +static void kvm_finalize_features(struct virtio_device *vdev) > +{ > + unsigned int i, bits; > + struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; > + /* Second half of bitmap is features we accept. */ > + u8 *out_features = kvm_vq_features(desc) + desc->feature_len; > + > + /* Give virtio_ring a chance to accept features. */ > + vring_transport_features(vdev); > + > + memset(out_features, 0, desc->feature_len); > + bits = min_t(unsigned, desc->feature_len, sizeof(vdev->features)) * 8; > + for (i = 0; i < bits; i++) { > + if (test_bit(i, vdev->features)) > + out_features[i / 8] |= (1 << (i % 8)); > + } > +} > + > +/* > + * Reading and writing elements in config space > + */ > +static void kvm_get(struct virtio_device *vdev, unsigned int offset, > + void *buf, unsigned len) > +{ > + struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; > + > + BUG_ON(offset + len > desc->config_len); > + memcpy(buf, kvm_vq_configspace(desc) + offset, len); > +} > + > +static void kvm_set(struct virtio_device *vdev, unsigned int offset, > + const void *buf, unsigned len) > +{ > + struct kvm_device_desc *desc = to_kvmdev(vdev)->desc; > + > + BUG_ON(offset + len > desc->config_len); > + memcpy(kvm_vq_configspace(desc) + offset, buf, len); > +} > + > +/* > + * The operations to get and set the status word just access > + * the status field of the device descriptor. set_status will also > + * make a hypercall to the host, to tell about status changes > + */ > +static u8 kvm_get_status(struct virtio_device *vdev) > +{ > + return to_kvmdev(vdev)->desc->status; > +} > + > +static void kvm_set_status(struct virtio_device *vdev, u8 status) > +{ > + BUG_ON(!status); > + to_kvmdev(vdev)->desc->status = status; > + hcall_virtio(KVM_VIRTIO_SET_STATUS, to_kvmdev(vdev)->desc_pa); > +} > + > +/* > + * To reset the device, we use the KVM_VIRTIO_RESET hypercall, using the > + * descriptor address. The Host will zero the status and all the > + * features. > + */ > +static void kvm_reset(struct virtio_device *vdev) > +{ > + hcall_virtio(KVM_VIRTIO_RESET, to_kvmdev(vdev)->desc_pa); > +} > + > +/* > + * When the virtio_ring code wants to notify the Host, it calls us here and > we > + * make a hypercall. We hand the address of the virtqueue so the Host > + * knows which virtqueue we're talking about. > + */ > +static void kvm_notify(struct virtqueue *vq) > +{ > + struct kvm_vqinfo *vqi = vq->priv; > + > + hcall_virtio(KVM_VIRTIO_NOTIFY, vqi->config->pa); > +} > + > +/* > + * Must set some caching mode to keep set_pte() happy. > + * It doesn't matter what we choose, because the PFN > + * is illegal, so we're going to take a page fault anyway. > + */ > +static inline pgprot_t io_prot(void) > +{ > + return hv_pte_set_mode(PAGE_KERNEL, HV_PTE_MODE_UNCACHED); > +} > + > +/* > + * This routine finds the first virtqueue described in the configuration of > + * this device and sets it up. > + */ > +static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, > + unsigned index, > + void (*callback)(struct virtqueue *vq), > + const char *name) > +{ > + struct kvm_device *kdev = to_kvmdev(vdev); > + struct kvm_vqinfo *vqi; > + struct kvm_vqconfig *config; > + struct virtqueue *vq; > + long irq; > + int err = -EINVAL; > + > + if (index >= kdev->desc->num_vq) > + return ERR_PTR(-ENOENT); > + > + vqi = kzalloc(sizeof(*vqi), GFP_KERNEL); > + if (!vqi) > + return ERR_PTR(-ENOMEM); > + > + config = kvm_vq_config(kdev->desc)+index; > + > + vqi->config = config; > + vqi->pages = generic_remap_prot(config->pa, > + vring_size(config->num, > + KVM_TILE_VIRTIO_RING_ALIGN), > + 0, io_prot()); > + if (!vqi->pages) { > + err = -ENOMEM; > + goto out; > + } > + > + vq = vring_new_virtqueue(index, config->num, KVM_TILE_VIRTIO_RING_ALIGN, > + vdev, 0, vqi->pages, > + kvm_notify, callback, name); > + if (!vq) { > + err = -ENOMEM; > + goto unmap; > + } > + > + /* > + * Trigger the IPI interrupt in SW way. > + * TODO: We do not need to create one irq for each vq. A bit wasteful. > + */ > + irq = create_irq(); > + if (irq < 0) { > + err = -ENXIO; > + goto del_virtqueue; > + } > + > + tile_irq_activate(irq, TILE_IRQ_SW_CLEAR); > + > + if (request_irq(irq, vring_interrupt, 0, dev_name(&vdev->dev), vq)) { > + err = -ENXIO; > + destroy_irq(irq); > + goto del_virtqueue; > + } > + > + config->irq = irq; > + > + vq->priv = vqi; > + return vq; > + > +del_virtqueue: > + vring_del_virtqueue(vq); > +unmap: > + vunmap(vqi->pages); > +out: > + return ERR_PTR(err); > +} > + > +static void kvm_del_vq(struct virtqueue *vq) > +{ > + struct kvm_vqinfo *vqi = vq->priv; > + > + vring_del_virtqueue(vq); > + vunmap(vqi->pages); > + kfree(vqi); > +} > + > +static void kvm_del_vqs(struct virtio_device *vdev) > +{ > + struct virtqueue *vq, *n; > + > + list_for_each_entry_safe(vq, n, &vdev->vqs, list) > + kvm_del_vq(vq); > +} > + > +static int kvm_find_vqs(struct virtio_device *vdev, unsigned nvqs, > + struct virtqueue *vqs[], > + vq_callback_t *callbacks[], > + const char *names[]) > +{ > + struct kvm_device *kdev = to_kvmdev(vdev); > + int i; > + > + /* We must have this many virtqueues. */ > + if (nvqs > kdev->desc->num_vq) > + return -ENOENT; > + > + for (i = 0; i < nvqs; ++i) { > + vqs[i] = kvm_find_vq(vdev, i, callbacks[i], names[i]); > + if (IS_ERR(vqs[i])) > + goto error; > + } > + return 0; > + > +error: > + kvm_del_vqs(vdev); > + return PTR_ERR(vqs[i]); > +} > + > +/* > + * The config ops structure as defined by virtio config > + */ > +static struct virtio_config_ops kvm_vq_config_ops = { > + .get_features = kvm_get_features, > + .finalize_features = kvm_finalize_features, > + .get = kvm_get, > + .set = kvm_set, > + .get_status = kvm_get_status, > + .set_status = kvm_set_status, > + .reset = kvm_reset, > + .find_vqs = kvm_find_vqs, > + .del_vqs = kvm_del_vqs, > +}; > + > +/* > + * The root device for the kvm virtio devices. > + * This makes them appear as /sys/devices/kvm_tile/0,1,2 not > /sys/devices/0,1,2. > + */ > +static struct device *kvm_root; > + > +/* > + * adds a new device and register it with virtio > + * appropriate drivers are loaded by the device model > + */ > +static void add_kvm_device(struct kvm_device_desc *d, unsigned int offset) > +{ > + struct kvm_device *kdev; > + > + kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); > + if (!kdev) { > + pr_emerg("Cannot allocate kvm dev %u type %u\n", > + offset, d->type); > + return; > + } > + > + kdev->vdev.dev.parent = kvm_root; > + kdev->vdev.id.device = d->type; > + kdev->vdev.config = &kvm_vq_config_ops; > + kdev->desc = d; > + kdev->desc_pa = PFN_PHYS(max_pfn) + offset; > + > + if (register_virtio_device(&kdev->vdev) != 0) { > + pr_err("Failed to register kvm device %u type %u\n", > + offset, d->type); > + kfree(kdev); > + } > +} > + > +/* > + * scan_devices() simply iterates through the device page. > + * The type 0 is reserved to mean "end of devices". > + */ > +static void scan_devices(void) > +{ > + unsigned int i; > + struct kvm_device_desc *d; > + > + for (i = 0; i < PAGE_SIZE; i += desc_size(d)) { > + d = kvm_devices + i; > + > + if (d->type == 0) > + break; > + > + add_kvm_device(d, i); > + } > +} > + > +/* > + * Init function for virtio. > + * devices are in a single page above the top of "normal" mem. > + */ > +static int __init kvm_devices_init(void) > +{ > + int rc = -ENOMEM; > + > + kvm_root = root_device_register("kvm_tile"); > + if (IS_ERR(kvm_root)) { > + rc = PTR_ERR(kvm_root); > + pr_err("Could not register kvm_tile root device"); > + return rc; > + } > + > + kvm_devices = generic_remap_prot(PFN_PHYS(max_pfn), PAGE_SIZE, > + 0, io_prot()); > + if (!kvm_devices) { > + kvm_devices = NULL; > + root_device_unregister(kvm_root); > + return rc; > + } > + > + scan_devices(); > + return 0; > +} > + > +/* code for early console output with virtio_console */ > +static __init int early_put_chars(u32 vtermno, const char *buf, int len) > +{ > + char scratch[512]; > + > + if (len > sizeof(scratch) - 1) > + len = sizeof(scratch) - 1; > + scratch[len] = '\0'; > + memcpy(scratch, buf, len); > + hcall_virtio(KVM_VIRTIO_NOTIFY, __pa(scratch)); > + > + return len; > +} > + > +static int __init tile_virtio_console_init(void) > +{ > + return virtio_cons_early_init(early_put_chars); > +} > +console_initcall(tile_virtio_console_init); > + > +/* > + * We do this after core stuff, but before the drivers. > + */ > +postcore_initcall(kvm_devices_init); > diff --git a/arch/tile/kernel/relocate_kernel_64.S > b/arch/tile/kernel/relocate_kernel_64.S > index 1c09a4f..02bc446 100644 > --- a/arch/tile/kernel/relocate_kernel_64.S > +++ b/arch/tile/kernel/relocate_kernel_64.S > @@ -34,11 +34,11 @@ STD_ENTRY(relocate_new_kernel) > addi sp, sp, -8 > /* we now have a stack (whether we need one or not) */ > > +#ifdef RELOCATE_NEW_KERNEL_VERBOSE > moveli r40, hw2_last(hv_console_putc) > shl16insli r40, r40, hw1(hv_console_putc) > shl16insli r40, r40, hw0(hv_console_putc) > > -#ifdef RELOCATE_NEW_KERNEL_VERBOSE > moveli r0, 'r' > jalr r40 > > @@ -176,10 +176,12 @@ STD_ENTRY(relocate_new_kernel) > > /* we should not get here */ > > +#ifdef RELOCATE_NEW_KERNEL_VERBOSE > moveli r0, '?' > jalr r40 > moveli r0, '\n' > jalr r40 > +#endif > > j .Lhalt > > @@ -237,7 +239,9 @@ STD_ENTRY(relocate_new_kernel) > j .Lloop > > > -.Lerr: moveli r0, 'e' > +.Lerr: > +#ifdef RELOCATE_NEW_KERNEL_VERBOSE > + moveli r0, 'e' > jalr r40 > moveli r0, 'r' > jalr r40 > @@ -245,6 +249,7 @@ STD_ENTRY(relocate_new_kernel) > jalr r40 > moveli r0, '\n' > jalr r40 > +#endif > .Lhalt: > moveli r41, hw2_last(hv_halt) > shl16insli r41, r41, hw1(hv_halt) > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/