提交 fdaad471 编写于 作者: P Peter Maydell

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

target-arm queue:
 * implement XScale cache lockdown cp15 ops
 * fix v7M CPUID base register
 * implement WFE and YIELD as yields for A64
 * fix A64 "BLR LR"
 * support Cortex-A57 in virt machine model
 * a few other minor AArch64 bugfixes

# gpg: Signature made Thu 01 May 2014 15:42:17 BST using RSA key ID 14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"

* remotes/pmaydell/tags/pull-target-arm-20140501:
  hw/arm/virt: Add support for Cortex-A57
  hw/arm/virt: Put GIC register banks on 64K boundaries
  hw/arm/virt: Create the GIC ourselves rather than (ab)using a15mpcore_priv
  target-arm: Correct a comment refering to EL0
  target-arm: A64: Fix a typo when declaring TLBI ops
  target-arm: A64: Handle blr lr
  target-arm: Make vbar_write 64bit friendly on 32bit hosts
  target-arm: implement WFE/YIELD as a yield for AArch64
  armv7m_nvic: fix CPUID Base Register
  target-arm: Implement XScale cache lockdown operations as NOPs
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
...@@ -75,8 +75,6 @@ typedef struct MemMapEntry { ...@@ -75,8 +75,6 @@ typedef struct MemMapEntry {
typedef struct VirtBoardInfo { typedef struct VirtBoardInfo {
struct arm_boot_info bootinfo; struct arm_boot_info bootinfo;
const char *cpu_model; const char *cpu_model;
const char *qdevname;
const char *gic_compatible;
const MemMapEntry *memmap; const MemMapEntry *memmap;
const int *irqmap; const int *irqmap;
int smp_cpus; int smp_cpus;
...@@ -98,10 +96,10 @@ typedef struct VirtBoardInfo { ...@@ -98,10 +96,10 @@ typedef struct VirtBoardInfo {
static const MemMapEntry a15memmap[] = { static const MemMapEntry a15memmap[] = {
/* Space up to 0x8000000 is reserved for a boot ROM */ /* Space up to 0x8000000 is reserved for a boot ROM */
[VIRT_FLASH] = { 0, 0x8000000 }, [VIRT_FLASH] = { 0, 0x8000000 },
[VIRT_CPUPERIPHS] = { 0x8000000, 0x8000 }, [VIRT_CPUPERIPHS] = { 0x8000000, 0x20000 },
/* GIC distributor and CPU interfaces sit inside the CPU peripheral space */ /* GIC distributor and CPU interfaces sit inside the CPU peripheral space */
[VIRT_GIC_DIST] = { 0x8001000, 0x1000 }, [VIRT_GIC_DIST] = { 0x8000000, 0x10000 },
[VIRT_GIC_CPU] = { 0x8002000, 0x1000 }, [VIRT_GIC_CPU] = { 0x8010000, 0x10000 },
[VIRT_UART] = { 0x9000000, 0x1000 }, [VIRT_UART] = { 0x9000000, 0x1000 },
[VIRT_MMIO] = { 0xa000000, 0x200 }, [VIRT_MMIO] = { 0xa000000, 0x200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
...@@ -117,16 +115,16 @@ static const int a15irqmap[] = { ...@@ -117,16 +115,16 @@ static const int a15irqmap[] = {
static VirtBoardInfo machines[] = { static VirtBoardInfo machines[] = {
{ {
.cpu_model = "cortex-a15", .cpu_model = "cortex-a15",
.qdevname = "a15mpcore_priv", .memmap = a15memmap,
.gic_compatible = "arm,cortex-a15-gic", .irqmap = a15irqmap,
},
{
.cpu_model = "cortex-a57",
.memmap = a15memmap, .memmap = a15memmap,
.irqmap = a15irqmap, .irqmap = a15irqmap,
}, },
{ {
.cpu_model = "host", .cpu_model = "host",
/* We use the A15 private peripheral model to get a V2 GIC */
.qdevname = "a15mpcore_priv",
.gic_compatible = "arm,cortex-a15-gic",
.memmap = a15memmap, .memmap = a15memmap,
.irqmap = a15irqmap, .irqmap = a15irqmap,
}, },
...@@ -251,8 +249,9 @@ static void fdt_add_gic_node(const VirtBoardInfo *vbi) ...@@ -251,8 +249,9 @@ static void fdt_add_gic_node(const VirtBoardInfo *vbi)
qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle); qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", gic_phandle);
qemu_fdt_add_subnode(vbi->fdt, "/intc"); qemu_fdt_add_subnode(vbi->fdt, "/intc");
/* 'cortex-a15-gic' means 'GIC v2' */
qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible", qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
vbi->gic_compatible); "arm,cortex-a15-gic");
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3);
qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0); qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0);
qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg", qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
...@@ -263,6 +262,56 @@ static void fdt_add_gic_node(const VirtBoardInfo *vbi) ...@@ -263,6 +262,56 @@ static void fdt_add_gic_node(const VirtBoardInfo *vbi)
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle); qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", gic_phandle);
} }
static void create_gic(const VirtBoardInfo *vbi, qemu_irq *pic)
{
/* We create a standalone GIC v2 */
DeviceState *gicdev;
SysBusDevice *gicbusdev;
const char *gictype = "arm_gic";
int i;
if (kvm_irqchip_in_kernel()) {
gictype = "kvm-arm-gic";
}
gicdev = qdev_create(NULL, gictype);
qdev_prop_set_uint32(gicdev, "revision", 2);
qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus);
/* Note that the num-irq property counts both internal and external
* interrupts; there are always 32 of the former (mandated by GIC spec).
*/
qdev_prop_set_uint32(gicdev, "num-irq", NUM_IRQS + 32);
qdev_init_nofail(gicdev);
gicbusdev = SYS_BUS_DEVICE(gicdev);
sysbus_mmio_map(gicbusdev, 0, vbi->memmap[VIRT_GIC_DIST].base);
sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base);
/* Wire the outputs from each CPU's generic timer to the
* appropriate GIC PPI inputs, and the GIC's IRQ output to
* the CPU's IRQ input.
*/
for (i = 0; i < smp_cpus; i++) {
DeviceState *cpudev = DEVICE(qemu_get_cpu(i));
int ppibase = NUM_IRQS + i * 32;
/* physical timer; we wire it up to the non-secure timer's ID,
* since a real A15 always has TrustZone but QEMU doesn't.
*/
qdev_connect_gpio_out(cpudev, 0,
qdev_get_gpio_in(gicdev, ppibase + 30));
/* virtual timer */
qdev_connect_gpio_out(cpudev, 1,
qdev_get_gpio_in(gicdev, ppibase + 27));
sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
}
for (i = 0; i < NUM_IRQS; i++) {
pic[i] = qdev_get_gpio_in(gicdev, i);
}
fdt_add_gic_node(vbi);
}
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic) static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
{ {
char *nodename; char *nodename;
...@@ -340,8 +389,6 @@ static void machvirt_init(QEMUMachineInitArgs *args) ...@@ -340,8 +389,6 @@ static void machvirt_init(QEMUMachineInitArgs *args)
MemoryRegion *sysmem = get_system_memory(); MemoryRegion *sysmem = get_system_memory();
int n; int n;
MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *ram = g_new(MemoryRegion, 1);
DeviceState *dev;
SysBusDevice *busdev;
const char *cpu_model = args->cpu_model; const char *cpu_model = args->cpu_model;
VirtBoardInfo *vbi; VirtBoardInfo *vbi;
...@@ -404,25 +451,7 @@ static void machvirt_init(QEMUMachineInitArgs *args) ...@@ -404,25 +451,7 @@ static void machvirt_init(QEMUMachineInitArgs *args)
vmstate_register_ram_global(ram); vmstate_register_ram_global(ram);
memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram); memory_region_add_subregion(sysmem, vbi->memmap[VIRT_MEM].base, ram);
dev = qdev_create(NULL, vbi->qdevname); create_gic(vbi, pic);
qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
/* Note that the num-irq property counts both internal and external
* interrupts; there are always 32 of the former (mandated by GIC spec).
*/
qdev_prop_set_uint32(dev, "num-irq", NUM_IRQS + 32);
qdev_init_nofail(dev);
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, vbi->memmap[VIRT_CPUPERIPHS].base);
fdt_add_gic_node(vbi);
for (n = 0; n < smp_cpus; n++) {
DeviceState *cpudev = DEVICE(qemu_get_cpu(n));
sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
}
for (n = 0; n < NUM_IRQS; n++) {
pic[n] = qdev_get_gpio_in(dev, n);
}
create_uart(vbi, pic); create_uart(vbi, pic);
......
...@@ -173,7 +173,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) ...@@ -173,7 +173,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset)
return 10000; return 10000;
case 0xd00: /* CPUID Base. */ case 0xd00: /* CPUID Base. */
cpu = ARM_CPU(current_cpu); cpu = ARM_CPU(current_cpu);
return cpu->env.cp15.c0_cpuid; return cpu->midr;
case 0xd04: /* Interrupt Control State. */ case 0xd04: /* Interrupt Control State. */
/* VECTACTIVE */ /* VECTACTIVE */
val = s->gic.running_irq[0]; val = s->gic.running_irq[0];
......
...@@ -657,7 +657,7 @@ static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, ...@@ -657,7 +657,7 @@ static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri,
* contexts. (ARMv8 would permit us to do no masking at all, but ARMv7 * contexts. (ARMv8 would permit us to do no masking at all, but ARMv7
* requires the bottom five bits to be RAZ/WI because they're UNK/SBZP.) * requires the bottom five bits to be RAZ/WI because they're UNK/SBZP.)
*/ */
env->cp15.c12_vbar = value & ~0x1Ful; env->cp15.c12_vbar = value & ~0x1FULL;
} }
static uint64_t ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri)
...@@ -1578,6 +1578,21 @@ static const ARMCPRegInfo xscale_cp_reginfo[] = { ...@@ -1578,6 +1578,21 @@ static const ARMCPRegInfo xscale_cp_reginfo[] = {
.cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW,
.fieldoffset = offsetof(CPUARMState, cp15.c1_xscaleauxcr), .fieldoffset = offsetof(CPUARMState, cp15.c1_xscaleauxcr),
.resetvalue = 0, }, .resetvalue = 0, },
/* XScale specific cache-lockdown: since we have no cache we NOP these
* and hope the guest does not really rely on cache behaviour.
*/
{ .name = "XSCALE_LOCK_ICACHE_LINE",
.cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0,
.access = PL1_W, .type = ARM_CP_NOP },
{ .name = "XSCALE_UNLOCK_ICACHE",
.cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1,
.access = PL1_W, .type = ARM_CP_NOP },
{ .name = "XSCALE_DCACHE_LOCK",
.cp = 15, .opc1 = 0, .crn = 9, .crm = 2, .opc2 = 0,
.access = PL1_RW, .type = ARM_CP_NOP },
{ .name = "XSCALE_UNLOCK_DCACHE",
.cp = 15, .opc1 = 0, .crn = 9, .crm = 2, .opc2 = 1,
.access = PL1_W, .type = ARM_CP_NOP },
REGINFO_SENTINEL REGINFO_SENTINEL
}; };
...@@ -1893,51 +1908,51 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { ...@@ -1893,51 +1908,51 @@ static const ARMCPRegInfo v8_cp_reginfo[] = {
.access = PL1_W, .type = ARM_CP_NOP }, .access = PL1_W, .type = ARM_CP_NOP },
/* TLBI operations */ /* TLBI operations */
{ .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 0, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbiall_write }, .writefn = tlbiall_write },
{ .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VAE1IS", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 1, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 1,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_va_write }, .writefn = tlbi_aa64_va_write },
{ .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64, { .name = "TLBI_ASIDE1IS", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 2, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 2,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_asid_write }, .writefn = tlbi_aa64_asid_write },
{ .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VAAE1IS", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 3, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 3,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_vaa_write }, .writefn = tlbi_aa64_vaa_write },
{ .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VALE1IS", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 5, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 5,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_va_write }, .writefn = tlbi_aa64_va_write },
{ .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VAALE1IS", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 3, .opc2 = 7, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 7,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_vaa_write }, .writefn = tlbi_aa64_vaa_write },
{ .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VMALLE1", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 0, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 0,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbiall_write }, .writefn = tlbiall_write },
{ .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VAE1", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 1, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 1,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_va_write }, .writefn = tlbi_aa64_va_write },
{ .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64, { .name = "TLBI_ASIDE1", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 2, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 2,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_asid_write }, .writefn = tlbi_aa64_asid_write },
{ .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VAAE1", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 3, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 3,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_vaa_write }, .writefn = tlbi_aa64_vaa_write },
{ .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VALE1", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 5, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 5,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_va_write }, .writefn = tlbi_aa64_va_write },
{ .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64, { .name = "TLBI_VAALE1", .state = ARM_CP_STATE_AA64,
.opc0 = 1, .opc2 = 0, .crn = 8, .crm = 7, .opc2 = 7, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 7, .opc2 = 7,
.access = PL1_W, .type = ARM_CP_NO_MIGRATE, .access = PL1_W, .type = ARM_CP_NO_MIGRATE,
.writefn = tlbi_aa64_vaa_write }, .writefn = tlbi_aa64_vaa_write },
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
......
...@@ -418,7 +418,7 @@ void HELPER(exception_return)(CPUARMState *env) ...@@ -418,7 +418,7 @@ void HELPER(exception_return)(CPUARMState *env)
goto illegal_return; goto illegal_return;
} }
if (new_el == 0 && (spsr & PSTATE_SP)) { if (new_el == 0 && (spsr & PSTATE_SP)) {
/* Return to EL1 with M[0] bit set */ /* Return to EL0 with M[0] bit set */
goto illegal_return; goto illegal_return;
} }
env->aarch64 = 1; env->aarch64 = 1;
......
...@@ -1151,6 +1151,8 @@ static void handle_hint(DisasContext *s, uint32_t insn, ...@@ -1151,6 +1151,8 @@ static void handle_hint(DisasContext *s, uint32_t insn,
return; return;
case 1: /* YIELD */ case 1: /* YIELD */
case 2: /* WFE */ case 2: /* WFE */
s->is_jmp = DISAS_WFE;
return;
case 4: /* SEV */ case 4: /* SEV */
case 5: /* SEVL */ case 5: /* SEVL */
/* we treat all as NOP at least for now */ /* we treat all as NOP at least for now */
...@@ -1507,8 +1509,10 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) ...@@ -1507,8 +1509,10 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
switch (opc) { switch (opc) {
case 0: /* BR */ case 0: /* BR */
case 2: /* RET */ case 2: /* RET */
tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
break; break;
case 1: /* BLR */ case 1: /* BLR */
tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
tcg_gen_movi_i64(cpu_reg(s, 30), s->pc); tcg_gen_movi_i64(cpu_reg(s, 30), s->pc);
break; break;
case 4: /* ERET */ case 4: /* ERET */
...@@ -1527,7 +1531,6 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) ...@@ -1527,7 +1531,6 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
return; return;
} }
tcg_gen_mov_i64(cpu_pc, cpu_reg(s, rn));
s->is_jmp = DISAS_JUMP; s->is_jmp = DISAS_JUMP;
} }
...@@ -10765,6 +10768,10 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, ...@@ -10765,6 +10768,10 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu,
case DISAS_EXC: case DISAS_EXC:
case DISAS_SWI: case DISAS_SWI:
break; break;
case DISAS_WFE:
gen_a64_set_pc_im(dc->pc);
gen_helper_wfe(cpu_env);
break;
case DISAS_WFI: case DISAS_WFI:
/* This is a special case because we don't want to just halt the CPU /* This is a special case because we don't want to just halt the CPU
* if trying to debug across a WFI. * if trying to debug across a WFI.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册