diff --git a/default-configs/unicore32-softmmu.mak b/default-configs/unicore32-softmmu.mak
index 726a338..4d4fbfc 100644
--- a/default-configs/unicore32-softmmu.mak
+++ b/default-configs/unicore32-softmmu.mak
@@ -1,2 +1,3 @@
 # Default configuration for unicore32-softmmu
 CONFIG_PUV3=y
+CONFIG_PTIMER=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 6d49ce8..c835a49 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -68,6 +68,7 @@ hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
 
 # PKUnity SoC devices
 hw-obj-$(CONFIG_PUV3) += puv3_intc.o
+hw-obj-$(CONFIG_PUV3) += puv3_ost.o
 
 # PCI watchdog devices
 hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
diff --git a/hw/puv3.c b/hw/puv3.c
index 2870455..5a8a27c 100644
--- a/hw/puv3.c
+++ b/hw/puv3.c
@@ -46,6 +46,9 @@ static void puv3_soc_init(CPUUniCore32State *env)
     for (i = 0; i < PUV3_IRQS_NR; i++) {
         irqs[i] = qdev_get_gpio_in(dev, i);
     }
+
+    /* Initialize minimal necessary devices for kernel booting */
+    sysbus_create_simple("puv3_ost", PUV3_OST_BASE, irqs[PUV3_IRQS_OST0]);
 }
 
 static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size)
diff --git a/hw/puv3_ost.c b/hw/puv3_ost.c
new file mode 100644
index 0000000..dd30cad
--- /dev/null
+++ b/hw/puv3_ost.c
@@ -0,0 +1,151 @@
+/*
+ * OSTimer device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "sysbus.h"
+#include "ptimer.h"
+
+#undef DEBUG_PUV3
+#include "puv3.h"
+
+/* puv3 ostimer implementation. */
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    QEMUBH *bh;
+    qemu_irq irq;
+    ptimer_state *ptimer;
+
+    uint32_t reg_OSMR0;
+    uint32_t reg_OSCR;
+    uint32_t reg_OSSR;
+    uint32_t reg_OIER;
+} PUV3OSTState;
+
+static uint64_t puv3_ost_read(void *opaque, target_phys_addr_t offset,
+        unsigned size)
+{
+    PUV3OSTState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x10: /* Counter Register */
+        ret = s->reg_OSMR0 - (uint32_t)ptimer_get_count(s->ptimer);
+        break;
+    case 0x14: /* Status Register */
+        ret = s->reg_OSSR;
+        break;
+    case 0x1c: /* Interrupt Enable Register */
+        ret = s->reg_OIER;
+        break;
+    default:
+        DPRINTF("Bad offset %x\n", (int)offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+    return ret;
+}
+
+static void puv3_ost_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    PUV3OSTState *s = opaque;
+
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+    switch (offset) {
+    case 0x00: /* Match Register 0 */
+        s->reg_OSMR0 = value;
+        if (s->reg_OSMR0 > s->reg_OSCR) {
+            ptimer_set_count(s->ptimer, s->reg_OSMR0 - s->reg_OSCR);
+        } else {
+            ptimer_set_count(s->ptimer, s->reg_OSMR0 +
+                    (0xffffffff - s->reg_OSCR));
+        }
+        ptimer_run(s->ptimer, 2);
+        break;
+    case 0x14: /* Status Register */
+        assert(value == 0);
+        if (s->reg_OSSR) {
+            s->reg_OSSR = value;
+            qemu_irq_lower(s->irq);
+        }
+        break;
+    case 0x1c: /* Interrupt Enable Register */
+        s->reg_OIER = value;
+        break;
+    default:
+        DPRINTF("Bad offset %x\n", (int)offset);
+    }
+}
+
+static const MemoryRegionOps puv3_ost_ops = {
+    .read = puv3_ost_read,
+    .write = puv3_ost_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void puv3_ost_tick(void *opaque)
+{
+    PUV3OSTState *s = opaque;
+
+    DPRINTF("ost hit when ptimer counter from 0x%x to 0x%x!\n",
+            s->reg_OSCR, s->reg_OSMR0);
+
+    s->reg_OSCR = s->reg_OSMR0;
+    if (s->reg_OIER) {
+        s->reg_OSSR = 1;
+        qemu_irq_raise(s->irq);
+    }
+}
+
+static int puv3_ost_init(SysBusDevice *dev)
+{
+    PUV3OSTState *s = FROM_SYSBUS(PUV3OSTState, dev);
+
+    s->reg_OIER = 0;
+    s->reg_OSSR = 0;
+    s->reg_OSMR0 = 0;
+    s->reg_OSCR = 0;
+
+    sysbus_init_irq(dev, &s->irq);
+
+    s->bh = qemu_bh_new(puv3_ost_tick, s);
+    s->ptimer = ptimer_init(s->bh);
+    ptimer_set_freq(s->ptimer, 50 * 1000 * 1000);
+
+    memory_region_init_io(&s->iomem, &puv3_ost_ops, s, "puv3_ost",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_ost_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_ost_init;
+}
+
+static const TypeInfo puv3_ost_info = {
+    .name = "puv3_ost",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3OSTState),
+    .class_init = puv3_ost_class_init,
+};
+
+static void puv3_ost_register_type(void)
+{
+    type_register_static(&puv3_ost_info);
+}
+
+type_init(puv3_ost_register_type)
