[虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(二)

这篇文章的理解,需要一些专业知识了。

我们可以创建模拟自己的外设吗?

我们已经知道什么是qemu了,我们可以通过qmeu的提供的外设,DIY一个计算机了。

但是我们可能还不满足,我们可以自己制造一个外设吗?

答案是可以的。而且这是了解计算机体系结构的一个很好的实践活动。

watchdog 外设

watchdog, 即看门狗。 如果狗饿了,便会”咬人“(CPU),让CPU重新启动。 为了不让狗狗”咬人“,我们需要不停的喂他。

我们将创建一个最简单的PCI的外设watchdog。如果你是一个硬件工程师或集成电路工程师,那么你肯定知道watchdog是什么东西, 并且可以很轻松的自行设计一个watchdog了。

但是我们这里创建的watchdog,是基于qemu架构的软件模拟的设备。

预备知识:

1. git 基本操作。

2. C 语言。

3. PCI的一些知识。

4. 阅读qemu 的文档 http://wiki.qemu.org/Manual

开发平台:

linux

 实践:

1. clone一个qemu的仓库 http://wiki.qemu.org/Download

$ git clone git://git.qemu.org/qemu.git

2. 切换到一个新分支,一定从5d92c74 检出(checkout

$ git checkout -b watchdog 5d92c74

 3. 写源代码。

将下面的代码watchdog_source.patch(见代码目录)保存到本地, 注意去掉代码前面的行好。 现在不分析代码。以后会分析。这个代码已经测试过是可以运行的。

然后应用到qemu中。

$ git apply watchdog_source.patch

4. 配置qemu  http://wiki.qemu.org/Documentation/9psetup

$ ./configure '--target-list=x86_64-softmmu' '--enable-debug' '--enable-kvm' '--enable-spice' '--prefix=/home/shhfeng/qemu/'

4. 编译源代码。 http://wiki.qemu.org/Hosts/Linux

$ make

5. 测试代码 http://wiki.qemu.org/Documentation/QemuIoTests

将下面的代码watchdog_testcase.patch(见代码目录)保存到本地, 注意去掉代码前面的行好。 现在不分析代码。以后会分析。这个代码已经测试过是可以运行的。

然后应用到qemu中。

$ git apply watchdog_testcase.patch

$ make check-qtest-x86_64

6. 启动qemu with cstl-watchdog

$ x86_64-softmmu/qemu-system-x86_64 -device cstl-watchdog

注意:这里不能使用: $ x86_64-softmmu/qemu-system-x86_64 -watchdog cstl-watchdog

是因为没有在cstl-watchdog.c 中定义: WatchdogTimerModel

此外watchdog没有支持中断, 请参考 http://www.cnblogs.com/lihuidashen/p/4462220.html

代码目录:

1. watchdog_source.patch

  1 diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs
  2 index 4b0374a..8f34e78 100644
  3 --- a/hw/watchdog/Makefile.objs
  4 +++ b/hw/watchdog/Makefile.objs
  5 @@ -1,3 +1,3 @@
  6 -common-obj-y += watchdog.o
  7 +common-obj-y += watchdog.o cstl-watchdog.o
  8  common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
  9  common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
 10 diff --git a/hw/watchdog/cstl-watchdog.c b/hw/watchdog/cstl-watchdog.c
 11 new file mode 100644
 12 index 0000000..3ce043a
 13 --- /dev/null
 14 +++ b/hw/watchdog/cstl-watchdog.c
 15 @@ -0,0 +1,197 @@
 16 +/*
 17 + * Watch Dog Timer Demo
 18 + *
 19 + * This work is licensed under the terms of the GNU GPL, version 2 or later.
 20 + * See the COPYING file in the top-level directory.
 21 + *
 22 + */
 23 +
 24 +#include "qemu/timer.h"
 25 +#include "hw/hw.h"
 26 +#include "hw/pci/pci.h"
 27 +#include "sysemu/sysemu.h"
 28 +
 29 +typedef struct CSTLWatchdogState {
 30 +    PCIDevice dev;
 31 +
 32 +    uint8_t activated;
 33 +
 34 +    uint8_t triggered;
 35 +
 36 +    uint32_t missed_ticks;
 37 +
 38 +    QEMUTimer *watchdog_timer;
 39 +
 40 +    uint32_t expiration_ticks;
 41 +
 42 +    MemoryRegion io;
 43 +} CSTLWatchdogState;
 44 +#define TYPE_CSTL_WATCHDOG "cstl-watchdog"
 45 +#define CSTL_WATCHDOG(obj) \
 46 +    OBJECT_CHECK(CSTLWatchdogState, (obj), TYPE_CSTL_WATCHDOG)
 47 +
 48 +static void cwd_timer_event(void *opaque)
 49 +{
 50 +    CSTLWatchdogState *s = CSTL_WATCHDOG(opaque);
 51 +
 52 +    (void)s;
 53 +
 54 +    printf("watch dog fire!\n");
 55 +    if (!s->triggered) {
 56 +        s->missed_ticks++;
 57 +    }
 58 +
 59 +    s->triggered = 0;
 60 +
 61 +    if (s->missed_ticks > 1) {
 62 +        printf("WARNING: missed watchdog tick\n");
 63 +    }
 64 +
 65 +    if (s->missed_ticks > s->expiration_ticks) {
 66 +        printf("Watchdog expired!\n");
 67 +        qemu_system_reset_request();
 68 +    }
 69 +
 70 +
 71 +    if (s->activated) {
 72 +        timer_mod(s->watchdog_timer,
 73 +                  qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000);
 74 +    }
 75 +}
 76 +
 77 +static uint64_t cwd_io_read(void *opaque, hwaddr addr,
 78 +                            unsigned size)
 79 +{
 80 +    CSTLWatchdogState *s = CSTL_WATCHDOG(opaque);
 81 +
 82 +    switch (addr) {
 83 +    case 0x00:
 84 +        return 0x42;
 85 +    case 0x01:
 86 +        return s->activated;
 87 +    default:
 88 +        break;
 89 +    }
 90 +
 91 +    return 0;
 92 +}
 93 +
 94 +static void cwd_io_write(void *opaque, hwaddr addr,
 95 +                         uint64_t val, unsigned size)
 96 +{
 97 +    CSTLWatchdogState *s = CSTL_WATCHDOG(opaque);
 98 +
 99 +    switch (addr) {
100 +    case 0x00:
101 +        /* read-only */
102 +        break;
103 +    case 0x01:
104 +        s->activated = !!val;
105 +
106 +        if (s->activated) {
107 +            printf("Activated!\n");
108 +            cwd_timer_event(s);
109 +        } else {
110 +            printf("Deactivated!\n");
111 +            timer_del(s->watchdog_timer);
112 +        }
113 +        break;
114 +    case 0x02:
115 +        s->triggered = 1;
116 +        s->missed_ticks = 0;
117 +        break;
118 +    default:
119 +        break;
120 +    }
121 +}
122 +
123 +static const MemoryRegionOps cwd_io_ops = {
124 +    .read = cwd_io_read,
125 +    .write = cwd_io_write,
126 +    .endianness = DEVICE_LITTLE_ENDIAN,
127 +};
128 +
129 +static const VMStateDescription vmstate_cwd = {
130 +    .name = TYPE_CSTL_WATCHDOG,
131 +    .version_id = 1,
132 +    .fields      = (VMStateField []) {
133 +        VMSTATE_PCI_DEVICE(dev, CSTLWatchdogState),
134 +        VMSTATE_UINT8(activated, CSTLWatchdogState),
135 +        VMSTATE_TIMER(watchdog_timer, CSTLWatchdogState),
136 +        VMSTATE_UINT8(triggered, CSTLWatchdogState),
137 +        VMSTATE_UINT32(missed_ticks, CSTLWatchdogState),
138 +        VMSTATE_END_OF_LIST()
139 +    }
140 +};
141 +
142 +static void cwd_unrealize(PCIDevice *dev)
143 +{
144 +}
145 +
146 +static int cwd_realize(PCIDevice *pci_dev)
147 +{
148 +    CSTLWatchdogState *s = CSTL_WATCHDOG(pci_dev);
149 +
150 +    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
151 +
152 +    return 0;
153 +}
154 +
155 +static void cwd_reset(DeviceState *dev)
156 +{
157 +    CSTLWatchdogState *s = CSTL_WATCHDOG(dev);
158 +
159 +    s->activated = 0;
160 +    s->triggered = 0;
161 +    s->missed_ticks = 0;
162 +}
163 +
164 +static void cwd_initfn(Object *obj)
165 +{
166 +    CSTLWatchdogState *s = CSTL_WATCHDOG(obj);
167 +
168 +    memory_region_init_io(&s->io, OBJECT(s), &cwd_io_ops, s, "cstl-watchdog-io", 16);
169 +
170 +    s->watchdog_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cwd_timer_event, s);
171 +}
172 +
173 +static Property cwd_properties[] = {
174 +    DEFINE_PROP_UINT32("expiration-ticks", CSTLWatchdogState,
175 +                       expiration_ticks, 10),
176 +    DEFINE_PROP_END_OF_LIST(),
177 +};
178 +
179 +static void cwd_class_init(ObjectClass *klass, void *data)
180 +{
181 +    DeviceClass *dc = DEVICE_CLASS(klass);
182 +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
183 +
184 +    k->init = cwd_realize;
185 +    k->exit = cwd_unrealize;
186 +    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
187 +    k->device_id = 0x0101;
188 +    k->revision = 0x01;
189 +    k->class_id = PCI_CLASS_SYSTEM_OTHER;
190 +    dc->reset = cwd_reset;
191 +    dc->vmsd = &vmstate_cwd;
192 +    dc->props = cwd_properties;
193 +}
194 +
195 +static TypeInfo cstl_watchdog_info = {
196 +    .name          = TYPE_CSTL_WATCHDOG,
197 +    .parent        = TYPE_PCI_DEVICE,
198 +    .instance_init = cwd_initfn,
199 +    .instance_size = sizeof(CSTLWatchdogState),
200 +    .class_init    = cwd_class_init,
201 +};
202 +
203 +static void register_types(void)
204 +{
205 +    type_register_static(&cstl_watchdog_info);
206 +}
207 +
208 +type_init(register_types)
View Code

 

 2. watchdog_testcase.patch

  1 diff --git a/tests/Makefile b/tests/Makefile
  2 index 471b4c8..0a8f2cd 100644
  3 --- a/tests/Makefile
  4 +++ b/tests/Makefile
  5 @@ -116,6 +116,7 @@ gcov-files-i386-y += hw/block/hd-geometry.c
  6  check-qtest-i386-y += tests/boot-order-test$(EXESUF)
  7  check-qtest-i386-y += tests/acpi-test$(EXESUF)
  8  check-qtest-i386-y += tests/rtc-test$(EXESUF)
  9 +check-qtest-i386-y += tests/cwd-test$(EXESUF)
 10  check-qtest-i386-y += tests/i440fx-test$(EXESUF)
 11  check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
 12  check-qtest-i386-y += tests/blockdev-test$(EXESUF)
 13 @@ -242,6 +243,7 @@ libqos-pc-obj-y += tests/libqos/malloc-pc.o
 14  libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
 15  
 16  tests/rtc-test$(EXESUF): tests/rtc-test.o
 17 +tests/cwd-test$(EXESUF): tests/cwd-test.o
 18  tests/m48t59-test$(EXESUF): tests/m48t59-test.o
 19  tests/endianness-test$(EXESUF): tests/endianness-test.o
 20  tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y)
 21 diff --git a/tests/cwd-test.c b/tests/cwd-test.c
 22 new file mode 100644
 23 index 0000000..380a313
 24 --- /dev/null
 25 +++ b/tests/cwd-test.c
 26 @@ -0,0 +1,120 @@
 27 +/*
 28 + * QTest testcase for the CSTL Watchdog
 29 + *
 30 + * This work is licensed under the terms of the GNU GPL, version 2 or later.
 31 + * See the COPYING file in the top-level directory.
 32 + *
 33 + */
 34 +#include "libqtest.h"
 35 +#include "hw/pci/pci_ids.h"
 36 +#include "hw/pci/pci_regs.h"
 37 +
 38 +#include <glib.h>
 39 +#include <unistd.h>
 40 +
 41 +static uint32_t pci_config_read(uint8_t bus, uint8_t devfn,
 42 +                                uint8_t addr, int size)
 43 +{
 44 +    outl(0xcf8, (bus << 16) | (devfn << 8) | addr | (1u << 31));
 45 +    if (size == 1) {
 46 +        return inb(0xcfc);
 47 +    } else if (size == 2) {
 48 +        return inw(0xcfc);
 49 +    }
 50 +    return inl(0xcfc);
 51 +}
 52 +
 53 +static void pci_config_write(uint8_t bus, uint8_t devfn,
 54 +                             uint32_t addr, int size, uint32_t value)
 55 +{
 56 +    outl(0xcf8, (bus << 16) | (devfn << 8) | addr | (1u << 31));
 57 +    if (size == 1) {
 58 +        outb(0xcfc, value);
 59 +    } else if (size == 2) {
 60 +        outw(0xcfc, value);
 61 +    } else {
 62 +        outl(0xcfc, value);
 63 +    }
 64 +}
 65 +
 66 +static void cwd_probe(uint8_t bus, uint8_t devfn)
 67 +{
 68 +    uint32_t bar0 = 0xc000;
 69 +    int i;
 70 +
 71 +    pci_config_write(bus, devfn, PCI_COMMAND, 2,
 72 +                     (PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
 73 +    pci_config_write(bus, devfn, PCI_BASE_ADDRESS_0, 4, bar0);
 74 +
 75 +    g_assert_cmpint(inb(bar0 + 0x00), ==, 0x42);
 76 +
 77 +    outb(bar0 + 0x01, 0x03); // activate device
 78 +    g_assert_cmpint(inb(bar0 + 0x01), ==, 0x01); // confirm activation
 79 +
 80 +    for (i = 0; i < 2 * 10; i++) {
 81 +        outb(bar0 + 0x02, 0x32);
 82 +        g_usleep(500000);
 83 +    }
 84 +
 85 +    outb(bar0 + 0x01, 0x00); // deactivate device
 86 +}
 87 +
 88 +static void basic_init(void)
 89 +{
 90 +    int slot;
 91 +
 92 +    for (slot = 0; slot < 32; slot++) {
 93 +        uint8_t fn;
 94 +
 95 +        for (fn = 0; fn < 8; fn++) {
 96 +            uint8_t devfn = (slot << 3) | fn;
 97 +            uint16_t device_id;
 98 +            uint16_t vendor_id;
 99 +
100 +            vendor_id = pci_config_read(0, devfn, PCI_VENDOR_ID, 2);
101 +            device_id = pci_config_read(0, devfn, PCI_DEVICE_ID, 2);
102 +
103 +            if (vendor_id == 0xFFFF || device_id == 0xFFFF) {
104 +                break;
105 +            }
106 +
107 +            if (vendor_id == 0x1af4 && device_id == 0x0101) {
108 +                cwd_probe(0, devfn);
109 +                return;
110 +            }
111 +        }
112 +    }
113 +
114 +    g_assert_not_reached();
115 +}
116 +
117 +int main(int argc, char **argv)
118 +{
119 +    QTestState *s = NULL;
120 +    char *cmd;
121 +    int ret;
122 +
123 +    g_test_init(&argc, &argv, NULL);
124 +
125 +    cmd = g_strdup_printf("-device cstl-watchdog,expiration-ticks=%d",
126 +                          g_test_rand_int_range(2, 11));
127 +
128 +    s = qtest_start(cmd);
129 +
130 +    g_free(cmd);
131 +
132 +    qtest_add_func("/basic/init", basic_init);
133 +
134 +    ret = g_test_run();
135 +
136 +    if (s) {
137 +        qtest_quit(s);
138 +    }
139 +
140 +    return ret;
141 +}
View Code

 

stackoverflow 上也有讨论怎么增加一个设备  How to add a new device in QEMU source code?

posted @ 2014-07-06 08:53  lvmxh  阅读(3694)  评论(2编辑  收藏  举报