From a4fb8f496a49976e8b6669a427d518c4dc98268d Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 4 Mar 2012 02:17:54 +0100 Subject: Initial support for the ChipCity CC32RS512 smart card The CC32RS512 is a Smart Card SoC, based on an SC100 ARM core, 18kByte RAM and 512kByte FLASH. It contains a number of integrated peripherals such as the ISO7816 Slave Controller, AES,DES, etc. This emulator is just emulating very basic behavior at this point. --- Makefile.target | 1 + hw/cc32_iso_slave.c | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/cc32rs512.c | 227 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 529 insertions(+) create mode 100644 hw/cc32_iso_slave.c create mode 100644 hw/cc32rs512.c diff --git a/Makefile.target b/Makefile.target index 343eb7849..7513e5d28 100644 --- a/Makefile.target +++ b/Makefile.target @@ -374,6 +374,7 @@ obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o obj-arm-y += pl041.o lm4549.o +obj-arm-y += cc32rs512.o cc32_iso_slave.o obj-arm-$(CONFIG_FDT) += device_tree.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o diff --git a/hw/cc32_iso_slave.c b/hw/cc32_iso_slave.c new file mode 100644 index 000000000..91e36a532 --- /dev/null +++ b/hw/cc32_iso_slave.c @@ -0,0 +1,301 @@ +/* + * ChipCity CC32RS512 ISO7816 Slave Controller emulation + * + * Copyright (C) 2012 Harald Welte + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw.h" +#include "sysbus.h" +#include "trace.h" +#include "qemu-char.h" +#include "qemu-error.h" + +enum iso_slave_reg { + ISOCON = 0x00, + ISOCON1 = 0x04, + ISOCON2 = 0x08, + ISOSTS = 0x0c, + ISOBRC = 0x10, + ISOBUF = 0x14, + ISODIO = 0x18, + ISOMSK = 0x1c, + ISODMACON = 0x30, + ISODMASTS = 0x34, + ISODMABFAD = 0x38, + ISODMABFLEN = 0x3c, + ISODMABFPT = 0x40, + ISODMAMSK = 0x44, + ISOTCON = 0x50, + ISOTDAT = 0x54, + ISOTRLD = 0x58, + ISOTMSK = 0x5c, + ISONULL = 0x60, +}; + +#define ISOCON_TR (1 << 5) +#define ISOCON_TACT (1 << 4) + +#define ISOSTS_TBE (1 << 0) +#define ISOSTS_RBF (1 << 1) +#define ISOSTS_PE (1 << 2) +#define ISOSTS_OE (1 << 3) + + +#define NUM_REGS 25 + +struct CC32IsoSlaveState { + SysBusDevice busdev; + MemoryRegion iomem; + CharDriverState *chr; + qemu_irq irq; + + uint32_t regs[NUM_REGS]; +}; +typedef struct CC32IsoSlaveState CC32IsoSlaveState; + +static void iso_slave_update_irq(CC32IsoSlaveState *s) +{ + unsigned int irq = 0; + + if (s->regs[ISOSTS>>2] & s->regs[ISOMSK>>2]) + irq = 1; + + qemu_set_irq(s->irq, irq); +} + +static uint64_t iso_slave_read(void *opaque, target_phys_addr_t addr, unsigned size) +{ + CC32IsoSlaveState *s = opaque; + uint32_t r; + uint32_t reg_idx = addr >> 2; + + if (addr & 3) { + error_report("cc32_iso_slave: unaligned read access"); + return 0; + } + + switch (addr) { + case ISOBUF: + r = s->regs[reg_idx]; + s->regs[ISOSTS>>2] &= ~ISOSTS_RBF; + default: + r = s->regs[reg_idx]; + break; + } + + return r; +} + +static void iso_slave_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + CC32IsoSlaveState *s = opaque; + uint32_t reg_idx = addr >> 2; + uint32_t val32 = value; + uint32_t mask = 0; + + switch (addr) { + case ISOCON: + mask = (1 << 5) | (1 << 7); + break; + case ISOCON1: + mask = 0x3F; + break; + case ISOCON2: + mask = (1 << 0) | (1 << 2) | (1 << 7); + break; + case ISOSTS: + if (val32 & (1 << 2)) { + val32 &= ~(1 << 2); + mask |= (1 << 2); + } + if (val32 & (1 << 3)) { + val32 &= ~(1 <<3); + mask |= (1 << 3); + } + break; + case ISOMSK: + mask = 0x8F; + break; + case ISOBRC: + case ISOBUF: + /* check if we are in transmitting mode */ + if (s->regs[ISOCON>>2] & ISOCON_TR) { + uint8_t ch = val32; + qemu_chr_fe_write(s->chr, &ch, 1); + s->regs[ISOCON>>2] &= ~ISOCON_TACT; + mask = 0; + } else + mask = 0xFF; + break; + case ISODIO: + case ISODMACON: + mask = 3; + break; + case ISODMASTS: + case ISODMAMSK: + mask = 0x07; + break; + case ISODMABFAD: + mask = 0xffffff; + break; + case ISODMABFLEN: + mask = 0x1FF; + break; + case ISODMABFPT: + mask = 0; + break; + case ISOTCON: + mask = 0x0F; + break; + case ISOTMSK: + mask = 1; + break; + case ISOTDAT: + case ISOTRLD: + mask = 0xFFFF; + break; + case ISONULL: + mask = 0xFF; + break; + default: + mask = 0; + break; + } + + s->regs[reg_idx] = (s->regs[reg_idx] & ~mask) | (val32 & mask); + + iso_slave_update_irq(s); +} + +static const MemoryRegionOps iso_slave_ops = { + .read = iso_slave_read, + .write = iso_slave_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void iso_slave_rx(void *opaque, const uint8_t *buf, int size) +{ + CC32IsoSlaveState *s = opaque; + + if (s->regs[ISOSTS>>2] & ISOSTS_RBF) + s->regs[ISOSTS>>2] |= ISOSTS_OE; + else + s->regs[ISOSTS>>2] |= ISOSTS_RBF; + + s->regs[ISOBUF>>2] = *buf; + + iso_slave_update_irq(s); +} + +static int iso_slave_can_rx(void *opaque) +{ + CC32IsoSlaveState *s = opaque; + + /* check if we are in receive mode */ + if (s->regs[ISOCON>>2] & ISOCON_TR) + return 0; + + return 1; +} + +static void iso_slave_event(void *opaque, int event) +{ +} + +static void iso_slave_reset(DeviceState *d) +{ + CC32IsoSlaveState *s = container_of(d, CC32IsoSlaveState, busdev.qdev); + int i; + + for (i = 0; i < NUM_REGS; i++) { + switch (i << 2) { + case ISOCON: + case ISOSTS: + case ISOBRC: + case ISODIO: + case ISOTMSK: + s->regs[i] = 0x00000001; + break; + case ISOCON1: + case ISODMAMSK: + s->regs[i] = 0x00000007; + break; + case ISONULL: + s->regs[i] = 0x00000060; + break; + default: + s->regs[i] = 0; + break; + } + } +} + +static int iso_slave_init(SysBusDevice *dev) +{ + CC32IsoSlaveState *s = FROM_SYSBUS(typeof(*s), dev); + + sysbus_init_irq(dev, &s->irq); + + memory_region_init_io(&s->iomem, &iso_slave_ops, s, "iso7816_slave", 0x64); + sysbus_init_mmio(dev, &s->iomem); + + s->chr = qemu_char_get_next_serial(); + if (s->chr) { + qemu_chr_add_handlers(s->chr, iso_slave_can_rx, iso_slave_rx, iso_slave_event, s); + } + + return 0; +} + +static const VMStateDescription vmstate_iso_slave = { + .name = "cc32-iso-slave", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, CC32IsoSlaveState, NUM_REGS), + VMSTATE_END_OF_LIST() + }, +}; + +static void iso_slave_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = iso_slave_init; + dc->reset = iso_slave_reset; + dc->vmsd = &vmstate_iso_slave; +} + +static TypeInfo iso_slave_info = { + .name = "cc32-iso-slave", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CC32IsoSlaveState), + .class_init = iso_slave_class_init, +}; + +static void iso_slave_register_types(void) +{ + type_register_static(&iso_slave_info); +} + +type_init(iso_slave_register_types); diff --git a/hw/cc32rs512.c b/hw/cc32rs512.c new file mode 100644 index 000000000..48dbd0207 --- /dev/null +++ b/hw/cc32rs512.c @@ -0,0 +1,227 @@ +/* + * ChipCity CC32RS512 Smart Card emulation + * + * Copyright (C) 2012 Harald Welte + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "sysbus.h" +#include "arm-misc.h" +#include "devices.h" +#include "boards.h" +#include "exec-memory.h" + +enum cc32_sysc_reg { + SCCM0 = 0x00, + SCSYS = 0x04, + SCCKOUT = 0x20, + SCRSTFLG = 0x28, + SCRSTEN = 0x2C, + SCSFTRST = 0x30, + SCRSTCON0 = 0x34, + SCRSTCON4 = 0x38, + SCSLEEP = 0x3C, + SCGCON = 0x40, + SCINTSTS = 0x44, + SCINTEN = 0x48, + SCGINT0 = 0x5C, + SCGLEV = 0x64, + SCWUT = 0x68, + SCCM4 = 0x7C, +}; + +#define NUM_REGS (SCCM4 + 4) + +typedef struct cc32_sysc_state +{ + SysBusDevice busdev; + MemoryRegion iomem; + + uint32_t level; + uint32_t irq_enabled; + + qemu_irq parent_irq; + qemu_irq parent_fiq; + +} cc32_sysc_state; + +#define FIQ_MASK (1 << 8) +#define IRQ_MASK ~FIQ_MASK + +static void cc32_sysc_update(cc32_sysc_state *s) +{ + uint32_t flags; + + flags = (s->level & s->irq_enabled & IRQ_MASK); + qemu_set_irq(s->parent_irq, flags != 0); + + flags = (s->level & s->irq_enabled & FIQ_MASK); + qemu_set_irq(s->parent_fiq, flags != 0); +} + +static void cc32_sysc_set_irq(void *opaque, int irq, int level) +{ + cc32_sysc_state *s = (cc32_sysc_state *)opaque; + + if (level) + s->level |= (1 << irq); + else + s->level &= ~(1 << irq); + + cc32_sysc_update(s); +} + +static uint64_t cc32_sysc_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + cc32_sysc_state *s = (cc32_sysc_state *)opaque; + + switch (offset) { + case SCINTSTS: + return s->level & s->irq_enabled; + case SCINTEN: + return s->irq_enabled; + } + + return 0; +} + +static void cc32_sysc_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + cc32_sysc_state *s = (cc32_sysc_state *)opaque; + switch (offset) { + case SCINTEN: + s->irq_enabled = value; + break; + } + cc32_sysc_update(s); +} + +static const MemoryRegionOps cc32_sysc_ops = { + .read = cc32_sysc_read, + .write = cc32_sysc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int cc32_sysc_init(SysBusDevice *dev) +{ + cc32_sysc_state *s = FROM_SYSBUS(cc32_sysc_state, dev); + + qdev_init_gpio_in(&dev->qdev, cc32_sysc_set_irq, 32); + sysbus_init_irq(dev, &s->parent_irq); + sysbus_init_irq(dev, &s->parent_fiq); + memory_region_init_io(&s->iomem, &cc32_sysc_ops, s, "cc32-sysc", 8); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void cc32_sysc_reset(DeviceState *d) +{ + cc32_sysc_state *s = container_of(d, cc32_sysc_state, busdev.qdev); + + s->irq_enabled = 0xFFFEF7FF; + /* FIXME */ +} + +static void cc32_sysc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = cc32_sysc_init; + dc->reset = cc32_sysc_reset; +} + +static TypeInfo cc32_sysc_info = { + .name = "cc32-sysc", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(cc32_sysc_state), + .class_init = cc32_sysc_class_init, +}; + +static void cc32_register_types(void) +{ + type_register_static(&cc32_sysc_info); +} + +type_init(cc32_register_types) + + + +struct arm_boot_info cc32rs512_binfo; + +static void cc32rs512_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + CPUState *env; + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *ram = g_new(MemoryRegion, 1); + MemoryRegion *rsa_ram = g_new(MemoryRegion, 1); + MemoryRegion *flash = g_new(MemoryRegion, 1); + qemu_irq *cpu_irq; + qemu_irq pic[32]; + DeviceState *dev; + int i; + + if (!cpu_model) + cpu_model = "arm926"; + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + + memory_region_init_ram(flash, "cc32rs512.flash", 512*1024); + memory_region_add_subregion(sysmem, 0, flash); + + memory_region_init_ram(ram, "cc32rs512.ram", 16*1024); + memory_region_add_subregion(sysmem, 0xc0000, ram); + + memory_region_init_ram(rsa_ram, "cc32rs512.rsa_ram", 2*1024); + memory_region_add_subregion(sysmem, 0xd4000, rsa_ram); + + cc32rs512_binfo.ram_size = ram_size; + cc32rs512_binfo.kernel_filename = kernel_filename; + + cpu_irq = arm_pic_init_cpu(env); + dev = sysbus_create_varargs("cc32-sysc", 0x0F0000, + cpu_irq[ARM_PIC_CPU_IRQ], + cpu_irq[ARM_PIC_CPU_FIQ], NULL); + + for (i = 0; i < 32; i++) + pic[i] = qdev_get_gpio_in(dev, i); + + sysbus_create_simple("cc32-iso-slave", 0x0F8800, pic[8]); + + arm_load_kernel(env, &cc32rs512_binfo); +} + +static QEMUMachine cc32rs512_machine = { + .name = "cc32rs512", + .desc = "Chip City Smart Card (SC100)", + .init = cc32rs512_init, + .max_cpus = 1, +}; + +static void cc32rs512_machine_init(void) +{ + qemu_register_machine(&cc32rs512_machine); +} + +machine_init(cc32rs512_machine_init); -- cgit v1.2.3