From patchwork Thu Apr 10 16:56:52 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Jones X-Patchwork-Id: 3963541 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id A7979BFF02 for ; Thu, 10 Apr 2014 16:57:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 5434620823 for ; Thu, 10 Apr 2014 16:57:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1EDEE20819 for ; Thu, 10 Apr 2014 16:57:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758763AbaDJQ51 (ORCPT ); Thu, 10 Apr 2014 12:57:27 -0400 Received: from mx1.redhat.com ([209.132.183.28]:3340 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758560AbaDJQ5Z (ORCPT ); Thu, 10 Apr 2014 12:57:25 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s3AGvN7v030352 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 10 Apr 2014 12:57:23 -0400 Received: from hawk.usersys.redhat.com.com (dhcp-1-170.brq.redhat.com [10.34.1.170]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id s3AGv4Us021128; Thu, 10 Apr 2014 12:57:22 -0400 From: Andrew Jones To: kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org Cc: christoffer.dall@linaro.org Subject: [PATCH v4 11/19] add minimal virtio support for devtree virtio-mmio Date: Thu, 10 Apr 2014 18:56:52 +0200 Message-Id: <1397149020-3501-12-git-send-email-drjones@redhat.com> In-Reply-To: <1397149020-3501-1-git-send-email-drjones@redhat.com> References: <1397149020-3501-1-git-send-email-drjones@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-7.5 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Support the bare minimum of virtio to enable access to the virtio-mmio config space of a device. Currently this implementation must use a device tree to find the device. Signed-off-by: Andrew Jones --- v4: - split from the virtio-testdev patch - search a table to "discover" that the device must be DT/virtio-mmio, which doesn't change anything, but looks less hacky than comments saying the device must be DT/virtio-mmio... - manage own pool of virtio-mmio pre-allocated device structures in order to avoid needing access to the heap --- lib/libcflat.h | 3 ++ lib/virtio.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/virtio.h | 89 +++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 lib/virtio.c create mode 100644 lib/virtio.h diff --git a/lib/libcflat.h b/lib/libcflat.h index c7c31be1cc8e5..5bb66d01dfc53 100644 --- a/lib/libcflat.h +++ b/lib/libcflat.h @@ -62,6 +62,9 @@ extern long atol(const char *ptr); #define ARRAY_SIZE(_a) (sizeof(_a)/sizeof((_a)[0])) #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) #define NULL ((void *)0UL) diff --git a/lib/virtio.c b/lib/virtio.c new file mode 100644 index 0000000000000..e7161ff591e4c --- /dev/null +++ b/lib/virtio.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2014, Red Hat Inc, Andrew Jones + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ +#include "libcflat.h" +#include "devicetree.h" +#include "asm/spinlock.h" +#include "asm/io.h" +#include "virtio.h" + +enum virtio_hwdesc_type { + VIRTIO_HWDESC_TYPE_DT = 0, /* device tree */ + NR_VIRTIO_HWDESC_TYPES, +}; + +enum virtio_bus_type { + VIRTIO_BUS_TYPE_MMIO = 0, /* virtio-mmio */ + NR_VIRTIO_BUS_TYPES, +}; + +struct virtio_bind_bus { + bool (*hwdesc_probe)(void); + struct virtio_dev *(*device_bind)(u32 devid); +}; + +static struct virtio_dev *vm_dt_device_bind(u32 devid); + +static struct virtio_bind_bus +virtio_bind_busses[NR_VIRTIO_HWDESC_TYPES][NR_VIRTIO_BUS_TYPES] = { + +[VIRTIO_HWDESC_TYPE_DT] = { + + [VIRTIO_BUS_TYPE_MMIO] = { + .hwdesc_probe = dt_available, + .device_bind = vm_dt_device_bind, + }, +}, +}; + +struct virtio_dev *virtio_bind(u32 devid) +{ + struct virtio_bind_bus *bus; + struct virtio_dev *dev; + int i, j; + + for (i = 0; i < NR_VIRTIO_HWDESC_TYPES; ++i) { + for (j = 0; j < NR_VIRTIO_BUS_TYPES; ++j) { + + bus = &virtio_bind_busses[i][j]; + + if (!bus->hwdesc_probe()) + continue; + + dev = bus->device_bind(devid); + if (dev) + return dev; + } + } + + return NULL; +} + +/****************************************************** + * virtio-mmio support (config space only) + ******************************************************/ + +static void vm_get(struct virtio_dev *vdev, unsigned offset, + void *buf, unsigned len) +{ + struct virtio_mmio_dev *vmdev = to_virtio_mmio_dev(vdev); + u8 *p = buf; + unsigned i; + + for (i = 0; i < len; ++i) + p[i] = readb(vmdev->base + VIRTIO_MMIO_CONFIG + offset + i); +} + +static void vm_set(struct virtio_dev *vdev, unsigned offset, + const void *buf, unsigned len) +{ + struct virtio_mmio_dev *vmdev = to_virtio_mmio_dev(vdev); + const u8 *p = buf; + unsigned i; + + for (i = 0; i < len; ++i) + writeb(p[i], vmdev->base + VIRTIO_MMIO_CONFIG + offset + i); +} + +#define NR_VM_DEVICES 32 +static struct spinlock vm_lock; +static struct virtio_mmio_dev vm_devs[NR_VM_DEVICES]; +static struct virtio_conf_ops vm_confs[NR_VM_DEVICES]; +static int nr_vm_devs; + +static struct virtio_mmio_dev *vm_new_device(u32 devid) +{ + struct virtio_mmio_dev *vmdev; + + if (nr_vm_devs >= NR_VM_DEVICES) + return NULL; + + spin_lock(&vm_lock); + vmdev = &vm_devs[nr_vm_devs]; + vmdev->vdev.config = &vm_confs[nr_vm_devs]; + ++nr_vm_devs; + spin_unlock(&vm_lock); + + vmdev->vdev.id.device = devid; + vmdev->vdev.id.vendor = -1; + vmdev->vdev.config->get = vm_get; + vmdev->vdev.config->set = vm_set; + + return vmdev; +} + +/****************************************************** + * virtio-mmio device tree support + ******************************************************/ + +struct vm_dt_info { + u32 devid; + void *base; +}; + +static int vm_dt_match(const struct dt_device *dev, int fdtnode) +{ + struct vm_dt_info *info = (struct vm_dt_info *)dev->info; + dt_pbus_addr_t base; + + dt_device_bind_node((struct dt_device *)dev, fdtnode); + + assert(dt_pbus_get_baseaddr(dev, &base) == 0); + assert(sizeof(long) == 8 || !(base >> 32)); + + info->base = (void *)(unsigned long)base; + + return readl(info->base + VIRTIO_MMIO_DEVICE_ID) == info->devid; +} + +static struct virtio_dev *vm_dt_device_bind(u32 devid) +{ + struct virtio_mmio_dev *vmdev; + struct dt_device dt_dev; + struct dt_bus dt_bus; + struct vm_dt_info info; + int node; + + dt_bus_init_defaults(&dt_bus); + dt_bus.match = vm_dt_match; + + info.devid = devid; + + dt_device_init(&dt_dev, &dt_bus, &info); + + node = dt_device_find_compatible(&dt_dev, "virtio,mmio"); + assert(node >= 0 || node == -FDT_ERR_NOTFOUND); + + if (node == -FDT_ERR_NOTFOUND) + return NULL; + + vmdev = vm_new_device(devid); + vmdev->base = info.base; + + return &vmdev->vdev; +} diff --git a/lib/virtio.h b/lib/virtio.h new file mode 100644 index 0000000000000..163b9912c4e05 --- /dev/null +++ b/lib/virtio.h @@ -0,0 +1,89 @@ +#ifndef _VIRTIO_H_ +#define _VIRTIO_H_ +/* + * A minimal implementation of virtio for virtio-mmio config space + * access. + * + * Copyright (C) 2014, Red Hat Inc, Andrew Jones + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ +#include "libcflat.h" + +struct virtio_devid { + u32 device; + u32 vendor; +}; + +struct virtio_dev { + struct virtio_devid id; + struct virtio_conf_ops *config; +}; + +struct virtio_conf_ops { + void (*get)(struct virtio_dev *vdev, unsigned offset, + void *buf, unsigned len); + void (*set)(struct virtio_dev *vdev, unsigned offset, + const void *buf, unsigned len); +}; + +extern struct virtio_dev *virtio_bind(u32 devid); + +static inline u8 +virtio_config_readb(struct virtio_dev *vdev, unsigned offset) +{ + u8 val; + vdev->config->get(vdev, offset, &val, 1); + return val; +} + +static inline u16 +virtio_config_readw(struct virtio_dev *vdev, unsigned offset) +{ + u16 val; + vdev->config->get(vdev, offset, &val, 2); + return val; +} + +static inline u32 +virtio_config_readl(struct virtio_dev *vdev, unsigned offset) +{ + u32 val; + vdev->config->get(vdev, offset, &val, 4); + return val; +} + +static inline void +virtio_config_writeb(struct virtio_dev *vdev, unsigned offset, u8 val) +{ + vdev->config->set(vdev, offset, &val, 1); +} + +static inline void +virtio_config_writew(struct virtio_dev *vdev, unsigned offset, u16 val) +{ + vdev->config->set(vdev, offset, &val, 2); +} + +static inline void +virtio_config_writel(struct virtio_dev *vdev, unsigned offset, u32 val) +{ + vdev->config->set(vdev, offset, &val, 4); +} + +/****************************************************** + * virtio-mmio + ******************************************************/ + +#define VIRTIO_MMIO_DEVICE_ID 0x008 +#define VIRTIO_MMIO_CONFIG 0x100 + +#define to_virtio_mmio_dev(vdev_ptr) \ + container_of(vdev_ptr, struct virtio_mmio_dev, vdev) + +struct virtio_mmio_dev { + struct virtio_dev vdev; + void *base; +}; + +#endif /* _VIRTIO_H_ */