Introduction

So, I (Stephen Warren/srwarren/swarren) just got an AC100 and started hacking on it, intending to do stuff like getting mainline U-Boot and a mainline kernel running. I learned a lot, and figured out some stuff that isn't in the wiki, even if it is in people's heads… So, here's a brain-dump in case it's useful to the general community:

Tools

I used nvflash and bootloader.bin (a/k/a fastboot.bin) from the Linux4Tegra alpha release (Harmony version of bootloader.bin). Other versions might work.

Backing up

The current wiki backup examples use –read (a partition). To me, there are some slight disadvantages to this, since it'll be more difficult to restore from individual partition images if you've modified or broken your partition table. I chose to do a plain byte-for-byte backup of the entire flash.

nvflash's –rawdeviceread option can do this, but has one problem: It fails if asked to read the entire flash (it's too big for one dump), so I had to run multiple nvflash commands in sequence to grab the MMC content in chunks:

#!/bin/bash

sectors=3879936
sectors_per=131072

sectors_read=0
block_id=0
downloads=
while [ ${sectors_read} -lt ${sectors} ]; do
    block_id_str=`printf %03d ${block_id}`
    f=ac100-image-${block_id_str}.bin
    sectors_this=$(( ${sectors} - ${sectors_read} ))
    if [ ${sectors_this} -gt ${sectors_per} ]; then
        sectors_this=${sectors_per}
    fi
    downloads="${downloads} --rawdeviceread"
    downloads="${downloads} ${sectors_read} ${sectors_this} ${f}"
    sectors_read=$((${sectors_read} + ${sectors_this}))
    block_id=$((${block_id} + 1))
done
 
set -x

sudo ./nvflash --bl bootloader.bin --getpartitiontable ac100-pt.txt
sudo ./nvflash -r ${downloads}

(For correctness, you should probably add “–odmdata 0x800c0085” to all the nvflash commands. See http://nv-tegra.nvidia.com/gitweb/?p=linux-2.6.git;a=blob;f=arch/arm/mach-tegra/odm_kit/query/harmony/tegra_devkit_custopt.h;h=1ec7010911454f19a5018952fd245785a62c59ad;hb=0e52d7fe25b11a656c376a37890be219470661fb for details)

To restore your MMC to its original state, you should be able to use the exact same script; just replacing –rawdeviceread with –rawdevicewrite (also there's no need to run the –getpartitiontable commands when restoring)

Extracting a usable BCT

The BCT is 4080 bytes long at the start of MMC. To extract it, run:

dd if=ac100-image-000.bin of=ac100.bct bs=4080 count=1

The BCT that's on the device contains information about the location of the bootloader within the MMC. In order to use the BCT for purposes such as flashing a complete new custom partition table, or a new bootloader, you'll need to create a copy of the BCT, and remove this information, so that nvflash has space to add it back when using writing it to the device:

cp ac100.bct ac100.bct.orig
dd if=/dev/zero of=ac100.bct seek=2704 bs=1 count=48 conv=notrunc

If you don't nvflash might fail later with e.g.:

bootloader status: BCT is full; no more bootloaders can be added (code: 16) message: nverror:0x4 (0x4) flags: 0

For more details on the BCT format, see the cbootimage project on gitorious; it creates boot images for Tegra: https://gitorious.org/cbootimage.

Mainline U-Boot status

(as of 2011/11/19)

(2012/01/13 UPDATE: most of the patches mentioned are upstream now, see uboot for the temporary tree with a known working version – paulf)

Mainline U-Boot has some Tegra support now. There's quite a bit of activity going on to upstream more. I posted preliminary support for the AC100; see http://patchwork.ozlabs.org/patch/126617/.

This supports: UART console over UARTA (JP1), SD card in the SD slot, built-in MMC flash. There is no (nvec) keyboard or display support for Tegra in mainline U-Boot at present. You may be able to crib display support from the ChromeOS U-Boot.

My patch relies on a bunch of other patches in patchwork. Below is a list of what I have in my local branch. Not all of this is necessarily required, but much is at least for patch context. These may be found in patchwork linked above.

tegra2: Add support for Paz00 (Toshiba AC100)
tegra2: Fix conflicting pinmux for UARTA
MAINTAINERS: Fix my email address
Revert "mmc: retry the cmd8 to meet 74 clocks requirement in the spec"
mmc: Implement card detection.
tegra2: Add common Avionic Design Tamonten support.
tegra2: Move tegra2_mmc_init() prototype to public header.
tegra2: Change CONFIG_SYS_TEXT_BASE to 0x00108000.
tegra2: Always build with USE_PRIVATE_LIBGCC=yes.
tegra2: Fix out-of-tree build for Ventana.
tegra2: Don't use board pointer before it is set up
tegra2: Remove unneeded 'dynamic ram size' message
tegra2: Remove unused low-level Tegra2 UART code
tegra2: Remove unneeded config option
tegra2: Remove unneeded boot code
tegra2: Enable instruction cache
arm: Move CP15 init out of cpu_init_crit()
tegra2: Simplify tegra_start() boot path
tegra2: Add arch_cpu_init() to fire up Cortex-A9
image: Don't detect XIP images as overlapping.
image: Implement IH_TYPE_KERNEL_NOLOAD
tegra2: Use new GPIO APIs in gpio_config_uart()
tegra2: Add support for Ventana
tegra2: Modify MMC driver to handle power and cd GPIOs
tegra2: Move board_mmc_init into board files
disk: part_efi: fix regression due to incorrect buffer usage

Running an updated U-Boot from RAM

nvflash can be used to download U-Boot directly into RAM and run it. This saves screwing around with flashing the MMC. The command to do this is:

sudo ./nvflash --bl ~/git_wa/u-boot/u-boot.bin --sync

Note that nvflash will attempt to communicate with the downloaded code over USB. This will fail to work, and nvflash will hang until the USB cable is removed. However, U-Boot will actually be running just fine.

Burning U-Boot to MMC

You can use this command:

sudo ./nvflash --bl bootloader.bin --bct ac100.bct --odmdata 0x800c0085 --download 4 ~/git_wa/u-boot/u-boot.bin

Note: This works for me now. It did not work for me before when ALL of the following were true:

a) Using original partition table. b) Missing the –odmdata option in the command above. c) Using the BCT as read from the device, without removing the bootloader information from it as described above in “Extracting a usable BCT”.

I don't know which of those things made it start working.

Note: You can't update the bootloader using –rawdevicewrite to the EBT partition's location, because certain information about the bootloader (such as a crypto hash of its binary) is stored in the BCT. –download updates this, whereas –rawdevicewrite would not.

Flashing a custom partition table (and updated bootloader)

On my Tegra machines, I typically write only the bootloader to internal flash, and store the kernel and filesystem on an SD card. This way, testing new kernels is as simple as writing a file to an SD card, and I can share it across all my machines. I wanted a similar partition setup on my AC100 as on my other machines to keep things consistent. To do this, I created a “flash.cfg” more in line with those from Linux4Tegra alpha:

[device]
type=hsmmc
instance=3

[partition]
name=BCT
id=2
type=boot_config_table
allocation_policy=sequential
filesystem_type=basic
size=4096
file_system_attribute=0
partition_attribute=0
allocation_attribute=8
percent_reserved=0

[partition]
name=PT
id=3
type=partition_table
allocation_policy=sequential
filesystem_type=basic
size=4096
file_system_attribute=0
partition_attribute=0
allocation_attribute=8
percent_reserved=0

[partition]
name=EBT
id=4
type=bootloader
allocation_policy=sequential
filesystem_type=basic
size=1048576
file_system_attribute=0
partition_attribute=0
allocation_attribute=8
percent_reserved=0
filename=u-boot.bin

and ran the following nvflash command to write this to the AC100:

bin=bootloader.bin
cfg=flash.cfg
bct=ac100.bct
odm=0x800c0085

sudo ./nvflash \
  --bct ${bct} \
  --setbct \
  --odmdata ${odm} \
  --configfile ${cfg} \
  --create \
  --bl ${bin} \
  --go

You could expand flash.cfg to add an extra partition covering the rest device if you wanted to put the Linux install on the MMC here. The kernel would need support for the “tegraparts” command-line option for this to work (mainline doesn't have this, although the existing AC100 Ubuntu kernels do)

Note: I wrote my flash.cfg above to fit within the first boot sector, as described in the next section.

MMC boot sectors

The MMC device has two 1MiB “boot sectors” at the start. Note from stuw: some ac100 have two 2Mib “boot sectors”

Note that the term “boot sectors” comes from a flash memory background, and really has nothing to do with the Tegra boot process as far as I know.

nvflash's sector numbers begin at the start of the first boot sector, continue through the first boot sector, through the second boot sectors, and finally through the balance of the flash. I think that nvflash has no knowledge at all of these boot sectors.

At a HW level, it's plausible these have more error-redundancy, since “boot sectors” are typically used to store important things such as boot loaders.

At a SW level, if I understand correctly, these are more of an informational construct. However, both (mainline) U-Boot and the (mainline) Linux kernel honor this information when accessing the device.

In particular, the U-Boot command to read the first sector in the MMC:

mmc dev 0
mmc read ${loadaddr} 0 1

actually reads from the first sector of the “general” area /after/ the boot-sectors. However, you can access the boot-sectors by providing an extra parameter to “mmc dev”:

mmc dev 0 1 # First boot sector
mmc dev 0 2 # Second boot sector

If an MMC device had “general purpose” partitions built-in, I imagine there would be more legal values for the second parameter to “mmc dev”.

Equally, when (mainline) Linux boots, it creates the following device nodes:

/dev/mmcblk1boot0 # Boot sector 0:   0..1 MiB
/dev/mmcblk1boot1 # Boot sector 1:   1..2 MiB
/dev/mmcblk1      # Everything else: 2..  MiB

If an MMC device had “general purpose” partitions built-in, extra devices would be exposed:

/dev/mmcblk1gp0
/dev/mmcblk1gp1
/dev/mmcblk1gp2
/dev/mmcblk1gp3

Any or all of those Linux devices may have partition tables, which would space device names such as /dev/mmcblk1p1, /dev/mmcblk1boot0p1, /dev/mmcblk1gp0p1.

Bear this in mind when trying to validate Flash content written by nvflash from U-Boot or Linux!

Partition table support in U-Boot and the kernel

Neither mainline U-Boot nor the mainline kernel support Tegra's custom partition scheme. Moving forward, we could:

* Upstream support to both U-Boot and the kernel. If we do this, I'd at least suggest having the kernel parse the partition table rather than using the tegraparts= command-line option.

* Use something standard like GPT. I'd suggest this.

Using a GPT partition table

Given the existence of MMC boot-sectors, and the fact both U-Boot and Linux expose separate devices for each of the boot partitions and the rest of the MMC (the “main” partition), I suggest creating a GPT at 2M into the device (i.e. at the start of the main portion of the flash after the boot sectors and covering until the end of the device. Then, write the BCT, Tegra PT, and bootloader into the first (or both) bootsector(s). This keeps the GPT-based and Tegra-PT-based portions of the flash completely separate. An advantage here is that the BCT can be stored right at the start of flash, which is where it's supposed to be.

A diagram:

+-----+--------+---------------------------------------+
| MMC | Boot 0 | BCT                                   |
|     |        | Tegra PT (only covers area in boot 0) |
|     |        | Bootloader                            |
|     +--------+---------------------------------------+
|     | Boot 1 | Unused, or you could make the Tegra   |
|     |        | PT cover this area too.               |
|     +--------+---------------------------------------+
|     | Main   | Primary GPT                           |
|     |        | GPT partition 1                       |
|     |        | ...                                   |
|     |        | GPT partition n                       |
|     |        | Backup GPT                            |
+-----+--------+---------------------------------------+

Additional background: Some/many Tegra devices have a relatively small NAND or SPI flash that's laid out with just the BCT, Tegra PT, EBT sections, i.e. the content of “boot 0” above. Any Linux filesystems are then stored in MMC, and laid out in a standard fashion (e.g. GPT, MBR) without regard for the content of the NAND/SPI boot flash.

You can create this by:

Write the BCT, Tegra PT, bootloader to flash as I described above.

Create a disk image for the main partition by:

dd if=/dev/zero of=image.bin bs=16384 count=484864

parted image.bin
  unit s
  mklabel gpt
  # This covers the whole rest of the MMC, but isn't aligned;
  # I'm not sure why alignment matters
  mkpart primary 34 15515614
  name 1 Linux
  quit

The following writes the 34 GPT (512-byte) sectors to the start of the main partition of the MMC:

sudo ./nvflash --bl bootloader.bin --rawdevicewrite 1024 9 image.bin

Note: (9 * 2048 bytes per sector) / 512 bytes per sector is 36, two more than the 34-sector size of the GPT.

You should also really extract the last 34 sectors of image.bin to another file, and write them to the very end of the MMC too, so the backup GPT is there. I'll leave that as an exercise for the reader!

With this setup, you could generate a new BCT/PT/U-Boot image and dd it directly into /dev/mmcblk1boot0 when updating from Linux. Of course, this is only for U-Boot updates from a running Linux system, which would probably be rare. In order to write to /dev/mmcblkXbootY, you'll need disable the “force readonly” flag for the boot partition(s) using “echo 0 > /sys/block/mmcblkXbootY/force_ro”. See http://comments.gmane.org/gmane.linux.kernel.mmc/10115.

If on other Tegra systems, the MMC doesn't have these boot sectors, you'll have to create the GPT right at the start of the MMC. This means the BCT must be put somewhere else. Note that NVIDIA will certainly not provide support for this configuration; the Tegra Boot ROM documentation states that the BCT MUST be at offset zero into the boot device. However, moving the BCT does appear to work in practice, since the boot ROM (at least on Tegra20) searches through the device for a valid BCT. Note that the TrimSlice firmware updater SD card images use this technique, but with a regular PC/MBR partition table.

+-----+---------------------------------------+
| MMC | Primary GPT                           |
|     | GPT Partition 1 "BCT" at sector 256   |
|     | GPT Partition 2 "BOOTLOADER"          |
|     | GPT partition 3 "LINUX"               |
|     | ...                                   |
|     | Backup GPT                            |
+-----+---------------------------------------+

Notes: I chose sector 256 for the start of the BCT partition (i.e. 128K into flash) to match the location that (the “trimslice” branch of) cbootimage uses when moving the BCT (search for MMC_SECONDARY_BCT_LOCATION_IN_BLOCKS in the code). Also, I /think/ the Tegra boot ROM searches for the BCT at the start of each “journal block” on the MMC, and apparently they're 128K in size.

To generate this kind of image, create and partition image.bin just like above (but remember to use the appropriate size to match your MMC). In order to install the BCT and boot-loader into the image, you'll need to:

a) Use bct_dump (from cbootimage) to create ac100-cboot.cfg.

b) Edit ac100-cboot.cfg to add the following line at the end:

BootLoader    = u-boot.bin,0x00108000,0x00108000,Complete;

c) Use cbootimage to generate an MMC image (so, in the “trimslice” branch, MMC_SECONDARY_BCT_LOCATION_IN_BLOCKS is applied), using ac100.bct and the u-boot.bin you desire.

cbootimage -d ac100-cboot.cfg cboot.bin

(Note: Use of cbootimage is required in order to write the correct bootloader sector numbers and verification hash into the BCT)

d) Extract portions of the cbootimage result and write them to image.bin:

# BCT
dd if=cboot.bin of=image.bin bs=1024 count=16 skip=128 seek=128 conv=notrunc
# U-Boot
dd if=cboot.bin of=image.bin bs=1024 count=1024 skip=144 seek=144 conv=notrunc

You can probably just write u-boot.bin directly into image.bin rather than extracting it from cboot.bin first, but I wanted to make sure I copied the entire partition, in case the verification hash in the BCT included any padding sectors after the bootloader image.

Now, copy the relevant parts of image.bin onto MMC. You'll need to copy quite a few more than 34 (9) sectors this time…

With this setup, I imagine there would be some tool running on Linux that read the current BCT from /dev/mmcblk1p1, updated it based on knowledge of the BOOTLOADER partition's location and new u-boot.bin content, and then wrote it back direct to /dev/mmcblk1p1, and wrote u-boot.bin direct to /dev/mmcblk1p2. Of course, this is only for U-Boot updates from a running Linux system, which would probably be rare.

In either partioning setup, I'd expect U-Boot to use ext2load to load the kernel from /boot in the first GPT partition, or something like that. Therefore, a kernel update would be as simple as copying a new file over e.g. /boot/vmlinux.uimg

Log In