• [虚拟化/云][全栈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!
    ");
     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
    ");
     63 +    }
     64 +
     65 +    if (s->missed_ticks > s->expiration_ticks) {
     66 +        printf("Watchdog expired!
    ");
     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!
    ");
    108 +            cwd_timer_event(s);
    109 +        } else {
    110 +            printf("Deactivated!
    ");
    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?

  • 相关阅读:
    PTA(Advanced Level)1009.Product of Polynomials
    PTA(Advanced Level)1002.A+B for Polynomials
    PTA(Advanced Level)1065.A+B and C
    PTA(Advanced Level)1046.Shortest Distance
    PTA(Advanced Level)1042.Shuffling Machine
    PTA(Basic Level)1046.划拳
    PTA(Basic Level)1060.爱丁顿数
    PTA(Basic Level)1053.住房空置率
    PTA(Basic Level)1023.组个最小数
    DOM4J熟知
  • 原文地址:https://www.cnblogs.com/shaohef/p/3800486.html
Copyright © 2020-2023  润新知