target-arm queue:

* raspi2: implement RNG module
  * raspi2: implement new SD card controller (but don't wire it up)
  * sdhci: bugfixes for block transfers
  * virt: fix cpu object reference leak
  * Add missing fp_access_check() to aarch64 crypto instructions
  * cputlb: Don't assume do_unassigned_access() never returns
  * virt: Add a user option to disallow ITS instantiation
  * i.MX timers: fix reset handling
  * ARMv7M NVIC: rewrite to fix broken priority handling and masking
  * exynos: Fix proper mapping of CPUs by providing real cluster ID
  * exynos: Fix Linux kernel division by zero for PLLs
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJYtW/TAAoJEDwlJe0UNgzezv4P/3j+WOVgVlNL8AQ3RFEzzzz4
 IszdrQIFcZ5ICT3MDgH/JMjkpj/C13eGo9eiIFlOvVjtsLlneW10frEB6SGP4ype
 KpFDHji0cm9MT7gdbgbWbextGU8w7xWV43JmSmEuOxkF/r64u/Ap3CXudB58A+Rv
 NvbJMHkkR5Q0MIDA4EkOCLn/Ihh78sd99p8+EV3Gu89KiiB4xRf9D3k/O+Sdh58L
 yvPNat0tjJolzZkAUf6RieFN1F7oBXazR13+E8fDy5OTr25K+S7mehBwSJtQ7dGo
 VjhR7eMJdyyzi+l+OezQFCUmZI9pENcDdhspSl2mOkPRrQi4gwjEszPcmcNhCNGQ
 mguQjk7f5KHtLDDzL1HFr+4sKZdoptXZC18JupjN9oCHJvMq4MDHJaUH0bwrHals
 GhE7cM3aNg8ItJu694ruMLY13Z0+B+TmSLFktRYrjJe3qJEfOQE4EKWXXUZaEe5j
 L13HPP4nInAUU7kvpuepiYHiR4zBTTgEqRBVdQ/qCkLSuO/EH2TbT9u6pifAtI1S
 OkBidnbatWflUwLMMa6jt7ZUx+yDsH7y7C1WxmytnPzKudMMOZ5MxI54yLgEEFTs
 SoelwzfSZb2PlOw3h3UwyRDz3CehkDMUMqzIoqF7Wn/UVb6GHvldq/eVpKOOxtG7
 nVTTYBFuSil0LV/LST4X
 =3qLp
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170228' into staging

target-arm queue:
 * raspi2: implement RNG module
 * raspi2: implement new SD card controller (but don't wire it up)
 * sdhci: bugfixes for block transfers
 * virt: fix cpu object reference leak
 * Add missing fp_access_check() to aarch64 crypto instructions
 * cputlb: Don't assume do_unassigned_access() never returns
 * virt: Add a user option to disallow ITS instantiation
 * i.MX timers: fix reset handling
 * ARMv7M NVIC: rewrite to fix broken priority handling and masking
 * exynos: Fix proper mapping of CPUs by providing real cluster ID
 * exynos: Fix Linux kernel division by zero for PLLs

# gpg: Signature made Tue 28 Feb 2017 12:40:51 GMT
# gpg:                using RSA key 0x3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20170228: (27 commits)
  hw/arm/exynos: Fix proper mapping of CPUs by providing real cluster ID
  hw/arm/exynos: Fix Linux kernel division by zero for PLLs
  bcm2835_sdhost: add bcm2835 sdhost controller
  armv7m: Allow SHCSR writes to change pending and active bits
  armv7m: Raise correct kind of UsageFault for attempts to execute ARM code
  armv7m: Check exception return consistency
  armv7m: Extract "exception taken" code into functions
  armv7m: VECTCLRACTIVE and VECTRESET are UNPREDICTABLE
  armv7m: Simpler and faster exception start
  armv7m: Remove unused armv7m_nvic_acknowledge_irq() return value
  armv7m: Escalate exceptions to HardFault if necessary
  arm: gic: Remove references to NVIC
  armv7m: Fix condition check for taking exceptions
  armv7m: Rewrite NVIC to not use any GIC code
  armv7m: Implement reading and writing of PRIGROUP
  armv7m: Rename nvic_state to NVICState
  ARM i.MX timers: fix reset handling
  hw/arm/virt: Add a user option to disallow ITS instantiation
  cputlb: Don't assume do_unassigned_access() never returns
  Add missing fp_access_check() to aarch64 crypto instructions
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-02-28 14:50:15 +00:00
commit 1bbe5dc66b
26 changed files with 1894 additions and 328 deletions

View File

@ -769,14 +769,13 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr)
pd = iotlbentry->addr & ~TARGET_PAGE_MASK;
mr = iotlb_to_region(cpu, pd, iotlbentry->attrs);
if (memory_region_is_unassigned(mr)) {
CPUClass *cc = CPU_GET_CLASS(cpu);
if (cc->do_unassigned_access) {
cc->do_unassigned_access(cpu, addr, false, true, 0, 4);
} else {
report_bad_exec(cpu, addr);
exit(1);
}
cpu_unassigned_access(cpu, addr, false, true, 0, 4);
/* The CPU's unassigned access hook might have longjumped out
* with an exception. If it didn't (or there was no hook) then
* we can't proceed further.
*/
report_bad_exec(cpu, addr);
exit(1);
}
p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend);
return qemu_ram_addr_from_host_nofail(p);

View File

@ -86,6 +86,11 @@ static void bcm2835_peripherals_init(Object *obj)
object_property_add_const_link(OBJECT(&s->property), "dma-mr",
OBJECT(&s->gpu_bus_mr), &error_abort);
/* Random Number Generator */
object_initialize(&s->rng, sizeof(s->rng), TYPE_BCM2835_RNG);
object_property_add_child(obj, "rng", OBJECT(&s->rng), NULL);
qdev_set_parent_bus(DEVICE(&s->rng), sysbus_get_default());
/* Extended Mass Media Controller */
object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI);
object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL);
@ -226,6 +231,16 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0,
qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY));
/* Random Number Generator */
object_property_set_bool(OBJECT(&s->rng), true, "realized", &err);
if (err) {
error_propagate(errp, err);
return;
}
memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
/* Extended Mass Media Controller */
object_property_set_int(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
&err);

View File

@ -24,6 +24,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu-common.h"
#include "qemu/log.h"
#include "cpu.h"
#include "hw/boards.h"
#include "sysemu/sysemu.h"
@ -74,6 +75,9 @@
/* PMU SFR base address */
#define EXYNOS4210_PMU_BASE_ADDR 0x10020000
/* Clock controller SFR base address */
#define EXYNOS4210_CLK_BASE_ADDR 0x10030000
/* Display controllers (FIMD) */
#define EXYNOS4210_FIMD0_BASE_ADDR 0x11C00000
@ -138,6 +142,16 @@ void exynos4210_write_secondary(ARMCPU *cpu,
info->smp_loader_start);
}
static uint64_t exynos4210_calc_affinity(int cpu)
{
uint64_t mp_affinity;
/* Exynos4210 has 0x9 as cluster ID */
mp_affinity = (0x9 << ARM_AFF1_SHIFT) | cpu;
return mp_affinity;
}
Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
unsigned long ram_size)
{
@ -163,6 +177,8 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
}
s->cpu[n] = ARM_CPU(cpuobj);
object_property_set_int(cpuobj, exynos4210_calc_affinity(n),
"mp-affinity", &error_abort);
object_property_set_int(cpuobj, EXYNOS4210_SMP_PRIVATE_BASE_ADDR,
"reset-cbar", &error_abort);
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
@ -297,6 +313,8 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem,
*/
sysbus_create_simple("exynos4210.pmu", EXYNOS4210_PMU_BASE_ADDR, NULL);
sysbus_create_simple("exynos4210.clk", EXYNOS4210_CLK_BASE_ADDR, NULL);
/* PWM */
sysbus_create_varargs("exynos4210.pwm", EXYNOS4210_PWM_BASE_ADDR,
s->irq_table[exynos4210_get_irq(22, 0)],

View File

@ -535,7 +535,6 @@ static void create_v2m(VirtMachineState *vms, qemu_irq *pic)
static void create_gic(VirtMachineState *vms, qemu_irq *pic)
{
/* We create a standalone GIC */
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
DeviceState *gicdev;
SysBusDevice *gicbusdev;
const char *gictype;
@ -605,7 +604,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
fdt_add_gic_node(vms);
if (type == 3 && !vmc->no_its) {
if (type == 3 && vms->its) {
create_its(vms, gicdev);
} else if (type == 2) {
create_v2m(vms, pic);
@ -1378,6 +1377,7 @@ static void machvirt_init(MachineState *machine)
}
object_property_set_bool(cpuobj, true, "realized", NULL);
object_unref(cpuobj);
}
fdt_add_timer_nodes(vms);
fdt_add_cpu_nodes(vms);
@ -1480,6 +1480,20 @@ static void virt_set_highmem(Object *obj, bool value, Error **errp)
vms->highmem = value;
}
static bool virt_get_its(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
return vms->its;
}
static void virt_set_its(Object *obj, bool value, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
vms->its = value;
}
static char *virt_get_gic_version(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
@ -1540,6 +1554,7 @@ type_init(machvirt_machine_init);
static void virt_2_9_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms);
/* EL3 is disabled by default on virt: this makes us consistent
* between KVM and TCG for this board, and it also allows us to
@ -1579,6 +1594,19 @@ static void virt_2_9_instance_init(Object *obj)
"Set GIC version. "
"Valid values are 2, 3 and host", NULL);
if (vmc->no_its) {
vms->its = false;
} else {
/* Default allows ITS instantiation */
vms->its = true;
object_property_add_bool(obj, "its", virt_get_its,
virt_set_its, NULL);
object_property_set_description(obj, "its",
"Set on/off to enable/disable "
"ITS instantiation",
NULL);
}
vms->memmap = a15memmap;
vms->irqmap = a15irqmap;
}

View File

@ -156,17 +156,6 @@ static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
}
}
static void gic_set_irq_nvic(GICState *s, int irq, int level,
int cm, int target)
{
if (level) {
GIC_SET_LEVEL(irq, cm);
GIC_SET_PENDING(irq, target);
} else {
GIC_CLEAR_LEVEL(irq, cm);
}
}
static void gic_set_irq_generic(GICState *s, int irq, int level,
int cm, int target)
{
@ -214,8 +203,6 @@ static void gic_set_irq(void *opaque, int irq, int level)
if (s->revision == REV_11MPCORE) {
gic_set_irq_11mpcore(s, irq, level, cm, target);
} else if (s->revision == REV_NVIC) {
gic_set_irq_nvic(s, irq, level, cm, target);
} else {
gic_set_irq_generic(s, irq, level, cm, target);
}
@ -367,7 +354,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs)
return 1023;
}
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
if (s->revision == REV_11MPCORE) {
/* Clear pending flags for both level and edge triggered interrupts.
* Level triggered IRQs will be reasserted once they become inactive.
*/
@ -589,11 +576,6 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs)
DPRINTF("Set %d pending mask %x\n", irq, cm);
GIC_SET_PENDING(irq, cm);
}
} else if (s->revision == REV_NVIC) {
if (GIC_TEST_LEVEL(irq, cm)) {
DPRINTF("Set nvic %d pending mask %x\n", irq, cm);
GIC_SET_PENDING(irq, cm);
}
}
group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm);
@ -768,7 +750,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
} else if (offset < 0xf10) {
goto bad_reg;
} else if (offset < 0xf30) {
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
if (s->revision == REV_11MPCORE) {
goto bad_reg;
}
@ -802,9 +784,6 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs)
case 2:
res = gic_id_gicv2[(offset - 0xfd0) >> 2];
break;
case REV_NVIC:
/* Shouldn't be able to get here */
abort();
default:
res = 0;
}
@ -1028,7 +1007,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
continue; /* Ignore Non-secure access of Group0 IRQ */
}
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
if (s->revision == REV_11MPCORE) {
if (value & (1 << (i * 2))) {
GIC_SET_MODEL(irq + i);
} else {
@ -1046,7 +1025,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
goto bad_reg;
} else if (offset < 0xf20) {
/* GICD_CPENDSGIRn */
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
if (s->revision == REV_11MPCORE) {
goto bad_reg;
}
irq = (offset - 0xf10);
@ -1060,7 +1039,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
}
} else if (offset < 0xf30) {
/* GICD_SPENDSGIRn */
if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
if (s->revision == REV_11MPCORE) {
goto bad_reg;
}
irq = (offset - 0xf20);

View File

@ -99,9 +99,7 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
* [N+32..N+63] PPIs for CPU 1
* ...
*/
if (s->revision != REV_NVIC) {
i += (GIC_INTERNAL * s->num_cpu);
}
i += (GIC_INTERNAL * s->num_cpu);
qdev_init_gpio_in(DEVICE(s), handler, i);
for (i = 0; i < s->num_cpu; i++) {
@ -121,16 +119,12 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler,
memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
if (s->revision != REV_NVIC) {
/* This is the main CPU interface "for this core". It is always
* present because it is required by both software emulation and KVM.
* NVIC is not handled here because its CPU interface is different,
* neither it can use KVM.
*/
memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100);
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
}
/* This is the main CPU interface "for this core". It is always
* present because it is required by both software emulation and KVM.
*/
memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL,
s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100);
sysbus_init_mmio(sbd, &s->cpuiomem[0]);
}
static void arm_gic_common_realize(DeviceState *dev, Error **errp)
@ -162,7 +156,7 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp)
}
if (s->security_extn &&
(s->revision == REV_11MPCORE || s->revision == REV_NVIC)) {
(s->revision == REV_11MPCORE)) {
error_setg(errp, "this GIC revision does not implement "
"the security extensions");
return;
@ -255,7 +249,6 @@ static Property arm_gic_common_properties[] = {
DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32),
/* Revision can be 1 or 2 for GIC architecture specification
* versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC.
* (Internally, 0xffffffff also indicates "not a GIC but an NVIC".)
*/
DEFINE_PROP_UINT32("revision", GICState, revision, 1),
/* True if the GIC should implement the security extensions */

File diff suppressed because it is too large Load Diff

View File

@ -25,9 +25,7 @@
#define ALL_CPU_MASK ((unsigned)(((1 << GIC_NCPU) - 1)))
/* The NVIC has 16 internal vectors. However these are not exposed
through the normal GIC interface. */
#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0)
#define GIC_BASE_IRQ 0
#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm)
#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm)
@ -75,7 +73,6 @@
/* The special cases for the revision property: */
#define REV_11MPCORE 0
#define REV_NVIC 0xffffffff
void gic_set_pending_private(GICState *s, int cpu, int irq);
uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs);
@ -87,7 +84,7 @@ void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val,
static inline bool gic_test_pending(GICState *s, int irq, int cm)
{
if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) {
if (s->revision == REV_11MPCORE) {
return s->irq_state[irq].pending & cm;
} else {
/* Edge-triggered interrupts are marked pending on a rising edge, but

View File

@ -161,3 +161,18 @@ gicv3_redist_write(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size,
gicv3_redist_badwrite(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor %x write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d: error"
gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor %x interrupt %d level changed to %d"
gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor %x pending SGI %d"
# hw/intc/armv7m_nvic.c
nvic_recompute_state(int vectpending, int exception_prio) "NVIC state recomputed: vectpending %d exception_prio %d"
nvic_set_prio(int irq, uint8_t prio) "NVIC set irq %d priority %d"
nvic_irq_update(int vectpending, int pendprio, int exception_prio, int level) "NVIC vectpending %d pending prio %d exception_prio %d: setting irq line to %d"
nvic_escalate_prio(int irq, int irqprio, int runprio) "NVIC escalating irq %d to HardFault: insufficient priority %d >= %d"
nvic_escalate_disabled(int irq) "NVIC escalating irq %d to HardFault: disabled"
nvic_set_pending(int irq, int en, int prio) "NVIC set pending irq %d (enabled: %d priority %d)"
nvic_clear_pending(int irq, int en, int prio) "NVIC clear pending irq %d (enabled: %d priority %d)"
nvic_set_pending_level(int irq) "NVIC set pending: irq %d higher prio than vectpending: setting irq line to 1"
nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (prio %d)"
nvic_complete_irq(int irq) "NVIC complete IRQ %d"
nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d"
nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"

View File

@ -26,7 +26,7 @@ obj-$(CONFIG_IVSHMEM) += ivshmem.o
obj-$(CONFIG_REALVIEW) += arm_sysctl.o
obj-$(CONFIG_NSERIES) += cbus.o
obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o
obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o exynos4210_clk.o
obj-$(CONFIG_IMX) += imx_ccm.o
obj-$(CONFIG_IMX) += imx31_ccm.o
obj-$(CONFIG_IMX) += imx25_ccm.o
@ -42,6 +42,7 @@ obj-$(CONFIG_OMAP) += omap_sdrc.o
obj-$(CONFIG_OMAP) += omap_tap.o
obj-$(CONFIG_RASPI) += bcm2835_mbox.o
obj-$(CONFIG_RASPI) += bcm2835_property.o
obj-$(CONFIG_RASPI) += bcm2835_rng.o
obj-$(CONFIG_SLAVIO) += slavio_misc.o
obj-$(CONFIG_ZYNQ) += zynq_slcr.o
obj-$(CONFIG_ZYNQ) += zynq-xadc.o

149
hw/misc/bcm2835_rng.c Normal file
View File

@ -0,0 +1,149 @@
/*
* BCM2835 Random Number Generator emulation
*
* Copyright (C) 2017 Marcin Chojnacki <marcinch7@gmail.com>
*
* 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/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "crypto/random.h"
#include "hw/misc/bcm2835_rng.h"
static uint32_t get_random_bytes(void)
{
uint32_t res;
Error *err = NULL;
if (qcrypto_random_bytes((uint8_t *)&res, sizeof(res), &err) < 0) {
/* On failure we don't want to return the guest a non-random
* value in case they're really using it for cryptographic
* purposes, so the best we can do is die here.
* This shouldn't happen unless something's broken.
* In theory we could implement this device's full FIFO
* and interrupt semantics and then just stop filling the
* FIFO. That's a lot of work, though, so we assume any
* errors are systematic problems and trust that if we didn't
* fail as the guest inited then we won't fail later on
* mid-run.
*/
error_report_err(err);
exit(1);
}
return res;
}
static uint64_t bcm2835_rng_read(void *opaque, hwaddr offset,
unsigned size)
{
BCM2835RngState *s = (BCM2835RngState *)opaque;
uint32_t res = 0;
assert(size == 4);
switch (offset) {
case 0x0: /* rng_ctrl */
res = s->rng_ctrl;
break;
case 0x4: /* rng_status */
res = s->rng_status | (1 << 24);
break;
case 0x8: /* rng_data */
res = get_random_bytes();
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"bcm2835_rng_read: Bad offset %x\n",
(int)offset);
res = 0;
break;
}
return res;
}
static void bcm2835_rng_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
BCM2835RngState *s = (BCM2835RngState *)opaque;
assert(size == 4);
switch (offset) {
case 0x0: /* rng_ctrl */
s->rng_ctrl = value;
break;
case 0x4: /* rng_status */
/* we shouldn't let the guest write to bits [31..20] */
s->rng_status &= ~0xFFFFF; /* clear 20 lower bits */
s->rng_status |= value & 0xFFFFF; /* set them to new value */
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"bcm2835_rng_write: Bad offset %x\n",
(int)offset);
break;
}
}
static const MemoryRegionOps bcm2835_rng_ops = {
.read = bcm2835_rng_read,
.write = bcm2835_rng_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_bcm2835_rng = {
.name = TYPE_BCM2835_RNG,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(rng_ctrl, BCM2835RngState),
VMSTATE_UINT32(rng_status, BCM2835RngState),
VMSTATE_END_OF_LIST()
}
};
static void bcm2835_rng_init(Object *obj)
{
BCM2835RngState *s = BCM2835_RNG(obj);
memory_region_init_io(&s->iomem, obj, &bcm2835_rng_ops, s,
TYPE_BCM2835_RNG, 0x10);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
}
static void bcm2835_rng_reset(DeviceState *dev)
{
BCM2835RngState *s = BCM2835_RNG(dev);
s->rng_ctrl = 0;
s->rng_status = 0;
}
static void bcm2835_rng_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = bcm2835_rng_reset;
dc->vmsd = &vmstate_bcm2835_rng;
}
static TypeInfo bcm2835_rng_info = {
.name = TYPE_BCM2835_RNG,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BCM2835RngState),
.class_init = bcm2835_rng_class_init,
.instance_init = bcm2835_rng_init,
};
static void bcm2835_rng_register_types(void)
{
type_register_static(&bcm2835_rng_info);
}
type_init(bcm2835_rng_register_types)

164
hw/misc/exynos4210_clk.c Normal file
View File

@ -0,0 +1,164 @@
/*
* Exynos4210 Clock Controller Emulation
*
* Copyright (c) 2017 Krzysztof Kozlowski <krzk@kernel.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 of the License, or
* (at your option) any later version.
*
* 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 "qemu/osdep.h"
#include "hw/sysbus.h"
#include "qemu/log.h"
#define TYPE_EXYNOS4210_CLK "exynos4210.clk"
#define EXYNOS4210_CLK(obj) \
OBJECT_CHECK(Exynos4210ClkState, (obj), TYPE_EXYNOS4210_CLK)
#define CLK_PLL_LOCKED BIT(29)
#define EXYNOS4210_CLK_REGS_MEM_SIZE 0x15104
typedef struct Exynos4210Reg {
const char *name; /* for debug only */
uint32_t offset;
uint32_t reset_value;
} Exynos4210Reg;
/* Clock controller register base: 0x10030000 */
static const Exynos4210Reg exynos4210_clk_regs[] = {
{"EPLL_LOCK", 0xc010, 0x00000fff},
{"VPLL_LOCK", 0xc020, 0x00000fff},
{"EPLL_CON0", 0xc110, 0x00300301 | CLK_PLL_LOCKED},
{"EPLL_CON1", 0xc114, 0x00000000},
{"VPLL_CON0", 0xc120, 0x00240201 | CLK_PLL_LOCKED},
{"VPLL_CON1", 0xc124, 0x66010464},
{"APLL_LOCK", 0x14000, 0x00000fff},
{"MPLL_LOCK", 0x14004, 0x00000fff},
{"APLL_CON0", 0x14100, 0x00c80601 | CLK_PLL_LOCKED},
{"APLL_CON1", 0x14104, 0x0000001c},
{"MPLL_CON0", 0x14108, 0x00c80601 | CLK_PLL_LOCKED},
{"MPLL_CON1", 0x1410c, 0x0000001c},
};
#define EXYNOS4210_REGS_NUM ARRAY_SIZE(exynos4210_clk_regs)
typedef struct Exynos4210ClkState {
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t reg[EXYNOS4210_REGS_NUM];
} Exynos4210ClkState;
static uint64_t exynos4210_clk_read(void *opaque, hwaddr offset,
unsigned size)
{
const Exynos4210ClkState *s = (Exynos4210ClkState *)opaque;
const Exynos4210Reg *regs = exynos4210_clk_regs;
unsigned int i;
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
if (regs->offset == offset) {
return s->reg[i];
}
regs++;
}
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read offset 0x%04x\n",
__func__, (uint32_t)offset);
return 0;
}
static void exynos4210_clk_write(void *opaque, hwaddr offset,
uint64_t val, unsigned size)
{
Exynos4210ClkState *s = (Exynos4210ClkState *)opaque;
const Exynos4210Reg *regs = exynos4210_clk_regs;
unsigned int i;
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
if (regs->offset == offset) {
s->reg[i] = val;
return;
}
regs++;
}
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write offset 0x%04x\n",
__func__, (uint32_t)offset);
}
static const MemoryRegionOps exynos4210_clk_ops = {
.read = exynos4210_clk_read,
.write = exynos4210_clk_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false
}
};
static void exynos4210_clk_reset(DeviceState *dev)
{
Exynos4210ClkState *s = EXYNOS4210_CLK(dev);
unsigned int i;
/* Set default values for registers */
for (i = 0; i < EXYNOS4210_REGS_NUM; i++) {
s->reg[i] = exynos4210_clk_regs[i].reset_value;
}
}
static void exynos4210_clk_init(Object *obj)
{
Exynos4210ClkState *s = EXYNOS4210_CLK(obj);
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
/* memory mapping */
memory_region_init_io(&s->iomem, obj, &exynos4210_clk_ops, s,
TYPE_EXYNOS4210_CLK, EXYNOS4210_CLK_REGS_MEM_SIZE);
sysbus_init_mmio(dev, &s->iomem);
}
static const VMStateDescription exynos4210_clk_vmstate = {
.name = TYPE_EXYNOS4210_CLK,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(reg, Exynos4210ClkState, EXYNOS4210_REGS_NUM),
VMSTATE_END_OF_LIST()
}
};
static void exynos4210_clk_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = exynos4210_clk_reset;
dc->vmsd = &exynos4210_clk_vmstate;
}
static const TypeInfo exynos4210_clk_info = {
.name = TYPE_EXYNOS4210_CLK,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210ClkState),
.instance_init = exynos4210_clk_init,
.class_init = exynos4210_clk_class_init,
};
static void exynos4210_clk_register(void)
{
qemu_log_mask(LOG_GUEST_ERROR, "Clock init\n");
type_register_static(&exynos4210_clk_info);
}
type_init(exynos4210_clk_register)

View File

@ -6,3 +6,4 @@ common-obj-$(CONFIG_SDHCI) += sdhci.o
obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o
obj-$(CONFIG_OMAP) += omap_mmc.o
obj-$(CONFIG_PXA2XX) += pxa2xx_mmci.o
obj-$(CONFIG_RASPI) += bcm2835_sdhost.o

429
hw/sd/bcm2835_sdhost.c Normal file
View File

@ -0,0 +1,429 @@
/*
* Raspberry Pi (BCM2835) SD Host Controller
*
* Copyright (c) 2017 Antfield SAS
*
* Authors:
* Clement Deschamps <clement.deschamps@antfield.fr>
* Luc Michel <luc.michel@antfield.fr>
*
* 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/osdep.h"
#include "qemu/log.h"
#include "sysemu/blockdev.h"
#include "hw/sd/bcm2835_sdhost.h"
#define TYPE_BCM2835_SDHOST_BUS "bcm2835-sdhost-bus"
#define BCM2835_SDHOST_BUS(obj) \
OBJECT_CHECK(SDBus, (obj), TYPE_BCM2835_SDHOST_BUS)
#define SDCMD 0x00 /* Command to SD card - 16 R/W */
#define SDARG 0x04 /* Argument to SD card - 32 R/W */
#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */
#define SDCDIV 0x0c /* Start value for clock divider - 11 R/W */
#define SDRSP0 0x10 /* SD card rsp (31:0) - 32 R */
#define SDRSP1 0x14 /* SD card rsp (63:32) - 32 R */
#define SDRSP2 0x18 /* SD card rsp (95:64) - 32 R */
#define SDRSP3 0x1c /* SD card rsp (127:96) - 32 R */
#define SDHSTS 0x20 /* SD host status - 11 R */
#define SDVDD 0x30 /* SD card power control - 1 R/W */
#define SDEDM 0x34 /* Emergency Debug Mode - 13 R/W */
#define SDHCFG 0x38 /* Host configuration - 2 R/W */
#define SDHBCT 0x3c /* Host byte count (debug) - 32 R/W */
#define SDDATA 0x40 /* Data to/from SD card - 32 R/W */
#define SDHBLC 0x50 /* Host block count (SDIO/SDHC) - 9 R/W */
#define SDCMD_NEW_FLAG 0x8000
#define SDCMD_FAIL_FLAG 0x4000
#define SDCMD_BUSYWAIT 0x800
#define SDCMD_NO_RESPONSE 0x400
#define SDCMD_LONG_RESPONSE 0x200
#define SDCMD_WRITE_CMD 0x80
#define SDCMD_READ_CMD 0x40
#define SDCMD_CMD_MASK 0x3f
#define SDCDIV_MAX_CDIV 0x7ff
#define SDHSTS_BUSY_IRPT 0x400
#define SDHSTS_BLOCK_IRPT 0x200
#define SDHSTS_SDIO_IRPT 0x100
#define SDHSTS_REW_TIME_OUT 0x80
#define SDHSTS_CMD_TIME_OUT 0x40
#define SDHSTS_CRC16_ERROR 0x20
#define SDHSTS_CRC7_ERROR 0x10
#define SDHSTS_FIFO_ERROR 0x08
/* Reserved */
/* Reserved */
#define SDHSTS_DATA_FLAG 0x01
#define SDHCFG_BUSY_IRPT_EN (1 << 10)
#define SDHCFG_BLOCK_IRPT_EN (1 << 8)
#define SDHCFG_SDIO_IRPT_EN (1 << 5)
#define SDHCFG_DATA_IRPT_EN (1 << 4)
#define SDHCFG_SLOW_CARD (1 << 3)
#define SDHCFG_WIDE_EXT_BUS (1 << 2)
#define SDHCFG_WIDE_INT_BUS (1 << 1)
#define SDHCFG_REL_CMD_LINE (1 << 0)
#define SDEDM_FORCE_DATA_MODE (1 << 19)
#define SDEDM_CLOCK_PULSE (1 << 20)
#define SDEDM_BYPASS (1 << 21)
#define SDEDM_WRITE_THRESHOLD_SHIFT 9
#define SDEDM_READ_THRESHOLD_SHIFT 14
#define SDEDM_THRESHOLD_MASK 0x1f
#define SDEDM_FSM_MASK 0xf
#define SDEDM_FSM_IDENTMODE 0x0
#define SDEDM_FSM_DATAMODE 0x1
#define SDEDM_FSM_READDATA 0x2
#define SDEDM_FSM_WRITEDATA 0x3
#define SDEDM_FSM_READWAIT 0x4
#define SDEDM_FSM_READCRC 0x5
#define SDEDM_FSM_WRITECRC 0x6
#define SDEDM_FSM_WRITEWAIT1 0x7
#define SDEDM_FSM_POWERDOWN 0x8
#define SDEDM_FSM_POWERUP 0x9
#define SDEDM_FSM_WRITESTART1 0xa
#define SDEDM_FSM_WRITESTART2 0xb
#define SDEDM_FSM_GENPULSES 0xc
#define SDEDM_FSM_WRITEWAIT2 0xd
#define SDEDM_FSM_STARTPOWDOWN 0xf
#define SDDATA_FIFO_WORDS 16
static void bcm2835_sdhost_update_irq(BCM2835SDHostState *s)
{
uint32_t irq = s->status &
(SDHSTS_BUSY_IRPT | SDHSTS_BLOCK_IRPT | SDHSTS_SDIO_IRPT);
qemu_set_irq(s->irq, !!irq);
}
static void bcm2835_sdhost_send_command(BCM2835SDHostState *s)
{
SDRequest request;
uint8_t rsp[16];
int rlen;
request.cmd = s->cmd & SDCMD_CMD_MASK;
request.arg = s->cmdarg;
rlen = sdbus_do_command(&s->sdbus, &request, rsp);
if (rlen < 0) {
goto error;
}
if (!(s->cmd & SDCMD_NO_RESPONSE)) {
#define RWORD(n) (((uint32_t)rsp[n] << 24) | (rsp[n + 1] << 16) \
| (rsp[n + 2] << 8) | rsp[n + 3])
if (rlen == 0 || (rlen == 4 && (s->cmd & SDCMD_LONG_RESPONSE))) {
goto error;
}
if (rlen != 4 && rlen != 16) {
goto error;
}
if (rlen == 4) {
s->rsp[0] = RWORD(0);
s->rsp[1] = s->rsp[2] = s->rsp[3] = 0;
} else {
s->rsp[0] = RWORD(12);
s->rsp[1] = RWORD(8);
s->rsp[2] = RWORD(4);
s->rsp[3] = RWORD(0);
}
#undef RWORD
}
return;
error:
s->cmd |= SDCMD_FAIL_FLAG;
s->status |= SDHSTS_CMD_TIME_OUT;
}
static void bcm2835_sdhost_fifo_push(BCM2835SDHostState *s, uint32_t value)
{
int n;
if (s->fifo_len == BCM2835_SDHOST_FIFO_LEN) {
/* FIFO overflow */
return;
}
n = (s->fifo_pos + s->fifo_len) & (BCM2835_SDHOST_FIFO_LEN - 1);
s->fifo_len++;
s->fifo[n] = value;
}
static uint32_t bcm2835_sdhost_fifo_pop(BCM2835SDHostState *s)
{
uint32_t value;
if (s->fifo_len == 0) {
/* FIFO underflow */
return 0;
}
value = s->fifo[s->fifo_pos];
s->fifo_len--;
s->fifo_pos = (s->fifo_pos + 1) & (BCM2835_SDHOST_FIFO_LEN - 1);
return value;
}
static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s)
{
uint32_t value = 0;
int n;
int is_read;
is_read = (s->cmd & SDCMD_READ_CMD) != 0;
if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))) {
if (is_read) {
n = 0;
while (s->datacnt && s->fifo_len < BCM2835_SDHOST_FIFO_LEN) {
value |= (uint32_t)sdbus_read_data(&s->sdbus) << (n * 8);
s->datacnt--;
n++;
if (n == 4) {
bcm2835_sdhost_fifo_push(s, value);
n = 0;
value = 0;
}
}
if (n != 0) {
bcm2835_sdhost_fifo_push(s, value);
}
} else { /* write */
n = 0;
while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
if (n == 0) {
value = bcm2835_sdhost_fifo_pop(s);
n = 4;
}
n--;
s->datacnt--;
sdbus_write_data(&s->sdbus, value & 0xff);
value >>= 8;
}
}
}
if (s->datacnt == 0) {
s->status |= SDHSTS_DATA_FLAG;
s->edm &= ~0xf;
s->edm |= SDEDM_FSM_DATAMODE;
if (s->config & SDHCFG_DATA_IRPT_EN) {
s->status |= SDHSTS_SDIO_IRPT;
}
if ((s->cmd & SDCMD_BUSYWAIT) && (s->config & SDHCFG_BUSY_IRPT_EN)) {
s->status |= SDHSTS_BUSY_IRPT;
}
if ((s->cmd & SDCMD_WRITE_CMD) && (s->config & SDHCFG_BLOCK_IRPT_EN)) {
s->status |= SDHSTS_BLOCK_IRPT;
}
bcm2835_sdhost_update_irq(s);
}
s->edm &= ~(0x1f << 4);
s->edm |= ((s->fifo_len & 0x1f) << 4);
}
static uint64_t bcm2835_sdhost_read(void *opaque, hwaddr offset,
unsigned size)
{
BCM2835SDHostState *s = (BCM2835SDHostState *)opaque;
uint32_t res = 0;
switch (offset) {
case SDCMD:
res = s->cmd;
break;
case SDHSTS:
res = s->status;
break;
case SDRSP0:
res = s->rsp[0];
break;
case SDRSP1:
res = s->rsp[1];
break;
case SDRSP2:
res = s->rsp[2];
break;
case SDRSP3:
res = s->rsp[3];
break;
case SDEDM:
res = s->edm;
break;
case SDVDD:
res = s->vdd;
break;
case SDDATA:
res = bcm2835_sdhost_fifo_pop(s);
bcm2835_sdhost_fifo_run(s);
break;
case SDHBCT:
res = s->hbct;
break;
case SDHBLC:
res = s->hblc;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
res = 0;
break;
}
return res;
}
static void bcm2835_sdhost_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
BCM2835SDHostState *s = (BCM2835SDHostState *)opaque;
switch (offset) {
case SDCMD:
s->cmd = value;
if (value & SDCMD_NEW_FLAG) {
bcm2835_sdhost_send_command(s);
bcm2835_sdhost_fifo_run(s);
s->cmd &= ~SDCMD_NEW_FLAG;
}
break;
case SDTOUT:
break;
case SDCDIV:
break;
case SDHSTS:
s->status &= ~value;
bcm2835_sdhost_update_irq(s);
break;
case SDARG:
s->cmdarg = value;
break;
case SDEDM:
if ((value & 0xf) == 0xf) {
/* power down */
value &= ~0xf;
}
s->edm = value;
break;
case SDHCFG:
s->config = value;
bcm2835_sdhost_fifo_run(s);
break;
case SDVDD:
s->vdd = value;
break;
case SDDATA:
bcm2835_sdhost_fifo_push(s, value);
bcm2835_sdhost_fifo_run(s);
break;
case SDHBCT:
s->hbct = value;
break;
case SDHBLC:
s->hblc = value;
s->datacnt = s->hblc * s->hbct;
bcm2835_sdhost_fifo_run(s);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
__func__, offset);
break;
}
}
static const MemoryRegionOps bcm2835_sdhost_ops = {
.read = bcm2835_sdhost_read,
.write = bcm2835_sdhost_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription vmstate_bcm2835_sdhost = {
.name = TYPE_BCM2835_SDHOST,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(cmd, BCM2835SDHostState),
VMSTATE_UINT32(cmdarg, BCM2835SDHostState),
VMSTATE_UINT32(status, BCM2835SDHostState),
VMSTATE_UINT32_ARRAY(rsp, BCM2835SDHostState, 4),
VMSTATE_UINT32(config, BCM2835SDHostState),
VMSTATE_UINT32(edm, BCM2835SDHostState),
VMSTATE_UINT32(vdd, BCM2835SDHostState),
VMSTATE_UINT32(hbct, BCM2835SDHostState),
VMSTATE_UINT32(hblc, BCM2835SDHostState),
VMSTATE_INT32(fifo_pos, BCM2835SDHostState),
VMSTATE_INT32(fifo_len, BCM2835SDHostState),
VMSTATE_UINT32_ARRAY(fifo, BCM2835SDHostState, BCM2835_SDHOST_FIFO_LEN),
VMSTATE_UINT32(datacnt, BCM2835SDHostState),
VMSTATE_END_OF_LIST()
}
};
static void bcm2835_sdhost_init(Object *obj)
{
BCM2835SDHostState *s = BCM2835_SDHOST(obj);
qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
TYPE_BCM2835_SDHOST_BUS, DEVICE(s), "sd-bus");
memory_region_init_io(&s->iomem, obj, &bcm2835_sdhost_ops, s,
TYPE_BCM2835_SDHOST, 0x1000);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq);
}
static void bcm2835_sdhost_reset(DeviceState *dev)
{
BCM2835SDHostState *s = BCM2835_SDHOST(dev);
s->cmd = 0;
s->cmdarg = 0;
s->edm = 0x0000c60f;
s->config = 0;
s->hbct = 0;
s->hblc = 0;
s->datacnt = 0;
s->fifo_pos = 0;
s->fifo_len = 0;
}
static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = bcm2835_sdhost_reset;
dc->vmsd = &vmstate_bcm2835_sdhost;
}
static TypeInfo bcm2835_sdhost_info = {
.name = TYPE_BCM2835_SDHOST,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(BCM2835SDHostState),
.class_init = bcm2835_sdhost_class_init,
.instance_init = bcm2835_sdhost_init,
};
static const TypeInfo bcm2835_sdhost_bus_info = {
.name = TYPE_BCM2835_SDHOST_BUS,
.parent = TYPE_SD_BUS,
.instance_size = sizeof(SDBus),
};
static void bcm2835_sdhost_register_types(void)
{
type_register_static(&bcm2835_sdhost_info);
type_register_static(&bcm2835_sdhost_bus_info);
}
type_init(bcm2835_sdhost_register_types)

View File

@ -119,6 +119,7 @@
(SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
(SDHC_CAPAB_TOCLKFREQ))
#define MASK_TRNMOD 0x0037
#define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val))
static uint8_t sdhci_slotint(SDHCIState *s)
@ -486,6 +487,11 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
if (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || !s->blkcnt) {
qemu_log_mask(LOG_UNIMP, "infinite transfer is not supported\n");
return;
}
/* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
* possible stop at page boundary if initial address is not page aligned,
* allow them to work properly */
@ -564,7 +570,6 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
}
/* single block SDMA transfer */
static void sdhci_sdma_transfer_single_block(SDHCIState *s)
{
int n;
@ -583,10 +588,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s)
sdbus_write_data(&s->sdbus, s->fifo_buffer[n]);
}
}
if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
s->blkcnt--;
}
s->blkcnt--;
sdhci_end_transfer(s);
}
@ -797,11 +799,6 @@ static void sdhci_data_transfer(void *opaque)
if (s->trnmod & SDHC_TRNS_DMA) {
switch (SDHC_DMA_TYPE(s->hostctl)) {
case SDHC_CTRL_SDMA:
if ((s->trnmod & SDHC_TRNS_MULTI) &&
(!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) {
break;
}
if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
sdhci_sdma_transfer_single_block(s);
} else {
@ -1022,7 +1019,11 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
/* Writing to last byte of sdmasysad might trigger transfer */
if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
sdhci_sdma_transfer_multi_blocks(s);
if (s->trnmod & SDHC_TRNS_MULTI) {
sdhci_sdma_transfer_multi_blocks(s);
} else {
sdhci_sdma_transfer_single_block(s);
}
}
break;
case SDHC_BLKSIZE:
@ -1050,7 +1051,7 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
if (!(s->capareg & SDHC_CAN_DO_DMA)) {
value &= ~SDHC_TRNS_DMA;
}
MASKED_WRITE(s->trnmod, mask, value);
MASKED_WRITE(s->trnmod, mask, value & MASK_TRNMOD);
MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16);
/* Writing to the upper byte of CMDREG triggers SD command generation */

View File

@ -296,18 +296,23 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size)
return reg_value;
}
static void imx_gpt_reset(DeviceState *dev)
{
IMXGPTState *s = IMX_GPT(dev);
static void imx_gpt_reset_common(IMXGPTState *s, bool is_soft_reset)
{
/* stop timer */
ptimer_stop(s->timer);
/*
* Soft reset doesn't touch some bits; hard reset clears them
/* Soft reset and hard reset differ only in their handling of the CR
* register -- soft reset preserves the values of some bits there.
*/
s->cr &= ~(GPT_CR_EN|GPT_CR_ENMOD|GPT_CR_STOPEN|GPT_CR_DOZEN|
GPT_CR_WAITEN|GPT_CR_DBGEN);
if (is_soft_reset) {
/* Clear all CR bits except those that are preserved by soft reset. */
s->cr &= GPT_CR_EN | GPT_CR_ENMOD | GPT_CR_STOPEN | GPT_CR_DOZEN |
GPT_CR_WAITEN | GPT_CR_DBGEN |
(GPT_CR_CLKSRC_MASK << GPT_CR_CLKSRC_SHIFT);
} else {
s->cr = 0;
}
s->sr = 0;
s->pr = 0;
s->ir = 0;
@ -333,6 +338,18 @@ static void imx_gpt_reset(DeviceState *dev)
}
}
static void imx_gpt_soft_reset(DeviceState *dev)
{
IMXGPTState *s = IMX_GPT(dev);
imx_gpt_reset_common(s, true);
}
static void imx_gpt_reset(DeviceState *dev)
{
IMXGPTState *s = IMX_GPT(dev);
imx_gpt_reset_common(s, false);
}
static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
@ -348,7 +365,7 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value,
s->cr = value & ~0x7c14;
if (s->cr & GPT_CR_SWR) { /* force reset */
/* handle the reset */
imx_gpt_reset(DEVICE(s));
imx_gpt_soft_reset(DEVICE(s));
} else {
/* set our freq, as the source might have changed */
imx_gpt_set_freq(s);

View File

@ -19,6 +19,7 @@
#include "hw/dma/bcm2835_dma.h"
#include "hw/intc/bcm2835_ic.h"
#include "hw/misc/bcm2835_property.h"
#include "hw/misc/bcm2835_rng.h"
#include "hw/misc/bcm2835_mbox.h"
#include "hw/sd/sdhci.h"
@ -41,6 +42,7 @@ typedef struct BCM2835PeripheralState {
BCM2835DMAState dma;
BCM2835ICState ic;
BCM2835PropertyState property;
BCM2835RngState rng;
BCM2835MboxState mboxes;
SDHCIState sdhci;
} BCM2835PeripheralState;

View File

@ -93,6 +93,7 @@ typedef struct {
FWCfgState *fw_cfg;
bool secure;
bool highmem;
bool its;
bool virt;
int32_t gic_version;
struct arm_boot_info bootinfo;

View File

@ -0,0 +1,27 @@
/*
* BCM2835 Random Number Generator emulation
*
* Copyright (C) 2017 Marcin Chojnacki <marcinch7@gmail.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef BCM2835_RNG_H
#define BCM2835_RNG_H
#include "hw/sysbus.h"
#define TYPE_BCM2835_RNG "bcm2835-rng"
#define BCM2835_RNG(obj) \
OBJECT_CHECK(BCM2835RngState, (obj), TYPE_BCM2835_RNG)
typedef struct {
SysBusDevice busdev;
MemoryRegion iomem;
uint32_t rng_ctrl;
uint32_t rng_status;
} BCM2835RngState;
#endif

View File

@ -0,0 +1,48 @@
/*
* Raspberry Pi (BCM2835) SD Host Controller
*
* Copyright (c) 2017 Antfield SAS
*
* Authors:
* Clement Deschamps <clement.deschamps@antfield.fr>
* Luc Michel <luc.michel@antfield.fr>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef BCM2835_SDHOST_H
#define BCM2835_SDHOST_H
#include "hw/sysbus.h"
#include "hw/sd/sd.h"
#define TYPE_BCM2835_SDHOST "bcm2835-sdhost"
#define BCM2835_SDHOST(obj) \
OBJECT_CHECK(BCM2835SDHostState, (obj), TYPE_BCM2835_SDHOST)
#define BCM2835_SDHOST_FIFO_LEN 16
typedef struct {
SysBusDevice busdev;
SDBus sdbus;
MemoryRegion iomem;
uint32_t cmd;
uint32_t cmdarg;
uint32_t status;
uint32_t rsp[4];
uint32_t config;
uint32_t edm;
uint32_t vdd;
uint32_t hbct;
uint32_t hblc;
int32_t fifo_pos;
int32_t fifo_len;
uint32_t fifo[BCM2835_SDHOST_FIFO_LEN];
uint32_t datacnt;
qemu_irq irq;
} BCM2835SDHostState;
#endif

View File

@ -574,6 +574,7 @@ void cpu_loop(CPUARMState *env)
switch(trapnr) {
case EXCP_UDEF:
case EXCP_NOCP:
case EXCP_INVSTATE:
{
TaskState *ts = cs->opaque;
uint32_t opcode;

View File

@ -338,13 +338,6 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
CPUARMState *env = &cpu->env;
bool ret = false;
if (interrupt_request & CPU_INTERRUPT_FIQ
&& !(env->daif & PSTATE_F)) {
cs->exception_index = EXCP_FIQ;
cc->do_interrupt(cs);
ret = true;
}
/* ARMv7-M interrupt return works by loading a magic value
* into the PC. On real hardware the load causes the
* return to occur. The qemu implementation performs the
@ -354,9 +347,16 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
* the stack if an interrupt occurred at the wrong time.
* We avoid this by disabling interrupts when
* pc contains a magic address.
*
* ARMv7-M interrupt masking works differently than -A or -R.
* There is no FIQ/IRQ distinction. Instead of I and F bits
* masking FIQ and IRQ interrupts, an exception is taken only
* if it is higher priority than the current execution priority
* (which depends on state like BASEPRI, FAULTMASK and the
* currently active exception).
*/
if (interrupt_request & CPU_INTERRUPT_HARD
&& !(env->daif & PSTATE_I)
&& (armv7m_nvic_can_take_pending_exception(env->nvic))
&& (env->regs[15] < 0xfffffff0)) {
cs->exception_index = EXCP_IRQ;
cc->do_interrupt(cs);

View File

@ -57,6 +57,7 @@
#define EXCP_VFIQ 15
#define EXCP_SEMIHOST 16 /* semihosting call */
#define EXCP_NOCP 17 /* v7M NOCP UsageFault */
#define EXCP_INVSTATE 18 /* v7M INVSTATE UsageFault */
#define ARMV7M_EXCP_RESET 1
#define ARMV7M_EXCP_NMI 2
@ -1356,9 +1357,27 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx,
uint32_t cur_el, bool secure);
/* Interface between CPU and Interrupt controller. */
#ifndef CONFIG_USER_ONLY
bool armv7m_nvic_can_take_pending_exception(void *opaque);
#else
static inline bool armv7m_nvic_can_take_pending_exception(void *opaque)
{
return true;
}
#endif
void armv7m_nvic_set_pending(void *opaque, int irq);
int armv7m_nvic_acknowledge_irq(void *opaque);
void armv7m_nvic_complete_irq(void *opaque, int irq);
void armv7m_nvic_acknowledge_irq(void *opaque);
/**
* armv7m_nvic_complete_irq: complete specified interrupt or exception
* @opaque: the NVIC
* @irq: the exception number to complete
*
* Returns: -1 if the irq was not active
* 1 if completing this irq brought us back to base (no active irqs)
* 0 if there is still an irq active after this one was completed
* (Ignoring -1, this is the same as the RETTOBASE value before completion.)
*/
int armv7m_nvic_complete_irq(void *opaque, int irq);
/* Interface for defining coprocessor registers.
* Registers are defined in tables of arm_cp_reginfo structs

View File

@ -6002,22 +6002,165 @@ static void switch_v7m_sp(CPUARMState *env, bool new_spsel)
}
}
static void do_v7m_exception_exit(CPUARMState *env)
static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
{
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
MemTxResult result;
hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4;
uint32_t addr;
addr = address_space_ldl(cs->as, vec,
MEMTXATTRS_UNSPECIFIED, &result);
if (result != MEMTX_OK) {
/* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
* which would then be immediately followed by our failing to load
* the entry vector for that HardFault, which is a Lockup case.
* Since we don't model Lockup, we just report this guest error
* via cpu_abort().
*/
cpu_abort(cs, "Failed to read from exception vector table "
"entry %08x\n", (unsigned)vec);
}
return addr;
}
static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr)
{
/* Do the "take the exception" parts of exception entry,
* but not the pushing of state to the stack. This is
* similar to the pseudocode ExceptionTaken() function.
*/
CPUARMState *env = &cpu->env;
uint32_t addr;
armv7m_nvic_acknowledge_irq(env->nvic);
switch_v7m_sp(env, 0);
/* Clear IT bits */
env->condexec_bits = 0;
env->regs[14] = lr;
addr = arm_v7m_load_vector(cpu);
env->regs[15] = addr & 0xfffffffe;
env->thumb = addr & 1;
}
static void v7m_push_stack(ARMCPU *cpu)
{
/* Do the "set up stack frame" part of exception entry,
* similar to pseudocode PushStack().
*/
CPUARMState *env = &cpu->env;
uint32_t xpsr = xpsr_read(env);
/* Align stack pointer if the guest wants that */
if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) {
env->regs[13] -= 4;
xpsr |= 0x200;
}
/* Switch to the handler mode. */
v7m_push(env, xpsr);
v7m_push(env, env->regs[15]);
v7m_push(env, env->regs[14]);
v7m_push(env, env->regs[12]);
v7m_push(env, env->regs[3]);
v7m_push(env, env->regs[2]);
v7m_push(env, env->regs[1]);
v7m_push(env, env->regs[0]);
}
static void do_v7m_exception_exit(ARMCPU *cpu)
{
CPUARMState *env = &cpu->env;
uint32_t type;
uint32_t xpsr;
bool ufault = false;
bool return_to_sp_process = false;
bool return_to_handler = false;
bool rettobase = false;
/* We can only get here from an EXCP_EXCEPTION_EXIT, and
* arm_v7m_do_unassigned_access() enforces the architectural rule
* that jumps to magic addresses don't have magic behaviour unless
* we're in Handler mode (compare pseudocode BXWritePC()).
*/
assert(env->v7m.exception != 0);
/* In the spec pseudocode ExceptionReturn() is called directly
* from BXWritePC() and gets the full target PC value including
* bit zero. In QEMU's implementation we treat it as a normal
* jump-to-register (which is then caught later on), and so split
* the target value up between env->regs[15] and env->thumb in
* gen_bx(). Reconstitute it.
*/
type = env->regs[15];
if (env->thumb) {
type |= 1;
}
qemu_log_mask(CPU_LOG_INT, "Exception return: magic PC %" PRIx32
" previous exception %d\n",
type, env->v7m.exception);
if (extract32(type, 5, 23) != extract32(-1, 5, 23)) {
qemu_log_mask(LOG_GUEST_ERROR, "M profile: zero high bits in exception "
"exit PC value 0x%" PRIx32 " are UNPREDICTABLE\n", type);
}
if (env->v7m.exception != ARMV7M_EXCP_NMI) {
/* Auto-clear FAULTMASK on return from other than NMI */
env->daif &= ~PSTATE_F;
}
if (env->v7m.exception != 0) {
armv7m_nvic_complete_irq(env->nvic, env->v7m.exception);
switch (armv7m_nvic_complete_irq(env->nvic, env->v7m.exception)) {
case -1:
/* attempt to exit an exception that isn't active */
ufault = true;
break;
case 0:
/* still an irq active now */
break;
case 1:
/* we returned to base exception level, no nesting.
* (In the pseudocode this is written using "NestedActivation != 1"
* where we have 'rettobase == false'.)
*/
rettobase = true;
break;
default:
g_assert_not_reached();
}
switch (type & 0xf) {
case 1: /* Return to Handler */
return_to_handler = true;
break;
case 13: /* Return to Thread using Process stack */
return_to_sp_process = true;
/* fall through */
case 9: /* Return to Thread using Main stack */
if (!rettobase &&
!(env->v7m.ccr & R_V7M_CCR_NONBASETHRDENA_MASK)) {
ufault = true;
}
break;
default:
ufault = true;
}
if (ufault) {
/* Bad exception return: instead of popping the exception
* stack, directly take a usage fault on the current stack.
*/
env->v7m.cfsr |= R_V7M_CFSR_INVPC_MASK;
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
v7m_exception_taken(cpu, type | 0xf0000000);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing "
"stackframe: failed exception return integrity check\n");
return;
}
/* Switch to the target stack. */
switch_v7m_sp(env, (type & 4) != 0);
switch_v7m_sp(env, return_to_sp_process);
/* Pop registers. */
env->regs[0] = v7m_pop(env);
env->regs[1] = v7m_pop(env);
@ -6041,11 +6184,24 @@ static void do_v7m_exception_exit(CPUARMState *env)
/* Undo stack alignment. */
if (xpsr & 0x200)
env->regs[13] |= 4;
/* ??? The exception return type specifies Thread/Handler mode. However
this is also implied by the xPSR value. Not sure what to do
if there is a mismatch. */
/* ??? Likewise for mismatches between the CONTROL register and the stack
pointer. */
/* The restored xPSR exception field will be zero if we're
* resuming in Thread mode. If that doesn't match what the
* exception return type specified then this is a UsageFault.
*/
if (return_to_handler == (env->v7m.exception == 0)) {
/* Take an INVPC UsageFault by pushing the stack again. */
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
env->v7m.cfsr |= R_V7M_CFSR_INVPC_MASK;
v7m_push_stack(cpu);
v7m_exception_taken(cpu, type | 0xf0000000);
qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: "
"failed exception return integrity check\n");
return;
}
/* Otherwise, we have a successful exception exit. */
qemu_log_mask(CPU_LOG_INT, "...successful exception return\n");
}
static void arm_log_exception(int idx)
@ -6063,37 +6219,11 @@ static void arm_log_exception(int idx)
}
}
static uint32_t arm_v7m_load_vector(ARMCPU *cpu)
{
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
MemTxResult result;
hwaddr vec = env->v7m.vecbase + env->v7m.exception * 4;
uint32_t addr;
addr = address_space_ldl(cs->as, vec,
MEMTXATTRS_UNSPECIFIED, &result);
if (result != MEMTX_OK) {
/* Architecturally this should cause a HardFault setting HSFR.VECTTBL,
* which would then be immediately followed by our failing to load
* the entry vector for that HardFault, which is a Lockup case.
* Since we don't model Lockup, we just report this guest error
* via cpu_abort().
*/
cpu_abort(cs, "Failed to read from exception vector table "
"entry %08x\n", (unsigned)vec);
}
return addr;
}
void arm_v7m_cpu_do_interrupt(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
CPUARMState *env = &cpu->env;
uint32_t xpsr = xpsr_read(env);
uint32_t lr;
uint32_t addr;
arm_log_exception(cs->exception_index);
@ -6106,28 +6236,30 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
/* For exceptions we just mark as pending on the NVIC, and let that
handle it. */
/* TODO: Need to escalate if the current priority is higher than the
one we're raising. */
switch (cs->exception_index) {
case EXCP_UDEF:
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
env->v7m.cfsr |= R_V7M_CFSR_UNDEFINSTR_MASK;
return;
break;
case EXCP_NOCP:
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
env->v7m.cfsr |= R_V7M_CFSR_NOCP_MASK;
return;
break;
case EXCP_INVSTATE:
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE);
env->v7m.cfsr |= R_V7M_CFSR_INVSTATE_MASK;
break;
case EXCP_SWI:
/* The PC already points to the next instruction. */
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC);
return;
break;
case EXCP_PREFETCH_ABORT:
case EXCP_DATA_ABORT:
/* TODO: if we implemented the MPU registers, this is where we
* should set the MMFAR, etc from exception.fsr and exception.vaddress.
*/
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM);
return;
break;
case EXCP_BKPT:
if (semihosting_enabled()) {
int nr;
@ -6142,39 +6274,20 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs)
}
}
armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG);
return;
break;
case EXCP_IRQ:
env->v7m.exception = armv7m_nvic_acknowledge_irq(env->nvic);
break;
case EXCP_EXCEPTION_EXIT:
do_v7m_exception_exit(env);
do_v7m_exception_exit(cpu);
return;
default:
cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index);
return; /* Never happens. Keep compiler happy. */
}
/* Align stack pointer if the guest wants that */
if ((env->regs[13] & 4) && (env->v7m.ccr & R_V7M_CCR_STKALIGN_MASK)) {
env->regs[13] -= 4;
xpsr |= 0x200;
}
/* Switch to the handler mode. */
v7m_push(env, xpsr);
v7m_push(env, env->regs[15]);
v7m_push(env, env->regs[14]);
v7m_push(env, env->regs[12]);
v7m_push(env, env->regs[3]);
v7m_push(env, env->regs[2]);
v7m_push(env, env->regs[1]);
v7m_push(env, env->regs[0]);
switch_v7m_sp(env, 0);
/* Clear IT bits */
env->condexec_bits = 0;
env->regs[14] = lr;
addr = arm_v7m_load_vector(cpu);
env->regs[15] = addr & 0xfffffffe;
env->thumb = addr & 1;
v7m_push_stack(cpu);
v7m_exception_taken(cpu, lr);
qemu_log_mask(CPU_LOG_INT, "... as %d\n", env->v7m.exception);
}
/* Function used to synchronize QEMU's AArch64 register set with AArch32

View File

@ -10933,6 +10933,10 @@ static void disas_crypto_aes(DisasContext *s, uint32_t insn)
return;
}
if (!fp_access_check(s)) {
return;
}
/* Note that we convert the Vx register indexes into the
* index within the vfp.regs[] array, so we can share the
* helper with the AArch32 instructions.
@ -10997,6 +11001,10 @@ static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn)
return;
}
if (!fp_access_check(s)) {
return;
}
tcg_rd_regno = tcg_const_i32(rd << 1);
tcg_rn_regno = tcg_const_i32(rn << 1);
tcg_rm_regno = tcg_const_i32(rm << 1);
@ -11060,6 +11068,10 @@ static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn)
return;
}
if (!fp_access_check(s)) {
return;
}
tcg_rd_regno = tcg_const_i32(rd << 1);
tcg_rn_regno = tcg_const_i32(rn << 1);

View File

@ -7990,9 +7990,13 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
TCGv_i32 addr;
TCGv_i64 tmp64;
/* M variants do not implement ARM mode. */
/* M variants do not implement ARM mode; this must raise the INVSTATE
* UsageFault exception.
*/
if (arm_dc_feature(s, ARM_FEATURE_M)) {
goto illegal_op;
gen_exception_insn(s, 4, EXCP_INVSTATE, syn_uncategorized(),
default_exception_el(s));
return;
}
cond = insn >> 28;
if (cond == 0xf){