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.
This commit is contained in:
parent
2aeabc0817
commit
a4fb8f496a
|
@ -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
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* ChipCity CC32RS512 ISO7816 Slave Controller emulation
|
||||
*
|
||||
* Copyright (C) 2012 Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* ChipCity CC32RS512 Smart Card emulation
|
||||
*
|
||||
* Copyright (C) 2012 Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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);
|
Reference in New Issue