Creating a bootable SD Card

From SEGGER Knowledge Base
Jump to navigation Jump to search

Creating a bootable SD Card

Replace:

  • <BOOT_IMAGE> with the required boot image file name.
  • <BOOTLOADER_OFFSET> with the required byte offset.
  • <OFFSET_SECTORS> with <BOOTLOADER_OFFSET> / 512 if using dd with bs=512.
  • Device identifiers very carefully—selecting the wrong disk can overwrite a system drive.

Important warnings

Warning:
These procedures write directly to a physical storage device. Selecting the wrong device may overwrite a system drive and cause permanent data loss.
  • Run the commands with administrator or root privileges.
  • Double-check the SD device identifier and capacity.
  • Unmount or dismount all SD partitions before writing.
  • The bootloader overwrites data beginning at the specified offset.
  • Ensure that the bootloader fits before the next partition or protected area.
  • Prefer an offset aligned to the card’s logical sector size, commonly 512 bytes.

Example conversion:

Offset in sectors = Offset in bytes / 512

For example, an offset of 32768 bytes corresponds to:

32768 / 512 = 64 sectors

The examples below use placeholders rather than assuming a particular offset.

Linux

1. Insert and identify the SD card

lsblk -o NAME,PATH,SIZE,MODEL,TYPE,MOUNTPOINTS

Example device:

/dev/sdb

Use the whole device, such as /dev/sdb, not a partition such as /dev/sdb1.

2. Unmount all card partitions

sudo umount /dev/sdb?*

Unmount only the partitions that belong to the SD card.

3. Write the bootloader

Using a byte offset:

sudo dd if=<BOOT_IMAGE> of=/dev/sdb bs=1 seek=<BOOTLOADER_OFFSET> \
    conv=notrunc,fsync status=progress

For a sector-aligned offset, the following is normally faster:

sudo dd if=<BOOT_IMAGE> of=/dev/sdb bs=512 seek=<OFFSET_SECTORS> \
    conv=notrunc,fsync status=progress

Example for an offset of 32 KiB:

sudo dd if=<BOOT_IMAGE> of=/dev/sdb bs=512 seek=64 \
    conv=notrunc,fsync status=progress

Flush pending writes:

sync

4. Optional verification

sudo cmp -n "$(stat -c '%s' <BOOT_IMAGE>)" \
    <BOOT_IMAGE> <(sudo dd if=/dev/sdb bs=1 skip=<BOOTLOADER_OFFSET> status=none)

Remove the card only after all writes have completed.

macOS

1. Insert and identify the SD card

diskutil list external physical

Example device:

/dev/disk4

Verify its size before continuing.

2. Unmount the entire card

diskutil unmountDisk /dev/disk4

3. Write the bootloader

Use the raw device, /dev/rdisk4, for better performance.

Using a byte offset:

sudo dd if=<BOOT_IMAGE> of=/dev/rdisk4 bs=1 seek=<BOOTLOADER_OFFSET> \
    conv=notrunc

Using 512-byte sectors:

sudo dd if=<BOOT_IMAGE> of=/dev/rdisk4 bs=512 seek=<OFFSET_SECTORS> \
    conv=notrunc

Example for an offset of 32 KiB:

sudo dd if=<BOOT_IMAGE> of=/dev/rdisk4 bs=512 seek=64 \
    conv=notrunc

Flush pending writes:

sync

4. Optional verification

sudo dd if=/dev/rdisk4 bs=1 skip=<BOOTLOADER_OFFSET> \
    count="$(stat -f '%z' <BOOT_IMAGE>)" 2>/dev/null | cmp - <BOOT_IMAGE>

A successful comparison produces no output.

5. Eject the card

diskutil eject /dev/disk4

Windows: Native PowerShell Method

This method uses PowerShell and .NET without installing third-party writing tools.

1. Identify the SD disk number

Open PowerShell as Administrator:

Get-Disk | Format-Table Number, FriendlyName, BusType, Size, PartitionStyle

Identify the card by its size and device name.

For example, if the card is Disk 3, its raw device path is:

\\.\PhysicalDrive3
Warning:
Do not continue until the disk number has been verified.

2. Dismount the card’s volumes

List partitions and drive letters:

Get-Partition -DiskNumber 3 |
    Format-Table PartitionNumber, DriveLetter, Size

For every assigned drive letter, dismount it. For example:

mountvol E: /p

Close File Explorer windows and any applications accessing the card.

3. Write the bootloader

Save the following as Write-Bootloader.ps1:

param(
    [Parameter(Mandatory = $true)]
    [string]$Image,

    [Parameter(Mandatory = $true)]
    [int]$DiskNumber,

    [Parameter(Mandatory = $true)]
    [UInt64]$Offset
)

$ErrorActionPreference = "Stop"

$imagePath = (Resolve-Path $Image).Path
$devicePath = "\\.\PhysicalDrive$DiskNumber"

$disk = Get-Disk -Number $DiskNumber

Write-Host "Target: $devicePath"
Write-Host "Device: $($disk.FriendlyName)"
Write-Host "Size:   $($disk.Size) bytes"
Write-Host "Offset: $Offset bytes"
Write-Host "Image:  $imagePath"

$answer = Read-Host "Type WRITE to continue"
if ($answer -cne "WRITE") {
    throw "Operation cancelled"
}

$imageStream = $null
$deviceStream = $null

try {
    $imageStream = [System.IO.File]::Open(
        $imagePath,
        [System.IO.FileMode]::Open,
        [System.IO.FileAccess]::Read,
        [System.IO.FileShare]::Read
    )

    if (($Offset + $imageStream.Length) -gt $disk.Size) {
        throw "The image does not fit on the selected disk at this offset"
    }

    $deviceStream = [System.IO.FileStream]::new(
        $devicePath,
        [System.IO.FileMode]::Open,
        [System.IO.FileAccess]::ReadWrite,
        [System.IO.FileShare]::ReadWrite
    )

    [void]$deviceStream.Seek(
        [Int64]$Offset,
        [System.IO.SeekOrigin]::Begin
    )

    $imageStream.CopyTo($deviceStream)
    $deviceStream.Flush($true)

    Write-Host "Bootloader written successfully"
}
finally {
    if ($deviceStream) {
        $deviceStream.Dispose()
    }

    if ($imageStream) {
        $imageStream.Dispose()
    }
}

Run it from an elevated PowerShell terminal:

Set-ExecutionPolicy -Scope Process Bypass
.\Write-Bootloader.ps1 `
    -Image .\<BOOT_IMAGE> `
    -DiskNumber 3 `
    -Offset <BOOTLOADER_OFFSET>

Example for a 32 KiB offset:

.\Write-Bootloader.ps1 `
    -Image .\<BOOT_IMAGE> `
    -DiskNumber 3 `
    -Offset 32768

4. Safely remove the card

After the command succeeds, use the Windows “Safely Remove Hardware” function before removing the card.

Windows: Cygwin

Start Cygwin Terminal as Administrator.

1. Identify available disks

cat /proc/partitions

Cygwin typically exposes physical disks as devices such as:

/dev/sda
/dev/sdb
/dev/sdc

Identify the SD card by its capacity. Do not assume that /dev/sdb is always the card.

2. Dismount Windows volumes

Before writing, close applications using the card and dismount each assigned drive letter from an elevated Windows terminal:

mountvol E: /p

3. Write the bootloader

Using a byte offset:

dd if=<BOOT_IMAGE> of=/dev/sdX bs=1 seek=<BOOTLOADER_OFFSET> \
    conv=notrunc,fsync status=progress

Using 512-byte sectors:

dd if=<BOOT_IMAGE> of=/dev/sdX bs=512 seek=<OFFSET_SECTORS> \
    conv=notrunc,fsync status=progress

Example for an offset of 32 KiB:

dd if=<BOOT_IMAGE> of=/dev/sdX bs=512 seek=64 \
    conv=notrunc,fsync status=progress

Replace /dev/sdX with the verified physical card device.

Windows: MSYS2

Run the MSYS2 terminal as Administrator. First obtain the Windows disk number using elevated PowerShell:

Get-Disk | Format-Table Number, FriendlyName, BusType, Size

Dismount any assigned card volumes:

mountvol E: /p

GNU dd can then be directed to the Windows physical-device path. For example, for Disk 3:

MSYS2_ARG_CONV_EXCL='*' dd if=<BOOT_IMAGE> \
    of='//./PhysicalDrive3' bs=512 seek=<OFFSET_SECTORS> \
    conv=notrunc,fsync status=progress

Example for a 32 KiB offset:

MSYS2_ARG_CONV_EXCL='*' dd if=<BOOT_IMAGE> \
    of='//./PhysicalDrive3' bs=512 seek=64 \
    conv=notrunc,fsync status=progress

MSYS2 raw-device handling can vary between runtime and coreutils versions. If the physical-device path is rejected, use the native PowerShell method above.

General verification using a checksum

The most reliable verification procedure is:

  1. Read exactly the size of <BOOT_IMAGE> from the target offset.
  2. Save it to a temporary file.
  3. Compare the SHA-256 hashes.

Linux example

size=$(stat -c '%s' <BOOT_IMAGE>)

sudo dd if=/dev/sdb of=bootloader-readback.bin \
    bs=1 skip=<BOOTLOADER_OFFSET> count="$size" status=progress

sha256sum <BOOT_IMAGE> bootloader-readback.bin

macOS example

size=$(stat -f '%z' <BOOT_IMAGE>)

sudo dd if=/dev/rdisk4 of=bootloader-readback.bin \
    bs=1 skip=<BOOTLOADER_OFFSET> count="$size"

shasum -a 256 <BOOT_IMAGE> bootloader-readback.bin

The two hashes must be identical.