SD 2.0 and SDHC cards (also MMC >4GB)

I'm looking for some information and/or some hardware in this installment.

Today, I noticed that the SD 2.0 specification has been released by the SD association. The simplified version, that is. The full spec, like the 1.x versions, doesn't include the security features. Looks like there's a number of new 'host' requirements. I can't yet deside if these requirements are at a hardware or a software level yet...

So, if someone wants to purcahse me a 8GB SD or MMC card, I'll be happy to make it work.

I've started in earnest on the mmc layer. It should be done shortly. The qdmmc driver that Bernd wrote lead the way, but making commands properly be async was difficult in its structure, so I've decided to try it with the proper layering instead.


SD/MMC FreeBSD Driver Outline

There's many different flash standards in the world today. SD and MMC are two related flash card standards. These cards are used by different cameras. Readers of these cards are available for USB, PC Card and CardBus. In addition, native readers are present on many laptops and other portable devices. Here's a little background on MMC and SD. I'm writing software for MMC/SD for FreeBSD, and this will give some background for people that wish to help.

Many of the readers of MMC and SD cards emulate something else. USB cards look the same as the thumb drives, which are already supported by umass(4). PC Card and CardBus cards tend to look like ata drives, which are supported by ata(4). The native host interfaces have their own interfaces. There's two common ones in laptops, and a bunch of other interfaces in embedded devices. Since the other reads emulate something else, they aren't relevant to my FreeBSD work, so I'll not describe them further.

My efforts will concentrate on two different host adapter interfaces. The first one is the MCI interface present on the Atmel AT91RM9200 (and some newer atmel parts). This interface is relatively simple. The second one is the SD Host Controller Standard conforming host interfaces. This interface was recently documented by the SD Card Association and is present in many laptops.

For FreeBSD the software will have three layers. At the 'bottom' level is the host interface. All host interface drivers present a standard API to the bus layer. The bus layer, for the mmc/sd bus, handles all the device enumeration as well as arbitrating access to the bus. The top layer is for the SD/MMC card as well as SDIO cards that are appearing in the market place.

As a first step towards understanding, Berndt Walter write a quick and dirty driver for mmc. I added sd support for it and am debugging it. Once that's basically working, the next step is to do a proper driver with proper layering. Linux already has a system, and I'll be taking a look at it to see what I can learn from their experiences.

Anyway, back to the standards.

MMC stands for Multi Media Card. This standard was created many years ago as a small form-factor flash card. This standard was invented by the Multi Media Card Association. This standard was popular early on, but was lacking security features. The standards through version 3.31 only specifies a one wire data bus running at 25MHz. While adequate for smaller capacity cards, for large capacity cards, this presented a significant bottleneck.

The SD Association (Secure Digital) based their work on the 2.2 version of the MMC standard. They added a number of incompatible commands (on purpose, so one could tell the cards apart), changed the electrical stuff a little to make things simpler, added three more data lines as well as security features needed to deliver secure content on these cards. The SD Association later defined SDIO cards, which allow for more things than just memory cards. Today, most cards that you'll find in the marketplace today are SD cards. For a long time, this interface was totally undocumented without a nasty NDA. Recently, much of the SD standard has been released, and that's the information I'm using to create the drivers.


Cross tools and arm hacking

In the past few days I've been hacking on arm related stuff. I've worked on build tool issues (mostly integrating cogent's changes to support arm). In addition, I've had time to improve hardware support a little.

I've been getting some excellent feedback on the arm build changes, and many of them have been integrated into the tree. More to follow soon after I catch up to the changes that have already been committed which were (slightly) different than the ones I proposed. We may yet get binutils and friends merged into 6.2. The arm vs armel vs armeb issues, however, remain open.

After spending a lot of time on the build tools, it was refreshing to return to the hardware side of things. The iic bus is hanging on write transaction. I don't know if this is an interaction with the boot blocks (they too hang in the same place in some cases), or if it is a problem in the kernel code. Careful study is needed. Alas, pressures elsewhere mean that careful study must be deferred for a bit.

My main thrust has been in MMC/SD integration. I've mostly finished boot2 for at91. boot2 likely is a lame name, since it isn't the second stage boot loader, but rather the primary boot loader for this machine (redboot and uboot were rejected early on as being too large). However, despite the name (which it gets from where I stole it from), I have it loading kernels off ufs1 partitions on SD cards. I hope to get MMC card support added as well.

In addition to booting, I've been hacking on the qdmmc driver. I've added sd support, for the most part, and have refactored it to boot. Once we have this working, I'll get going on the more generic SD/MMC layer that supports things like SDIO cards.

I have a good collection of SD cards (16MB, 256MB, 512MB and 4GB), but only one MMC card (256MB). If you have MMC cards that you aren't presently using and would like to donate to my collection, please contact me at my FreeBSD email address.


Notes on identical .o's

The FreeBSD project just decided to remove the portability ifdefs obscuring the USB code. These ifdefs had been a growning problem for many years. They had even caused a few bugs due to subtle differences in semantics they helped to paste over. While the portability stuff may have been helpful when the code was first committed, in the long run it had the effect of making the code too difficult to maintain. The benefits from code sharing had long ago been overwhelmed by the increased maintenance burdon of its presense.

I wanted to remove this code myself. There was broad consensus that this was the right thing to do. In addition, many similar changes were being made in a USB stack rewrite that was happening outside the tree. That effort had partially removed these ifdefs, which ballooned the diffs to over 85k lines. To help with that effort as well, I thought it would make sense to make the changes in the base FreeBSD repositoy.

I wanted to make sure that any changes I made didn't break anything that was working now. Since I didn't have the resources to test every single USB device, I decided to only make those changes that didn't change the resulting .o file. If the .o file didn't change, then I knew I couldn't have broken anything. To see if a file changed, I took an MD5 of the file before and after my changes. Any change that changed the md5 I reverted. Any that didn't I retained. I thought I'd record a few of my observations and useful tricks.

First, choice of compiler flags is critical. One must have the same compiler flags between different runs. While some trivial files were the same at different levels of optimization, most of them change quite dramatically when optimization is changed.

After making dozens of changes, I hit some interesting cases where the MD5s were changing. Sometimes, when I deleted a blank line, the MD5 would change. Why would removing a blank line cause the .o to change. The answer to the puzzle turned out to be the '-g' flag used by default in the amd64 kernel config file. The key to understanding why is understanding what '-g' does. It adds meta-information about the source code to the .o file to help debuggers with their tasks. This information includes the types of the arguments to functions, the line numbers that correspond to different addresses in the .o file and the like. All of these will cause small changes to the .o, which change the MD5 produced.

Finally, after I'd removed '-g' from the command line I encountered one last issue. In FreeBSD, all the files have a $FreeBSD$ keyword. This keyword is expanded into a .comment section so that developers can find out from the binaries what sources users had when they encountered a problem. Since cvs expands this keyword to include date/time the file was committed as well as the version number of the file, committing files to the FreeBSD repository causes this to change, resulting in a change to the binary produced. One way to "sanitize" the .o files is to remove the .comment section. strip -R .comment ensures that the MD5 of the file doesn't change with the new keyword expansion after it has been committed.


One may wonder why I used md5(1) rather than diff(1). The answer is that it is a little easier to use. "md5 *.o > /tmp/golden-md5" is easier to type and keep around than a whole lot of .o files. In addition, you still need to use the change avoidance techniques described here no matter what the method of determining "same or different."


Cross Building FreeBSD

I've been getting more and more questions about FreeBSD embedded systems: What is supported? How can I build for them? What can be done to improve the cross development environment? How can I help? and so on. These are all very good questions, so I thought I'd start a series of articles on the FreeBSD build system and embedding FreeBSD into products.

For this article, I'll be providing background on the FreeBSD build system as it relates to embedded systems. For an emebedded system, one usually has little more than a microcontroller with an MMU to power the entire system. While these systems work well in their target niche, they don't work so well as a general purpose platform. They lack the memory and I/O bandwidth necessary to compile applications that run on them. Server machines (or even workstations) do a much better job at these tasks, but are for a different computer architecture. One is left with the choice of compiling natively on a slow machine, or cross building on a fast machine. I'll discuss some basic aspects of cross compilation in this article.

I'll assume that you are familiar with FreeBSD's build system. If not, there are many resources available on the web. One of the better ones is the FreeBSD handbook. Future articles will build on this base, hopefully with sufficient links.

Each FreeBSD platform has a MACHINE and a MACHINE_ARCH that uniquely define it. The MACHINE_ARCH is the CPU family that executes the object code. MACHINE_ARCH is something like "i386" or "sparc64." Some CPU families come in different flavors that are mutually incompatible (such as MIPS processors using different byte ordering). These families would get a different MACHINE_ARCH for each flavor (so you'd have mipseb and mipsel, for example, to distinguish the two different flavors of MIPS CPUs). If you are familar with the gnu compilers and such, this field roughly corresponds to "CPU" field of their "CPU-MANUFACTURER-OS" identifier. I say "corresponds" because FreeBSD uses "amd64" as the MACHINE_ARCH for AMD's extensions to the x86 architecture (the official name of the architecture), while gcc uses the older x86_64 designation.

MACHINE defines the way the machine is put together. Often times machines are build with the same CPU, but with differing glue chips and support software. In cases like this, if the machines are very similar, FreeBSD will support that with one MACHINE type. In other cases, the differences are large enough to warrant an entirely separate MACHINE for that machine. What differentiates one MACHINE type from another with the same MACHINE_ARCH are things like BIOS interface, boot procedure, disk partitioning, and expansion bus technologies. For example, the FreeBSD/i386 and FreeBSD/pc98 platforms both execute on i386 compatible CPUs, but they have completely different bus topologies, boot procedures, BIOS interfaces, and disk layout.

When one builds FreeBSD on one platform to execute on another platform, that is called cross compiling. Cross compilation support is integrated into the normal FreeBSD build system. It is activated by defining TARGET and TARGET_ARCH of the machine you are targeting and using the normal make targets. For example:
make TARGET=arm TARGET_ARCH=arm buildworld
will build the entire userland portion of the system for an ARM machine. Similarly, the following:
make TARGET=arm TARGET_ARCH=arm installworld DESTDIR=/foo
will install these components into the directory tree /foo. One can even build and install a kernel for the target platform:

After cross compilation support was added to the tree, the FreeBSD project added two additional make targets which allow use of the build tools outside of the build tree. The first is "buildenv." It forks a shell with the same build environment used to build FreeBSD. This target is for interactive development. Much of the Atmel AT91RM9200 port was done using this target, for example. However, there's no ability to specify a command to run in this sub-shell, nor does it lend itself well to automated building systems. The make target "buildenvvar" was created to try to fill the gap in the latter problem. This target prints the build environment variables used by the build system. We used it at my day joy to help automate building our products on an x86 processor for an embedded ARM design we're producing. This works well for many applications, but not for all. We have found that we can make it work for simple applications, but more complicated one, with more elaberate build systems, require something more sophisticated. In a future article, I'll describe how we solved that problem.

Many times, cross building can be useful for "compile testing" changes to the tree. There's a make target called "universe" that builds the entire tree for each of the Tier 1 and 2 architectures, along with all the kernels that it can find. In many cases, these "compile tests" of the system find problems that would otherwise be found by others, thus saving much embarassment. The drawback, however, to a "universe" build is that it takes several hours on a very fast machine.

I hope that you've found the above information useful, and that it answers your basic questions in this area. Feel free to leave feedback with additional questions or requests for clarification and I'll see what I can do about addressing them in the feedback section, or in future articles.