* [PATCH v2 0/3] mtd: rawnand: sunxi: support the Allwinner randomized OOB format
@ 2026-09-04 21:22 James Hilliard
2026-09-04 21:22 ` [PATCH v2 1/3] dt-bindings: mtd: sunxi: Add randomized OOB flag James Hilliard
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: James Hilliard @ 2026-09-04 21:22 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Maxime Ripard
Cc: linux-mtd, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, James Hilliard
The sunxi NAND driver applies page randomization from its normal
hardware-ECC page callbacks. Each page starts from a page-dependent seed.
When protected OOB bytes are transferred separately, the driver uses
precomputed randomizer states advanced according to the ECC step size.
Erased-page detection and its physical-data fallback also live in these
callbacks. Randomization is therefore part of the driver's hardware-ECC
page format rather than an independent MTD access mode. MTD_OPS_RAW
continues to bypass both ECC and randomization and exposes physical main
and OOB bytes.
The mainline format compensates the bad block marker before writes and
after reads so that it remains plain on flash. On H6 and H616 it also
fills the OOB space left after ECC with protected user data. Allwinner
NAND firmware instead leaves the marker in the randomizer data stream
and, on H6 and H616, uses a fixed protected user-data placement. These
choices produce incompatible physical OOB layouts.
The Allwinner BSP implementations for the SoCs covered by the two older
mainline compatibles (sun4i, sun5i, sun7i, sun8iw3 and sun8iw5) likewise
pass their protected user-data words to the controller unchanged while
page randomization is enabled. Their 128-entry seed tables match mainline,
and those controllers have the same fixed four-byte-per-step user-data
layout, so only the marker handling differs on those generations.
Add allwinner,randomized-oob to select the firmware's normal-page format
for the configured controller hardware-ECC geometry. The property marks
the NAND as requiring scrambling for normal hardware-ECC transfers, omits
the marker compensation, and selects the controller-specific protected
user-data placement. H6 and H616 use four protected bytes per 1 KiB ECC
step, capped at 16 bytes, with the complete protected region placed before
the first ECC step. Older controllers keep their already matching fixed
user-data placement. Assuming the standard ECC geometry is configured to
match the media, these choices make normal reads and writes compatible with
BSP-derived images.
The property does not change the per-operation raw-access behavior. Raw
accesses continue to return the physical representation, including a
randomized marker when that is what the firmware format stores. A physical
marker cannot be both plain and compatible with firmware which stores it
randomized. Normalizing only the marker in MTD_OPS_RAW would make the result
non-physical, while enabling full-page de-randomization would make it a
decoded access rather than a raw one. Preserving both the firmware format
and existing raw semantics therefore requires raw access to expose the
randomized physical marker.
The raw-NAND core's bad-block and flash-BBT paths remain on decoded
accesses: nand_block_bad() uses ecc.read_oob(), OOB scans use
MTD_OPS_PLACE_OOB, and BBT data reads use mtd_read(). They therefore receive
the logical marker and BBT contents through the hardware-ECC/randomizer
path.
When the property is absent, retain the existing mainline marker handling
and H6/H616 maximized OOB layout.
Tested on an H616 board with a Toshiba TC58NVG1S3H NAND using matching
Linux and U-Boot implementations. Linux initialized the NAND, SPL and
U-Boot cold-booted from it, U-Boot loaded the FIT from UBI, and Linux
attached the same UBI with 2004 good PEBs, two bad PEBs and no corrupted
PEBs. A target-timed 64 MiB read through /dev/mtd6 completed without
reported ECC, timeout or I/O errors.
Validation completed with strict checkpatch, dt_binding_check and an
arm64 W=1 object build.
Assisted-by: OpenAI Codex (gpt-5.6-sol, max)
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes in v2:
- rebase on the current MTD nand/next branch
- retain the merged protected-OOB allocation, BBM reservation and
stack-buffer fixes
- clarify that randomization is part of the normal hardware-ECC page
format while MTD_OPS_RAW continues to expose physical bytes
- explain why a BSP-compatible BBM remains randomized in physical raw data
- document the decoded bad-block and flash-BBT access paths
- reject the firmware OOB format with software or disabled ECC
- document the BSP page-format compatibility contract and the
older-controller format audit
- Link to v1: https://patch.msgid.link/20260810-submit-sunxi-nand-vendor-oob-layout-v1-v1-0-463853a14ad9@gmail.com
---
James Hilliard (3):
dt-bindings: mtd: sunxi: Add randomized OOB flag
mtd: rawnand: sunxi: support randomized OOB formats
mtd: rawnand: sunxi: select the packed H6/H616 OOB layout
.../bindings/mtd/allwinner,sun4i-a10-nand.yaml | 10 ++++
drivers/mtd/nand/raw/sunxi_nand.c | 56 ++++++++++++++++++----
2 files changed, 56 insertions(+), 10 deletions(-)
---
base-commit: def559a215699aa2f2d8ef98f30156b9d562a154
change-id: 20260810-submit-sunxi-nand-vendor-oob-layout-v1-e3114d10cc9c
Best regards,
--
James Hilliard <james.hilliard1@gmail.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/3] dt-bindings: mtd: sunxi: Add randomized OOB flag
2026-09-04 21:22 [PATCH v2 0/3] mtd: rawnand: sunxi: support the Allwinner randomized OOB format James Hilliard
@ 2026-09-04 21:22 ` James Hilliard
2026-09-04 21:22 ` [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats James Hilliard
2026-09-04 21:22 ` [PATCH v2 3/3] mtd: rawnand: sunxi: select the packed H6/H616 OOB layout James Hilliard
2 siblings, 0 replies; 5+ messages in thread
From: James Hilliard @ 2026-09-04 21:22 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Maxime Ripard
Cc: linux-mtd, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, James Hilliard
Allwinner NAND firmware stores the bad block marker through the
controller randomizer. The mainline driver instead compensates the first
two randomized bytes so that the marker remains plain on flash.
On H6 and H616, the firmware also uses a fixed protected user-data
placement while mainline fills the OOB space left after ECC. Add an
opt-in property selecting the firmware's normal-page format for the
configured controller hardware-ECC geometry. This covers the
vendor-specific randomizer, bad block marker representation and protected
user-data placement needed to access BSP-derived images.
The property does not change raw-access semantics. Raw operations still
bypass ECC and randomization and expose physical main and OOB bytes.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
.../devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml b/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
index 9d061e2216cb..8ecafc40ddcb 100644
--- a/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
+++ b/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
@@ -79,6 +79,16 @@ patternProperties:
minimum: 0
maximum: 1
+ allwinner,randomized-oob:
+ type: boolean
+ description:
+ Select the normal-page format used by Allwinner NAND firmware for
+ the configured controller hardware-ECC geometry. Normal page
+ transfers use the controller randomizer, the bad block marker remains
+ in its data stream instead of being compensated to stay plain on
+ flash, and protected user data uses the controller-specific placement
+ expected by the firmware.
+
unevaluatedProperties: false
required:
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats
2026-09-04 21:22 [PATCH v2 0/3] mtd: rawnand: sunxi: support the Allwinner randomized OOB format James Hilliard
2026-09-04 21:22 ` [PATCH v2 1/3] dt-bindings: mtd: sunxi: Add randomized OOB flag James Hilliard
@ 2026-09-04 21:22 ` James Hilliard
2026-09-08 15:51 ` Miquel Raynal
2026-09-04 21:22 ` [PATCH v2 3/3] mtd: rawnand: sunxi: select the packed H6/H616 OOB layout James Hilliard
2 siblings, 1 reply; 5+ messages in thread
From: James Hilliard @ 2026-09-04 21:22 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Maxime Ripard
Cc: linux-mtd, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, James Hilliard
The controller randomizer covers the bad block marker along with the
rest of the OOB data. The driver currently compensates the marker bytes
before writes and after reads so that they remain plain on flash.
Allwinner NAND firmware instead stores the marker through the
randomizer. Media using that format appears to contain bad blocks unless
the randomizer is enabled while reading the marker.
Honor the allwinner,randomized-oob property by marking the NAND as
requiring scrambling during normal hardware-ECC transfers and leaving
the marker in the randomizer data stream. Keep the existing plain marker
behavior when the property is absent. Reject the property with software
or disabled ECC because those paths do not operate the controller
randomizer.
MTD_OPS_RAW behavior remains unchanged: raw accesses bypass both ECC and
randomization and expose the physical representation. The raw-NAND
core's bad-block and flash-BBT paths use decoded accesses and therefore
continue through the hardware-ECC/randomizer path.
This changes marker handling on all supported controllers; the H6/H616
protected user-data placement is handled separately.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index 83666dd6cb2a..c1246a9268ca 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -265,6 +265,7 @@ struct sunxi_nfc_timings {
* @clk_rate: clk_rate required for this NAND chip
* @timing_cfg: TIMING_CFG register value for this NAND chip
* @timing_ctl: TIMING_CTL register value for this NAND chip
+ * @randomized_oob: use the randomized normal-page OOB format
* @nsels: number of CS lines required by the NAND chip
* @sels: array of CS lines descriptions
* @user_data_bytes: array of user data lengths for all ECC steps
@@ -277,6 +278,7 @@ struct sunxi_nand_chip {
u32 timing_cfg;
u32 timing_ctl;
u8 *user_data_bytes;
+ bool randomized_oob;
int nsels;
struct sunxi_nand_chip_sel sels[] __counted_by(nsels);
};
@@ -945,8 +947,9 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
}
}
- /* De-randomize the Bad Block Marker. */
- if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
+ /* Undo hardware de-randomization for a plain on-flash BBM. */
+ if (bbm && (nand->options & NAND_NEED_SCRAMBLING) &&
+ !sunxi_nand->randomized_oob)
sunxi_nfc_randomize_bbm(nand, page, oob);
}
@@ -1006,8 +1009,9 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
u8 user_data[SUNXI_NFC_MAX_USER_DATA_SZ] = {};
- /* Randomize the Bad Block Marker. */
- if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
+ /* Pre-randomize the BBM so the hardware writes it plain on flash. */
+ if (bbm && (nand->options & NAND_NEED_SCRAMBLING) &&
+ !sunxi_nand->randomized_oob) {
memcpy(user_data, oob, user_data_sz);
sunxi_nfc_randomize_bbm(nand, page, user_data);
oob = user_data;
@@ -2229,6 +2233,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
static int sunxi_nand_attach_chip(struct nand_chip *nand)
{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
const struct nand_ecc_props *requirements =
nanddev_get_ecc_requirements(&nand->base);
struct nand_ecc_ctrl *ecc = &nand->ecc;
@@ -2238,6 +2244,14 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand)
if (nand->bbt_options & NAND_BBT_USE_FLASH)
nand->bbt_options |= NAND_BBT_NO_OOB;
+ if (sunxi_nand->randomized_oob &&
+ ecc->engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
+ return dev_err_probe(nfc->dev, -EINVAL,
+ "Allwinner OOB format requires controller ECC\n");
+
+ if (sunxi_nand->randomized_oob)
+ nand->options |= NAND_NEED_SCRAMBLING;
+
if (nand->options & NAND_NEED_SCRAMBLING)
nand->options |= NAND_NO_SUBPAGE_WRITE;
@@ -2467,6 +2481,9 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
if (!sunxi_nand)
return -ENOMEM;
+ sunxi_nand->randomized_oob =
+ of_property_read_bool(np, "allwinner,randomized-oob");
+
sunxi_nand->nsels = nsels;
for (i = 0; i < nsels; i++) {
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 3/3] mtd: rawnand: sunxi: select the packed H6/H616 OOB layout
2026-09-04 21:22 [PATCH v2 0/3] mtd: rawnand: sunxi: support the Allwinner randomized OOB format James Hilliard
2026-09-04 21:22 ` [PATCH v2 1/3] dt-bindings: mtd: sunxi: Add randomized OOB flag James Hilliard
2026-09-04 21:22 ` [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats James Hilliard
@ 2026-09-04 21:22 ` James Hilliard
2 siblings, 0 replies; 5+ messages in thread
From: James Hilliard @ 2026-09-04 21:22 UTC (permalink / raw)
To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, Maxime Ripard
Cc: linux-mtd, devicetree, linux-arm-kernel, linux-sunxi,
linux-kernel, James Hilliard
The H6/H616 controller can configure a separate protected user-data
length for every ECC step. Mainline fills all space left after ECC with
user data. This changes the physical ECC offsets from those used by
Allwinner NAND firmware, so pages written by one layout cannot be
decoded with the other.
The allwinner,randomized-oob property selects the firmware's normal-page
format for the configured controller hardware-ECC geometry. Use four
protected user-data bytes per 1 KiB ECC step, cap the total at 16 bytes,
and assign the entire total to ECC step zero. Reserve the same total before
maximizing ECC strength so the selected strength leaves enough room.
Keep the existing maximized mainline user-data layout when the property
is absent. Older controllers have fixed four-byte user-data registers
whose placement already matches the firmware and need no additional
change.
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/mtd/nand/raw/sunxi_nand.c | 31 +++++++++++++++++++++++++------
1 file changed, 25 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index c1246a9268ca..1265d900fc27 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -224,6 +224,9 @@
#define USER_DATA_SZ 4
#define SUNXI_NFC_MAX_USER_DATA_SZ 32
+/* The randomized H6/H616 layout packs at most 16 bytes before ECC step 0. */
+#define SUNXI_NFC_H6_MAX_USER_DATA_SZ 16
+
/**
* struct sunxi_nand_chip_sel - stores information related to NAND Chip Select
*
@@ -2037,8 +2040,14 @@ static void sunxi_nand_detach_chip(struct nand_chip *nand)
sunxi_nand->user_data_bytes = NULL;
}
-static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize,
- int ecc_bytes, int nsectors)
+static unsigned int sunxi_nfc_h6_user_data_sz(int nsectors)
+{
+ return min(nsectors * USER_DATA_SZ,
+ SUNXI_NFC_H6_MAX_USER_DATA_SZ);
+}
+
+static int sunxi_nfc_init_user_data(struct nand_chip *nand, uint32_t oobsize,
+ int ecc_bytes, int nsectors)
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
@@ -2054,6 +2063,12 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize
if (!sunxi_nand->user_data_bytes)
return -ENOMEM;
+ if (sunxi_nand->randomized_oob) {
+ sunxi_nand->user_data_bytes[0] =
+ sunxi_nfc_h6_user_data_sz(nsectors);
+ return 0;
+ }
+
for (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) {
for (i = 0; i < c->nuser_data_tab; i++) {
if (c->user_data_len_tab[i] > remaining_bytes)
@@ -2111,6 +2126,10 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
bytes -= 2;
bytes -= total_user_data_sz;
+ } else if (sunxi_nand->randomized_oob) {
+ total_user_data_sz =
+ sunxi_nfc_h6_user_data_sz(nsectors);
+ bytes -= total_user_data_sz;
} else {
/*
* User-data lengths are encoded in four-byte units. Reserve
@@ -2177,12 +2196,12 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
nsectors = mtd->writesize / ecc->size;
/*
- * The rationale for variable data length is to prioritize maximum ECC
- * strength, and then use the remaining space for user data.
+ * The default variable-length layout prioritizes maximum ECC strength,
+ * then uses the remaining space for user data.
*/
if (nfc->caps->reg_user_data_len) {
- ret = sunxi_nfc_maximize_user_data(nand, mtd->oobsize,
- ecc->bytes, nsectors);
+ ret = sunxi_nfc_init_user_data(nand, mtd->oobsize,
+ ecc->bytes, nsectors);
if (ret)
return ret;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats
2026-09-04 21:22 ` [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats James Hilliard
@ 2026-09-08 15:51 ` Miquel Raynal
0 siblings, 0 replies; 5+ messages in thread
From: Miquel Raynal @ 2026-09-08 15:51 UTC (permalink / raw)
To: James Hilliard
Cc: Richard Weinberger, Vignesh Raghavendra, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Maxime Ripard, linux-mtd, devicetree,
linux-arm-kernel, linux-sunxi, linux-kernel
Hi James,
On 04/09/2026 at 15:22:12 -06, James Hilliard <james.hilliard1@gmail.com> wrote:
> The controller randomizer covers the bad block marker along with the
> rest of the OOB data. The driver currently compensates the marker bytes
> before writes and after reads so that they remain plain on flash.
>
> Allwinner NAND firmware instead stores the marker through the
> randomizer. Media using that format appears to contain bad blocks unless
> the randomizer is enabled while reading the marker.
>
> Honor the allwinner,randomized-oob property by marking the NAND as
> requiring scrambling during normal hardware-ECC transfers and leaving
> the marker in the randomizer data stream. Keep the existing plain marker
> behavior when the property is absent. Reject the property with software
> or disabled ECC because those paths do not operate the controller
> randomizer.
>
> MTD_OPS_RAW behavior remains unchanged: raw accesses bypass both ECC and
> randomization and expose the physical representation. The raw-NAND
> core's bad-block and flash-BBT paths use decoded accesses and therefore
> continue through the hardware-ECC/randomizer path.
>
> This changes marker handling on all supported controllers; the H6/H616
> protected user-data placement is handled separately.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
I believe it is still not completely working. If you look at the error
path in the read functions, in case we face a factory bad block, there
will be no data, we will face the "page is maybe empty" case where we
still address columns like before. Since data is randomized with these
accesses, I believe we would return errors which will not be interpreted
correctly by the core. The discovery of the factory bad block markers is
thus broken during BBT scan. But a BBT read is also broken, because even
if errors are ignored, we perform pattern matching on data which is, if
my understanding of your implementation is correct, not de-randomized in
this case.
Thanks,
Miquèl
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-08 15:52 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 21:22 [PATCH v2 0/3] mtd: rawnand: sunxi: support the Allwinner randomized OOB format James Hilliard
2026-09-04 21:22 ` [PATCH v2 1/3] dt-bindings: mtd: sunxi: Add randomized OOB flag James Hilliard
2026-09-04 21:22 ` [PATCH v2 2/3] mtd: rawnand: sunxi: support randomized OOB formats James Hilliard
2026-09-08 15:51 ` Miquel Raynal
2026-09-04 21:22 ` [PATCH v2 3/3] mtd: rawnand: sunxi: select the packed H6/H616 OOB layout James Hilliard
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox