http://www.linuxforu.com/2011/07/qemu-for-embedded-systems-development-part-2/
Using QEMU for Embedded Systems Development, Part 2
By Manoj Kumar on July 1, 2011 in Coding, Developers · 3 Comments
QEMU for embedded programming
In the previous articles, we learnt how to use QEMU for a generic Linux OS installation, for networking using OpenVPN and TAP/TUN, for cross-compilation of the Linux kernel for ARM, to boot the kernel from QEMU, and how to build a small filesystem and then mount it on the vanilla kernel. Now we will step out further.
First of all, I would like to explain the need for a bootloader. The bootloader is code that is used to load the kernel into RAM, and then specify which partition will be mounted as the root filesystem. The bootloader resides in the MBR (Master Boot Record). In general-purpose computing machines, an important component is the BIOS (Basic Input Output System). The BIOS contains the low-level drivers for devices like the keyboard, mouse, display, etc. It initiates the bootloader, which then loads the kernel. Linux users are very familiar with boot-loaders like GRUB (Grand Unified Boot-Loader) and LILO (Linux Loader).
Micro-controller programmers are very familiar with the term “Bare-Metal Programming”. It means that there is nothing between your program and the processor — the code you write runs directly on the processor. It becomes the programmer’s responsibility to check each and every possible condition that can corrupt the system.
Now, let us build a small program for the ARM Versatile Platform Baseboard, which will run on the QEMU emulator, and then print a message on the serial console. Downloaded the tool-chain for ARM EABI from here. As described in the previous article, add this tool-chain in your PATH.
By default, QEMU redirects the serial console output to the terminal, when it is initialised with the nographic option:
qemu−system−arm−−help|grepnographic−nographicdisablegraphicaloutputandredirectserialI/Ostoconsole.Whenusing−nographic,press′ctrl−ah′togetsomehelp.Wecanmakegooduseofthisfeature;let′swritesomedatatotheserialport,anditcanbeagoodworkingexample.Beforegoingfurther,wemustmakesurewhichprocessortheGNUEABItool−chainsupports,andwhichprocessorQEMUcanemulate.Thereshouldbeasimilarprocessorsupportedbyboththetool−chainandtheemulator.Let′scheckfirstinQEMU.Intheearlierarticles,wecompiledtheQEMUsourcecode,sousethatsourcecodetogetthelistofthesupportedARMprocessors: cd (your-path)/qemu/qemu-0.14.0/hw
grep "arm" versatilepb.c
#include "arm-misc.h"
static struct arm_boot_info versatile_binfo;
cpu_model = "arm926";
It’s very clear that the “arm926″ is supported by QEMU. Let’s check its availability in the GNU ARM tool-chain: cd (your-path)/CodeSourcery/Sourcery_G++_Lite/share/doc/arm-arm-none-eabi/info
catgcc.info|greparm|head−n20..‘strongarm1110′,‘arm8′,‘arm810′,‘arm9′,‘arm9e′,‘arm920′,‘arm920t′,‘arm922t′,‘arm946e−s′,‘arm966e−s′,‘arm968e−s′,‘arm926ej−s′,‘arm940t′,‘arm9tdmi′,‘arm10tdmi′,‘arm1020t′,‘arm1026ej−s′,‘arm10e′,‘arm1020e′,‘arm1022e′,‘arm1136j−s′,Great!!TheARM926EJ−SprocessorissupportedbytheGNUARMtool−chain.Now,let′swritesomedatatotheserialportofthisprocessor.AswearenotusinganyheaderfilethatdescribestheaddressofUART0,wemustfinditmanually,fromthefile(your−path)/qemu/qemu−0.14.0/hw/versatilepb.c:/∗0x101f0000Smartcard0.∗//∗0x101f1000UART0.∗//∗0x101f2000UART1.∗//∗0x101f3000UART2.∗/Opensourcecodeissopowerful,itgivesyoueachandeverydetail.UART0ispresentataddress0x101f1000.Fortestingpurposes,wecanwritedatadirectlytothisaddress,andcheckoutputontheterminal.Ourfirsttestprogramisabare−metalprogramrunningdirectlyontheprocessor,withoutthehelpofabootloader.Wehavetocreatethreeimportantfiles.Firstofall,letusdevelopasmallapplicationprogram(init.c):volatileunsignedchar∗constUART0PTR=(unsignedchar∗)0x0101f1000;voiddisplay(constchar∗string)while(∗string!=′\0′)∗UART0PTR=∗string;string++;intmyinit()display("HelloOpenWorld\n");Let′srunthroughthiscodesnippet.First,wedeclaredavolatilevariablepointer,andassignedtheaddressoftheserialport(UART0).Thefunctionmyinit(),isthemainroutine.Itmerelycallsthefunctiondisplay(),whichwritesastringtotheUART0.Engineersfamiliarwithbase−levelmicro−controllerprogrammingwillfindthisveryeasy.Ifyouarenotexperiencedinembeddedsystemsprogramming,thenyoucansticktothebasicsofdigitalelectronics.Themicroprocessorisanintegratedchip,withinput/outputlines,differentports,etc.TheARM926EJ−Shasfourserialports(informationobtainedfromitsdata−sheet);andtheyhavetheirdatalines(theaddress).Whentheprocessorisprogrammedtowritedatatooneoftheserialports,itwritesdatatotheselines.That′swhatthisprogramdoes.Thenextstepistodevelopthestartupcodefortheprocessor.Whenaprocessorispoweredon,itjumpstoaspecifiedlocation,readscodefromthatlocation,andexecutesit.Eveninthecaseofareset(likeonadesktopmachine),theprocessorjumpstoapredefinedlocation.Here′sthestartupcode,startup.s:.globalStartStart:LDRsp,=sptopBLmyinitB.Inthefirstline,Startisdeclaredasglobal.ThenextlineisthebeginningofStart‘scode.Wesettheaddressofthestacktosptop.(TheinstructionLDRwillmovethedatavalueofsptopinthestackpointer(sp).TheinstructionBLwillinstructtheprocessortojumptomyinit(previouslydefinedininit.c).ThentheprocessorwillstepintoaninfiniteloopwiththeinstructionB.,whichislikeawhile(1)orfor(;;)loop.Ifwedon′tdothis,oursystemwillcrash.Thebasicsofembeddedsystemsprogrammingisthatourcodeshouldrunintoaninfiniteloop.Now,thefinaltaskistowritealinkerscriptforthesetwofiles(linker.ld):ENTRY(Start)SECTIONS.=0x10000;startup:startup.o(.text).data:∗(.data).bss:∗(.bss).=.+0x500;sptop=.;ThefirstlinetellsthelinkerthattheentrypointisStart(definedinstartup.s).Asthisisabasicprogram,wecanignoretheInterruptssection.TheQEMUemulator,whenexecutedwiththe−kerneloption,startsexecutionfromtheaddress0x10000,sowemustplaceourcodeatthisaddress.That′swhatwehavedoneinLine4.Thesection“SECTIONS”,definesthedifferentsectionsofaprogram.Inthis,startup.oformsthetext(code)part.Thencomesthesubsequentdataandthebsspart.Thefinalstepistodefinetheaddressofthestackpointer.Thestackusuallygrowsdownward,soit′sbettertogiveitasafeaddress.Wehaveaverysmallcodesnippet,andcanplacethestackat0x500aheadofthecurrentposition.Thevariablesptopwillstoretheaddressforthestack.Wearenowdonewiththecodingpart.Let′scompileandlinkthesefiles.Assemblethestartup.sfilewith: arm-none-eabi-as -mcpu=arm926ej-s startup.s -o startup.o
Compile init.c:
arm−none−eabi−gcc−c−mcpu=arm926ej−sinit.c−oinit.oLinktheobjectfilesintoanELFfile: arm-none-eabi-ld -T linker.ld init.o startup.o -o output.elf
Finally, create a binary file from the ELF file:
arm−none−eabi−objcopy−Obinaryoutput.elfoutput.binTheaboveinstructionsareeasytounderstand.AllthetoolsusedarepartoftheARMtool−chain.Checktheirhelp/manpagesfordetails.Afterallthesesteps,finallywewillrunourprogramontheQEMUemulator: qemu-system-arm -M versatilepb -nographic -kernel output.bin
The above command has been explained in previous articles (1, 2), so we won’t go into the details. The binary file is executed on QEMU and will write the message “Hello Open World” to UART0 of the ARM926EJ-S, which QEMU redirects as output in the terminal.
Acknowledgement
This article is inspired by the following blog post: “Hello world for bare metal ARM using QEMU“.
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(284) | 评论(0) | 转发(0) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通