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

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

我们已经知道什么是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

 diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs
index 4b0374a..8f34e78
--- a/hw/watchdog/Makefile.objs
+++ b/hw/watchdog/Makefile.objs
@@ -, +, @@
-common-obj-y += watchdog.o
+common-obj-y += watchdog.o cstl-watchdog.o
common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o
common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o
diff --git a/hw/watchdog/cstl-watchdog.c b/hw/watchdog/cstl-watchdog.c
new file mode
index ..3ce043a
--- /dev/null
+++ b/hw/watchdog/cstl-watchdog.c
@@ -, +, @@
+/*
+ * Watch Dog Timer Demo
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/timer.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "sysemu/sysemu.h"
+
+typedef struct CSTLWatchdogState {
+ PCIDevice dev;
+
+ uint8_t activated;
+
+ uint8_t triggered;
+
+ uint32_t missed_ticks;
+
+ QEMUTimer *watchdog_timer;
+
+ uint32_t expiration_ticks;
+
+ MemoryRegion io;
+} CSTLWatchdogState;
+#define TYPE_CSTL_WATCHDOG "cstl-watchdog"
+#define CSTL_WATCHDOG(obj) \
+ OBJECT_CHECK(CSTLWatchdogState, (obj), TYPE_CSTL_WATCHDOG)
+
+static void cwd_timer_event(void *opaque)
+{
+ CSTLWatchdogState *s = CSTL_WATCHDOG(opaque);
+
+ (void)s;
+
+ printf("watch dog fire!\n");
+ if (!s->triggered) {
+ s->missed_ticks++;
+ }
+
+ s->triggered = ;
+
+ if (s->missed_ticks > ) {
+ printf("WARNING: missed watchdog tick\n");
+ }
+
+ if (s->missed_ticks > s->expiration_ticks) {
+ printf("Watchdog expired!\n");
+ qemu_system_reset_request();
+ }
+
+
+ if (s->activated) {
+ timer_mod(s->watchdog_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + );
+ }
+}
+
+static uint64_t cwd_io_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ CSTLWatchdogState *s = CSTL_WATCHDOG(opaque);
+
+ switch (addr) {
+ case 0x00:
+ return 0x42;
+ case 0x01:
+ return s->activated;
+ default:
+ break;
+ }
+
+ return ;
+}
+
+static void cwd_io_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ CSTLWatchdogState *s = CSTL_WATCHDOG(opaque);
+
+ switch (addr) {
+ case 0x00:
+ /* read-only */
+ break;
+ case 0x01:
+ s->activated = !!val;
+
+ if (s->activated) {
+ printf("Activated!\n");
+ cwd_timer_event(s);
+ } else {
+ printf("Deactivated!\n");
+ timer_del(s->watchdog_timer);
+ }
+ break;
+ case 0x02:
+ s->triggered = ;
+ s->missed_ticks = ;
+ break;
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps cwd_io_ops = {
+ .read = cwd_io_read,
+ .write = cwd_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_cwd = {
+ .name = TYPE_CSTL_WATCHDOG,
+ .version_id = ,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, CSTLWatchdogState),
+ VMSTATE_UINT8(activated, CSTLWatchdogState),
+ VMSTATE_TIMER(watchdog_timer, CSTLWatchdogState),
+ VMSTATE_UINT8(triggered, CSTLWatchdogState),
+ VMSTATE_UINT32(missed_ticks, CSTLWatchdogState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void cwd_unrealize(PCIDevice *dev)
+{
+}
+
+static int cwd_realize(PCIDevice *pci_dev)
+{
+ CSTLWatchdogState *s = CSTL_WATCHDOG(pci_dev);
+
+ pci_register_bar(pci_dev, , PCI_BASE_ADDRESS_SPACE_IO, &s->io);
+
+ return ;
+}
+
+static void cwd_reset(DeviceState *dev)
+{
+ CSTLWatchdogState *s = CSTL_WATCHDOG(dev);
+
+ s->activated = ;
+ s->triggered = ;
+ s->missed_ticks = ;
+}
+
+static void cwd_initfn(Object *obj)
+{
+ CSTLWatchdogState *s = CSTL_WATCHDOG(obj);
+
+ memory_region_init_io(&s->io, OBJECT(s), &cwd_io_ops, s, "cstl-watchdog-io", );
+
+ s->watchdog_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cwd_timer_event, s);
+}
+
+static Property cwd_properties[] = {
+ DEFINE_PROP_UINT32("expiration-ticks", CSTLWatchdogState,
+ expiration_ticks, ),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void cwd_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->init = cwd_realize;
+ k->exit = cwd_unrealize;
+ k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+ k->device_id = 0x0101;
+ k->revision = 0x01;
+ k->class_id = PCI_CLASS_SYSTEM_OTHER;
+ dc->reset = cwd_reset;
+ dc->vmsd = &vmstate_cwd;
+ dc->props = cwd_properties;
+}
+
+static TypeInfo cstl_watchdog_info = {
+ .name = TYPE_CSTL_WATCHDOG,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_init = cwd_initfn,
+ .instance_size = sizeof(CSTLWatchdogState),
+ .class_init = cwd_class_init,
+};
+
+static void register_types(void)
+{
+ type_register_static(&cstl_watchdog_info);
+}
+
+type_init(register_types)

2. watchdog_testcase.patch

 diff --git a/tests/Makefile b/tests/Makefile
index 471b4c8..0a8f2cd
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -, +, @@ gcov-files-i386-y += hw/block/hd-geometry.c
check-qtest-i386-y += tests/boot-order-test$(EXESUF)
check-qtest-i386-y += tests/acpi-test$(EXESUF)
check-qtest-i386-y += tests/rtc-test$(EXESUF)
+check-qtest-i386-y += tests/cwd-test$(EXESUF)
check-qtest-i386-y += tests/i440fx-test$(EXESUF)
check-qtest-i386-y += tests/fw_cfg-test$(EXESUF)
check-qtest-i386-y += tests/blockdev-test$(EXESUF)
@@ -, +, @@ libqos-pc-obj-y += tests/libqos/malloc-pc.o
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o tests/rtc-test$(EXESUF): tests/rtc-test.o
+tests/cwd-test$(EXESUF): tests/cwd-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
tests/endianness-test$(EXESUF): tests/endianness-test.o
tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y)
diff --git a/tests/cwd-test.c b/tests/cwd-test.c
new file mode
index ..380a313
--- /dev/null
+++ b/tests/cwd-test.c
@@ -, +, @@
+/*
+ * QTest testcase for the CSTL Watchdog
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+#include "libqtest.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/pci_regs.h"
+
+#include <glib.h>
+#include <unistd.h>
+
+static uint32_t pci_config_read(uint8_t bus, uint8_t devfn,
+ uint8_t addr, int size)
+{
+ outl(0xcf8, (bus << ) | (devfn << ) | addr | (1u << ));
+ if (size == ) {
+ return inb(0xcfc);
+ } else if (size == ) {
+ return inw(0xcfc);
+ }
+ return inl(0xcfc);
+}
+
+static void pci_config_write(uint8_t bus, uint8_t devfn,
+ uint32_t addr, int size, uint32_t value)
+{
+ outl(0xcf8, (bus << ) | (devfn << ) | addr | (1u << ));
+ if (size == ) {
+ outb(0xcfc, value);
+ } else if (size == ) {
+ outw(0xcfc, value);
+ } else {
+ outl(0xcfc, value);
+ }
+}
+
+static void cwd_probe(uint8_t bus, uint8_t devfn)
+{
+ uint32_t bar0 = 0xc000;
+ int i;
+
+ pci_config_write(bus, devfn, PCI_COMMAND, ,
+ (PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+ pci_config_write(bus, devfn, PCI_BASE_ADDRESS_0, , bar0);
+
+ g_assert_cmpint(inb(bar0 + 0x00), ==, 0x42);
+
+ outb(bar0 + 0x01, 0x03); // activate device
+ g_assert_cmpint(inb(bar0 + 0x01), ==, 0x01); // confirm activation
+
+ for (i = ; i < * ; i++) {
+ outb(bar0 + 0x02, 0x32);
+ g_usleep();
+ }
+
+ outb(bar0 + 0x01, 0x00); // deactivate device
+}
+
+static void basic_init(void)
+{
+ int slot;
+
+ for (slot = ; slot < ; slot++) {
+ uint8_t fn;
+
+ for (fn = ; fn < ; fn++) {
+ uint8_t devfn = (slot << ) | fn;
+ uint16_t device_id;
+ uint16_t vendor_id;
+
+ vendor_id = pci_config_read(, devfn, PCI_VENDOR_ID, );
+ device_id = pci_config_read(, devfn, PCI_DEVICE_ID, );
+
+ if (vendor_id == 0xFFFF || device_id == 0xFFFF) {
+ break;
+ }
+
+ if (vendor_id == 0x1af4 && device_id == 0x0101) {
+ cwd_probe(, devfn);
+ return;
+ }
+ }
+ }
+
+ g_assert_not_reached();
+}
+
+int main(int argc, char **argv)
+{
+ QTestState *s = NULL;
+ char *cmd;
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ cmd = g_strdup_printf("-device cstl-watchdog,expiration-ticks=%d",
+ g_test_rand_int_range(, ));
+
+ s = qtest_start(cmd);
+
+ g_free(cmd);
+
+ qtest_add_func("/basic/init", basic_init);
+
+ ret = g_test_run();
+
+ if (s) {
+ qtest_quit(s);
+ }
+
+ return ret;
+}

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

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

    目的 1. 使用verilog/vhdl设计一个PCI的watchdog设备. 2. 通过systemverilog 写testbench. 很久之前研究过AC97的verilog代码.但是很久没用v ...

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

    通过前面的操作,我们已经可以创建一个带有我们自己的PCI的watchdog外设qemu 虚拟机了. 目的: 1. 了解我们的外设情况. 2. 为在guest中开发我们自己的linux PCI驱动程序做 ...

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

    目的: 结合现在比较流行的技术,通过一个demo 展示一个全栈式设计的各种技能. 一个全栈式的工程师,应该能设计通过verilog/VHDL做logical设计.能写内核驱动,能架站. 要熟悉veri ...

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

    目的: 1. 为我们自己的watchdog写一个驱动 步骤: 通过之前的介绍,我们很容易猜想到写我们基于PCI的watchdog驱动,可以分2个步骤. 1. 探测加载PCI设备 这部分代码跟我们的设备 ...

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

    目的: 1. 通过网页读取watchdog的信息 2. 通过网页设置watchdog 准备工作: 1. 选择一个web框架,选用 cherrypy $ sudo apt-get install pyt ...

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

    目的: 1. 了解PCI的基本知识,为完成watchdog的设备做准备. 准备知识: 简单的说,PCI 设备分3个空间. 配置空间,IO空间,内存地址空间. PCI设备厂家决定了外设是使用IO空间还是 ...

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

    目标: 1. 完成最终的设备驱动,增加具体的watchdog设备操作的代码. 测试代码: 代码最终实现见cwd_demo.c 代码只实现了read与write.  没有实现ioctl. 因此,我们可以 ...

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

    我们已经设计了一个基于qemu的watchdog了.下一步工作就是创建一个含有我们的watchdog的虚拟计算机器了. 准备工作: 1. 使用virt-manager或者virsh创建一个虚拟机器. ...

  9. 《全栈营销之如何制作个人博客》之二:php环境安装及个人博客后台搭建 让你的博客跑起来

    上一节我们讲了个人博客用什么开发语言,用什么CMS系统,从这一节我们就开始真正的干货,这一节我们讨论一下PHP环境的安装,及个人博客后台的搭建,让你的博客在正常的PHP环境中运行起来,你就可以进行后台 ...

随机推荐

  1. Drawable类及XMLDrawable的使用

    一.性质 可直接使用.png..jpg..gif.9.png等图片作为资源,也可使用多种XML文件作为资源.(就是这些资源都能生成Drawable对象).并对XML文件作出相关处理 二.XMLDraw ...

  2. 在非MVC环境下使用 Razor引擎

    Razor引擎下载地址: http://github.com/Antaris/RazorEngine 解析Model: string template = "Hello @Model.Nam ...

  3. web编码(转)

    问题2.浏览器编码方式是根据“响应标头-response header”中的键为“Content-Type”的值来自动选择判断,而不会简单的根据你在html中看到的标签值<meta http-e ...

  4. perl 对象 bless 引用

    [root@dr-mysql01 ~]# cat aa.pl use LWP::UserAgent; use Data::Dumper; my $ua = LWP::UserAgent->new ...

  5. POJ 2976 Dropping tests(二分答案)

    [题目链接]  http://poj.org/problem?id=2976 [题目大意] 给出每门成绩的总分和得分,去除k门成绩之后 使得剩余的成绩分数和除以总分得到的数字最大,要求精度在三位小数之 ...

  6. printdir-deldir-bmp

    #include<unistd.h> #include<stdio.h> #include<dirent.h> #include<string.h> # ...

  7. netbean7.4 保存远程项目的时候老是跳警告框的解决方案

    在任意位置新建一个空白文件,然后在 管理远程连接里面=>已知的主机文件=>点浏览就行了

  8. aix光盘安装包 aix puppet agent 自动化安装

    脚本待处理事务1,替换指定行数据2,获取$1 :字段分割 [Tips Notes,byRui]从光盘等安装媒介中isntallp -l -d /opt/ruiyhe or /dev/cd0 搜索所有的 ...

  9. Weinre - 远程调试工具

              Weinre  代表Web Inspector Remote,是一种远程调试工具.借助于网络,可以在PC上直接调试运行在移动设备上的远程页面,中文意思是远程Web检查器,有了Wei ...

  10. AndroidUI 视图动画-透明动画效果 (AlphaAnimation)

    1.新建一个Android项目,Activity添加一个按钮如下代码: <Button android:id="@+id/btnAiphaAnimation" android ...