. "$BST_LIB" # BST-TIMEOUT: 400 # 161 cases, ~150 execs, no loops and no corpus. MEASURED at # # elapsed=103s on fra2 (2026-08-26, # # ==BST-SUITE mmc-utils elapsed=103s bound=300s cases=161==) # # and declared at ~4x that. Nothing here waits: every mmc call # # either decides in argv, reads a 32-byte file out of tmpfs, or # # is refused at open(2)/ioctl(2), and returns at once. # mmc-utils -- the eMMC/SD userland on a machine with no MMC controller. # # WHAT THIS MACHINE CAN AND CANNOT TEST. mmc-utils is ONE program, /usr/bin/mmc, # dispatching 29 verbs. Most of them are ioctl clients: they open a device node # and drive it with MMC_IOC_CMD / MMC_IOC_MULTI_CMD from linux/mmc/ioctl.h. This # kernel registers no MMC host -- there is no /sys/class/mmc_host, no mmcblk # node, and no block layer at all (CONFIG_BLOCK is not set, the same fact # 89-hdparm.test records) -- so the cards those verbs exist to talk to are not # here. Whether that is still true is NOT assumed: the probe below reads the # live system for a controller, and every gated case runs the real assertion the # moment one appears. # # WHAT IS THEREFORE ASSERTED FOR REAL, and it is most of the program: # # - THE WHOLE DISPATCHER. mmc.c matches argv against the verb table by PREFIX, # so `mmc ci r ` is `mmc cid read `; check_ambiguity rejects a # prefix that fits more than one verb; and the per-verb nargs field is # checked before any device is touched. Every one of those decisions is # arithmetic on argv and calls to fprintf, and each has its own exit status. # # - THE OFFLINE REGISTER DECODERS, WHICH ARE THE BULK OF THE PORT. This is the # finding that shaped the suite: `mmc cid read` / `csd read` / `scr read` # take a DIRECTORY, not a device. lsmmc.c's process_dir() chdir()s into it # and fopen()s ordinary files named `type`, `cid`, `csd` and `scr` -- the # sysfs layout, but nothing checks that it IS sysfs. No ioctl, no device, no # controller. Staging that directory in tmpfs therefore drives print_mmc_cid, # print_sd_cid, print_mmc_csd, print_sd_csd and print_sd_scr -- with # parse_bin/to_binstr underneath them -- end to end, exactly the way # `hdparm --Istdin` carries 89-hdparm.test. Every payload below is synthetic # and every asserted field was CHOSEN when it was generated, so each case is # a round trip through the decoder rather than a tautology; the capacity # lines go further and check the arithmetic the decoder does on top of the # fields. # # - AND THE DEEPEST REACHABLE POINT, WITH ITS OWN CONTROL. open(2) on a path # that is not a device SUCCEEDS when the path is an ordinary file, so an # empty regular file carries every ioctl verb past the open into a real # ioctl(MMC_IOC_CMD) on a real descriptor. The kernel answers ENOTTY and # upstream reports it with perror + its own diagnostic. The control is the # SAME argv against a path that does not exist, which must fail at open(2) # instead: the only thing that differs between the two is whether the ioctl # was reached. A binary that trapped on the first ioctl would produce none of # this, so these cases are also the runtime no-trap proof. # # EVERY EXPECTATION HERE WAS READ OFF A NATIVE x86-64 BUILD of the same SRCREV # (b5ca140312, poky/meta recipes-devtools/mmc) during the port -- the exit # statuses, the message wording, and which stream each goes to -- rather than # transcribed from the source. # # FOUR UPSTREAM BEHAVIOURS ARE PINNED RATHER THAN PATCHED, because they are # mmc-utils' on every architecture and a future change to either should be # visible here rather than silent (mmc-utils is a third-party public repository; # see no-public-upstream-reports.md): # - `mmc read -h` prints its usage on STDOUT and exits 255, not 0. # - a process_dir() failure returns -1, which main() passes to exit(), so every # register-file error exits 255. # - softreset and preidle discard issue_cmd0()'s result and return 0 whatever # the ioctl did, so they are the two verbs that "succeed" with no controller. # - the three bootbus operands are keywords, and each has its own diagnostic. require mmc mmc-utils/present W=${TMPDIR:-/tmp}/bst-mmc.$$ out=$W.out; reg=$W.reg mkdir -p "$W.d" 2>/dev/null : > "$out" : > "$reg" # stage [ ...] -- a fake sysfs card directory. stage() { _d=$W.d/$1; _t=$2; shift 2 mkdir -p "$_d" 2>/dev/null printf '%s\n' "$_t" > "$_d/type" while [ $# -ge 2 ]; do printf '%s\n' "$2" > "$_d/$1"; shift 2; done printf '%s' "$_d" } # ------------------------------------------------------------------ probe ---- # Read the live system for an MMC controller, by the routes the kernel would # have to provide first. Nothing below is hardcoded: `host` is set only from # what this machine actually reports. host="" for h in /sys/class/mmc_host/mmc*; do [ -d "$h" ] && { host=$h; break; } done blk="" for b in /dev/mmcblk*; do [ -e "$b" ] && { blk=$b; break; } done # --------------------------------------------------------------- identity ---- check_out mmc-utils/path "/usr/bin/mmc" command -v mmc # VERSION is -DVERSION=$(git describe --abbrev=6 --always --tags) taken at build # time, and this recipe's tree carries no tags, so it is the abbreviated SRCREV. # Asserting it pins the version the image ships: a poky SRCREV bump reddens this # case rather than passing quietly against a different program. check_out mmc-utils/version "b5ca14" mmc help # With no arguments mmc prints the usage on STDOUT and exits 0 -- help is the # documented no-argument behaviour here, not an error path. check_rc mmc-utils/noargs-rc 0 sh -c "mmc > '$out' 2>&1" check_out mmc-utils/noargs-usage "Usage:" cat "$out" # `help`, `-h` and `--help` are the same three spellings of that path. check_rc mmc-utils/help-rc 0 sh -c "mmc help > '$out' 2>&1" check_rc mmc-utils/dash-h-rc 0 sh -c "mmc -h > '$out' 2>&1" check_rc mmc-utils/dashdash-rc 0 sh -c "mmc --help > '$out' 2>&1" # The verb table is printed from the same array the dispatcher matches against, # so these assert the table itself, across all four of its shapes: a two-word # verb, a three-word verb, a one-word verb, and one carrying an optional operand. check_out mmc-utils/help-extcsd "mmc extcsd read " cat "$out" check_out mmc-utils/help-wpboot "mmc writeprotect boot get " cat "$out" check_out mmc-utils/help-cid "mmc cid read " cat "$out" check_out mmc-utils/help-sanit "mmc sanitize [timeout_ms]" cat "$out" check_out mmc-utils/help-trailer "mmc help|--help|-h" cat "$out" # ------------------------------------------------------------- dispatcher ---- # An unmatched verb is exit 1, with the diagnostic on stderr and the usage on # stdout -- two different streams, asserted as two cases. check_rc mmc-utils/unknown-rc 1 sh -c "mmc nosuchverb > /dev/null 2>'$out'" check_out mmc-utils/unknown-msg "ERROR: unknown command 'nosuchverb'" cat "$out" check_rc mmc-utils/unknown-usage-rc 1 sh -c "mmc nosuchverb 2>/dev/null > '$out'" check_out mmc-utils/unknown-usage "Usage:" cat "$out" # A single word cannot match a two-word verb (argc-1 < ncmds), so `mmc cid` is # not an incomplete command -- it is an unknown one. check_rc mmc-utils/partial-rc 1 sh -c "mmc cid > /dev/null 2>'$out'" check_out mmc-utils/partial-msg "ERROR: unknown command 'cid'" cat "$out" # Prefix matching: argv is matched against the verb WORD BY WORD and each word # need only be a prefix. Both of these are the full command. check_out mmc-utils/abbrev-cid "manufacturer: 0x15" mmc ci r "$(stage ab MMC cid 15014242535431323313deadbeefc554)" check_out mmc-utils/abbrev-csd "SPEC_VERS: 0x4" mmc cs r "$(stage ab2 MMC csd 903200320f5903ffedb7ffef8a404066)" # ...and a prefix that fits more than one verb is refused with exit 2, naming # which word was ambiguous. `c` fits `cache`, `csd` and `cid`. check_rc mmc-utils/ambiguous-rc 2 sh -c "mmc c e /dev/null > /dev/null 2>'$out'" check_out mmc-utils/ambiguous-msg "ERROR: in command 'c', 'c' is ambiguous" cat "$out" # The nargs field is checked before any device is opened, and it has two forms: # a minimum (negative nargs) and an exact count. check_rc mmc-utils/nargs-min-rc 2 sh -c "mmc cid read > /dev/null 2>'$out'" check_out mmc-utils/nargs-min-msg "ERROR: 'cid read' requires minimum 1 arg(s)" cat "$out" check_rc mmc-utils/nargs-exact-rc 2 sh -c "mmc extcsd write '$reg' > /dev/null 2>'$out'" check_out mmc-utils/nargs-exact-msg "ERROR: 'extcsd write' requires 3 arg(s)" cat "$out" check_rc mmc-utils/nargs-min3-rc 2 sh -c "mmc bootpart enable 0 0 > /dev/null 2>'$out'" check_out mmc-utils/nargs-min3-msg "ERROR: 'bootpart enable' requires minimum 3 arg(s)" cat "$out" # `--help` after a PARTIAL verb prints the advanced help of every verb that # still matches, then the version, and exits 0. Two words match `extcsd`. check_rc mmc-utils/adv-help-rc 0 sh -c "mmc extcsd --help > '$out' 2>&1" check_out mmc-utils/adv-help-read "mmc extcsd read " cat "$out" check_out mmc-utils/adv-help-write "mmc extcsd write " cat "$out" check_out mmc-utils/adv-help-vers "b5ca14" cat "$out" # There is no --version / -V: they take the unknown-command path like any other # unmatched word, which is worth pinning because it is the spelling a script # reaches for first. check_rc mmc-utils/no-version-opt-rc 1 sh -c "mmc --version > /dev/null 2>'$out'" check_out mmc-utils/no-version-opt-msg "ERROR: unknown command '--version'" cat "$out" # ------------------------------------------------- the offline CID decoder ---- # 0x15 is in lsmmc.c's MMC manufacturer table; every other field was chosen when # the payload was generated, so each case round-trips the bit slicing: # MID 0x15 CBX 1(BGA) OID 0x42 PNM "BST123" PRV 1.3 PSN 0xdeadbeef # MDT year nibble 0xc -> 1997+12 month 5 -> jun CRC 0x2a D=$(stage mmc0 MMC cid 15014242535431323313deadbeefc554) check_rc mmc-utils/cid-mmc-rc 0 sh -c "mmc cid read '$D' > '$out' 2>&1" check_out mmc-utils/cid-mmc-mid "manufacturer: 0x15 (Samsung/SanDisk/LG) oid: 0x42" cat "$out" check_out mmc-utils/cid-mmc-product "product: 'BST123' 1.3" cat "$out" check_out mmc-utils/cid-mmc-serial "serial: 0xdeadbeef" cat "$out" check_out mmc-utils/cid-mmc-mdt "manufacturing date: 2009 jun" cat "$out" # The MMC CID path talks to no device, so anything on stderr would mean it tried. sh -c "mmc cid read '$D' > /dev/null 2>'$out'" if [ -s "$out" ]; then bst_notok mmc-utils/cid-mmc-silent "stderr not empty: $(_bst_1line "$(cat "$out")")" else bst_ok mmc-utils/cid-mmc-silent; fi # -v is a different renderer over the same decode, and it prints three fields the # plain form never does -- CBX, PRV as raw hex, and the CRC. sh -c "mmc cid read -v '$D' > '$out' 2>&1" check_out mmc-utils/cid-mmc-v-head "======MMC/CID======" cat "$out" check_out mmc-utils/cid-mmc-v-cbx "CBX: 0x1 (BGA)" cat "$out" check_out mmc-utils/cid-mmc-v-prv "PRV: 0x13 (1.3)" cat "$out" check_out mmc-utils/cid-mmc-v-psn "PSN: 0xdeadbeef" cat "$out" check_out mmc-utils/cid-mmc-v-crc "CRC: 0x2a" cat "$out" # MDT is rendered month-nibble-first, which is the reverse of the on-wire order # the parse reads it in. Pinned as upstream's, not corrected. check_out mmc-utils/cid-mmc-v-mdt "MDT: 0x5c 2009 jun" cat "$out" # A MID absent from the table takes the other branch of get_manufacturer, and # CBX 2 is a different arm of the same switch. DU=$(stage mmcU MMC cid 9a02074e4f4c4953540100c0ffee3b22) check_out mmc-utils/cid-mmc-unlisted "manufacturer: 0x9a (Unlisted) oid: 0x7" mmc cid read "$DU" check_out mmc-utils/cid-mmc-v-pop "CBX: 0x2 (PoP)" mmc cid read -v "$DU" # `type` selects the decoder, and the SD CID is a DIFFERENT bit layout from the # MMC one -- 16-bit ASCII OID, 40-bit PNM, an 8-bit year with a 2000 epoch -- # so this is a second decoder, not the same one with a different table. DS=$(stage sd0 SD cid 035344425354534427123456780179aa) check_rc mmc-utils/cid-sd-rc 0 sh -c "mmc cid read '$DS' > '$out' 2>&1" check_out mmc-utils/cid-sd-mid "manufacturer: 'SanDisk' 'SD'" cat "$out" check_out mmc-utils/cid-sd-product "product: 'BSTSD' 2.7" cat "$out" check_out mmc-utils/cid-sd-serial "serial: 0x12345678" cat "$out" check_out mmc-utils/cid-sd-mdt "manufacturing date: 2023 oct" cat "$out" check_out mmc-utils/cid-sd-v-oid "OID: SD" mmc cid read -v "$DS" # ------------------------------------------------- the offline CSD decoder ---- # MMC CSD. The plain renderer prints only two lines -- upstream's choice, pinned # here -- and -v prints the whole of it. DC=$(stage mmcc MMC csd 903200320f5903ffedb7ffef8a404066) check_rc mmc-utils/csd-mmc-rc 0 sh -c "mmc csd read '$DC' > '$out' 2>&1" check_out mmc-utils/csd-mmc-spec "SPEC_VERS: 0x4 (v4.0-v4.3)" cat "$out" check_out mmc-utils/csd-mmc-ccc "CCC: 0x0f5 (class: 7, 6, 5, 4, 2, 0," cat "$out" sh -c "mmc csd read -v '$DC' > '$out' 2>&1" check_out mmc-utils/csd-mmc-v-head "======MMC/CSD======" cat "$out" check_out mmc-utils/csd-mmc-v-struct "CSD_STRUCTURE: 0x2 (v1.2)" cat "$out" # TAAC and TRAN_SPEED are each recomposed from two fields (value<<3 | unit) and # then rendered through a units table, so these check arithmetic, not a slice. check_out mmc-utils/csd-mmc-v-taac "TAAC: 0x32 (250.00ns)" cat "$out" check_out mmc-utils/csd-mmc-v-tran "TRAN_SPEED: 0x32 (26.00MHz/s)" cat "$out" check_out mmc-utils/csd-mmc-v-nsac "NSAC: 0 clocks" cat "$out" check_out mmc-utils/csd-mmc-v-rbl "READ_BL_LEN: 0x9 (512 bytes)" cat "$out" check_out mmc-utils/csd-mmc-v-rbp "READ_BL_PARTIAL: 0x0 (only 512 byte and READ_BL_LEN block size)" cat "$out" check_out mmc-utils/csd-mmc-v-dsr "DSR_IMP: 0x0 (configurable driver stage not available)" cat "$out" # The four VDD fields index two different current tables -- min and max are not # the same lookup -- so a decoder that used one table for both would print 35mA # four times and fail here. check_out mmc-utils/csd-mmc-v-vddrmin "VDD_R_CURR_MIN: 0x5 (35mA)" cat "$out" check_out mmc-utils/csd-mmc-v-vddrmax "VDD_R_CURR_MAX: 0x5 (45mA)" cat "$out" check_out mmc-utils/csd-mmc-v-erase "ERASE_GRP_MULT: 0x1f (1024 write blocks/erase group)" cat "$out" check_out mmc-utils/csd-mmc-v-wpgrp "WP_GRP_SIZE: 0x0f (16 blocks/write protect group)" cat "$out" check_out mmc-utils/csd-mmc-v-r2w "R2W_FACTOR: 0x2 (Write 2 times read)" cat "$out" check_out mmc-utils/csd-mmc-v-fmt "FILE_FORMAT: 0x0 (partition table)" cat "$out" check_out mmc-utils/csd-mmc-v-crc "CRC: 0x33" cat "$out" # SD CSD v0. C_SIZE 0xea7, C_SIZE_MULT 7 and READ_BL_LEN 9 give # (3751+1) * 2^(7+2) * 2^9 = 983564288 bytes -- the capacity line is the decoder # doing that arithmetic, which no single-field case can check. D0=$(stage sd_v0 SD csd 001a00325b5983a9edb7ffff0a400056) check_rc mmc-utils/csd-sdv0-rc 0 sh -c "mmc csd read '$D0' > '$out' 2>&1" check_out mmc-utils/csd-sdv0-classes "card classes: 10 switch, 8 application specific, 7 lock card, 5 erase, 4 block write, 2 block read, 0 basic," cat "$out" check_out mmc-utils/csd-sdv0-capacity "capacity: 938.00Mbyte (983564288 bytes, 1921024 sectors, 512 bytes each)" cat "$out" sh -c "mmc csd read -v '$D0' > '$out' 2>&1" check_out mmc-utils/csd-sdv0-v-head "======SD/CSD======" cat "$out" check_out mmc-utils/csd-sdv0-v-struct "CSD_STRUCTURE: 0" cat "$out" check_out mmc-utils/csd-sdv0-v-csize "C_SIZE: 0xea7" cat "$out" check_out mmc-utils/csd-sdv0-v-mult "C_SIZE_MULT: 0x7" cat "$out" check_out mmc-utils/csd-sdv0-v-tran "TRAN_SPEED: 0x32 (25.00Mbit/s)" cat "$out" # SD CSD v1 (SDHC/SDXC) is a THIRD layout: a 22-bit C_SIZE where v0 has 12, no # VDD or C_SIZE_MULT at all, and a different capacity formula -- # (30719+1) * 512 * 1024 = 16106127360 bytes. D1=$(stage sd_v1 SD csd 4070001a5b59000077ff7f800a4000ee) check_rc mmc-utils/csd-sdv1-rc 0 sh -c "mmc csd read '$D1' > '$out' 2>&1" check_out mmc-utils/csd-sdv1-capacity "capacity: 15.00Gbyte (16106127360 bytes, 31457280 sectors, 512 bytes each)" cat "$out" sh -c "mmc csd read -v '$D1' > '$out' 2>&1" check_out mmc-utils/csd-sdv1-v-struct "CSD_STRUCTURE: 1" cat "$out" check_out mmc-utils/csd-sdv1-v-csize "C_SIZE: 0x0077ff" cat "$out" check_out mmc-utils/csd-sdv1-v-sector "SECTOR_SIZE: 0x7f (Erasable sector: 128 blocks)" cat "$out" check_out mmc-utils/csd-sdv1-v-wpgrp "WP_GRP_SIZE: 0x00 (Write protect group: 1 blocks)" cat "$out" # This payload's TAAC and TRAN_SPEED are deliberately values the SD spec does not # permit, so it also exercises the two validity warnings -- the only branch in # the decoder that judges its input rather than rendering it. check_out mmc-utils/csd-sdv1-warn-taac "Warn: Invalid TAAC (should be 0x0e)" cat "$out" check_out mmc-utils/csd-sdv1-warn-tran "Warn: Invalid TRAN_SPEED (should be 0x32, 0x5a, 0x0b or 0x2b" cat "$out" # A CSD_STRUCTURE the decoder has no layout for is reported and abandoned -- on # stdout, and still exit 0. D3=$(stage sd_v3 SD csd c0000000000000000000000000000000) check_rc mmc-utils/csd-unknown-rc 0 sh -c "mmc csd read '$D3' > '$out' 2>&1" check_out mmc-utils/csd-unknown-msg "Unknown CSD structure: 0x3" cat "$out" # ------------------------------------------------- the offline SCR decoder ---- DR=$(stage sd_scr SD scr 0285800300000000) check_rc mmc-utils/scr-rc 0 sh -c "mmc scr read '$DR' > '$out' 2>&1" check_out mmc-utils/scr-version "version: SD 3.0x" cat "$out" check_out mmc-utils/scr-widths "bus widths: 4bit, 1bit," cat "$out" sh -c "mmc scr read -v '$DR' > '$out' 2>&1" check_out mmc-utils/scr-v-head "======SD/SCR======" cat "$out" check_out mmc-utils/scr-v-struct "SCR_STRUCTURE: 0x0 (SCR v1.0)" cat "$out" check_out mmc-utils/scr-v-spec "SD_SPEC: 0x2 (SD v2.00/v3.0x)" cat "$out" check_out mmc-utils/scr-v-sec "SD_SECURITY: 0x0 (no security)" cat "$out" check_out mmc-utils/scr-v-cmd "CMD_SUPPORT: 0x3 (CMD23 CMD20" cat "$out" # ------------------------------------- the register-file error lattice -------- # Each of these fails at a DIFFERENT point of process_dir, and each has its own # diagnostic. All exit 255: process_dir returns -1 and main() hands that to # exit(), which truncates to 255 -- upstream's, pinned rather than corrected. check_rc mmc-utils/err-nodir-rc 255 sh -c "mmc cid read '$W.d/nosuchdir' > /dev/null 2>'$out'" check_out mmc-utils/err-nodir "MMC/SD information directory" cat "$out" DN=$W.d/notype; mkdir -p "$DN" 2>/dev/null check_rc mmc-utils/err-notype-rc 255 sh -c "mmc cid read '$DN' > /dev/null 2>'$out'" check_out mmc-utils/err-notype-open "Could not open MMC/SD file 'type'." cat "$out" check_out mmc-utils/err-notype-msg "Could not read card interface type in directory" cat "$out" DB=$(stage badtype FOO) check_rc mmc-utils/err-badtype-rc 255 sh -c "mmc cid read '$DB' > /dev/null 2>'$out'" check_out mmc-utils/err-badtype "Unknown type: 'FOO'" cat "$out" DM=$(stage nocid MMC) check_rc mmc-utils/err-nocid-rc 255 sh -c "mmc cid read '$DM' > /dev/null 2>'$out'" check_out mmc-utils/err-nocid "Could not read card identity in directory" cat "$out" DE=$(stage emptycid MMC); : > "$DE/cid" check_rc mmc-utils/err-emptycid-rc 255 sh -c "mmc cid read '$DE' > /dev/null 2>'$out'" check_out mmc-utils/err-emptycid "Could not read data from MMC/SD file 'cid'." cat "$out" DK=$(stage nocsd MMC) check_rc mmc-utils/err-nocsd-rc 255 sh -c "mmc csd read '$DK' > /dev/null 2>'$out'" check_out mmc-utils/err-nocsd "Could not read card specific data in" cat "$out" # -h and an unknown option both leave through parse_opts, printing the option # usage on STDOUT and exiting 255. getopt's own complaint names argv[0], which # prepare_args() rewrote to the full verb -- so this also asserts that rewrite. check_rc mmc-utils/opt-h-rc 255 sh -c "mmc cid read -h > '$out' 2>&1" check_out mmc-utils/opt-h-usage "Usage: print mmc [-h] [-v] " cat "$out" check_rc mmc-utils/opt-bad-rc 255 sh -c "mmc cid read -Z x > /dev/null 2>'$out'" check_out mmc-utils/opt-bad-getopt "mmc cid read: invalid option -- 'Z'" cat "$out" check_out mmc-utils/opt-bad-msg "Unknown option '?' encountered." cat "$out" # ----------------------------------------------- what read_file() tolerates --- # read_file trims surrounding whitespace and to_binstr accepts either case, and # parse_bin stops when the format is consumed rather than when the string is -- # so a longer value decodes to the same card. Three spellings, one answer, which # is a stronger statement than any of them alone. DW=$(stage ws MMC cid " 15014242535431323313deadbeefc554 ") DUP=$(stage up MMC cid "15014242535431323313DEADBEEFC554") DL=$(stage lng MMC cid "15014242535431323313deadbeefc554ffff") want="product: 'BST123' 1.3" check_out mmc-utils/tol-whitespace "$want" mmc cid read "$DW" check_out mmc-utils/tol-uppercase "$want" mmc cid read "$DUP" check_out mmc-utils/tol-trailing "$want" mmc cid read "$DL" # --------------------------------------------- the argv validation lattice ---- # These reject before any device is opened, so they say nothing about MMC and # everything about the program. The three bootbus operands are keywords and each # has its own message -- a lattice, walked one rung at a time. check_rc mmc-utils/bb-mode-rc 1 sh -c "mmc bootbus set bogus x1 x1 '$reg' > /dev/null 2>'$out'" check_out mmc-utils/bb-mode "illegal specified" cat "$out" check_rc mmc-utils/bb-reset-rc 1 sh -c "mmc bootbus set dual bogus x1 '$reg' > /dev/null 2>'$out'" check_out mmc-utils/bb-reset "illegal specified" cat "$out" check_rc mmc-utils/bb-width-rc 1 sh -c "mmc bootbus set dual retain bogus '$reg' > /dev/null 2>'$out'" check_out mmc-utils/bb-width "illegal specified" cat "$out" check_rc mmc-utils/erase-type-rc 1 sh -c "mmc erase bogustype 0 100 '$reg' > /dev/null 2>'$out'" check_out mmc-utils/erase-type "Unknown erase type: bogustype" cat "$out" # start > end is arithmetic on two parsed integers, and the diagnostic prints # both back in a fixed 8-digit hex form -- so it checks the parse and the format. check_rc mmc-utils/erase-range-rc 1 sh -c "mmc erase legacy 100 0 '$reg' > /dev/null 2>'$out'" check_out mmc-utils/erase-range "erase start [0x00000064] > erase end [0x00000000]" cat "$out" check_rc mmc-utils/wp-type-rc 1 sh -c "mmc writeprotect user set bogus 0 1 '$reg' > /dev/null 2>'$out'" check_out mmc-utils/wp-type 'Error, invalid "type"' cat "$out" # ffu and rpmb open their own file operands before touching the device, so an # absent one fails at open(2) with the tool's own wording. check_rc mmc-utils/ffu-noimage-rc 1 sh -c "mmc ffu '$W.d/nosuchimage' '$reg' > /dev/null 2>'$out'" check_out mmc-utils/ffu-noimage "image open failed: No such file or directory" cat "$out" check_rc mmc-utils/rpmb-nokey-rc 1 sh -c "mmc rpmb write-key '$reg' '$W.d/nosuchkey' > /dev/null 2>'$out'" check_out mmc-utils/rpmb-nokey "can't open key file: No such file or directory" cat "$out" # rpmb read-block's own usage check is a fourth arity form: nargs allows it, the # verb re-checks, and the message is the full usage line. check_rc mmc-utils/rpmb-usage-rc 1 sh -c "mmc rpmb read-block '$reg' 0 1 > /dev/null 2>'$out'" check_out mmc-utils/rpmb-usage "Usage: mmc rpmb read-block" cat "$out" # --------------------------------------- the device path reaches the OS ------- # mmc does not check that its argument is an MMC device: it opens whatever path # it is given and issues the ioctl. On this target that is what makes the path # testable -- ENOTTY comes back from the kernel with an errno the tool prints, # rather than the binary trapping the way an unresolved symbol would. Each of # these is a different ioctl call site with a different follow-up diagnostic. check_rc mmc-utils/notty-extcsd-rc 1 sh -c "mmc extcsd read '$reg' > /dev/null 2>'$out'" check_out mmc-utils/notty-extcsd-ioctl "ioctl: Inappropriate ioctl for device" cat "$out" check_out mmc-utils/notty-extcsd-msg "Could not read EXT_CSD from" cat "$out" check_rc mmc-utils/notty-status-rc 1 sh -c "mmc status get '$reg' > /dev/null 2>'$out'" check_out mmc-utils/notty-status "Could not read response to SEND_STATUS from" cat "$out" check_rc mmc-utils/notty-wpboot-rc 1 sh -c "mmc writeprotect boot get '$reg' > /dev/null 2>'$out'" check_out mmc-utils/notty-wpboot "Could not read EXT_CSD from" cat "$out" check_rc mmc-utils/notty-cache-rc 1 sh -c "mmc cache enable '$reg' > /dev/null 2>'$out'" check_out mmc-utils/notty-cache "Could not read EXT_CSD from" cat "$out" # A WRITE path, which fails at a different call site and names the byte it meant # to write and the offset it meant to write it to. check_rc mmc-utils/notty-sanitize-rc 1 sh -c "mmc sanitize '$reg' > /dev/null 2>'$out'" check_out mmc-utils/notty-sanitize "Could not write 0x01 to EXT_CSD[165] in" cat "$out" check_rc mmc-utils/notty-extwrite-rc 1 sh -c "mmc extcsd write 300 1 '$reg' > /dev/null 2>'$out'" check_out mmc-utils/notty-extwrite "Could not write 0x01 to EXT_CSD[300] in" cat "$out" # The RPMB ioctl is MMC_IOC_MULTI_CMD, a different ioctl number reported by its # own wording -- so this is a second ioctl entry point, not the same one again. check_rc mmc-utils/notty-rpmb-rc 1 sh -c "mmc rpmb read-counter '$reg' > /dev/null 2>'$out'" check_out mmc-utils/notty-rpmb "RPMB ioctl failed: Inappropriate ioctl for device" cat "$out" # THE CONTROL for every case above: the same argv against a path that does not # exist must fail at open(2) instead, with a different errno and no ioctl # diagnostic at all. Without this pair the ENOTTY cases would be consistent with # the open having failed too. check_rc mmc-utils/enoent-rc 1 sh -c "mmc extcsd read '$W.d/nosuchdev' > /dev/null 2>'$out'" check_out mmc-utils/enoent-open "open: No such file or directory" cat "$out" if grep -q 'Inappropriate ioctl for device' "$out"; then bst_notok mmc-utils/enoent-no-ioctl "an absent path still reached the ioctl" else bst_ok mmc-utils/enoent-no-ioctl fi # Running eight failed ioctls in one suite without dying is itself the runtime # no-trap statement for this binary; count the distinct diagnostics as one case. sh -c "mmc extcsd read '$reg'; mmc status get '$reg'; mmc cache disable '$reg'; mmc hwreset enable '$reg'" > /dev/null 2>"$out" n=$(grep -c 'Inappropriate ioctl for device' "$out") if [ "$n" -ge 4 ]; then bst_ok mmc-utils/multi-ioctl-survives else bst_notok mmc-utils/multi-ioctl-survives "expected >=4 ioctl diagnostics, got $n"; fi # softreset and preidle call issue_cmd0() and DISCARD its result, returning 0 # whatever the ioctl did -- so they are the two verbs that report success with no # controller present. Pinned as upstream's; a future version that propagated the # error would redden these, which is the point. check_rc mmc-utils/softreset-rc 0 sh -c "mmc softreset '$reg' > /dev/null 2>&1" check_rc mmc-utils/preidle-rc 0 sh -c "mmc preidle '$reg' > /dev/null 2>&1" # ------------------------------------------------ controller-gated cases ------ # Each runs the real assertion the moment this kernel registers an MMC host. if [ -n "$host" ]; then check mmc-utils/host-extcsd mmc extcsd read "$host" check_out mmc-utils/host-cid "manufacturer" mmc cid read "$host" else bst_skip mmc-utils/host-extcsd "no MMC controller on this kernel: no /sys/class/mmc_host/mmc* -- EXT_CSD has no card to be read from" bst_skip mmc-utils/host-cid "no MMC controller: the CID decoder itself is covered by the cid-* cases above, which need no device" fi if [ -n "$blk" ]; then check mmc-utils/blk-status mmc status get "$blk" else bst_skip mmc-utils/blk-status "no block layer on this kernel (CONFIG_BLOCK not set): no mmcblk node in /dev for SEND_STATUS to be issued to" fi rm -rf "$W".* 2>/dev/null