Linux is now one of the most widespread operating system for embedded devices due to its openess as well as the wide variety of platforms it can run on. Many manufacturer actually use it in firmware you can find on many devices: DVB-T decoders, routers, print servers, DVD players ... Most of the time the stock firmware is not really open to the consumer, even if it uses open source software. You might be interested in running a Linux based firmware for your router for various reasons: extending the use of a network protocol (such as IPv6), having new features, new piece of software inside, or for security reasons. A fully open-source firmware is de-facto needed for such applications, since you want to be free to use this or that version of a particular reason, be able to correct a particular bug. Few manufacturers do ship their routers with a Sample Development Kit, that would allow you to create your own and custom firmware and most of the time, when they do, you will most likely not be able to complete the firmware creation process. This is one of the reasons why OpenWrt and other firmware exists: providing a version independent, and tools independent firmware, that can be run on various platforms, known to be running Linux originally. \subsection{Which Operating System does this device run?} There is a lot of methods to ensure your device is running Linux. Some of them do need your router to be unscrewed and open, some can be done by probing the device using its external network interfaces. \subsubsection{Operating System fingerprinting and port scanning} A large bunch of tools over the Internet exists in order to let you do OS fingerprinting, we will show here an example using \textbf{nmap}: \begin{Verbatim} nmap -P0 -O Starting Nmap 4.20 ( http://insecure.org ) at 2007-01-08 11:05 CET Interesting ports on 192.168.2.1: Not shown: 1693 closed ports PORT STATE SERVICE 22/tcp open ssh 23/tcp open telnet 53/tcp open domain 80/tcp open http MAC Address: 00:13:xx:xx:xx:xx (Cisco-Linksys) Device type: broadband router Running: Linksys embedded OS details: Linksys WRT54GS v4 running OpenWrt w/Linux kernel 2.4.30 Network Distance: 1 hop \end{Verbatim} nmap is able to report whether your device uses a Linux TCP/IP stack, and if so, will show you which Linux kernel version is probably runs. This report is quite reliable and it can make the distinction between BSD and Linux TCP/IP stacks and others. Using the same tool, you can also do port scanning and service version discovery. For instance, the following command will report which IP-based services are running on the device, and which version of the service is being used: \begin{verbatim} nmap -P0 -sV Starting Nmap 4.20 ( http://insecure.org ) at 2007-01-08 11:06 CET Interesting ports on 192.168.2.1: Not shown: 1693 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh Dropbear sshd 0.48 (protocol 2.0) 23/tcp open telnet Busybox telnetd 53/tcp open domain ISC Bind dnsmasq-2.35 80/tcp open http OpenWrt BusyBox httpd MAC Address: 00:13:xx:xx:xx:xx (Cisco-Linksys) Service Info: Device: WAP \end{verbatim} The web server version, if identified, can be determining in knowing the Operating System. For instance, the \textbf{BOA} web server is typical from devices running an open-source Unix or Unix-like. \subsubsection{Wireless Communications Fingerprinting} Although this method is not really known and widespread, using a wireless scanner to discover which OS your router or Access Point run can be used. We do not have a clear example of how this could be achieved, but you will have to monitor raw 802.11 frames and compare them to a very similar device running a Linux based firmware. \subsubsection{Web server security exploits} The Linksys WRT54G was originally hacked by using a "ping bug" discovered in the web interface. This tip has not been fixed for months by Linksys, allowing people to enable the "boot\_wait" helper process via the web interface. Many web servers used in firmwares are open source web server, thus allowing the code to be audited to find an exploit. Once you know the web server version that runs on your device, by using \textbf{nmap -sV} or so, you might be interested in using exploits to reach shell access on your device. \subsubsection{Native Telnet/SSH access} Some firmwares might have restricted or unrestricted Telnet/SSH ac
# 
# Copyright (C) 2007 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
curdir:=target

$(curdir)/builddirs:=linux sdk imagebuilder toolchain
$(curdir)/builddirs-default:=linux
$(curdir)/builddirs-install:=linux $(if $(CONFIG_SDK),sdk) $(if $(CONFIG_IB),imagebuilder) $(if $(CONFIG_MAKE_TOOLCHAIN),toolchain)

$(curdir)/imagebuilder/install:=$(curdir)/linux/install

$(eval $(call stampfile,$(curdir),target,prereq,.config))
$(eval $(call stampfile,$(curdir),target,compile,$(TMP_DIR)/.build))
$(eval $(call stampfile,$(curdir),target,install,$(TMP_DIR)/.build))

$($(curdir)/stamp-install): $($(curdir)/stamp-compile) 

$(eval $(call subdir,$(curdir)))
r have a kernel/firmware size limitation ? \item does the bootloader expect a firmware format to be loaded with ? \item are the loaded files executed from RAM or flash ? \end{itemize} Net booting is something very convenient, because you will only have to set up network booting servers on your development station, and keep the original firmware on the device till you are sure you can replace it. This also prevents your device from being flashed, and potentially bricked every time you want to test a modification on the kernel/filesystem. If your device needs to be flashed every time you load a firmware, the bootlader might only accept a specific firmware format to be loaded, so that you will have to understand the firmware format as well. \subsubsection{Making binary drivers work} As we have explained before, manufacturers do release binary drivers in their GPL tarball. When those drivers are statically linked into the kernel, they become GPL as well, fortunately or unfortunately, most of the drivers are not statically linked. This anyway lets you a chance to dynamically link the driver with the current kernel version, and try to make them work together. This is one of the most tricky and grey part of the fully open source projects. Some drivers require few modifications to be working with your custom kernel, because they worked with an earlier kernel, and few modifications have been made to the kernel in-between those versions. This is for instance the case with the binary driver of the Broadcom BCM43xx Wireless Chipsets, where only few differences were made to the network interface structures. Some general principles can be applied no matter which kernel version is used in order to make binary drivers work with your custom kernel: \begin{itemize} \item turn on kernel debugging features such as: \begin{itemize} \item CONFIG\_DEBUG\_KERNEL \item CONFIG\_DETECT\_SOFTLOCKUP \item CONFIG\_DEBUG\_KOBJECT \item CONFIG\_KALLSYMS \item CONFIG\_KALLSYMS\_ALL \end{itemize} \item link binary drivers when possible to the current kernel version \item try to load those binary drivers \item catch the lockups and understand them \end{itemize} Most of the time, loading binary drivers will fail, and generate a kernel oops. You can know the last symbol the binary drivers attempted to use, and see in the kernel headers file, if you do not have to move some structures field before or after that symbol in order to keep compatibily with both the binary driver and the stock kernel drivers. \subsubsection{Understanding the firmware format} You might want to understand the firmware format, even if you are not yet capable of running a custom firmware on your device, because this is sometimes a blocking part of the flashing process. A firmware format is most of the time composed of the following fields: \begin{itemize} \item header, containing a firmware version and additional fields: Vendor, Hardware version ... \item CRC32 checksum on either the whole file or just part of it \item Binary and/or compressed kernel image \item Binary and/or compressed root filesystem image \item potential garbage \end{itemize} Once you have figured out how the firmware format is partitioned, you will have to write your own tool that produces valid firmware binaries. One thing to be very careful here is the endianness of either the machine that produces the binary firmware and the device that will be flashed using this binary firmware. \subsubsection{Writing a flash map driver} The flash map driver has an important role in making your custom firmware work because it is responsible of mapping the correct flash regions and associated rights to specific parts of the system such as: bootloader, kernel, user filesystem. Writing your own flash map driver is not really a hard task once you know how your firmware image and flash is structured. You will find below a commented example that covers the case of the device where the bootloader can pass to the kernel its partition plan. First of all, you need to make your flash map driver be visible in the kernel configuration options, this can be done by editing the file \ \textbf{linux/drivers/mtd/maps/Kconfig}: \begin{verbatim} config MTD_DEVICE_FLASH tristate "Device Flash device" depends on ARCHITECTURE && DEVICE help Flash memory access on DEVICE boards. Currently only works with Bootloader Foo and Bootloader Bar. \end{verbatim} Then add your source file to the \textbf{linux/drivers/mtd/maps/Makefile}, so that it will be compiled along with the kernel. \begin{verbatim} obj-\$(CONFIG_MTD_DEVICE_FLASH) += device-flash.o \end{verbatim} You can then write the kernel driver itself, by creating a \textbf{linux/drivers/mtd/maps/device-flash.c} C source file. \begin{verbatim} // Includes that are required for the flash map driver to know of the prototypes: #include #include #include #include #include #include #include // Put some flash map definitions here: #define WINDOW_ADDR 0x1FC00000 /* Real address of the flash */ #define WINDOW_SIZE 0x400000 /* Size of flash */ #define BUSWIDTH 2 /* Buswidth */ static void __exit device_mtd_cleanup(void); static struct mtd_info *device_mtd_info; static struct map_info devicd_map = { .name = "device", .size = WINDOW_SIZE, .bankwidth = BUSWIDTH, .phys = WINDOW_ADDR, }; static int __init device_mtd_init(void) { // Display that we found a flash map device printk("device: 0x\%08x at 0x\%08x\n", WINDOW_SIZE, WINDOW_ADDR); // Remap the device address to a kernel address device_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); // If impossible to remap, exit with the EIO error if (!device_map.virt) { printk("device: Failed to ioremap\n"); return -EIO; } // Initialize the device map simple_map_init(&device_map); /* MTD informations are closely linked to the flash map device you might also use "jedec_probe" "amd_probe" or "intel_probe" */ device_mtd_info = do_map_probe("cfi_probe", &device_map); if (device_mtd_info) { device_mtd_info->owner = THIS_MODULE; int parsed_nr_parts = 0; // We try here to use the partition schema provided by the bootloader specific code if (parsed_nr_parts == 0) { int ret = parse_bootloader_partitions(device_mtd_info, &parsed_parts, 0); if (ret > 0) { part_type = "BootLoader"; parsed_nr_parts = ret; } } add_mtd_partitions(devicd_mtd_info, parsed_parts, parsed_nr_parts); return 0; } iounmap(device_map.virt); return -ENXIO; } // This function will make the driver clean up the MTD device mapping static void __exit device_mtd_cleanup(void) { // If we found a MTD device before if (device_mtd_info) { // Delete every partitions del_mtd_partitions(device_mtd_info); // Delete the associated map map_destroy(device_mtd_info); } // If the virtual address is already in use if (device_map.virt) { // Unmap the physical address to a kernel space address iounmap(device_map.virt); // Reset the structure field device_map.virt = 0; } } // Macros that indicate which function is called on loading/unloading the module module_init(device_mtd_init); module_exit(device_mtd_cleanup); // Macros defining license and author, parameters can be defined here too. MODULE_LICENSE("GPL"); MODULE_AUTHOR("Me, myself and I $(KDIR)/vmlinux.bin.gz $(STAGING_DIR_HOST)/bin/lzma e $(KDIR)/vmlinux $(KDIR)/vmlinux.bin.l7 dd if=$(KDIR)/vmlinux.bin.l7 of=$(BIN_DIR)/openwrt-$(BOARD)-vmlinux.lzma bs=65536 conv=sync dd if=$(KDIR)/vmlinux.bin.gz of=$(BIN_DIR)/openwrt-$(BOARD)-vmlinux.gz bs=65536 conv=sync endef define Image/Build/squashfs $(call prepare_generic_squashfs,$(KDIR)/root.squashfs) endef define Image/Build $(call Image/Build/$(1)) dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-root.$(1) bs=128k conv=sync -$(STAGING_DIR_HOST)/bin/mkfwimage \ -B XS2 -v XS2.ar2316.OpenWrt \ -k $(BIN_DIR)/openwrt-$(BOARD)-vmlinux.lzma \ -r $(BIN_DIR)/openwrt-$(BOARD)-root.$(1) \ -o $(BIN_DIR)/openwrt-$(BOARD)-ubnt2-$(1).bin endef $(eval $(call BuildImage)) \end{Verbatim} \begin{itemize} \item \texttt{Image/BuildKernel} \\ This template defines changes to be made to the ELF kernel file \item \texttt{Image/Build} \\ This template defines the final changes to apply to the rootfs and kernel, either combined or separated firmware creation tools can be called here as well. \end{itemize}