U-Boot setexpr Widths: Separate Hexadecimal Operands from Memory-Read Size
A script uses setexpr.b and expects every result to fit
in one byte. That interpretation confuses the command’s memory-read
width with the width of its arithmetic. In U-Boot v2025.01, the
distinction can change an address calculation without producing a
command error.
The examples below are source-derived arithmetic examples, not
executed U-Boot sessions. They concern numeric setexpr,
excluding its regular-expression and formatting subcommands. A Buildroot
product must check the deployed architecture and command configuration
before relying on a width assumption.

Numeric text is
interpreted as hexadecimal
The setexpr
documentation describes assigning an evaluated result to an
environment variable and distinguishes ordinary values from
pointer-prefixed arguments. The implementation supplies the precise
numeric rule.
In get_arg(),
a non-pointer numeric operand is converted with hextoul().
Thus the text 10 represents hexadecimal ten, or decimal
sixteen. Adding the operands 10 and 10
produces hexadecimal 20, not decimal twenty.
This is especially easy to miss when a manufacturing value or
generated length arrives as decimal text. An environment variable is a
string; it does not carry a base annotation that survives into every
consuming command. Document the representation at each producer-consumer
boundary instead of assuming all commands interpret the same text
identically.
The suffix controls
dereferencing
When an operand begins with *, the command parses the
remaining text as an address and reads memory. The .b,
.w and .l cases select byte, 16-bit and 32-bit
reads in this tagged implementation. Other applicable width handling can
use unsigned long. The command’s default data-size argument
is four bytes.
For an operand without *, those read cases are not used.
Therefore a conceptual setexpr.b result 100 + 1 still
operates on numeric hexadecimal 100 and 1; the
.b suffix does not truncate the resulting 101
to one byte.
This is not permission to probe arbitrary addresses. Dereferencing
requires a valid, accessible region and alignment appropriate to the
platform. Reading an MMIO register can also have side effects. Use
ordinary numeric operands when testing arithmetic interpretation unless
a controlled memory fixture is specifically required.
Arithmetic uses unsigned
long
The numeric operator branch stores both operands and the result in
ulong values. Addition, subtraction, multiplication and
bitwise operations therefore follow that type’s width rather than the
suffix’s memory-read width. A script moved between builds with different
unsigned long widths needs a fresh boundary review.
The command does not turn address arithmetic into a checked range
calculation. A wrapped result can still be formatted and stored.
Division and remainder also require a valid nonzero divisor; this branch
does not provide a separate protective check before using it.
For a buffer interval, validate the starting address, requested
length and arithmetic result as distinct conditions. In particular, a
result below the start after unsigned addition can indicate wraparound,
but absence of wrap alone does not prove that the interval lies in
usable memory.
Output remains a
representation choice
The numeric path writes through env_set_hex(). Its
result is hexadecimal text for the next consumer. Passing that text to a
command expecting decimal input can create another interpretation error
even if setexpr calculated correctly.
Also note that numeric parsing here does not use an end pointer to
enforce complete-token consumption. Do not use command success as a
validator for arbitrary external numeric strings. Validate syntax and
allowed range before those values influence a boot address or
length.
A useful review worksheet contains four columns: input text, intended
numeric value, actual parser base and consuming type width. Add the
memory-read width only for pointer operands. This keeps the three
concepts separate instead of hiding them behind one suffix.
Focus tests on the
distinctions
Use harmless numeric cases that expose hexadecimal interpretation,
non-pointer .b behavior and values near the architecture’s
unsigned-long limit. Check the resulting string and the next command’s
interpretation. For pointer cases, use an approved ordinary-memory
fixture with known bytes and valid alignment.
The correction is usually small: make representation explicit, avoid
treating suffixes as arithmetic masks, and validate the complete
interval before using the computed address. No memory reads, image loads
or board measurements are claimed here.