这篇文章的理解,需要一些专业知识了。
我们可以创建模拟自己的外设吗?
我们已经知道什么是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)
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 +}
stackoverflow 上也有讨论怎么增加一个设备 How to add a new device in QEMU source code?