# BST-TIMEOUT: 300 # NOT MEASURED ON TARGET YET. Same shape as 57-libxau -- # # two probe execs plus a shell fork per scored value, ~70 # # cases against libxau's ~45 -- and libxau declares 300 on # # the strength of 79-libndp's measured 83 s for 160 cases. # # There is no file I/O here at all, so this suite is lighter # # than either. Replace this note with the measured elapsed # # from the ==BST-SUITE line the first time it runs on target. . "$BST_LIB" # libpciaccess 0.18 (poky recipes-graphics/xorg-lib) -- X.Org's platform- # independent front end to PCI configuration space. What ships on this arm is # /usr/lib/libpciaccess.a, /usr/include/pciaccess.h, /usr/lib/pkgconfig/pciaccess.pc # and this port's probe. Nothing executable: upstream's meson build declares # scanpci with a bare executable() and no `install : true`, so the one program in # the tarball is built and discarded. # # --------------------------------------------------------------------------- # WHAT IS MEASURABLE HERE IS THE REFUSAL PATH, AND IT IS THE WHOLE CONTRACT # # Every device operation in this library needs a device. This image has no PCI # bus -- no /sys/bus/pci, no /proc/bus/pci -- so pci_system_init() cannot succeed # and pci_sys stays null for the life of any process. That is not a gap in the # coverage; it is the contract every consumer of this library meets first on this # image, and the only one it will ever meet: # # * pci_system_init() answers with the errno from stat("/sys/bus/pci/devices"), # which is ENOENT, repeatably, rather than hanging or crashing; # * pci_system_cleanup() is safe before any init, after a failed one, and twice; # * the entry points that need an initialised system decline instead of faulting. # # --------------------------------------------------------------------------- # THE ORACLE IS A NATIVE x86-64 RUN WITH /sys EMPTIED # # Every pinned value below is what libpciaccess_probe.c printed when built # against libpciaccess 0.18 on x86-64 and run under `unshare -Urm` with a tmpfs # mounted over /sys (2026-08-28). Posing the absence natively is what makes this # an oracle rather than a re-reading of the target: the build host HAS a PCI bus, # and in that environment the same probe prints I01 0 and U01..U04 0. Those zeros # are the sensitivity control -- they are the evidence that the ones asserted # below can be produced and are not vacuous. # # THE ONE GROUP THAT IS NOT THE x86-64 ANSWER IS A. struct pci_id_match and # struct pci_slot_match are in-memory layouts, and this arm is ILP32 against the # reference host's LP64: 28/20 bytes here against 32/24 there, with a 4-byte # void*. Those are derived by hand from the declarations in pciaccess.h and pinned # as a statement about this toolchain's ABI. Reusing the host's would have been # wrong and would have looked exactly as convincing. # # --------------------------------------------------------------------------- # THE E GROUP DECIDES WHICH ANSWER IS RIGHT, RATHER THAN THIS FILE ASSUMING ONE # # "There is no PCI bus" is a property of the image today, not a law. If a future # kernel grows one, every I and U expectation below inverts -- and a suite that # had hard-coded the no-bus answers would go red for a machine that got BETTER. # So the probe reports what /sys actually holds (E04) and the scoring branches on # it, the same way 57-libxau scores its lock cases against the mechanism its probe # reports rather than against a guess. Both branches are a correct libpciaccess; # anything else is not. PROBE=${BST_LIBPCIACCESS_PROBE:-/usr/libexec/libpciaccess-tests/libpciaccess-probe} ARCHIVE=/usr/lib/libpciaccess.a PCFILE=/usr/lib/pkgconfig/pciaccess.pc HDR=/usr/include/pciaccess.h # The version the recipe pins. Asserted rather than read back, so a recipe bumped # without revisiting this suite is a red here instead of a silent change of # subject -- the values above are the oracle for THIS version only. WANT_VERSION=0.18 # The first BST result line of a suite is dropped by the guest console # (brintos/linux#96), consistently and every run. This case asserts something # already true so that what is lost costs nothing; remove it when #96 lands. if [ -f "$ARCHIVE" ]; then bst_ok libpciaccess/suite-canary else bst_notok libpciaccess/suite-canary "$ARCHIVE is not a file"; fi if [ ! -f "$ARCHIVE" ]; then bst_skip libpciaccess/present "libpciaccess not installed on this image" exit 0 fi bst_ok libpciaccess/present # --- the package is whole ----------------------------------------------------- check libpciaccess/staticlib test -s "$ARCHIVE" check libpciaccess/pkgconfig test -s "$PCFILE" check libpciaccess/hdr test -s "$HDR" # Static-only is image-wide, so the archive is the whole library deliverable. A # .so here would mean 0001 stopped applying and default_library went back to # deciding nothing. if [ -e /usr/lib/libpciaccess.so ] || [ -e /usr/lib/libpciaccess.so.0 ]; then bst_notok libpciaccess/no-shared "a shared libpciaccess is installed on a static-only distro" else bst_ok libpciaccess/no-shared fi # --- the .pc, which is the practical value of this package -------------------- # libpciaccess ships here as a build dependency for later graphics work, so what # a consumer recipe gets from pkg-config IS the deliverable, and it is asked of # pkg-config rather than read out of the file: the .pc stores `-L${libdir}` and # `-I${includedir}`, so a file-level read asserts the template and says nothing # about whether the prefix arithmetic still resolves to the directories holding # the files. require pkg-config libpciaccess/pkg-config-present check libpciaccess/pc-exists pkg-config --exists pciaccess check libpciaccess/pc-validate pkg-config --validate pciaccess # The recipe pins the version in three independent places -- the poky recipe # filename, meson's project() version and this field -- and only the third is # visible to a consumer. check_eq libpciaccess/pc-modversion "$WANT_VERSION" \ "$(pkg-config --modversion pciaccess 2>/dev/null)" # PKG_CONFIG_ALLOW_SYSTEM_* IS REQUIRED HERE. pkg-config suppresses an -I or -L # naming a directory already on the compiler's default paths, so both queries # answer with the flag stripped on an image whose prefix is /usr -- correctly, # and uselessly for this assertion. Setting the variables asks for the flags # pkg-config COMPUTED. The trailing space is pkg-config's own and is trimmed # rather than tolerated, because the result is used as a path below. cf=$(PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 pkg-config --cflags pciaccess 2>/dev/null | sed 's/[[:space:]]*$//') check_eq libpciaccess/pc-cflags "-I/usr/include" "$cf" d=${cf#-I} check_rc libpciaccess/pc-cflags-dir-real 0 test -f "$d/pciaccess.h" lf=$(PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --libs pciaccess 2>/dev/null | sed 's/[[:space:]]*$//') check_eq libpciaccess/pc-libs "-L/usr/lib -lpciaccess" "$lf" # THE PAIR, NOT THE TWO HALVES. A .pc that advertises -lpciaccess on an image # with no libpciaccess.a is the specific breakage -staticdev exists to prevent: # OE closes the -dev and -staticdev graphs differently, so the metadata arrives # for free and the archive never does, and the red would land on whoever consumed # this package rather than on this port. That is the class # 99-pkgconfig-link-surface.test sweeps for image-wide (BRINTOS-914, # BRINTOS-1172); this is the same question asked of this one package. if [ -s "$ARCHIVE" ] && printf '%s\n' "$lf" | grep -q -- '-lpciaccess'; then bst_ok libpciaccess/pc-link-surface-closed else bst_notok libpciaccess/pc-link-surface-closed "pkg-config offers '$lf' but $ARCHIVE is not on the image" fi # libpciaccess has no Requires: at all -- its only DEPENDS is util-macros, which # is build-time-only and contributes nothing to the installed .pc. Asserted, so a # version that grows a runtime dependency does not acquire one silently. check_eq libpciaccess/pc-no-requires "" \ "$(sed -n 's/^Requires: *//p' "$PCFILE" 2>/dev/null | head -1)" # --- the archive carries the API ---------------------------------------------- # grep rather than nm: the image ships no binutils nm, and the names are present # as plain bytes. -a where the grep supports it -- on this arm the archive members # are LLVM IR bitcode (ThinLTO), so a grep that suppresses matches in a file # containing NULs reports nothing. Probing keeps this measuring libpciaccess # rather than measuring the image's grep. if echo x | grep -aq x 2>/dev/null; then _grepq="grep -aq"; else _grepq="grep -q"; fi for sym in pci_system_init pci_system_cleanup pci_id_match_iterator_create \ pci_slot_match_iterator_create pci_device_next pci_iterator_destroy \ pci_device_find_by_slot pci_device_probe pci_device_cfg_read \ pci_get_strings; do if $_grepq "$sym" "$ARCHIVE" 2>/dev/null; then bst_ok "libpciaccess/sym-$sym" else bst_notok "libpciaccess/sym-$sym" "$sym absent from $ARCHIVE" fi done # The header must declare the whole of that API, since it is the only thing a # consumer recipe compiles against. for decl in pci_system_init pci_system_cleanup pci_id_match_iterator_create \ pci_slot_match_iterator_create pci_device_next pci_iterator_destroy \ pci_device_find_by_slot pci_get_strings PCI_MATCH_ANY; do if grep -q "$decl" "$HDR" 2>/dev/null; then bst_ok "libpciaccess/hdr-decl-$decl" else bst_notok "libpciaccess/hdr-decl-$decl" "$decl is not declared in $HDR" fi done # --- the probe ---------------------------------------------------------------- if [ ! -x "$PROBE" ]; then bst_skip libpciaccess/probe-runs "$PROBE absent from the image" exit 0 fi pout=${TMPDIR:-/tmp}/bst-libpciaccess-out.$$ "$PROBE" > "$pout" 2>&1 prc=$? if [ "$prc" -eq 0 ]; then bst_ok libpciaccess/probe-runs elif bst_execfail "$(head -c 200 "$pout" 2>/dev/null)"; then bst_unrun libpciaccess/probe-runs "$(head -c 200 "$pout" 2>/dev/null)" rm -f "$pout" exit 0 else bst_notok libpciaccess/probe-runs "probe exited $prc: $(_bst_1line "$(head -c 300 "$pout")")" fi if grep -q '^PROBE-END main$' "$pout" 2>/dev/null; then bst_ok libpciaccess/probe-complete else bst_notok libpciaccess/probe-complete "probe output has no PROBE-END terminator (last line: $(tail -1 "$pout" 2>/dev/null))" fi # val -- the probe's value for one case, empty when the probe stopped first. # Matches on the ID in field 1 rather than taking the line by position, because # the probe's output is captured with 2>&1 and a platform diagnostic could land # in the middle of it. val() { awk -v k="$1" '$1==k { $1=""; sub(/^ /,""); print; exit }' "$pout"; } # want -- score one probe value against a pinned answer. # A missing value is reported as the probe having stopped early rather than as a # mismatch against '', which is a different fault and localises differently. want() { _got=$(val "$1") if [ -z "$_got" ]; then bst_notok "libpciaccess/$2" "no value on target (probe stopped before $1); want='$3'" elif [ "$_got" = "$3" ]; then bst_ok "libpciaccess/$2" else bst_notok "libpciaccess/$2" "want:'$3' got:'$_got'" fi } # --- E: what PCI surface this machine actually has ---------------------------- # Reported, not pinned, except for the two that are structural. /sys itself is # mounted by inittab on every brintOS boot, so its absence is an image fault # rather than a libpciaccess finding, and it is worth separating from the rest. want E01 sysfs-mounted 1 bus=$(val E04) # --- I, T, U, C: the refusal contract ----------------------------------------- case "$bus" in 0) bst_ok libpciaccess/bus-absent # THE LOAD-BEARING CASE. ENOENT is not an arbitrary number: it is the errno # from the stat(2) of /sys/bus/pci/devices that linux_sysfs.c gates the whole # backend on. ENOSYS would mean no backend was compiled in at all -- the value # common_init.c initialises err to -- which is a different fault reached # through a different mistake (a build where __linux__ was not defined), and # the two must not read as the same result. want I01 init-rc-enoent 2 want I02 init-errno-name ENOENT want I03 init-rc-again 2 want I04 init-rc-repeatable 1 # The whole point of the U group: the same six entry points, after an init # that was attempted and refused rather than never attempted. Upstream's # guard is `pci_sys == NULL`, which is the same state -- but that is the # claim under test, not the premise. want U01 refuse-id-iter-after-failed-init 1 want U02 refuse-id-iter-any-after-failed-init 1 want U03 refuse-slot-iter-after-failed-init 1 want U04 refuse-slot-iter-any-after-failed-init 1 ;; 1) # A machine that grew a PCI bus. Not a failure of this port -- but the # expectations invert, and saying so is more useful than a wall of reds. bst_ok libpciaccess/bus-present want I01 init-rc-ok 0 want I02 init-errno-name OK want I03 init-rc-again 0 want I04 init-rc-repeatable 1 want U01 id-iter-created-after-init 0 want U02 id-iter-any-created-after-init 0 want U03 slot-iter-created-after-init 0 want U04 slot-iter-any-created-after-init 0 ;; "") bst_notok libpciaccess/bus-absent "no E04 value on target (probe stopped before it)" ;; *) bst_notok libpciaccess/bus-absent "probe reported E04='$bus'; neither 0 nor 1" ;; esac # --- T: the refusal contract with nothing initialised at all ------------------- # These hold in both branches: the T pass runs before any pci_system_init(), so # pci_sys is null however much PCI the machine has. want T01 refuse-id-iter-uninit 1 want T02 refuse-id-iter-any-uninit 1 want T03 refuse-slot-iter-uninit 1 want T04 refuse-slot-iter-any-uninit 1 want T05 device-next-null-iter 1 want T06 iterator-destroy-null 1 # pci_device_next(NULL) and pci_iterator_destroy(NULL) are argument guards, so # they must hold after the failed init too. want U05 device-next-null-iter-after-failed-init 1 want U06 iterator-destroy-null-after-failed-init 1 # --- C: teardown is safe wherever it is called -------------------------------- # Reaching each of these means pci_system_cleanup() returned rather than faulting. want C01 cleanup-before-any-init 1 want C02 cleanup-after-init 1 want C03 cleanup-twice 1 # And the library still answers the same afterwards: the teardown did not leave a # state that changes the result. Scored against whichever branch the bus took. want I06 init-unchanged-after-cleanup 1 # --- A: this arm's ABI -------------------------------------------------------- want A01 pci-match-any 4294967295 want A02 sizeof-id-match 28 want A03 sizeof-slot-match 20 want A04 sizeof-pciaddr 8 want A05 sizeof-pointer 4 want A06 offset-id-match-data 24 want A07 offset-slot-match-data 16 rm -f "$pout" # --- X: pci_device_find_by_slot, in its own process --------------------------- # THIS IS THE ONE CASE THAT CANNOT SHARE THE PROBE ABOVE. Upstream 0.18 reaches # pci_sys from pci_device_find_by_slot() without any guard -- it builds an # iterator on the stack and calls pci_device_next(), which tests only its # iterator argument -- so on an uninitialised system it dereferences null. # Measured on x86-64 at this version, unpatched, both with and without a PCI bus: # SIGSEGV, exit 139, twice each. This port carries the one-line guard # (0002-refuse-device-iteration-when-no-pci-system-is-initia.patch) that closes # it, and this case is what says the guard is on the image rather than only in # the layer. # # Run as its own process on purpose: without the patch it takes the whole probe # with it, and every case after it would report "probe stopped early" for a # reason that has nothing to do with them. xout=${TMPDIR:-/tmp}/bst-libpciaccess-x.$$ "$PROBE" findslot > "$xout" 2>&1 xrc=$? if grep -q '^X00 calling$' "$xout" 2>/dev/null; then bst_ok libpciaccess/findslot-reached else bst_notok libpciaccess/findslot-reached "probe did not reach the pci_device_find_by_slot call: $(_bst_1line "$(head -c 200 "$xout")")" fi xval=$(awk '$1=="X01" { print $2; exit }' "$xout" 2>/dev/null) if [ "$xrc" -eq 0 ] && [ "$xval" = "1" ]; then bst_ok libpciaccess/findslot-refuses elif [ -z "$xval" ]; then bst_notok libpciaccess/findslot-refuses "no X01: pci_device_find_by_slot did not return (rc=$xrc) -- the null-pci_sys guard is not in the shipped archive" else bst_notok libpciaccess/findslot-refuses "pci_device_find_by_slot returned non-NULL with X01=$xval on a machine reporting E04=$bus" fi if grep -q '^PROBE-END findslot$' "$xout" 2>/dev/null; then bst_ok libpciaccess/findslot-complete else bst_notok libpciaccess/findslot-complete "no PROBE-END terminator (rc=$xrc, last line: $(tail -1 "$xout" 2>/dev/null))" fi rm -f "$xout"