Message ID | 1451608294-16432-8-git-send-email-Andrew.Baumann@microsoft.com |
---|---|
State | New |
Headers | show |
On Thu, Dec 31, 2015 at 04:31:34PM -0800, Andrew Baumann wrote: > bcm2835/Pi1 requires more peripherals, and will be added in a later > patch series. > > Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com> > --- > > Notes: > v3: > * fix board setup to remain Pi1 compatible > * pass ram property > > hw/arm/Makefile.objs | 2 +- > hw/arm/raspi.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 181 insertions(+), 1 deletion(-) > create mode 100644 hw/arm/raspi.c > > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs > index f55f8d2..a711e4d 100644 > --- a/hw/arm/Makefile.objs > +++ b/hw/arm/Makefile.objs > @@ -11,7 +11,7 @@ obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o > obj-$(CONFIG_DIGIC) += digic.o > obj-y += omap1.o omap2.o strongarm.o > obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o > -obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o > +obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o > obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o > obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o > obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o > diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c > new file mode 100644 > index 0000000..b73f544 > --- /dev/null > +++ b/hw/arm/raspi.c > @@ -0,0 +1,180 @@ > +/* > + * Raspberry Pi emulation (c) 2012 Gregory Estrade > + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous > + * > + * Rasperry Pi 2 emulation Copyright (c) 2015, Microsoft > + * Written by Andrew Baumann > + * > + * This code is licensed under the GNU GPLv2 and later. > + */ > + > +/* Based on versatilepb.c, copyright terms below. */ > + > +/* No need to break and restart comments. > + * ARM Versatile Platform/Application Baseboard System emulation. > + * Looks unrelated, I think this has reached total-rewrite status. > + * Copyright (c) 2005-2007 CodeSourcery. > + * Written by Paul Brook > + * > + * This code is licensed under the GPL. > + */ > + > +#include "hw/arm/bcm2836.h" > +#include "qemu/error-report.h" > +#include "hw/boards.h" > +#include "hw/loader.h" > +#include "hw/arm/arm.h" > +#include "sysemu/sysemu.h" > + > +#define SMPBOOT_ADDR 0x300 /* this should leave enough space for ATAGS */ > +#define MVBAR_ADDR 0x400 /* secure vectors */ > +#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */ > +#define FIRMWARE_ADDR 0x8000 /* Pi loads kernel.img here by default */ > + > +/* Table of Linux board IDs for different Pi versions */ > +static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43}; > + > +typedef struct RaspiMachineState { > + union { > + Object obj; > + BCM2836State pi2; > + } soc; > + MemoryRegion ram; > +} RaspiMachineState; > + > +static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info) > +{ > + static const uint32_t smpboot[] = { > + 0xE1A0E00F, /* mov lr, pc */ > + 0xE3A0FE42, /* mov pc, #0x420 ;call BOARDSETUP_ADDR */ Check highbank blobs to see how machine code addresses can be done as relocatable. We should try and make these blobs relocatable from the top level address definitions where possible. > + 0xEE100FB0, /* mrc p15, 0, r0, c0, c0, 5;get core ID */ > + 0xE7E10050, /* ubfx r0, r0, #0, #2 ;extract LSB */ > + 0xE59F5014, /* ldr r5, =0x400000CC ;load mbox base */ > + 0xE320F001, /* 1: yield */ > + 0xE7953200, /* ldr r3, [r5, r0, lsl #4] ;read mbox for our core*/ > + 0xE3530000, /* cmp r3, #0 ;spin while zero */ > + 0x0AFFFFFB, /* beq 1b */ > + 0xE7853200, /* str r3, [r5, r0, lsl #4] ;clear mbox */ > + 0xE12FFF13, /* bx r3 ;jump to target */ > + 0x400000CC, /* (constant: mailbox 3 read/clear base) */ > + }; > + > + assert(SMPBOOT_ADDR + sizeof(smpboot) <= MVBAR_ADDR); > + rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot), > + info->smp_loader_start); > +} > + > +static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info) This is almost identical to Highbank, I'm guessing you are stubbing monitor firmware where you get away with nopping all the SMCs? Maybe we should split Highbanks version off to common code, and parameterise the few differences. write_board_setup_dummy_monitior(ARMCPU *cpu ..., uint32_t scr_flags); or something like it. Makes sense to be in arm/boot.c . > +{ > + static const uint32_t board_setup[] = { > + /* MVBAR_ADDR: secure monitor vectors */ > + 0xEAFFFFFE, /* (spin) */ > + 0xEAFFFFFE, /* (spin) */ > + 0xE1B0F00E, /* movs pc, lr ;SMC exception return */ > + 0xEAFFFFFE, /* (spin) */ > + 0xEAFFFFFE, /* (spin) */ > + 0xEAFFFFFE, /* (spin) */ > + 0xEAFFFFFE, /* (spin) */ > + 0xEAFFFFFE, /* (spin) */ > + /* BOARDSETUP_ADDR */ > + 0xE3A00B01, /* mov r0, #0x400 ;MVBAR_ADDR */ > + 0xEE0C0F30, /* mcr p15, 0, r0, c12, c0, 1 ;set MVBAR */ > + 0xE3A00031, /* mov r0, #0x31 ;enable AW, FW, NS */ > + 0xEE010F11, /* mcr p15, 0, r0, c1, c1, 0 ;write SCR */ If combining with HB, could you do this as read-modify-write? > + 0xE1A0100E, /* mov r1, lr ;save LR across SMC */ > + 0xE1600070, /* smc #0 ;monitor call */ > + 0xE1A0F001, /* mov pc, r1 ;return */ I'm looking at the Highbank version which doesn't save lr across the SMC and wondering why it doesn't. Is this banked by CPU mode and do you get from already-in-monitor-mode? Or simply, that Highbank code may have a bug? > + }; > + > + rom_add_blob_fixed("raspi_boardsetup", board_setup, sizeof(board_setup), > + MVBAR_ADDR); > +} > + > +static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) > +{ > + CPUState *cs = CPU(cpu); > + cpu_set_pc(cs, info->smp_loader_start); > +} > + > +static void setup_boot(MachineState *machine, int version, size_t ram_size) > +{ > + static struct arm_boot_info binfo; > + int r; > + > + binfo.board_id = raspi_boardid[version]; > + binfo.ram_size = ram_size; > + binfo.nb_cpus = smp_cpus; > + binfo.board_setup_addr = BOARDSETUP_ADDR; > + binfo.write_board_setup = write_board_setup; > + binfo.secure_board_setup = true; > + binfo.secure_boot = true; > + > + /* Pi2 requires SMP setup */ > + if (version == 2) { > + binfo.smp_loader_start = SMPBOOT_ADDR; > + binfo.write_secondary_boot = write_smpboot; > + binfo.secondary_cpu_reset_hook = reset_secondary; > + } > + > + /* If the user specified a "firmware" image (e.g. UEFI), we bypass > + the normal Linux boot process */ Multi-line comment style should be /* text * text */ > + if (machine->firmware) { > + /* load the firmware image (typically kernel.img) */ > + r = load_image_targphys(machine->firmware, FIRMWARE_ADDR, > + ram_size - FIRMWARE_ADDR); > + if (r < 0) { > + error_report("Failed to load firmware from %s", machine->firmware); > + exit(1); > + } > + > + /* set variables so arm_load_kernel does the right thing */ > + binfo.entry = FIRMWARE_ADDR; > + binfo.firmware_loaded = true; > + } else { > + /* Just let arm_load_kernel do everything for us... */ > + binfo.kernel_filename = machine->kernel_filename; > + binfo.kernel_cmdline = machine->kernel_cmdline; > + binfo.initrd_filename = machine->initrd_filename; > + } > + > + arm_load_kernel(ARM_CPU(first_cpu), &binfo); > +} > + > +static void raspi2_init(MachineState *machine) > +{ > + RaspiMachineState *s = g_new0(RaspiMachineState, 1); Use of the MachineState name-stem suggest that you are QOM inheriting from MachineState but you are not. So the struct just needs a rename to something without "MachineState" > + > + /* Initialise the SOC */ > + object_initialize(&s->soc.pi2, sizeof(s->soc.pi2), TYPE_BCM2836); > + object_property_add_child(OBJECT(machine), "soc", &s->soc.obj, > + &error_abort); > + > + /* Allocate and map RAM */ > + memory_region_allocate_system_memory(&s->ram, OBJECT(machine), "ram", > + machine->ram_size); > + memory_region_add_subregion_overlap(get_system_memory(), 0, &s->ram, 0); I thought the SoC handled this now? Why do you need to add to system_memory? > + > + /* Setup the SOC */ > + object_property_add_const_link(&s->soc.obj, "ram", OBJECT(&s->ram), > + &error_abort); Just cast using OBJECT() rather than having the union. > + object_property_set_bool(&s->soc.obj, true, "realized", &error_abort); > + > + /* Boot! */ Not really. You just setup the boot for core code to do it later. > + setup_boot(machine, 2, machine->ram_size); > +} > + > +static void raspi2_machine_init(MachineClass *mc) > +{ > + mc->desc = "Raspberry Pi 2"; > + mc->init = raspi2_init; > + mc->block_default_type = IF_SD; > + mc->no_parallel = 1; > + mc->no_floppy = 1; > + mc->no_cdrom = 1; Curious, what do these do from a user-visible point of view? Maybe we should add them to more ARM boards as they certainly make sense. Regards, Peter > + mc->max_cpus = BCM2836_NCPUS; > + /* XXX: Temporary restriction in RAM size from the full 1GB. Since > + * we do not yet support the framebuffer / GPU, we need to limit > + * RAM usable by the OS to sit below the peripherals. */ > + mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */ > +}; > +DEFINE_MACHINE("raspi2", raspi2_machine_init) > -- > 2.5.3 >
> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] > Sent: Monday, 11 January 2016 19:58 [...] > > +static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info > *info) > > This is almost identical to Highbank, I'm guessing you are stubbing monitor > firmware where you get away with nopping all the SMCs? Maybe we should > split > Highbanks version off to common code, and parameterise the few > differences. > > write_board_setup_dummy_monitior(ARMCPU *cpu ..., uint32_t scr_flags); > > or something like it. Makes sense to be in arm/boot.c . Actually, I added this only to make Linux happy (and yes, it was derived from highbank). Without it, I was seeing complaints about: Ignoring attempt to switch CPSR_A flag from non-secure world with SCR.AW bit clear Ignoring attempt to switch CPSR_F flag from non-secure world with SCR.FW bit clear I don't believe anything actually uses the SMC handler after boot. I think it's just an architectural requirement to flush the change to non-secure mode. I would prefer not to touch highbank, because I don't know how to test it. Is it better to submit this patch without the board setup? > > > +{ > > + static const uint32_t board_setup[] = { > > + /* MVBAR_ADDR: secure monitor vectors */ > > + 0xEAFFFFFE, /* (spin) */ > > + 0xEAFFFFFE, /* (spin) */ > > + 0xE1B0F00E, /* movs pc, lr ;SMC exception return */ > > + 0xEAFFFFFE, /* (spin) */ > > + 0xEAFFFFFE, /* (spin) */ > > + 0xEAFFFFFE, /* (spin) */ > > + 0xEAFFFFFE, /* (spin) */ > > + 0xEAFFFFFE, /* (spin) */ > > + /* BOARDSETUP_ADDR */ > > + 0xE3A00B01, /* mov r0, #0x400 ;MVBAR_ADDR */ > > + 0xEE0C0F30, /* mcr p15, 0, r0, c12, c0, 1 ;set MVBAR */ > > + 0xE3A00031, /* mov r0, #0x31 ;enable AW, FW, NS */ > > + 0xEE010F11, /* mcr p15, 0, r0, c1, c1, 0 ;write SCR */ > > If combining with HB, could you do this as read-modify-write? > > > + 0xE1A0100E, /* mov r1, lr ;save LR across SMC */ > > + 0xE1600070, /* smc #0 ;monitor call */ > > + 0xE1A0F001, /* mov pc, r1 ;return */ > > I'm looking at the Highbank version which doesn't save lr across the SMC and > wondering why it doesn't. Is this banked by CPU mode and do you get from > already-in-monitor-mode? Or simply, that Highbank code may have a bug? I think it's needed because I call the board setup blob on each core (from the smpboot blob), but highbank doesn't. I found that I needed to do this to avoid the above warnings on an SMP boot; I don't know why highbank doesn't. [...] > > + /* Allocate and map RAM */ > > + memory_region_allocate_system_memory(&s->ram, > OBJECT(machine), "ram", > > + machine->ram_size); > > + memory_region_add_subregion_overlap(get_system_memory(), 0, > &s->ram, 0); > > I thought the SoC handled this now? Why do you need to add to > system_memory? If I don't map it here, how do RAM accesses from the CPUs work? Or are you saying that I should still do this, but do it in the SoC not the board level? [...] > > +static void raspi2_machine_init(MachineClass *mc) > > +{ > > + mc->desc = "Raspberry Pi 2"; > > + mc->init = raspi2_init; > > + mc->block_default_type = IF_SD; > > > + mc->no_parallel = 1; > > + mc->no_floppy = 1; > > + mc->no_cdrom = 1; > > Curious, what do these do from a user-visible point of view? Maybe we > should > add them to more ARM boards as they certainly make sense. I think they turn off some redundant stuff in the UI (e.g., there's no View->Parallel menu option). I'm guessing they also disable the -cdrom and -fd* options, but didn't test that. Cheers, Andrew
On Tue, Jan 12, 2016 at 3:53 PM, Andrew Baumann <Andrew.Baumann@microsoft.com> wrote: >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] >> Sent: Monday, 11 January 2016 19:58 > [...] >> > +static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info >> *info) >> >> This is almost identical to Highbank, I'm guessing you are stubbing monitor >> firmware where you get away with nopping all the SMCs? Maybe we should >> split >> Highbanks version off to common code, and parameterise the few >> differences. >> >> write_board_setup_dummy_monitior(ARMCPU *cpu ..., uint32_t scr_flags); >> >> or something like it. Makes sense to be in arm/boot.c . > > Actually, I added this only to make Linux happy (and yes, it was derived from highbank). Without it, I was seeing complaints about: > Ignoring attempt to switch CPSR_A flag from non-secure world with SCR.AW bit clear > Ignoring attempt to switch CPSR_F flag from non-secure world with SCR.FW bit clear > > I don't believe anything actually uses the SMC handler after boot. I think it's just an architectural requirement to flush the change to non-secure mode. > > I would prefer not to touch highbank, because I don't know how to test it. Is it better to submit this patch without the board setup? > I can test Highbank for you, or you can use this project: https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg00755.html The build takes a while and costs about 50GB of disk space, but the amount of setup needed should be pretty low. FWIW, that was the test system that found the need for this FW in HB. >> >> > +{ >> > + static const uint32_t board_setup[] = { >> > + /* MVBAR_ADDR: secure monitor vectors */ >> > + 0xEAFFFFFE, /* (spin) */ >> > + 0xEAFFFFFE, /* (spin) */ >> > + 0xE1B0F00E, /* movs pc, lr ;SMC exception return */ >> > + 0xEAFFFFFE, /* (spin) */ >> > + 0xEAFFFFFE, /* (spin) */ >> > + 0xEAFFFFFE, /* (spin) */ >> > + 0xEAFFFFFE, /* (spin) */ >> > + 0xEAFFFFFE, /* (spin) */ >> > + /* BOARDSETUP_ADDR */ >> > + 0xE3A00B01, /* mov r0, #0x400 ;MVBAR_ADDR */ >> > + 0xEE0C0F30, /* mcr p15, 0, r0, c12, c0, 1 ;set MVBAR */ >> > + 0xE3A00031, /* mov r0, #0x31 ;enable AW, FW, NS */ >> > + 0xEE010F11, /* mcr p15, 0, r0, c1, c1, 0 ;write SCR */ >> >> If combining with HB, could you do this as read-modify-write? >> >> > + 0xE1A0100E, /* mov r1, lr ;save LR across SMC */ >> > + 0xE1600070, /* smc #0 ;monitor call */ >> > + 0xE1A0F001, /* mov pc, r1 ;return */ >> >> I'm looking at the Highbank version which doesn't save lr across the SMC and >> wondering why it doesn't. Is this banked by CPU mode and do you get from >> already-in-monitor-mode? Or simply, that Highbank code may have a bug? > > I think it's needed because I call the board setup blob on each core (from the smpboot blob), but highbank doesn't. I found that I needed to do this to avoid the above warnings on an SMP boot; I don't know why highbank doesn't. > > [...] >> > + /* Allocate and map RAM */ >> > + memory_region_allocate_system_memory(&s->ram, >> OBJECT(machine), "ram", >> > + machine->ram_size); >> > + memory_region_add_subregion_overlap(get_system_memory(), 0, >> &s->ram, 0); >> >> I thought the SoC handled this now? Why do you need to add to >> system_memory? > > If I don't map it here, how do RAM accesses from the CPUs work? > Do the CPUs not have their AS'es connected to your custom ASes by the SoC layer? > Or are you saying that I should still do this, but do it in the SoC not the board level? > I was hoping we could get away with 0 use of system_memory. Regards, Peter > [...] >> > +static void raspi2_machine_init(MachineClass *mc) >> > +{ >> > + mc->desc = "Raspberry Pi 2"; >> > + mc->init = raspi2_init; >> > + mc->block_default_type = IF_SD; >> >> > + mc->no_parallel = 1; >> > + mc->no_floppy = 1; >> > + mc->no_cdrom = 1; >> >> Curious, what do these do from a user-visible point of view? Maybe we >> should >> add them to more ARM boards as they certainly make sense. > > I think they turn off some redundant stuff in the UI (e.g., there's no View->Parallel menu option). I'm guessing they also disable the -cdrom and -fd* options, but didn't test that. > > Cheers, > Andrew
Hi Peter, > From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] > Sent: Tuesday, 12 January 2016 16:44 > On Tue, Jan 12, 2016 at 3:53 PM, Andrew Baumann > <Andrew.Baumann@microsoft.com> wrote: > >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] > >> Sent: Monday, 11 January 2016 19:58 > >> > + /* Allocate and map RAM */ > >> > + memory_region_allocate_system_memory(&s->ram, > >> OBJECT(machine), "ram", > >> > + machine->ram_size); > >> > + memory_region_add_subregion_overlap(get_system_memory(), 0, > >> &s->ram, 0); > >> > >> I thought the SoC handled this now? Why do you need to add to > >> system_memory? > > > > If I don't map it here, how do RAM accesses from the CPUs work? > > > > Do the CPUs not have their AS'es connected to your custom ASes by the SoC > layer? > > > Or are you saying that I should still do this, but do it in the SoC not the > board level? > > > > I was hoping we could get away with 0 use of system_memory. On further investigation, I don't think it's possible to wire up CPU ASes explicitly. There's no obvious property to set. Each cpu->as is initialised to point to address_space_memory at the top of cpu_exec_init(), and there is no way I could see to override that. I think what I have now is pretty clean, but if you'd prefer to do the mapping at into the global AS at SoC level that's fine with me too. Thanks, Andrew
On Thu, Jan 14, 2016 at 3:04 PM, Andrew Baumann <Andrew.Baumann@microsoft.com> wrote: > Hi Peter, > >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] >> Sent: Tuesday, 12 January 2016 16:44 >> On Tue, Jan 12, 2016 at 3:53 PM, Andrew Baumann >> <Andrew.Baumann@microsoft.com> wrote: >> >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] >> >> Sent: Monday, 11 January 2016 19:58 >> >> > + /* Allocate and map RAM */ >> >> > + memory_region_allocate_system_memory(&s->ram, >> >> OBJECT(machine), "ram", >> >> > + machine->ram_size); >> >> > + memory_region_add_subregion_overlap(get_system_memory(), 0, >> >> &s->ram, 0); >> >> >> >> I thought the SoC handled this now? Why do you need to add to >> >> system_memory? >> > >> > If I don't map it here, how do RAM accesses from the CPUs work? >> > >> >> Do the CPUs not have their AS'es connected to your custom ASes by the SoC >> layer? >> >> > Or are you saying that I should still do this, but do it in the SoC not the >> board level? >> > >> >> I was hoping we could get away with 0 use of system_memory. > > On further investigation, I don't think it's possible to wire up CPU ASes explicitly. There's no obvious property to set. Each cpu->as is initialised to point to address_space_memory at the top of cpu_exec_init(), and there is no way I could see to override that. > > I think what I have now is pretty clean, but if you'd prefer to do the mapping at into the global AS at SoC level that's fine with me too. > If it cant be done yet, what is here is a preferable approach. PMM has some work on list for CPU ASes that may be realted, moreso for the secure attributes though I think. Regards, Peter > Thanks, > Andrew
> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] > Sent: Thursday, 14 January 2016 15:35 > > On Thu, Jan 14, 2016 at 3:04 PM, Andrew Baumann > <Andrew.Baumann@microsoft.com> wrote: > > Hi Peter, > > > >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] > >> Sent: Tuesday, 12 January 2016 16:44 > >> On Tue, Jan 12, 2016 at 3:53 PM, Andrew Baumann > >> <Andrew.Baumann@microsoft.com> wrote: > >> >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] > >> >> Sent: Monday, 11 January 2016 19:58 > >> >> > + /* Allocate and map RAM */ > >> >> > + memory_region_allocate_system_memory(&s->ram, > >> >> OBJECT(machine), "ram", > >> >> > + machine->ram_size); > >> >> > + > memory_region_add_subregion_overlap(get_system_memory(), 0, > >> >> &s->ram, 0); > >> >> > >> >> I thought the SoC handled this now? Why do you need to add to > >> >> system_memory? > >> > > >> > If I don't map it here, how do RAM accesses from the CPUs work? > >> > > >> > >> Do the CPUs not have their AS'es connected to your custom ASes by the > SoC > >> layer? > >> > >> > Or are you saying that I should still do this, but do it in the SoC not the > >> board level? > >> > > >> > >> I was hoping we could get away with 0 use of system_memory. > > > > On further investigation, I don't think it's possible to wire up CPU ASes > explicitly. There's no obvious property to set. Each cpu->as is initialised to > point to address_space_memory at the top of cpu_exec_init(), and there is > no way I could see to override that. > > > > I think what I have now is pretty clean, but if you'd prefer to do the > mapping at into the global AS at SoC level that's fine with me too. > > > > If it cant be done yet, what is here is a preferable approach. PMM has > some work on list for CPU ASes that may be realted, moreso for the > secure attributes though I think. Ok. It looks like the patch you CCed me on would enable this. But I don't really want to wait for that with Pi, and honestly I don't think this is too bad -- there are only a handful of things in the system_memory space (the RAM, the peripherals blob, and the 2836 multi-core controller on pi2), and it seems unlikely to be of great benefit unless some future madman starts building multi-pi boards with bizarre mapping schemes. Let's hope not :) Andrew
On Thu, Jan 14, 2016 at 4:43 PM, Andrew Baumann <Andrew.Baumann@microsoft.com> wrote: >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] >> Sent: Thursday, 14 January 2016 15:35 >> >> On Thu, Jan 14, 2016 at 3:04 PM, Andrew Baumann >> <Andrew.Baumann@microsoft.com> wrote: >> > Hi Peter, >> > >> >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] >> >> Sent: Tuesday, 12 January 2016 16:44 >> >> On Tue, Jan 12, 2016 at 3:53 PM, Andrew Baumann >> >> <Andrew.Baumann@microsoft.com> wrote: >> >> >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] >> >> >> Sent: Monday, 11 January 2016 19:58 >> >> >> > + /* Allocate and map RAM */ >> >> >> > + memory_region_allocate_system_memory(&s->ram, >> >> >> OBJECT(machine), "ram", >> >> >> > + machine->ram_size); >> >> >> > + >> memory_region_add_subregion_overlap(get_system_memory(), 0, >> >> >> &s->ram, 0); >> >> >> >> >> >> I thought the SoC handled this now? Why do you need to add to >> >> >> system_memory? >> >> > >> >> > If I don't map it here, how do RAM accesses from the CPUs work? >> >> > >> >> >> >> Do the CPUs not have their AS'es connected to your custom ASes by the >> SoC >> >> layer? >> >> >> >> > Or are you saying that I should still do this, but do it in the SoC not the >> >> board level? >> >> > >> >> >> >> I was hoping we could get away with 0 use of system_memory. >> > >> > On further investigation, I don't think it's possible to wire up CPU ASes >> explicitly. There's no obvious property to set. Each cpu->as is initialised to >> point to address_space_memory at the top of cpu_exec_init(), and there is >> no way I could see to override that. >> > >> > I think what I have now is pretty clean, but if you'd prefer to do the >> mapping at into the global AS at SoC level that's fine with me too. >> > >> >> If it cant be done yet, what is here is a preferable approach. PMM has >> some work on list for CPU ASes that may be realted, moreso for the >> secure attributes though I think. > > Ok. It looks like the patch you CCed me on would enable this. But I don't really want to wait for that with Pi, and honestly I don't think this is too bad -- there are only a handful of things in the system_memory space (the RAM, the peripherals blob, and the 2836 multi-core controller on pi2), and it seems unlikely to be of great benefit unless some future madman starts building multi-pi boards with bizarre mapping schemes. Let's hope not :) > Agree Regards, Peter > Andrew
Hi Peter, > From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] > Sent: Tuesday, 12 January 2016 16:44 > On Tue, Jan 12, 2016 at 3:53 PM, Andrew Baumann > <Andrew.Baumann@microsoft.com> wrote: > >> From: Peter Crosthwaite [mailto:crosthwaitepeter@gmail.com] > >> Sent: Monday, 11 January 2016 19:58 > > [...] > >> > +static void write_board_setup(ARMCPU *cpu, const struct > arm_boot_info > >> *info) > >> > >> This is almost identical to Highbank, I'm guessing you are stubbing monitor > >> firmware where you get away with nopping all the SMCs? Maybe we > should > >> split > >> Highbanks version off to common code, and parameterise the few > >> differences. > >> > >> write_board_setup_dummy_monitior(ARMCPU *cpu ..., uint32_t > scr_flags); > >> > >> or something like it. Makes sense to be in arm/boot.c . > > > > Actually, I added this only to make Linux happy (and yes, it was derived > from highbank). Without it, I was seeing complaints about: > > Ignoring attempt to switch CPSR_A flag from non-secure world with > SCR.AW bit clear > > Ignoring attempt to switch CPSR_F flag from non-secure world with SCR.FW > bit clear > > > > I don't believe anything actually uses the SMC handler after boot. I think it's > just an architectural requirement to flush the change to non-secure mode. > > > > I would prefer not to touch highbank, because I don't know how to test it. > Is it better to submit this patch without the board setup? > > > > I can test Highbank for you, or you can use this project: > > https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg00755.html > > The build takes a while and costs about 50GB of disk space, but the > amount of setup needed should be pretty low. > > FWIW, that was the test system that found the need for this FW in HB. If it's easy for you, I'd prefer if you could test with Highbank. You should be able to apply patch 7/8 from v4 of the series (sent just now) independently. The changes are minor but significant: mostly avoiding armv7 instructions like movw, and loading the vectors and setup code as separate ROM blobs. Overall, I'm still a little worried about the long-term maintainability of this approach versus separate copies... Thanks, Andrew
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index f55f8d2..a711e4d 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -11,7 +11,7 @@ obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o obj-$(CONFIG_DIGIC) += digic.o obj-y += omap1.o omap2.o strongarm.o obj-$(CONFIG_ALLWINNER_A10) += allwinner-a10.o cubieboard.o -obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o +obj-$(CONFIG_RASPI) += bcm2835_peripherals.o bcm2836.o raspi.o obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp.o xlnx-ep108.o obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c new file mode 100644 index 0000000..b73f544 --- /dev/null +++ b/hw/arm/raspi.c @@ -0,0 +1,180 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +/* Based on versatilepb.c, copyright terms below. */ + +/* + * ARM Versatile Platform/Application Baseboard System emulation. + * + * Copyright (c) 2005-2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#include "hw/arm/bcm2836.h" +#include "qemu/error-report.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/arm/arm.h" +#include "sysemu/sysemu.h" + +#define SMPBOOT_ADDR 0x300 /* this should leave enough space for ATAGS */ +#define MVBAR_ADDR 0x400 /* secure vectors */ +#define BOARDSETUP_ADDR (MVBAR_ADDR + 0x20) /* board setup code */ +#define FIRMWARE_ADDR 0x8000 /* Pi loads kernel.img here by default */ + +/* Table of Linux board IDs for different Pi versions */ +static const int raspi_boardid[] = {[1] = 0xc42, [2] = 0xc43}; + +typedef struct RaspiMachineState { + union { + Object obj; + BCM2836State pi2; + } soc; + MemoryRegion ram; +} RaspiMachineState; + +static void write_smpboot(ARMCPU *cpu, const struct arm_boot_info *info) +{ + static const uint32_t smpboot[] = { + 0xE1A0E00F, /* mov lr, pc */ + 0xE3A0FE42, /* mov pc, #0x420 ;call BOARDSETUP_ADDR */ + 0xEE100FB0, /* mrc p15, 0, r0, c0, c0, 5;get core ID */ + 0xE7E10050, /* ubfx r0, r0, #0, #2 ;extract LSB */ + 0xE59F5014, /* ldr r5, =0x400000CC ;load mbox base */ + 0xE320F001, /* 1: yield */ + 0xE7953200, /* ldr r3, [r5, r0, lsl #4] ;read mbox for our core*/ + 0xE3530000, /* cmp r3, #0 ;spin while zero */ + 0x0AFFFFFB, /* beq 1b */ + 0xE7853200, /* str r3, [r5, r0, lsl #4] ;clear mbox */ + 0xE12FFF13, /* bx r3 ;jump to target */ + 0x400000CC, /* (constant: mailbox 3 read/clear base) */ + }; + + assert(SMPBOOT_ADDR + sizeof(smpboot) <= MVBAR_ADDR); + rom_add_blob_fixed("raspi_smpboot", smpboot, sizeof(smpboot), + info->smp_loader_start); +} + +static void write_board_setup(ARMCPU *cpu, const struct arm_boot_info *info) +{ + static const uint32_t board_setup[] = { + /* MVBAR_ADDR: secure monitor vectors */ + 0xEAFFFFFE, /* (spin) */ + 0xEAFFFFFE, /* (spin) */ + 0xE1B0F00E, /* movs pc, lr ;SMC exception return */ + 0xEAFFFFFE, /* (spin) */ + 0xEAFFFFFE, /* (spin) */ + 0xEAFFFFFE, /* (spin) */ + 0xEAFFFFFE, /* (spin) */ + 0xEAFFFFFE, /* (spin) */ + /* BOARDSETUP_ADDR */ + 0xE3A00B01, /* mov r0, #0x400 ;MVBAR_ADDR */ + 0xEE0C0F30, /* mcr p15, 0, r0, c12, c0, 1 ;set MVBAR */ + 0xE3A00031, /* mov r0, #0x31 ;enable AW, FW, NS */ + 0xEE010F11, /* mcr p15, 0, r0, c1, c1, 0 ;write SCR */ + 0xE1A0100E, /* mov r1, lr ;save LR across SMC */ + 0xE1600070, /* smc #0 ;monitor call */ + 0xE1A0F001, /* mov pc, r1 ;return */ + }; + + rom_add_blob_fixed("raspi_boardsetup", board_setup, sizeof(board_setup), + MVBAR_ADDR); +} + +static void reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) +{ + CPUState *cs = CPU(cpu); + cpu_set_pc(cs, info->smp_loader_start); +} + +static void setup_boot(MachineState *machine, int version, size_t ram_size) +{ + static struct arm_boot_info binfo; + int r; + + binfo.board_id = raspi_boardid[version]; + binfo.ram_size = ram_size; + binfo.nb_cpus = smp_cpus; + binfo.board_setup_addr = BOARDSETUP_ADDR; + binfo.write_board_setup = write_board_setup; + binfo.secure_board_setup = true; + binfo.secure_boot = true; + + /* Pi2 requires SMP setup */ + if (version == 2) { + binfo.smp_loader_start = SMPBOOT_ADDR; + binfo.write_secondary_boot = write_smpboot; + binfo.secondary_cpu_reset_hook = reset_secondary; + } + + /* If the user specified a "firmware" image (e.g. UEFI), we bypass + the normal Linux boot process */ + if (machine->firmware) { + /* load the firmware image (typically kernel.img) */ + r = load_image_targphys(machine->firmware, FIRMWARE_ADDR, + ram_size - FIRMWARE_ADDR); + if (r < 0) { + error_report("Failed to load firmware from %s", machine->firmware); + exit(1); + } + + /* set variables so arm_load_kernel does the right thing */ + binfo.entry = FIRMWARE_ADDR; + binfo.firmware_loaded = true; + } else { + /* Just let arm_load_kernel do everything for us... */ + binfo.kernel_filename = machine->kernel_filename; + binfo.kernel_cmdline = machine->kernel_cmdline; + binfo.initrd_filename = machine->initrd_filename; + } + + arm_load_kernel(ARM_CPU(first_cpu), &binfo); +} + +static void raspi2_init(MachineState *machine) +{ + RaspiMachineState *s = g_new0(RaspiMachineState, 1); + + /* Initialise the SOC */ + object_initialize(&s->soc.pi2, sizeof(s->soc.pi2), TYPE_BCM2836); + object_property_add_child(OBJECT(machine), "soc", &s->soc.obj, + &error_abort); + + /* Allocate and map RAM */ + memory_region_allocate_system_memory(&s->ram, OBJECT(machine), "ram", + machine->ram_size); + memory_region_add_subregion_overlap(get_system_memory(), 0, &s->ram, 0); + + /* Setup the SOC */ + object_property_add_const_link(&s->soc.obj, "ram", OBJECT(&s->ram), + &error_abort); + object_property_set_bool(&s->soc.obj, true, "realized", &error_abort); + + /* Boot! */ + setup_boot(machine, 2, machine->ram_size); +} + +static void raspi2_machine_init(MachineClass *mc) +{ + mc->desc = "Raspberry Pi 2"; + mc->init = raspi2_init; + mc->block_default_type = IF_SD; + mc->no_parallel = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->max_cpus = BCM2836_NCPUS; + /* XXX: Temporary restriction in RAM size from the full 1GB. Since + * we do not yet support the framebuffer / GPU, we need to limit + * RAM usable by the OS to sit below the peripherals. */ + mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */ +}; +DEFINE_MACHINE("raspi2", raspi2_machine_init)
bcm2835/Pi1 requires more peripherals, and will be added in a later patch series. Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com> --- Notes: v3: * fix board setup to remain Pi1 compatible * pass ram property hw/arm/Makefile.objs | 2 +- hw/arm/raspi.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 hw/arm/raspi.c