User:Pancakes/arm qemu realview-pb-a

From OSDev.wiki
Jump to navigation Jump to search

ARM QEMU REALVIEW-PB-A

This is an ARM board emulated by QEMU that supports 4 cores. I hope to help ease you into working with this board by giving you the information needed to at least get control over what is going on.


Multiple Cores On Boot

From my testing it appears that at least two cores boot on power up. This can cause problems because they both start at the same address. In order to control the chaos that will happen I am going to show you how to stop all other CPUs except CPU0. But, the main point is to catch the CPUs and make them each do what you need them to do.

During testing I noticed a time or two what appears to be 3 cores booting, but I was unable to produce the behavior at this time. But, this code should catch any core except for CPU0.

	/* stop all cpus but cpu 0 */
	asm("	mrc p15, 0, r0, c0, c0, 5\n\
		and r0, r0, #3\n\
		cmp r0, #0\n\
		____here: bne ____here\n"
	);

This will access the CPUID (for currently execting CPU), and if the ID is not zero then it will be placed into a busy loop thus only allowing CPU0 to continue.

Also, at this point you could branch each CPU to it's own handler and set a stack for each if desired.

This will use the serial to output the letter 'A' for CPU0, 'B' for CPU1, 'C' for CPU2, and 'D' for CPU3. But, from my testing only CPU0 and CPU1 start on a cold boot at your entry address when loading a flat binary file (by default loaded to 0x10000).

	asm("	mrc p15, 0, r0, c0, c0, 5\n\
		and r0, r0, #3\n\
		mov r1, #0x1000\n\
		lsl r1, r1, #16\n\
		orr r1, r1, #0x9000\n\
		mov r2, #65\n\
		add r2, r2, r0\n\
		str r2, [r1]\n\
		cmp r0, #0\n\
		____here: bne ____here\n"
	);

Hardware

This is the only datasheet that I have found, http://infocenter.arm.com/help/topic/com.arm.doc.dui0411d/DUI0411D_realview_platform_baseboard_ug.pdf. I am not sure how accurate it is to the revision the QEMU board was built too.

Here is a fairly accurate memory map of all the MMIO.

    /* Memory map for RealView Emulation Baseboard:  */
    /* 0x10000000 System registers.  */
    /* 0x10001000 System controller.  */
    /* 0x10002000 Two-Wire Serial Bus.  */
    /* 0x10003000 Reserved.  */
    /* 0x10004000 AACI.  */
    /* 0x10005000 MCI.  */
    /* 0x10006000 KMI0.  */
    /* 0x10007000 KMI1.  */
    /* 0x10009000 UART0.  */
    /* 0x1000a000 UART1.  */
    /* 0x1000b000 UART2.  */
    /* 0x1000c000 UART3.  */
    /* 0x1000d000 SSPI.  */
    /* 0x1000e000 SCI.  */
    /* 0x1000f000 Reserved.  */
    /* 0x10010000 Watchdog.  */
    /* 0x10011000 Timer 0+1.  */
    /* 0x10012000 Timer 2+3.  */
    /* 0x10013000 GPIO 0.  */
    /* 0x10014000 GPIO 1.  */
    /* 0x10015000 GPIO 2.  */
    /* 0x10002000 Two-Wire Serial Bus - DVI. (PB) */
    /* 0x10017000 RTC.  */
    /* 0x10018000 DMC.  */
    /* 0x10019000 PCI controller config.  */
    /* 0x10020000 CLCD.  */
    /* 0x10030000 DMA Controller.  */
    /* 0x10080000 SMC.  */
    /* 0x1e000000 GIC1. (PB) */
    /* 0x1e001000 GIC2. (PB) */
    /* 0x1e002000 GIC3. (PB) */
    /* 0x1e003000 GIC4. (PB) */
    /* 0x40000000 NOR flash.  */
    /* 0x44000000 DoC flash.  */
    /* 0x48000000 SRAM.  */
    /* 0x4c000000 Configuration flash.  */
    /* 0x4e000000 Ethernet.  */
    /* 0x4f000000 USB.  */
    /* 0x50000000 PISMO.  */
    /* 0x54000000 PISMO.  */
    /* 0x58000000 PISMO.  */
    /* 0x5c000000 PISMO.  */
    /* 0x60000000 PCI.  */
    /* 0x60000000 PCI Self Config.  */
    /* 0x61000000 PCI Config.  */
    /* 0x62000000 PCI IO.  */
    /* 0x63000000 PCI mem 0.  */
    /* 0x64000000 PCI mem 1.  */
    /* 0x68000000 PCI mem 2.  */

Setting Up PIC And Timer

Here is some simple demonstration code to setup the PIC and PIT for CPU0. For more information refer to the datasheet for each device.

This has ONLY been tested on QEMU. On real hardware you might need to do more, and you should refer to your datasheets on the hardware to diagnose problems. But, for QEMU this code should work for release QEMU-2.0.0-rc0.

#define CTRL_ENABLE			0x80
#define CTRL_MODE_FREE		0x00
#define CTRL_MODE_PERIODIC	0x40
#define CTRL_INT_ENABLE		(1<<5)
#define CTRL_DIV_NONE		0x00
#define CTRL_DIV_16			0x04
#define CTRL_DIV_256		0x08
#define CTRL_SIZE_32		0x02
#define CTRL_ONESHOT		0x01

#define REG_LOAD		0x00
#define REG_VALUE		0x01
#define REG_CTRL		0x02
#define REG_INTCLR		0x03
#define REG_INTSTAT		0x04
#define REG_INTMASK		0x05
#define REG_BGLOAD		0x06


        uint8   *picmmio1;
        uint8   *picmmio0;
        uint32  *pitmmio;
        uint32  *serialmmio;

        picmmio0 = (uint8*)0x1f000100;
        picmmio1 = (uint8*)0x1f001000;
        pit = (uint32*)0x10011000;
        serialmmio = (uint32*)0x10009000;
        

	/* talk to CPU interface*/
	/* enable PIC for CPU 0 */
	picmmio0[0] = 1;
	/* set priority mask for CPU 0 */
	picmmio0[4] = 0xff;

	/* talk to actual PIC stuff */
	/* enable PIC */
	picmmio1[0] = 1;
        /* 36 is the interrupt (on cold boot) for the timer below */	
	picmmio1[0x100 + (36 >> 3)] = (1 << (36 & 7));
	
	pitmmio[REG_LOAD] = KTASKTICKS;
	pitmmio[REG_BGLOAD] = KTASKTICKS;			
	pitmmio[REG_CTRL] = CTRL_ENABLE | CTRL_MODE_PERIODIC | CTRL_SIZE_32 | CTRL_DIV_NONE | CTRL_INT_ENABLE;
        pitmmio[REG_INTCLR] = ~0;		/* make sure interrupt is clear (might not be mandatory) */

        /* write character to serial output */
        serialmmio[0] = 'A';