4

A few years back I got into learning Arm architecture. I found Qemu and I used its realview a8 emulated board to program against based on Armv7. The board had a LCD controller, an interrupt controller, etc. I could find all their specs and ended up doing a very very basic scheduler, programming everything in Arm assembly and it was cool. Then I stopped and got busy with my job.

Now I am trying to get back to it, this time with Armv8 and AArch64. But I can't find what boards are supported for the AArch64. Querying Qemu shows the same board list for qemu-system-arm and qemu-system-aarch64. Even Armv7 based a8,a9 and A15 boards show up in qemu-system-aarch64 list. Does this mean there's no board emulation and I should program against a particular cpu like A53 (As I have seen in some examples online).

san216
  • 85
  • 1
  • 11

2 Answers2

4

The "how do I choose a board" question is quite a common one and we document the usual answer on the project's wiki: http://wiki.qemu.org/Documentation/Platforms/ARM

The short answer for AArch64 is that you want to use the "virt" board, unless you specifically know that you want to emulate one of the 64-bit Xilinx boards (which it sounds like you don't). You'll also need to specify the CPU type with -cpu cortex-a53, since the "virt" board's default is cortex-a15 (a 32-bit CPU).

The qemu-system-aarch64 binary supports all the 32-bit CPUs and boards, in the same way that qemu-system-x86_64 lets you run a 32-bit x86 CPU guest, which is why the list is so long and full of 32-bit boards. You cannot just try to use a 32-bit board with a -cpu cortex-a53, though -- this is like trying to plug a Core2Duo into an old i386 motherboard and will not function correctly even if QEMU doesn't print an error message about the combination.

For the virt board, since this is not modelling a real piece of hardware, its details are only specified in the QEMU source code and in the device tree blob we pass to the guest. For a bare metal guest OS, you need to know:

  1. there is boot flash at address 0x0 (which you can fill using the -bios or -pflash QEMU command line option)
  2. the UART is a pl011 at 0x0900000
  3. RAM starts at 0x40000000
  4. All other information about which devices are present and where they are in memory should be obtained from the device tree blob, which can be found at the bottom of RAM, assuming you are a bare metal blob loading via -bios or -pflash. (If you said you were a Linux kernel loading via -kernel then we pass the DTB in the way the kernel booting ABI specifies. Bare metal images usually shouldn't use -kernel, though.)
Peter Maydell
  • 9,707
  • 1
  • 19
  • 25
  • Last time around 4 years back when I got into arm, I did bare metal programming. I remember I used a command line like this : **$ qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin** following this [https://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/] . I read up further on how DTB works, so last time when I was using -Kernel option with Qemu, qemu was supplying me the DTB pointer in r2 but I simply ignored it and it all worked? – san216 Jul 22 '17 at 23:04
  • Also if now I have to use a virt board, I am guessing I'll have to have a DTB parser weather I use -bios option or the -Kernel option in order to gain access to the peripherals being emulated by qemu. I would like to have access to an interrupt controller I can program and do some basic Aarch64 exception handling. Is my line of thinking correct? – san216 Jul 22 '17 at 23:06
  • The versatilepb board is a model of real hardware, so QEMU models what the hardware has, and you can write guest code that assumes that hardware is present. For that kind of model, QEMU doesn't generate a DTB. The user can supply one if they like via the -dtb command line option. – Peter Maydell Jul 24 '17 at 12:03
  • You will need a DTB parser, yes. The dtb library is pretty straightforward and BSD licensed though. (In fact for purely local hacking you can cheat and hardcode the addresses of devices, if you fish them out of the source code or disassemble the dtb to look at it -- check out the '-machine dumpdtb=virt.dtb' option to get QEMU to just write its generated DTB to a file.) You probably also want to pass QEMU -machine gic-version=3 to give yourself a GICv3 rather than a GICv2 interrupt controller (v2 is the default and will work fine but v3 is a more modern bit of hardware.) – Peter Maydell Jul 24 '17 at 12:07
  • Thank you so much. I had no clue of all these Qemu options. You are awesome to be letting me know these. I'll experiment with these. One question regarding " For that kind of model, QEMU doesn't generate a DTB", I am guessing this is very specific to how Qemu models a board. On an actual versailepb board, I am assuming there would be a Dtb file somewhere in the board FW and the boot loader loads it in memory and hands over the pointer to it to the Kernel to allow a more generic Kernel/OS implementation, which is what DTB's main goal is. – san216 Jul 25 '17 at 08:35
  • Yes, for boards where we model real hardware (basically all of them *except* 'virt') QEMU just acts like a boot loader -- same as on real hardware you would configure u-boot with the guest kernel/initrd/dtb, you pass QEMU the guest kernel/initrd/dtb. (NB: although in theory you can have a dtb in the board f/w, more often it's up to the user to provide it with the kernel, because dtb contents have evolved gradually and there's no strong guarantee that old dtb + new kernel will work right.) – Peter Maydell Jul 27 '17 at 09:57
  • Returning back to this after a bit of time. You mentioned I wouldn't wanna use the -kernel option if I am running a bare metal code. I just created a file with a reset handler located at address Zero using a linker file and ran it with **qemu-system-aarch64 -s -S -machine virt -cpu cortex-a53 -kernel test.elf** and I could get my reset handler hit. Does qemu place my code at address zero in the system memory map and starts executing it but address 0x0 is reserved for boot flash as you mentioned earlier. Also is there a different option other than -kernel for bare metal. – san216 Aug 07 '17 at 21:30
  • I have mainly worked on windows and have very little experience with all things linux. Last time around my main focus was to learn the basics of ARM and I didn't bother about these auxiliary details of how the whole system comes together, the boot steps and components involved etc, This time around I am trying to focus all these too along with the ARMv8 architecture, hence these questions, some of which might come across as stupid : ) – san216 Aug 07 '17 at 21:35
  • If you use -kernel with an ELF file then QEMU will load your file where the ELF file says it needs to be loaded (whether that is a sensible place or not). However if you want "just load an ELF file" then the generic-loader device https://github.com/qemu/qemu/blob/master/docs/generic-loader.txt is a more natural way to get that behaviour -- -kernel's behaviour is not the same between different architectures as it's really supposed to be "load a Linux kernel somehow". – Peter Maydell Aug 08 '17 at 09:45
  • I asked this one as a separate question but nobody answered it. May be the question doesn't make sense but I need to understand so I'll ask it anyway: What exactly is virtio in Qemu virt board emulation. In qemu source virt.c, there's a system memory map with memory mapped range for PCIe. How is this to be used. Does qemu have any option to plug a PCIe device into the configuration and use the PCI address map range. I have limited knowledge of these lower level details. Trying to learn theconcepts of how a board is brought up in general along with some ARMv8 concepts. – san216 Aug 13 '17 at 06:34
2

Yes, you should to program against a particular cpu, as you say.

"-machine " - define a set of devices, accordance to board reference documents, without CPU.

"-cpu " - define a set of ISA features and register reset values that belongs to that particular CPU core, accordance to reference of that CPU core. (here is how qemu do it for aarch64)

Imagine qemu as an environment for software thread of target ISA. All interactions with peripheral devices are performed by load/stores and by interrupts delivery. To emulate peripheral device we need to know a MMIO base address, its interrupt number for GIC, and programming model of such device. "Board" in qemu terms is a set of such devices.

Qemu do not make any restrictions about usage of armv7 machines vs armv8 cpus, and vice versa. Here you could see how qemu place AArch64 bootloader to memory only if specified CPU supports this instruction set, else it will be Aarch32.

Also all "boards" available for qemu-system-arm is also available for qemu-system-aarch64: you could look at build configuration files at qemu sources.

Also all boards implemented at hw/arm/ dir. Their implementation is quite straightforward, all job concentrated at board_init functions: construction devices, assign interrupt lines, place bootloader & dtb at memory.

Jettatura
  • 534
  • 2
  • 10
  • So if I understand it correctly I can still use something like realview-pb-A8 and just switch the ISA using -cpu option thus getting to work with Aarch64 and using the board and peripheral specification to program them because they are CPU ISA independent. By the way, I'll admit I didn't understand the bootloader link because I don't have much context on how qemu works under the hood and these very low level things in general that I an trying to get better understanding of. Thanks for the dtb link. I have also been grappling with boot firmware, UEFI, etc and that link helped. – san216 Jul 20 '17 at 19:58