Embedded linux primer 2nd edition pdf download
One additional note regarding this example: Almost universally, the initrd image is compressed. This bootloader was designed with support for directly booting the Linux kernel. Using U-Boot, it is easy to include an initrd image with the kernel image. Listing shows a typical boot sequence containing an initial ramdisk image. Since it is based on busybox, it has many capabilities. Because busybox is 6.
You will learn more about busybox in Chapter It is conceptually similar to initrd, as described in the preceding section. For more details, see the Linux kernel documentation for this subsystem at From a practical perspective, initramfs is much easier to use. This simple difference contributes to the ease of use of initramfs and removes the requirement that you must be root to create it. It is integrated into the Linux kernel source tree, and a small default nearly empty image is built automatically when you build the kernel image.
Making changes to it is far easier than building and loading a new initrd image. Listing shows the contents of the Linux kernel The contents of Listing are shown after a kernel has been built. This causes driver module initialization routines to be called. This was described in detail in Chapter 5 and shown in Listing S Kconfig Makefile A build script in The default for recent Linux kernels looks like Listing Compiling busybox statically means it is not dependent on any system libraries.
The reason for the init symlink should be noted. This logic is found in To use it, simply add it to your kernel command line. Servers with large disk systems can take many hours to properly fsck through a collection of large EXT2 partitions. Each embedded project will likely have its own shutdown strategy. What works for one might or might not work for another. The scale of shutdown can range from a full System V shutdown scheme, to a simple script, to halt or reboot.
Several Linux utilities are available to assist in the shutdown process, including the shutdown, halt, and reboot commands. Of course, these must be available for your chosen architecture. If init is being used, issuing the command init 0 halts the system. The Linux shutdown command in conjunction with init exhibits this behavior. With this knowledge, you should be able to customize your own embedded system startup behavior.
They can be difficult to build from scratch because of complex dependencies by each application. System initialization based on init was presented, along with sample startup script configurations. We presented the mechanism and a sample configuration for using this powerful feature. It is easier to use, does not require loading a separate image, and is built automatically during each kernel build. A critical component of an embedded system, the bootloader provides the foundation from which the primary system software is spawned.
We follow this with an introduction to some common features of bootloaders. Armed with this background, we take a detailed look at a popular bootloader used for embedded systems.
We conclude this chapter by introducing a few of the more popular bootloaders. Numerous bootloaders are in use today. It would be impractical to go into much detail on even the most popular ones.
Therefore, we have chosen to explain concepts and use examples based on one of the more popular bootloaders in the open source community for Power Architecture, MIPS, ARM, and other architectures: the U-Boot bootloader. This early initialization code is part of the bootloader and is responsible for breathing life into the processor and related hardware components. Hardware designers use this information to arrange the layout of Flash memory on the board and to select which address range s the Flash memory responds to.
The bootloader provides this early initialization code and is responsible for initializing the board so that other programs can run.
This fact alone presents many challenges, some of which we examine here. It is responsible for locating, loading, and passing control to the primary operating system.
The application developer does not need to know or care much about these details. This is because the C runtime environment transparently provides this infrastructure. A bootloader developer enjoys no such luxury.
Every resource that a bootloader requires must be carefully initialized and allocated before it is used. They require specialized hardware controllers to enable read and write cycles.
To further complicate matters, DRAM must be constantly refreshed, or the data contained within will be lost. Modern DRAM chips support many modes of operation, such as burst mode and dual data rate for high-performance applications. Setting up a DRAM controller is the source of much frustration for the newcomer to embedded development.
This topic is beyond the scope of this book, but you can learn more about this important concept by consulting the references at the end of this chapter. Appendix D, 1 Some embedded designs protect the bootloader and provide callbacks to bootloader routines, but this is almost never a good design approach. Linux is far more capable than bootloaders, so there is often little point in doing so.
After it is initialized, memory can be used as a resource. Again, the complexity arises from the level of resources available for the bootloader to rely on. In a fully operational computer system running an operating system such as Linux, it is relatively easy to compile a program and invoke it from nonvolatile storage. The runtime libraries, operating system, and compiler work together to create the infrastructure necessary to load a program from nonvolatile storage into memory and pass control to it.
When compiled, it can be loaded into memory and executed simply by typing the name of the executable hello on the command line assuming, of course, that the executable exists somewhere on your PATH. This infrastructure does not exist when a bootloader gains control upon power-on. Instead, the bootloader must create its own operational context and move itself, if required, to a suitable location in RAM.
Furthermore, additional complexity is introduced by the requirement to execute from a read-only medium. The linker places startup prologue and shutdown epilogue code into the image.
These objects set up the proper execution context for your application, which typically starts at main. This is absolutely not the case with a typical bootloader. When the bootloader gets control, there is no context or prior execution environment. A typical system might 7. Consider what this means. In a typical C function, any local variables are stored on the stack, so a simple function like the one shown in Listing is unusable. When a bootloader gains control on power-on, there is no stack and no stack pointer.
The bootloader must create this execution context before any C functions are called. When the bootloader is compiled and linked, the developer must exercise complete control over how the image is constructed and linked. This is especially true if the bootloader is to relocate itself from Flash to RAM. Other processors use similar methods with different details.
How does a developer specify the layout of a binary image? A complete description of linker command scripts syntax is beyond the scope of this book. It directs the linker to place the section of code called.
This is because an erased Flash memory array contains all 1s. Notice that this code section cannot exceed 4 bytes in length in a machine with only 32 address bits. This branch location is a 4-byte Power Architecture instruction. Default values designed into the hardware ensure that fetches from Flash memory work properly. This also ensures that the system clock has some default values, but little else can be assumed. Indeed, most processors have no DRAM available at startup for temporary storage of variables or, worse, for a stack that is required to use C program calling conventions.
Some processors designed for embedded use have small amounts of on-chip static RAM available. When RAM is available, a stack can be allocated using part of that RAM, and a proper context can be constructed to run higher-level languages such as C.
This allows the rest of the processor and platform initialization to be written in something other than assembly language. Most of these have some level of commonality of features. For example, all of them have some capability to load and execute other programs, particularly an operating system.
Most interact with the user through a serial port. Support for various networking subsystems such as Ethernet is a very powerful but less common feature. The capability of a bootloader to support a wide variety of architectures and processors can be an important feature to larger development organizations. It is not uncommon for a single development organization to have multiple processors spanning more than one architecture. Investing in a single bootloader across multiple platforms ultimately results in lower development costs.
This section studies an existing bootloader that has become very popular in the embedded Linux community. It is maintained by Wolfgang Denx and hosted at www. U-Boot supports multiple architectures and has a large following of embedded developers and hardware manufacturers who have adopted it for use in their projects and who have contributed to its development.
They can be found in The last few lines are actual processor register values required to initialize the external bus controller for memory banks 0 and 1. You can see that these values can come only from detailed knowledge of the board and processor. This mechanism can be used to tell U-Boot how much and what kind of memory is on a given board, and where that memory is mapped. In a development environment, this is a huge time saver. Loading even a modest kernel image over a serial port 7. Furthermore, serial links are more prone to errors from poorly behaved serial terminals, line noise, and so on.
It should be noted that the DHCP protocol supports many more parameters than those detailed in Table These are simply the more common parameters you might encounter for embedded systems.
This is not trivial and is one of the tasks more suited to full-blown operating systems. Even with the underlying complexity, methods exist for loading images from this class of device. The simplest method is to support the hardware only.
The bootloader simply raw-loads from absolute sectors on the device. This scheme can be used by dedicating an unformatted partition from sector 0 on an IDE-compatible device such as CompactFlash and loading the data found there without any structure imposed on the data. After the kernel boots, Linux device drivers can be used to access the additional partitions. If you are porting Chapter 7 Bootloaders U-Boot to a custom board, you will likely have to modify U-Boot to understand your particular hardware.
The image is loaded into system memory at physical address 0x They exist in the U-Boot supports a large variety of popular CPUs and CPU families in use today, and a much larger collection of reference boards based on these processors. If you must add a new CPU, plan on substantially more effort. The good news is that someone before you has probably done the bulk of the work. Whether you are 7. Numerous other devices complete the design.
Many boards in the U-Boot source tree support the GP processor. After examining a few, we choose the AR The goal is to minimize any development work by borrowing from similar architectures in the spirit of open source. That step will come later. Image Name: Linux In this example, we appended -ep to indicate it is a kernel for that target. Throughout this discussion, these terms are used interchangeably. The DTB is a database that represents the hardware components on a given board.
A great starting point is the Denx Software Engineering wiki page. References are provided at the end of this chapter. Listing shows a boot sequence on a Power Architecture target using U-Boot. The primary difference here is that we loaded two images. The large image 1.
Notice that we placed the kernel and DTB at addresses 0x and 0xc, respectively. All the messages from Listing are produced by U-Boot. When we use the bootm command to boot the kernel, we add a third parameter, which tells U-Boot where we loaded the DTB. By now, you are probably wondering where the DTB came from.
If you look at the powerpc branch of any recent Linux kernel tree, you will see a directory called The hard answer is that you must provide a DTB for your custom board. Start with something close to your platform, and modify from there. At the risk of sounding redundant, there is no easy path. Listing shows a snippet of the device tree source DTS from a recent kernel source tree. Some of the data shown in Listing is self-explanatory.
A device node is an entry in the device tree, usually describing a single device or bus. Each node contains a set of properties that describe it.
It is, in fact, a tree structure. It can easily be represented by a familiar tree view, as shown in Listing Many of the CPU device node properties are self-explanatory. For example, we can see that the CPU has data and instruction cache line sizes of Chapter 7 Bootloaders 32 bytes and that these caches are both 32KB in size 0x bytes.
We see a couple properties that show clock frequencies, such as timebase-frequency and clockfrequency, both of which indicate that they are set by U-Boot. The properties called address-cells and size-cells are worth explaining. The memory device node offers no mysteries. From this node, it is obvious that this platform contains a single bank of memory starting at address 0, which is MB in size.
One of the most useful is the document produced by Power. Although a git tree is hosted on kernel. It is quite straightforward to use the device tree compiler. Note that the dtc compiler allows you to go in both directions. The command example just shown performs a compile from source to device tree binary, whereas a command like this produces source from the binary: myboard. These are found in One good reason to do this is if you are trying to boot a newer kernel on a target that has an older version of U-Boot that does not support the device tree blob.
Notice that some of these targets have the device tree binary included in the composite kernel image. You need to decide which is most appropriate for your particular platform and application. This is not intended to be a thorough tutorial; doing so would require a book of its own.
Consult the last section of this chapter for further study. Lilo has several components. Therefore, its primary purpose is simply to load and pass control to a secondary loader. Listing shows a simple lilo. It contains a delay instruction to wait for the user to press a key before the timeout 5 seconds, in this case. This allows the system operator to select from a list of OS images to boot. If the system operator presses the Tab key before the timeout, Lilo presents a list to choose from.
Lilo uses the label tag as the text to display for each image. Lilo loads this image from the hard drive. It has many enhanced features not found in Lilo. GRUB also supports booting across a network, which can be a tremendous asset in an embedded environment. The title entries from Listing are the image names presented to the user. Images are counted starting from 0.
In Listing , the default boot image is a Linux 2. Micromonitor is in use by board vendors such as Cogent and others. Has it been ported to a board similar to my own?
Does it support the features I need? Does it support the hardware devices I intend to use? Is there a large community of users where I might get support? Are there any commercial vendors from which I can purchase support? These are some of the questions you must answer when considering what bootloader to use in your embedded project. We covered one of the most popular bootloaders, U-Boot, in some detail.
We walked through the steps of a typical port to a board with similar support in U-Boot. It is the first piece of software that takes control upon applying power. It supports a large number of processors, reference hardware platforms, and custom boards. Appendix B contains a list of all the standard U-Boot command sets supported in a recent U-Boot release.
Lilo Bootloader www. The familiar device driver model found in UNIX and Linux provides a natural partitioning of functionality between your application code and hardware or kernel devices. This chapter helps you understand this model and the basics of Linux device driver architecture.
After reading this chapter, you will have a solid foundation for continuing your study of device drivers using one of the references listed at the end of this chapter. This chapter begins by presenting Linux device driver concepts and describing the build system for drivers within the kernel source tree. We examine the Linux device driver architecture and present an example of a simple working driver. We introduce the user space utilities for loading and unloading kernel modules.
We conclude this chapter with a discussion of the relationship between device drivers and the GNU Public License. This is because many popular legacy real-time operating systems do not have a similar architecture. The idea of virtual memory and kernel space versus user space frequently introduces complexity that is unfamiliar to experienced embedded developers.
One of the fundamental purposes of a device driver is to isolate the user programs from ready access to critical kernel data structures and hardware devices.
Furthermore, a well-written device driver hides from the user the complexity and variability of the hardware device. The device driver handles the details and isolates the user from the complexities and perils of hardware device 1 The terms module and device driver are used here interchangeably. The device driver provides a consistent user interface to a large variety of hardware devices.
Assuming that your device driver is reasonably well behaved, you can insert and remove the device driver from a running kernel at will during the development cycle instead of rebooting the kernel every time you want to test a change. Loadable modules have particular importance to embedded systems. For example, the module itself can be updated in a live system without the need for a reboot. Modules can be stored on media other than the root boot device, which can be space-constrained.
Of course, device drivers can also be statically compiled into the kernel, and, for many drivers, this is completely appropriate. In this case, the necessary modules and a script to load them would be included in the initial ramdisk image. Loadable modules are installed after the kernel has booted. Linux can request a module when a service is requested that requires a particular module.
Many terms have been and continue to be used interchangeably when discussing Linux device drivers. Throughout this and later chapters, the terms device driver, loadable kernel module LKM , loadable module, and module are all used to describe a kernel device driver module. Character devices can be thought of as serial streams of sequential data.
Examples of character devices include serial ports and keyboards. Block devices are characterized by the capability to read and write blocks of data to and from random locations on an addressable medium.
Examples of block devices include hard drives and USB Flash drives. Listing shows a loadable device driver module that contains the bare minimum structure to be loaded and unloaded by a running kernel. A device driver is a special kind of binary module. Unlike a stand-alone binary executable application, a device driver cannot simply be executed from a command prompt.
The 2. When properly built, the device driver binary module contains a. The build steps and compiler options required to create the. Here we outline a set of steps to harness the power of the Linux kernel build system without requiring you to become an expert in it, which is beyond the scope of this book. Although it is possible to load and execute kernel modules built against a different kernel version, it is risky to do so unless you are certain that the module does not rely on any features of your new kernel.
It is certainly possible to build your drivers outside the kernel source tree. For the sample driver introduced in Listing , the following changes were made to the stock Linux kernel source tree to enable building this sample driver. Starting from the top-level Linux source directory, create a directory under Add a menu item to the kernel configuration to enable building examples and to specify a built-in or loadable kernel module. Add the new examples subdirectory to the Create a makefile for the new examples directory, and add the hello1.
Create the driver hello1. Adding the examples directory under the Listing contains a patch that, when applied to the Do not compile examples. Y Yes. Compile examples and link with the final kernel image.
M Module. Compile examples as a dynamically loadable module. A dash - in the check box selects module, as indicated in the M column on the right. A check mark in the check box selects yes, indicating that the driver module should be compiled as part of the kernel proper. An empty check box indicates that the option is not selected. The additional lines of context are there so that the patch utility can determine where to insert the new line. Other than for consistency and readability, the location is irrelevant.
Having completed the steps in this section, the infrastructure is now in place to build the sample device driver. The beauty of this approach is that the driver is built automatically whenever a kernel build is invoked. Before we can load the module, we need to copy it to an appropriate location on our target system.
Although we could put it anywhere we want, a convention is in place for kernel modules and where they are populated on a running Linux system. As with module compilation, it is easiest to let the kernel build system do that for us. Since we are embedded developers, and we are crosscompiling, this is probably not what you intended. Listing shows the output resulting from loading and subsequently unloading the device driver on the embedded system. Erase from 0xx Program from 0xxdcd0 at 0x Unlock from 0x50fex Erase from 0x50fex Program from 0x03fdfx03fff at 0x50fe Lock from 0x50fex First, we load the image to be used to create the new partition.
We use our kernel image for the example and load it to memory address 0x We instruct Redboot to create the new partition in an area of Flash starting at 0x Compare this with the output shown in Listing The Redboot command for accomplishing this is shown next. Indeed, possibly the most straightforward way, though perhaps not the simplest, is to manually pass the partition information directly on the kernel command line. Of course, as you have learned, some bootloaders make that easy such as U-Boot , whereas others do not have a facility to pass a kernel command line to the kernel upon boot.
You can also pass the partition a name and, optionally, the read-only attribute. The Linux kernel source tree contains many examples of mapping drivers, located in Any one of these will provide a good example of how to create your own. The implementation details vary by architecture. Listing reproduces a section of Chances are very good that your chosen chip is also supported. This version supports many Flash types. Using one of the many examples in Better yet, unless the chip was just introduced with some newfangled interface, someone probably has already produced a driver.
Listing reproduces the relevant portions of The utilities are built separately from the primary MTD subsystem, which should be built from within your Linux kernel source tree. These utilities can be built in a manner similar to any other cross-compiled user space code. You must exercise caution when using these utilities, because Linux provides no protection from mistakes. A single-digit typo can wipe out the bootloader on your hardware platform. We highlight the most common and useful ones and leave it as an exercise for you to explore the rest.
A recent MTD snapshot contained more than 20 binary utilities. Hopefully their names are descriptive enough to give you some idea of their function. Our new partition created in Listing MyKernel shows up in the kernel running on the Coyote board, as detailed in Listing Here you can see the new partition we created instantiated as the kernel device mtd1.
We have the option of using the bootloader or the Linux kernel to place the initial image on the Redboot Flash partition. The following command creates a new partition on the Flash device called RootFS starting at physical memory address 0x, with a length of 30 blocks.
Remember, a block, generically called an erase unit, is KB on this Flash chip. This is accomplished with the following command from a Linux command prompt on your target board. As mentioned many times throughout this book, NFS root mount is your best friend during development. Listing displays the sequence. Both mtdchar and mtdblock are pseudo drivers used to provide either character-based or block-oriented access to the underlying Flash partition.
The second subtlety is the use of the read-only ro command-line switch on the mount command. It is perfectly acceptable to mount an ext2 image from Flash using the MTD block emulation driver for read-only purposes. However, there is no support for writing to an ext2 device using the mtdblock driver. This is because ext2 has no knowledge of Flash erase blocks. In addition to compression, JFFS2 supports wear leveling, a feature designed to increase Flash lifetime by fairly distributing the write cycles across the blocks of the device.
As mentioned elsewhere in this book, you should consider Flash memory as a writeoccasional medium. Be especially aware of any logging programs, such as syslogd. Here is the listing in long -l format: ls -l rootfs. Listing shows the command and results. Next we invoke the MTD utility mkfs. The -e instructs mkfs. The default block size is 64KB. When you are working with limited Flash memory, this is a substantial reduction in precious Flash resource usage.
It instructs the mkfs. Because we are targeting the ADI Engineering Coyote board, which contains an Intel IXP processor running in big-endian mode, this step is crucial for proper operation. If you fail to specify big-endian, you will get several screens full of complaints from the kernel as it tries to Listing provides the details, running the MTD utilities on our target hardware.
This provides progress updates and other useful information during the Flash operations. We have already seen how to boot a kernel with the Redboot exec command. JFFS2 maintains its indexing metadata in system memory and must read this index to build a complete directory tree each time the system boots.
In contrast, UBIFS maintains its indexing metadata on the Flash device itself, negating the need to scan and rebuild this data on each mount.
This will become clear in a moment. You will also need a fairly recent version of MTD Utils installed on your development workstation. It is critical that the correct parameters are passed to mkfs. For this example, we have named it ubifs. We use the ubinize tool part of the mtd-utils package for this.
Once again, we must use the correct parameters for our target environment. The ubinize. Here we specify the physical erase block size, given by the -p parameter. Recall from Listing that this was the image produced by the mkfs. We will use the ubiformat command for that. This is because NAND Flash as used by the UBI layer contains special headers that record the erase count, among other things, for each physical erase block.
This is used for wear leveling. Using ubiformat preserves these error count headers. Listing shows the details. Consider your need to use this erase utility, because it does not preserve error counters. To do so, pass the following kernel command-line parameters to the kernel: ubi. MTD is present in some form on many embedded systems. Several figures in this chapter detailed the configuration options.
Figure showed the chip drivers supported in a recent Linux kernel snapshot. These include Redboot partition information, kernel command-line parameters, and mapping drivers. In this chapter, we built a JFFS2 image and mounted it as root on our target device. It often serves as the foundation for a resource-limited embedded platform. This chapter introduces BusyBox and provides an excellent starting point for customizing your own BusyBox installation.
Previous chapters referred to BusyBox. This chapter presents the details of this useful package. After a brief introduction to BusyBox, we explore the BusyBox configuration utility. This is used to tailor BusyBox to your particular requirements. We then discuss the requirements for cross-compiling the BusyBox package. BusyBox operational issues are considered, including how it is used in an embedded system. We examine the BusyBox initialization sequence and explain how it departs from the standard System V initialization.
This chapter also presents a sample initialization script. After seeing the steps for installing BusyBox on a target system, you will learn about some of the BusyBox commands and their limitations. BusyBox provides compact replacements for many traditional full-blown utilities found on most desktop and embedded Linux distributions.
In some cases, only a subset of the usual command-line options are supported. The steps are similar: 1. Execute a configuration utility and enable your choice of features. Run make to build the package. Install the binary and a series of symbolic links1 on your target system. You can build and install BusyBox on your development workstation or your target embedded system. BusyBox works equally well in both environments.
Note that, in a similar fashion to the Linux kernel, make help produces much useful information on available make targets. However, some of the options deserve mention. Listing details the options found under Build Options in a recent BusyBox snapshot. Without this option, BusyBox requires various libraries so that it can run.
We can easily determine what libraries BusyBox or any other binary requires on our target system by using the ldd command. Listing is the output of ldd cross-compiled for ARM xscale. Had we elected to build BusyBox as a static binary, ldd would simply issue a message telling us that the BusyBox binary is not a dynamic executable. In other words, it requires no shared libraries to resolve any unresolved dependencies in the executable.
However, building an embedded application without shared libraries means that none of the familiar C library functions are available to your applications. This has been superseded by the more standard method of specifying an environment variable similar to building other packages such as the Linux kernel.
BusyBox can be invoked from the binary name itself, but it is usually launched via a Chapter 11 BusyBox symlink. Licensed under GPLv2. See source distribution for full notice.
Usage: busybox [function] [arguments] BusyBox is a multi-call binary that combines many common Unix utilities into a single executable. Most people will create a link to busybox for each function they wish to use and BusyBox will act like whatever it was invoked as! Currently defined functions: [, [[, addgroup, adduser, ar, ash, awk, basename, blkid, bunzip2, bzcat, cat, chattr, chgrp, chmod, chown, chpasswd, chroot, chvt, clear, cmp, cp, cpio, cryptpw, cut, date, dc, dd, deallocvt, delgroup, deluser, df, dhcprelay, diff, dirname, dmesg, du, dumpkmap, dumpleases, echo, egrep, env, expr, false, fbset, fbsplash, fdisk, fgrep, find, free, freeramdisk, fsck, fsck.
This list represents the set of utilities enabled in this particular BusyBox build. It describes BusyBox as a multicall binary, combining many common utilities into a single executable. This is the purpose of the symlinks mentioned earlier. BusyBox was intended to be invoked by a symlink named for the function it will perform. This removes the burden of having to type a two-word command to invoke a given function, and it presents the user with a set of familiar commands for the similarly named utilities.
Listings and should make this clear. Listing shows the target directory structure as built by the BusyBox package via the make install command in the busybox source tree. Listing expands on the directory structure of Listing Each line containing an ellipsis Again, the entries between cp and zcat have been omitted from this listing for readability.
With this symlink structure, the user simply enters the actual name of the utility to invoke its functionality. BusyBox examines how it was called. In other words, it reads argv[0] to determine what functionality is being requested. BusyBox handles the init functionality.
BusyBox handles system initialization differently from standard System V init. In general, you should not need to use an inittab if you are using BusyBox. Listing captures the console output at power-up on this BusyBox-based embedded system.
BusyBox v1. In our This is the default initialization script that BusyBox searches for. Instead of using inittab, this is the preferred method to initialize an embedded system based on BusyBox.
When it has completed initialization, BusyBox displays a prompt asking the user to press Enter to activate a console.
When BusyBox detects the Enter key, it executes an ash shell session waiting for user input. The Linux kernel contains code to disable job control if it detects the console on a serial terminal.
You can see that this simple package provides a powerful platform upon which to build your own system applications. Of course, it should be noted that without any support for libc and other system libraries, you would face a formidable task implementing your applications.
You would have to provide support for all the usual standard C library calls and other library functions that a typical C program relies on. Alternatively, you could statically link your applications against the libraries they depend on, but if you have more than a couple applications using this method, your applications will likely exceed the combined size of linking dynamically and having the shared libraries on your target.
It is here where your applications come to life in a BusyBox system. A simple rcS initialization script is provided in Listing This is explained more fully in Chapter 9. Next we launch the system loggers as early as possible, to capture any startup problems. The last thing we do before starting a shell is launch the Internet superserver xinetd.
For example, to initiate a telnet session to the board, xinetd intercepts the request for telnet connection and spawns a telnet server to handle the session. Instead of starting a shell, your own applications can be launched from this rcS initialization script.
Listing is a simple example of a telnet-enabled target board running basic services such as system and kernel loggers. Executing make install creates a directory structure containing the BusyBox executable and a symlink tree. As explained earlier, the symlink tree eliminates the need to type busybox command for each command. The symlink executes BusyBox as described previously and invokes the ls functionality. Review Listings and Listing shows the procedure. The script is very chatty; it outputs a line for each symlink created.
The ellipsis in the listing represents those that have been eliminated. This allows BusyBox functions that require root access to function properly even when invoked by a nonroot user. This is not strictly necessary, especially in an embedded Linux environment, where it is common to have only a root account on a system. The end result looks very similar to Listing It is useful to note that BusyBox also has an option to enable creation of this symlink tree on the target system at runtime.
BusyBox supports awk and sed, frequently found in Bash scripts. After you have done so, you will have a better appreciation of the capabilities of BusyBox and how it might be applicable to your own embedded Linux project.
As mentioned at the beginning of this chapter, many of the BusyBox commands contain a limited subset of features and options compared to their full-featured counterparts. In general, you can get help on any given BusyBox command at runtime by invoking the command with the --help option.
This produces a usage message with a brief description of each supported command option. The BusyBox gzip applet is a useful example of a BusyBox command that supports a limited set of options. Listing displays the output from gzip -help on a BusyBox target.
Its fullfeatured counterpart supports more than 15 different command-line options. No such support exists for BusyBox gzip. We present this information so that you can make an informed choice when deciding on BusyBox. In this way you can mix BusyBox utilities and the standard Linux utilities on the same embedded system.
BusyBox has also found a place in desktop and server distributions, as part of a rescue image as well as the initial ramdisk typically found in these distributions.
We also examined the differences in system initialization when using BusyBox-based systems. Appendix C itemizes all the available BusyBox commands from a recent release. This chapter examines the unique requirements of a cross-development environment and some of the tools and techniques that an embedded developer needs to know to be productive. We begin by examining a typical cross-development environment. We also look at differences in the toolchains for native versus embedded application development.
We then present host system requirements and detail the use of some important elements of your host system. We conclude this chapter with an example of a target board being hosted by a network-based host. When we use the term host in this context, we are referring to the development workstation that is sitting on your desktop and running your favorite Linux desktop distribution.
Therefore, native development denotes the compilation and building of applications on and for your host system. Cross-development denotes the compilation and building of applications on the host system that will be run on the embedded system.
A host PC is connected to a target board through one or more physical connections. It is most convenient if both serial and Ethernet ports are available on the target. Later, when we discuss kernel debugging, you will realize that a second serial port can be a valuable asset. The basic idea is that the host system provides the horsepower to run the compilers, debuggers, editors, and other utilities, and the target executes only the applications designed for it.
You can certainly run compilers and debuggers on the target system, but we assume that your host system has more resources available, including processor horsepower, RAM, disk storage, and Internet connectivity.
In fact, it is not uncommon for a target embedded board to have no human-input devices or output displays. Looking at a simple example will help uncover and explain some of the mystery. Actually, they are not assumptions, but a collection of rules that the compiler references to build a proper binary. If we omit the include directive containing the prototype for the printf function, the compiler emits this familiar message: hello.
This prologue deals with details such as the environment and arguments passed to your program, startup and shutdown housekeeping, exit handling, and more. This, of course, is the standard C library. You might already know that cpp is the C preprocessor component of the GNU gcc toolchain. We have added some formatting white space only to improve readability.
The default thread model is posix, which determines the thread library your application gets linked against if you employ threading functions. Finally, you see the default search directories for include directives. But what if we want to build hello. When we compile an application program for a Power Architecture target using a cross-compiler on our host machine, we must make sure that the compiler does not use the default host include directories or library paths.
Again, we have added some white space to the output for readability. Here you can see that the default search paths for include directories are now adjusted to point to your cross versions instead of the native include directories. This seemingly obscure detail is critical to being able to develop applications and compile open source packages for your embedded system. It is one of the most confusing topics to even experienced application developers who are new to embedded systems. You can download and compile one yourself or obtain one of the many commercial toolchains available.
Building one yourself is beyond the scope of this book; however, several good references are available. See the last section of this chapter for recommendations. The next major item you need is a Linux distribution targeted for your embedded system architecture. Again, the choices are to build your own or to obtain one of the commercial ones.
One of the more popular open source embedded system distributions is the aforementioned ELDK. The situation becomes much more complex if your requirements include support for multiple architectures and processors on your development workstation. This is the reason that commercial embedded Linux distributions exist.
This consists of a hardware probe connected to your host often via Ethernet and connected to your target via a debug connector on the board. Many Chapter 12 Embedded Development Environment solutions are available. This is not strictly necessary; indeed, some smaller embedded devices do not have an Ethernet interface.
However, this is the exception rather than the rule. Having an Ethernet connection available on your target board is worth its cost in silicon! Many embedded development systems and bootloaders support TFTP and assume that the developer will use it. Using TFTP from your bootloader to load the kernel will save you countless hours waiting for serial downloads, even at higher serial baud rates.
Of course, the details might vary, depending on which Linux distribution you choose for your development workstation. The guidelines presented here are based on popular desktop Linux distributions. The easiest way to do this is to run a TFTP server daemon. Most modern desktop Linux distributions have multiple packages available to provide this service.
The -s switch tells in. The BDI covered later in this book has such a capability, and it will not work without the -c. We have already established the reasons why an 2 Do not confuse this with the TFTP client package, which is named tftp-hpa. A DHCP server listens for requests from a DHCP client such as your target board and assigns addresses and other pertinent information to the client as part of the boot process. The client then responds by testing this IP address locally.
A booting Linux kernel does not have this capability and emits the same sequence every time it boots. As usual, our advice is to consult the documentation that came with your desktop Linux distribution. This is typically done through your main menu or at the command line.
Consult the Chapter 12 Embedded Development Environment documentation for your Linux distribution for details suitable for your environment. Consult the documentation associated with your distribution for instructions on how to do this.
In this example, dhcpd is considered a system service. Many nuances are involved with installing a DHCP server, so unless your server is on a private network, it is advisable to check with your system administrator before going live with your own.
The steps for setting up an NFS server vary depending on which desktop Linux distribution you are using. The NFS service must be started from either your startup scripts, a graphical menu, or the command line. You must do this each time you start your desktop Linux workstation. This and other services can be started automatically on booting. Consult the documentation for your desktop Linux distribution. In addition to enabling the service, your kernel must be compiled with support for NFS.
This is true on both your development workstation and your target board. Reference appendices include U-Boot and BusyBox commands. Real-time Phoenix: Build Highly Aws Certified Sysops Administrator Sobranie Sochineni V 6-i Apple Macos And Ios Machine Learning And Knowledge Build Chatbot Interactions: Responsive, Pci Dss: An Integrated Bioinformatics And Biomedical Engineering Programming With bit Arm English French German Italian Spanish.
0コメント