Skip to content

Storage internals

Reference data for the Zune HD storage stack: the module surface, the CE storage-manager API, the on-disk structures, the driver IOCTL codes, and the SoC register layout. This is the companion to Storage overview, which explains how the pieces fit together. Everything here is for Pavo firmware 4.5. Addresses and codes are firmware-specific and do not transfer to other versions.

Provenance: an unmarked value is device-verified on a live v4.5 unit, or recovered from the v4.5 firmware disassembly and then confirmed against the device. [static] marks a value taken from disassembly and not exercised on hardware. [unverified] marks an open item. Kernel-side load and driver-state addresses that are reallocated on each boot are noted as per-boot.

Module surface

Layer Module Role
Block driver libnvemmc.dll DSK*: stream driver over the NVIDIA SDMMC DDK.
Partition zpartstream.dll Zune partition-stream layer, owns the live partitioning.
Partition (MBR) mspart.dll MS-partition driver, loaded and fronts DSK1:.
Filesystem zbinfs.dll Read-only Zune binary filesystem (the OS image partition).
Filesystem zexfat.dll Read-write Zune exFAT (the data partitions).
Dispatch fsdmgr.dll, filesys.dll CE filesystem-driver manager and kernel filesys.
Application API coredll.dll Storage-manager and file exports.

The NK-side load bases float per boot for the demand-loaded filesystem drivers. The user-mode coredll.dll is fixed at 0x40320000. sdmemory.dll and sdbus.dll are not loaded, matching the absence of a removable-card path. In libnvemmc, DSK_Read is a stub that returns 0. All input and output, sector reads included, goes through DSK_IOControl.

CE storage-manager API

The topology-walk and file APIs live in coredll.dll (base 0x40320000).

Export RVA Live VA
OpenStore 0x02a880 0x4034a880
FindFirstStore 0x02a980 0x4034a980
FindNextStore 0x02a9ac 0x4034a9ac
FindCloseStore 0x02a9cc 0x4034a9cc
GetStoreInfo 0x02a9dc 0x4034a9dc
OpenPartition 0x02aa8c 0x4034aa8c
GetPartitionInfo 0x02ab18 0x4034ab18
FindFirstPartition 0x02ab74 0x4034ab74
FindNextPartition 0x02ab90 0x4034ab90
CreateFileW 0x016b64 0x40336b64
ReadFile 0x016ba0 0x40336ba0
WriteFile 0x016bcc 0x40336bcc
DeviceIoControl 0x016aa0 0x40336aa0
CloseHandle 0x01226c 0x4033226c

Call notes:

  • OpenStore is single-argument on this build: HANDLE OpenStore(LPCWSTR pszDeviceName), returning the handle directly. The two-argument OpenStore(LPCWSTR, LPHANDLE) form some CE references show is rejected.
  • FindFirstStore and FindNextStore take a STOREINFO* with dwSize preset to 0xF0. They return a search handle, not a store handle. Enumerate a store's partitions by OpenStore followed by FindFirstPartition.
  • FindFirstPartition takes the handle from OpenStore, with PARTINFO.dwSize preset to 0x128.
  • These calls require a normal user-mode thread context. Driving them from outside the shell is covered in the Lyra runtime reference.

STOREINFO

240 bytes, as populated by filesys.dll on this firmware. Verified by dumping the buffer after a successful FindFirstStore.

Offset Field Type Notes
+0x000 dwSize DWORD Always 0xF0, set by the caller.
+0x004 szDeviceName WCHAR[16] "DSK1:", "RAMDisk".
+0x054 dwDeviceClass DWORD
+0x058 dwDeviceType DWORD
+0x05C dwDeviceFlags DWORD
+0x060 szStoreName WCHAR[32] "eMMCFlashDisk".
+0x0B0 snNumSectors u64 Store size in sectors.
+0x0B8 dwBytesPerSector DWORD 0x800 (2048).
+0x0E4 dwPartitionCount DWORD
+0x0E8 dwMountCount DWORD

Observed (class, type, flags): DSK1: = (1, 2, 0x50); RAMDisk = (1, 0x20000000, 0).

PARTINFO

296 bytes. Verified by buffer dump on all three partitions.

Offset Field Type Notes
+0x000 dwSize DWORD Always 0x128.
+0x004 szPartitionName WCHAR[32] "Part00".
+0x044 szFileSys WCHAR[32] Names the FSD DLL: "zbinfs.dll", "zexfat.dll".
+0x084 szVolumeName WCHAR[] "ZBINFS", "Flash", "Flash2".
+0x108 snNumSectors u64
+0x124 bPartType BYTE MBR type byte: 0x21 zbinfs OS, 0x07 zexfat data.

szFileSys is the diagnostic field. It names the filesystem driver the partition is mounted with directly, independent of the MBR type byte.

Live topology

Read live from the storage manager.

Store 'DSK1:' ('eMMCFlashDisk')   class=1 type=2 flags=0x50
  15,716,608 sectors x 2048 B = 29.977 GiB, 3 partitions
    Part00  zbinfs.dll  'ZBINFS'      65,544 sectors = 128.0 MiB    type=0x21
    Part01  zexfat.dll  'Flash'       65,544 sectors = 128.0 MiB    type=0x07
    Part02  zexfat.dll  'Flash2'  15,585,480 sectors = 30,440.4 MiB type=0x07

Store 'RAMDisk' ('RAMDisk')       class=1 type=0x20000000 flags=0
  2,048 sectors x 512 B = 1.0 MiB, 1 partition (volatile scratch, not eMMC)

The three DSK1: partitions sum to the store total within about 80 KB.

On-disk layout (MBR)

Sector 0 of DSK1: is a DOS master boot record.

Field Value
Boot code (first bytes) e9 fd ff 00 ...
Partition [0] status 0x00
Partition [0] type 0x05 (extended)
Partition [0] LBA start 16
Partition [0] LBA count 15,716,592 (2048-byte sectors)
Partitions [1..3] empty
Signature @0x1FE 55 AA

The three storage-manager partitions are logical partitions chained as extended boot records inside this one extended partition. The eMMC uses 512-byte native blocks (the standard eMMC block size). The block driver folds four of them into each 2048-byte logical sector.

Disk IOCTL codes

The codes this firmware actually dispatches, recovered from fsdmgr's disk-read helper and confirmed by a working sector-0 read.

Name Code CTL_CODE
IOCTL_DISK_READ 0x75C08 CTL_CODE(7, 0x702, BUFFERED, FILE_READ_ACCESS)
IOCTL_DISK_WRITE 0x79C0C CTL_CODE(7, 0x703, BUFFERED, FILE_READ_ACCESS)
IOCTL_DISK_GETINFO 0x71C00 CTL_CODE(7, 0x700, BUFFERED, ANY_ACCESS)

The historical IOCTL_DISK_READ value 0x75400 from CE's public headers is not in the dispatch table on this firmware.

SG_REQ (CE 6 R3), the read/write request structure: a 20-byte header followed by one 8-byte SG_BUF per buffer.

Offset Field Notes
+0x00 sr_start Start sector.
+0x04 sr_num_sec Sector count.
+0x08 sr_num_sg Scatter-gather count.
+0x0C sr_status Written by the driver.
+0x10 reserved Zero.
+0x14 sr_sglist[0].sb_buf Buffer pointer.
+0x18 sr_sglist[0].sb_len Buffer length.

mspart.dll's PD_DeviceIoControl accepts only these codes and fails others with ERROR_GEN_FAILURE: 1, 2, 3 (legacy), 0x71C00, 0x71C4C, 0x71C50, 0x71C58, 0x71C64, 0x71C80, 0x75C08, 0x79C0C. The legacy single-integer read and write codes 4 and 5 are accepted only by libnvemmc's DSK_IOControl below mspart, not by mspart itself.

libnvemmc.dll DSK_* export RVAs (live at the per-boot libnvemmc base):

Export RVA Export RVA
DSK_Init 0x001f70 DSK_Seek 0x0014a0
DSK_Open 0x001478 DSK_IOControl 0x0021f8
DSK_Close 0x0013f4 DSK_PowerUp 0x0014ac
DSK_Read 0x001490 (stub) DSK_PowerDown 0x0014a8
DSK_Write 0x001498 DSK_Deinit 0x001418

DSK_IOControl also exposes eMMC partition-mode IOCTLs 0x321004 (GET_PARTITION_MODE) and 0x321008 (SET_PARTITION_MODE). On this device they are effectively inert: there are no boot, RPMB, or GPP partitions to switch to (see below).

Boot, RPMB, and GP partitions

The eMMC has no boot, RPMB, or general-purpose partitions. Read directly with CMD8 SEND_EXT_CSD, its provisioning multipliers are all zero, and a zero multiplier means the region is unprovisioned. EXT_CSD_REV reads 2, which the JEDEC table maps to revision 1.2 (eMMC 4.2), a revision predating the boot-partition feature (4.3) and the RPMB and GP-partition features (4.4), consistent with the zero multipliers.

EXT_CSD field Byte Value
SEC_COUNT 212 0x03C60000 (63,307,776, the full user area)
EXT_CSD_REV 192 2 (revision 1.2, eMMC 4.2)
BOOT_SIZE_MULT 226 0 (no boot partitions)
RPMB_SIZE_MULT 168 0 (no RPMB)
GP_SIZE_MULT (GPP1..4) 143..154 0 (no GP partitions)
PARTITIONING_SUPPORT 160 0

Reading EXT_CSD

CMD8 SEND_EXT_CSD cannot be programmed into the controller directly. The register layout is non-standard (below), and the SD clock is gated at idle: NvDdkHsmmcSendCommand does no clock control, and the clock is brought up by the read path through NvRm. The read drives the driver's own machinery instead:

  1. Ungate the SDMMC module clock. NvDdkHsmmcResume does this as NvRmPowerModuleClockControl(hRm, (instance << 16) | 0x11, clientId, enable), with hRm, instance, and clientId taken from the Hsmmc handle. The handle is at geom + 0x58 off the geometry struct; the arguments are handle[0], handle[+4], and handle[+0x30].
  2. Call NvDdkHsmmcRead(handle, 512, buffer, pCmd) with a hand-built pCmd of {index = 8, arg = 0, class = 1 (read), 0, 0, 0, 0, 0}. The driver issues CMD8 and DMAs the 512-byte EXT_CSD into the buffer. The call then returns NvError_NotImplemented from its block-read wrapper (CMD8 is not a block command), but the data is captured before that; a SEC_COUNT-matches-the-known-capacity check confirms the read.

Host tool: read-ext-csd.py.

SDMMC controller register map

NvDdkHsmmcSendCommand and NvDdkHsmmcGetCommandResponse (in libnvddk_misc.dll, module base 0xc08e0000) drive the controller at register base handle[+0x14]. The layout is not standard SDHCI:

Field Offset from base
Block size / count +0x04
Command / control word (writing this issues the command) +0x10
Command index (6-bit) +0x28
Argument +0x2c
Response +0x34

Writing the argument to +0x08 and the command to +0x0c (the standard SDHCI positions) hits registers that do not issue a command.

SoC storage registers

The controller MMIO base is handle[+0x14] (0xd2e60500 on the sampled boot). Its command-issue register layout is the non-standard one in the register map above, not standard SDHCI. The handle also records the APB DMA channel-4 physical base (0x70008500) at +0x10 and the power state at +0x68; the handle's own base address is reallocated per boot.

The SD clock is gated at idle. NvDdkHsmmcSendCommand does no clock control and makes no calls; the clock is ungated by the read path through NvRm (see the EXT_CSD read above). A command injected at the SendCommand entry hook finds the clock off, so it never latches.

The block driver caches the card geometry in its NvDdk device state. It parses the eMMC's EXT_CSD SEC_COUNT field (byte 212, a JEDEC value in 512-byte units) to 0x03C60000, 63,307,776 native sectors, a raw user-area capacity of 30.19 GiB. CE exposes 15,716,608 sectors of 2048 bytes (62,866,432 native, 29.977 GiB), and the 512-byte native block folds four-to-one into each logical sector. The struct base is reallocated per boot, so the field is found by its signature (SEC_COUNT flanked by 0 and 0x400), not a fixed address.

The reserved front region

CE maps DSK1: onto the top of the card. A DSK1: read of logical sector 0 reaches the eMMC as CMD18 READ_MULTIPLE_BLOCK for native block 441,344, so the block driver adds a fixed 441,344-block base to every logical read. That base plus the store size equals the full user area: 441,344 + 62,866,432 = 63,307,776 = SEC_COUNT. Native blocks 0 through 441,343 (215.5 MiB) sit in front of the store and hold the boot and recovery firmware.

DSK1: cannot address them: the store IOCTL bounds-checks against the store size, and no CreateFile path reaches libnvemmc below mspart. They are read by hooking NvDdkHsmmcSendCommand and, while armed, subtracting a delta from a read command's native block address, so a normal DSK1: read is remapped into the front region and served by the driver's own DMA path. The read technique is in the Lyra runtime reference. NvDdkHsmmcSendCommand(hHsmmc, pCmd) takes a 32-byte command structure:

Offset Field Notes
+0x00 command index 0x12 = CMD18 READ_MULTIPLE_BLOCK for reads
+0x04 block address native 512-byte block; a DSK1: read of logical sector N arrives as 441344 + 4*N
+0x10 logical sector the requested LBA

Layout, read from a full dump of the region. Offsets are into the front region; about 55 MiB is content and the rest is erased (0xFF) or zero.

Offset Size Contents
0 2 KiB, 24 copies every 128 KiB signed header: a signature prefix, an a1a1ddba marker at +0x5EC, then a PKCS#7 signedData catalog (OID 1.2.840.113549.1.7.2) naming device model Pavo, signed 2011-04-18 (UTCTime 110418181445Z). All 24 copies byte-identical
3.0 MiB 128 KiB Tegra boot configuration table, tagged BCT
3.1 MiB, 6.1 MiB 651.5 KiB each encrypted bootloader, two byte-identical copies
9.1 MiB, 76.8 MiB ~12 MiB each Windows CE boot image, two byte-identical copies: Tegra APX 2600, WOOKIE board support, NVIDIA bootloader (NvBl), CE kernel (kernel.dll, kitl.dll, coredll.dll)
144.4 MiB ~30 MiB Windows CE image with networking: RNDIS, RAS, libnvodm_kernel_query.dll

Boot-chain component identities

The bootloader, BCT, and CE images were identified against the extracted v4.5 firmware package (ZBoot.bin, NK.bin, Recovery.bin).

  • BCT. The 3.0 MiB block is a Tegra boot configuration table stored as a redundant 2 KiB record replicated 64 times across the 128 KiB slot. Each record carries a mirrored internal header (8f9e8d8b ffffffff 00000100 00000380 at both +0x00 and +0x28) with BCT tags at +0x44 and +0x54.
  • Bootloader. The copies at 3.1 and 6.1 MiB match the ZBoot stage. The firmware's ZBoot.bin unpacks to 667,360 bytes; the on-flash copy is that length padded to a 4 KiB boundary (667,648 bytes), encrypted (entropy 8.0 against 6.1 for the plaintext). A zero-key AES pass yields noise, so a real key is in use (see below); AES is the platform's secure-boot cipher.
  • CE images. The 12 MiB copies at 9.1 and 76.8 MiB are a v4.5 boot and recovery image, not byte-identical to either NK.bin or Recovery.bin. Unpacked and walked module by module (146 modules, 29 data files), they match those two except for the pointer relocation a different ROM layout requires (137 modules differ by under 1 percent, 32 are identical) and six modules that genuinely differ: nk.exe, zbinfs.dll, zbinfs_NK.hash, ZAccessoryDriver.dll, ZAcsState.dll, default.fdf.

Secure boot

The bootloader is encrypted, and a zero-key decrypt yields noise, so a real key is in use. The key is a secure-boot key in the AP16 fuse block at 0x7000F800. The fuse rows the bootloader references for its key material (0x7000F9A4..0x7000F9B4), read back from the running OS through the runtime's register-mapping primitive, return 0xFFFFFFFF, the all-ones of read-locked secure fuses. The key is burned but not software-readable, so the bootloader cannot be decrypted on the device. That those specific rows are the secure-boot and device keys is inferred from the bootloader's fuse references, not confirmed against a fuse map [unverified]. The signed header is a Microsoft signedData catalog; its coverage of the boot chain is not confirmed, since the on-flash copy is truncated to 2 KiB. The BCT and both CE images are stored unencrypted; the bootloader is the only encrypted payload.