Merge branch 'arm-devs.for-upstream' of git://git.linaro.org/people/pmaydell/qemu-arm

* 'arm-devs.for-upstream' of git://git.linaro.org/people/pmaydell/qemu-arm:
  hw/arm11mpcore: Fix broken realview_mpcore/arm11mpcore_priv properties
  arm: add device tree support
  arm: make sure that number of irqs can be represented in GICD_TYPER.
  arm: clean up GIC constants
This commit is contained in:
Blue Swirl 2012-03-03 17:53:56 +00:00
commit d9bafcd1db
9 changed files with 161 additions and 35 deletions

View File

@ -374,6 +374,7 @@ obj-arm-y += vexpress.o
obj-arm-y += strongarm.o obj-arm-y += strongarm.o
obj-arm-y += collie.o obj-arm-y += collie.o
obj-arm-y += pl041.o lm4549.o obj-arm-y += pl041.o lm4549.o
obj-arm-$(CONFIG_FDT) += device_tree.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o

1
configure vendored
View File

@ -3485,6 +3485,7 @@ case "$target_arch2" in
gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
target_phys_bits=32 target_phys_bits=32
target_llong_alignment=4 target_llong_alignment=4
target_libs_softmmu="$fdt_libs"
;; ;;
cris) cris)
target_nptl="yes" target_nptl="yes"

View File

@ -29,6 +29,7 @@ struct arm_boot_info {
const char *kernel_filename; const char *kernel_filename;
const char *kernel_cmdline; const char *kernel_cmdline;
const char *initrd_filename; const char *initrd_filename;
const char *dtb_filename;
target_phys_addr_t loader_start; target_phys_addr_t loader_start;
/* multicore boards that use the default secondary core boot functions /* multicore boards that use the default secondary core boot functions
* need to put the address of the secondary boot code, the boot reg, * need to put the address of the secondary boot code, the boot reg,

View File

@ -202,16 +202,7 @@ static int realview_mpcore_init(SysBusDevice *dev)
} }
static Property mpcore_rirq_properties[] = { static Property mpcore_rirq_properties[] = {
DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1),
/* The ARM11 MPCORE TRM says the on-chip controller may have
* anything from 0 to 224 external interrupt IRQ lines (with another
* 32 internal). We default to 32+32, which is the number provided by
* the ARM11 MPCore test chip in the Realview Versatile Express
* coretile. Other boards may differ and should set this property
* appropriately. Some Linux kernels may not boot if the hardware
* has more IRQ lines than the kernel expects.
*/
DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),
}; };
@ -233,6 +224,15 @@ static TypeInfo mpcore_rirq_info = {
static Property mpcore_priv_properties[] = { static Property mpcore_priv_properties[] = {
DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
/* The ARM11 MPCORE TRM says the on-chip controller may have
* anything from 0 to 224 external interrupt IRQ lines (with another
* 32 internal). We default to 32+32, which is the number provided by
* the ARM11 MPCore test chip in the Realview Versatile Express
* coretile. Other boards may differ and should set this property
* appropriately. Some Linux kernels may not boot if the hardware
* has more IRQ lines than the kernel expects.
*/
DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),
}; };

View File

@ -7,11 +7,14 @@
* This code is licensed under the GPL. * This code is licensed under the GPL.
*/ */
#include "config.h"
#include "hw.h" #include "hw.h"
#include "arm-misc.h" #include "arm-misc.h"
#include "sysemu.h" #include "sysemu.h"
#include "boards.h"
#include "loader.h" #include "loader.h"
#include "elf.h" #include "elf.h"
#include "device_tree.h"
#define KERNEL_ARGS_ADDR 0x100 #define KERNEL_ARGS_ADDR 0x100
#define KERNEL_LOAD_ADDR 0x00010000 #define KERNEL_LOAD_ADDR 0x00010000
@ -208,6 +211,67 @@ static void set_kernel_args_old(const struct arm_boot_info *info)
} }
} }
static int load_dtb(target_phys_addr_t addr, const struct arm_boot_info *binfo)
{
#ifdef CONFIG_FDT
uint32_t mem_reg_property[] = { cpu_to_be32(binfo->loader_start),
cpu_to_be32(binfo->ram_size) };
void *fdt = NULL;
char *filename;
int size, rc;
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
if (!filename) {
fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename);
return -1;
}
fdt = load_device_tree(filename, &size);
if (!fdt) {
fprintf(stderr, "Couldn't open dtb file %s\n", filename);
g_free(filename);
return -1;
}
g_free(filename);
rc = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
sizeof(mem_reg_property));
if (rc < 0) {
fprintf(stderr, "couldn't set /memory/reg\n");
}
rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
binfo->kernel_cmdline);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/bootargs\n");
}
if (binfo->initrd_size) {
rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
binfo->loader_start + INITRD_LOAD_ADDR);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
}
rc = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
binfo->loader_start + INITRD_LOAD_ADDR +
binfo->initrd_size);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
}
}
cpu_physical_memory_write(addr, fdt, size);
return 0;
#else
fprintf(stderr, "Device tree requested, "
"but qemu was compiled without fdt support\n");
return -1;
#endif
}
static void do_cpu_reset(void *opaque) static void do_cpu_reset(void *opaque)
{ {
CPUState *env = opaque; CPUState *env = opaque;
@ -222,10 +286,12 @@ static void do_cpu_reset(void *opaque)
} else { } else {
if (env == first_cpu) { if (env == first_cpu) {
env->regs[15] = info->loader_start; env->regs[15] = info->loader_start;
if (old_param) { if (!info->dtb_filename) {
set_kernel_args_old(info); if (old_param) {
} else { set_kernel_args_old(info);
set_kernel_args(info); } else {
set_kernel_args(info);
}
} }
} else { } else {
info->secondary_cpu_reset_hook(env, info); info->secondary_cpu_reset_hook(env, info);
@ -243,6 +309,7 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
uint64_t elf_entry; uint64_t elf_entry;
target_phys_addr_t entry; target_phys_addr_t entry;
int big_endian; int big_endian;
QemuOpts *machine_opts;
/* Load the kernel. */ /* Load the kernel. */
if (!info->kernel_filename) { if (!info->kernel_filename) {
@ -250,6 +317,13 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
exit(1); exit(1);
} }
machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
if (machine_opts) {
info->dtb_filename = qemu_opt_get(machine_opts, "dtb");
} else {
info->dtb_filename = NULL;
}
if (!info->secondary_cpu_reset_hook) { if (!info->secondary_cpu_reset_hook) {
info->secondary_cpu_reset_hook = default_reset_secondary; info->secondary_cpu_reset_hook = default_reset_secondary;
} }
@ -300,8 +374,25 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
} else { } else {
initrd_size = 0; initrd_size = 0;
} }
info->initrd_size = initrd_size;
bootloader[4] = info->board_id; bootloader[4] = info->board_id;
bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
/* for device tree boot, we pass the DTB directly in r2. Otherwise
* we point to the kernel args.
*/
if (info->dtb_filename) {
/* Place the DTB after the initrd in memory */
target_phys_addr_t dtb_start = TARGET_PAGE_ALIGN(info->loader_start
+ INITRD_LOAD_ADDR
+ initrd_size);
if (load_dtb(dtb_start, info)) {
exit(1);
}
bootloader[5] = dtb_start;
} else {
bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
}
bootloader[6] = entry; bootloader[6] = entry;
for (n = 0; n < sizeof(bootloader) / 4; n++) { for (n = 0; n < sizeof(bootloader) / 4; n++) {
bootloader[n] = tswap32(bootloader[n]); bootloader[n] = tswap32(bootloader[n]);
@ -311,7 +402,6 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
if (info->nb_cpus > 1) { if (info->nb_cpus > 1) {
info->write_secondary_boot(env, info); info->write_secondary_boot(env, info);
} }
info->initrd_size = initrd_size;
} }
info->is_linux = is_linux; info->is_linux = is_linux;

View File

@ -13,6 +13,8 @@
/* Maximum number of possible interrupts, determined by the GIC architecture */ /* Maximum number of possible interrupts, determined by the GIC architecture */
#define GIC_MAXIRQ 1020 #define GIC_MAXIRQ 1020
/* First 32 are private to each CPU (SGIs and PPIs). */
#define GIC_INTERNAL 32
//#define DEBUG_GIC //#define DEBUG_GIC
#ifdef DEBUG_GIC #ifdef DEBUG_GIC
@ -73,8 +75,9 @@ typedef struct gic_irq_state
#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
#define GIC_GET_PRIORITY(irq, cpu) \ #define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \
(((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32]) s->priority1[irq][cpu] : \
s->priority2[(irq) - GIC_INTERNAL])
#ifdef NVIC #ifdef NVIC
#define GIC_TARGET(irq) 1 #define GIC_TARGET(irq) 1
#else #else
@ -92,8 +95,8 @@ typedef struct gic_state
#ifndef NVIC #ifndef NVIC
int irq_target[GIC_MAXIRQ]; int irq_target[GIC_MAXIRQ];
#endif #endif
int priority1[32][NCPU]; int priority1[GIC_INTERNAL][NCPU];
int priority2[GIC_MAXIRQ - 32]; int priority2[GIC_MAXIRQ - GIC_INTERNAL];
int last_active[GIC_MAXIRQ][NCPU]; int last_active[GIC_MAXIRQ][NCPU];
int priority_mask[NCPU]; int priority_mask[NCPU];
@ -174,7 +177,7 @@ static void gic_set_irq(void *opaque, int irq, int level)
{ {
gic_state *s = (gic_state *)opaque; gic_state *s = (gic_state *)opaque;
/* The first external input line is internal interrupt 32. */ /* The first external input line is internal interrupt 32. */
irq += 32; irq += GIC_INTERNAL;
if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK)) if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
return; return;
@ -316,7 +319,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
if (irq >= s->num_irq) if (irq >= s->num_irq)
goto bad_reg; goto bad_reg;
res = 0; res = 0;
mask = (irq < 32) ? cm : ALL_CPU_MASK; mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (GIC_TEST_PENDING(irq + i, mask)) { if (GIC_TEST_PENDING(irq + i, mask)) {
res |= (1 << i); res |= (1 << i);
@ -328,7 +331,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
if (irq >= s->num_irq) if (irq >= s->num_irq)
goto bad_reg; goto bad_reg;
res = 0; res = 0;
mask = (irq < 32) ? cm : ALL_CPU_MASK; mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (GIC_TEST_ACTIVE(irq + i, mask)) { if (GIC_TEST_ACTIVE(irq + i, mask)) {
res |= (1 << i); res |= (1 << i);
@ -435,8 +438,8 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
value = 0xff; value = 0xff;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (value & (1 << i)) { if (value & (1 << i)) {
int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq); int mask = (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq);
int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK; int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
if (!GIC_TEST_ENABLED(irq + i, cm)) { if (!GIC_TEST_ENABLED(irq + i, cm)) {
DPRINTF("Enabled IRQ %d\n", irq + i); DPRINTF("Enabled IRQ %d\n", irq + i);
@ -460,7 +463,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
value = 0; value = 0;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (value & (1 << i)) { if (value & (1 << i)) {
int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK; int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
if (GIC_TEST_ENABLED(irq + i, cm)) { if (GIC_TEST_ENABLED(irq + i, cm)) {
DPRINTF("Disabled IRQ %d\n", irq + i); DPRINTF("Disabled IRQ %d\n", irq + i);
@ -502,10 +505,10 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
irq = (offset - 0x400) + GIC_BASE_IRQ; irq = (offset - 0x400) + GIC_BASE_IRQ;
if (irq >= s->num_irq) if (irq >= s->num_irq)
goto bad_reg; goto bad_reg;
if (irq < 32) { if (irq < GIC_INTERNAL) {
s->priority1[irq][cpu] = value; s->priority1[irq][cpu] = value;
} else { } else {
s->priority2[irq - 32] = value; s->priority2[irq - GIC_INTERNAL] = value;
} }
#ifndef NVIC #ifndef NVIC
} else if (offset < 0xc00) { } else if (offset < 0xc00) {
@ -515,7 +518,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
goto bad_reg; goto bad_reg;
if (irq < 29) if (irq < 29)
value = 0; value = 0;
else if (irq < 32) else if (irq < GIC_INTERNAL)
value = ALL_CPU_MASK; value = ALL_CPU_MASK;
s->irq_target[irq] = value & ALL_CPU_MASK; s->irq_target[irq] = value & ALL_CPU_MASK;
} else if (offset < 0xf00) { } else if (offset < 0xf00) {
@ -523,7 +526,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
if (irq >= s->num_irq) if (irq >= s->num_irq)
goto bad_reg; goto bad_reg;
if (irq < 32) if (irq < GIC_INTERNAL)
value |= 0xaa; value |= 0xaa;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
if (value & (1 << (i * 2))) { if (value & (1 << (i * 2))) {
@ -736,7 +739,7 @@ static void gic_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, s->enabled); qemu_put_be32(f, s->enabled);
for (i = 0; i < NUM_CPU(s); i++) { for (i = 0; i < NUM_CPU(s); i++) {
qemu_put_be32(f, s->cpu_enabled[i]); qemu_put_be32(f, s->cpu_enabled[i]);
for (j = 0; j < 32; j++) for (j = 0; j < GIC_INTERNAL; j++)
qemu_put_be32(f, s->priority1[j][i]); qemu_put_be32(f, s->priority1[j][i]);
for (j = 0; j < s->num_irq; j++) for (j = 0; j < s->num_irq; j++)
qemu_put_be32(f, s->last_active[j][i]); qemu_put_be32(f, s->last_active[j][i]);
@ -745,7 +748,7 @@ static void gic_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, s->running_priority[i]); qemu_put_be32(f, s->running_priority[i]);
qemu_put_be32(f, s->current_pending[i]); qemu_put_be32(f, s->current_pending[i]);
} }
for (i = 0; i < s->num_irq - 32; i++) { for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
qemu_put_be32(f, s->priority2[i]); qemu_put_be32(f, s->priority2[i]);
} }
for (i = 0; i < s->num_irq; i++) { for (i = 0; i < s->num_irq; i++) {
@ -773,7 +776,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
s->enabled = qemu_get_be32(f); s->enabled = qemu_get_be32(f);
for (i = 0; i < NUM_CPU(s); i++) { for (i = 0; i < NUM_CPU(s); i++) {
s->cpu_enabled[i] = qemu_get_be32(f); s->cpu_enabled[i] = qemu_get_be32(f);
for (j = 0; j < 32; j++) for (j = 0; j < GIC_INTERNAL; j++)
s->priority1[j][i] = qemu_get_be32(f); s->priority1[j][i] = qemu_get_be32(f);
for (j = 0; j < s->num_irq; j++) for (j = 0; j < s->num_irq; j++)
s->last_active[j][i] = qemu_get_be32(f); s->last_active[j][i] = qemu_get_be32(f);
@ -782,7 +785,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
s->running_priority[i] = qemu_get_be32(f); s->running_priority[i] = qemu_get_be32(f);
s->current_pending[i] = qemu_get_be32(f); s->current_pending[i] = qemu_get_be32(f);
} }
for (i = 0; i < s->num_irq - 32; i++) { for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
s->priority2[i] = qemu_get_be32(f); s->priority2[i] = qemu_get_be32(f);
} }
for (i = 0; i < s->num_irq; i++) { for (i = 0; i < s->num_irq; i++) {
@ -816,7 +819,16 @@ static void gic_init(gic_state *s, int num_irq)
hw_error("requested %u interrupt lines exceeds GIC maximum %d\n", hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
num_irq, GIC_MAXIRQ); num_irq, GIC_MAXIRQ);
} }
qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - 32); /* ITLinesNumber is represented as (N / 32) - 1 (see
* gic_dist_readb) so this is an implementation imposed
* restriction, not an architectural one:
*/
if (s->num_irq < 32 || (s->num_irq % 32)) {
hw_error("%d interrupt lines unsupported: not divisible by 32\n",
num_irq);
}
qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
for (i = 0; i < NUM_CPU(s); i++) { for (i = 0; i < NUM_CPU(s); i++) {
sysbus_init_irq(&s->busdev, &s->parent_irq[i]); sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
} }

View File

@ -578,6 +578,10 @@ static QemuOptsList qemu_machine_opts = {
.name = "append", .name = "append",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "Linux kernel command line", .help = "Linux kernel command line",
}, {
.name = "dtb",
.type = QEMU_OPT_STRING,
.help = "Linux kernel device tree file",
}, },
{ /* End of list */ } { /* End of list */ }
}, },

View File

@ -2037,6 +2037,15 @@ Use @var{file1} and @var{file2} as modules and pass arg=foo as parameter to the
first module. first module.
ETEXI ETEXI
DEF("dtb", HAS_ARG, QEMU_OPTION_dtb, \
"-dtb file use 'file' as device tree image\n", QEMU_ARCH_ARM)
STEXI
@item -dtb @var{file}
@findex -dtb
Use @var{file} as a device tree binary (dtb) image and pass it to the kernel
on boot.
ETEXI
STEXI STEXI
@end table @end table
ETEXI ETEXI

8
vl.c
View File

@ -2527,6 +2527,9 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_append: case QEMU_OPTION_append:
qemu_opts_set(qemu_find_opts("machine"), 0, "append", optarg); qemu_opts_set(qemu_find_opts("machine"), 0, "append", optarg);
break; break;
case QEMU_OPTION_dtb:
qemu_opts_set(qemu_find_opts("machine"), 0, "dtb", optarg);
break;
case QEMU_OPTION_cdrom: case QEMU_OPTION_cdrom:
drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS); drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS);
break; break;
@ -3346,6 +3349,11 @@ int main(int argc, char **argv, char **envp)
exit(1); exit(1);
} }
if (!linux_boot && machine_opts && qemu_opt_get(machine_opts, "dtb")) {
fprintf(stderr, "-dtb only allowed with -kernel option\n");
exit(1);
}
os_set_line_buffering(); os_set_line_buffering();
if (init_timer_alarm() < 0) { if (init_timer_alarm() < 0) {