[虚拟化/云][全栈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
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外设(二)的更多相关文章
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(九)
目的 1. 使用verilog/vhdl设计一个PCI的watchdog设备. 2. 通过systemverilog 写testbench. 很久之前研究过AC97的verilog代码.但是很久没用v ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(四)
通过前面的操作,我们已经可以创建一个带有我们自己的PCI的watchdog外设qemu 虚拟机了. 目的: 1. 了解我们的外设情况. 2. 为在guest中开发我们自己的linux PCI驱动程序做 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(一)
目的: 结合现在比较流行的技术,通过一个demo 展示一个全栈式设计的各种技能. 一个全栈式的工程师,应该能设计通过verilog/VHDL做logical设计.能写内核驱动,能架站. 要熟悉veri ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(六)
目的: 1. 为我们自己的watchdog写一个驱动 步骤: 通过之前的介绍,我们很容易猜想到写我们基于PCI的watchdog驱动,可以分2个步骤. 1. 探测加载PCI设备 这部分代码跟我们的设备 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(八)
目的: 1. 通过网页读取watchdog的信息 2. 通过网页设置watchdog 准备工作: 1. 选择一个web框架,选用 cherrypy $ sudo apt-get install pyt ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(五)
目的: 1. 了解PCI的基本知识,为完成watchdog的设备做准备. 准备知识: 简单的说,PCI 设备分3个空间. 配置空间,IO空间,内存地址空间. PCI设备厂家决定了外设是使用IO空间还是 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(七)
目标: 1. 完成最终的设备驱动,增加具体的watchdog设备操作的代码. 测试代码: 代码最终实现见cwd_demo.c 代码只实现了read与write. 没有实现ioctl. 因此,我们可以 ...
- [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(三)
我们已经设计了一个基于qemu的watchdog了.下一步工作就是创建一个含有我们的watchdog的虚拟计算机器了. 准备工作: 1. 使用virt-manager或者virsh创建一个虚拟机器. ...
- 《全栈营销之如何制作个人博客》之二:php环境安装及个人博客后台搭建 让你的博客跑起来
上一节我们讲了个人博客用什么开发语言,用什么CMS系统,从这一节我们就开始真正的干货,这一节我们讨论一下PHP环境的安装,及个人博客后台的搭建,让你的博客在正常的PHP环境中运行起来,你就可以进行后台 ...
随机推荐
- Java split方法源码分析
Java split方法源码分析 public String[] split(CharSequence input [, int limit]) { int index = 0; // 指针 bool ...
- 初学swift笔记字典、数组(四)
import Foundation //字典 元素顺序是无序的 //1.字典元素是键值对 (key:value) //key 一定是可哈希的 string\int\bool var dic1=[&qu ...
- MySql的一些问题
问题1:卸载重装mysql时,报1045和2003错误. 解决:点击skip,跳过这个错误.进到my.ini,在mysqld下面加一句:skip-grant-tables,保存.重启mysql服务,在 ...
- System V 机制(转)
引言 UNIX 内核管理的进程自主地操作,从而产生更稳定的系统.然而,每个开发人员最终都会遇到这样的情况,即其中一组进程需要与另一组进程通信,也许是为了交换数据或发送命令.这种通信称为进程间通信(In ...
- winform制作自定义控件(入门)
原文链接:http://blog.csdn.net/bychentufeiyang/article/details/7081402 与原文基本一致,只是例子变成VS2012环境,语言采用博主常用的 ...
- js返回当前时间的毫秒数
Date.now(); +new Date(); new Date().getTime();
- 转:JavaScript函数式编程(三)
转:JavaScript函数式编程(三) 作者: Stark伟 这是完结篇了. 在第二篇文章里,我们介绍了 Maybe.Either.IO 等几种常见的 Functor,或许很多看完第二篇文章的人都会 ...
- Compiling Qt 5.5.1 (With Qtwebkit) With Visual Studio 2015
I usually avoid writing articles about building a specific version of a software project but this ti ...
- FirewallD 详解
在CentOS7开始,默认是没有iptables的,而是使用了firewall防火墙.与时俱进,简单的整理了一下firewall的使用方法.关于详细的介绍参考官网,就不搬字了.这个网站有中文选项.可以 ...
- vc++深入跟踪MFC程序的执行流程
在MFC程序设计的学习过程中最令人感到难受,甚至于有时会动摇学习者信心的就是一种对于程序的一切细节都没有控制权的感觉.这种感觉来源于学习者不知道一个MFC程序是如何运行起来的(即一个MFC程序的执行流 ...