json: Provide a way to convert JSON to FDT

JSON is a rather more free format than devicetree, so it is sometimes
better to parse it into dtb format. This is widely used in U-Boot and we
can use the ofnode interface to access it.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2025-11-11 03:37:39 -07:00
parent 2887dcf6cf
commit 5da98448d8
3 changed files with 981 additions and 2 deletions

View File

@@ -9,6 +9,8 @@
#ifndef __JSON_H__
#define __JSON_H__
struct abuf;
/**
* json_print_pretty() - Print JSON with indentation
*
@@ -20,4 +22,36 @@
*/
void json_print_pretty(const char *json, int len);
/**
* json_to_fdt() - Convert JSON to a Flattened Device Tree (DTB) blob
*
* Parse a JSON string and convert it to a dtb blob. JSON objects become nodes;
* JSON properties become device tree properties. This is useful for converting
* LUKS2 metadata to a format that can be queried using U-Boot's ofnode APIs.
*
* This function temporarily modifies the JSON string in-place, writing nul
* terminators during parsing, then restores the original characters. The JSON
* string is only modified during the function call and is restored before
* returning.
*
* The resulting DTB contains copies of all data, so the JSON string can be
* freed or modified after this function returns.
*
* Conversion rules:
* - JSON objects → DT nodes
* - JSON strings → string properties
* - JSON numbers → u32 or u64 cell properties
* - JSON arrays of numbers → cell array properties (max MAX_ARRAY_SIZE)
* - JSON arrays of strings → stringlist properties (max MAX_ARRAY_SIZE)
* - JSON booleans → u32 properties (0 or 1). This breaks the dtb convention of
* simply using presence to indicate true, so that we can actually check
* what was present in the JSON data
* - JSON null → empty property
*
* @json: JSON string to parse (temporarily modified during call)
* @buf: abuf to init and populate with the DTB (called must uninit)
* Return: 0 on success, negative error code on failure
*/
int json_to_fdt(const char *json, struct abuf *buf);
#endif /* __JSON_H__ */

View File

@@ -1,13 +1,67 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* JSON pretty-printer
* JSON utilities including parser and devicetree converter
*
* Copyright (C) 2025 Canonical Ltd
* Written by Simon Glass <simon.glass@canonical.com>
*/
#include <abuf.h>
#include <ctype.h>
#include <errno.h>
#include <log.h>
#include <linux/libfdt.h>
#include <malloc.h>
/* Maximum number of elements in a JSON array */
#define MAX_ARRAY_SIZE 256
/* JSON token types */
enum json_token_type {
JSONT_EOF = 0,
JSONT_LBRACE, /* { */
JSONT_RBRACE, /* } */
JSONT_LBRACKET, /* [ */
JSONT_RBRACKET, /* ] */
JSONT_COLON, /* : */
JSONT_COMMA, /* , */
JSONT_STRING, /* "string" */
JSONT_NUMBER, /* 123 or 123.45 */
JSONT_TRUE, /* true */
JSONT_FALSE, /* false */
JSONT_NULL, /* null */
JSONT_ERROR
};
/**
* struct json_parser - JSON parser context
* @json: Input JSON string
* @pos: Current position in the input string
* @tok: Current token type
* @tok_start: Pointer to the start of the current token
* @tok_end: Pointer to the end of the current token
* @fdt: Flattened Device Tree buffer being constructed
* @fdt_size: Size of the FDT buffer in bytes
*/
struct json_parser {
const char *json;
const char *pos;
enum json_token_type tok;
const char *tok_start;
const char *tok_end;
void *fdt;
int fdt_size;
};
/* Increment position in JSON string */
#define INC() ctx->pos++
/* Set token type */
#define SET(t) ctx->tok = (t)
/* Forward declarations for recursive parser functions */
static int parse_object(struct json_parser *ctx, const char *node_name);
static int parse_value(struct json_parser *ctx, const char *prop_name);
/**
* print_indent() - Print indentation spaces
@@ -120,3 +174,559 @@ void json_print_pretty(const char *json, int len)
putc('\n');
}
/**
* skip_whitespace() - Skip whitespace characters
*
* @ctx: Parser context
*/
static void skip_whitespace(struct json_parser *ctx)
{
while (*ctx->pos && isspace(*ctx->pos))
INC();
}
/**
* next_token() - Get the next JSON token
*
* @ctx: Parser context
* Return: token type
*/
static enum json_token_type next_token(struct json_parser *ctx)
{
skip_whitespace(ctx);
if (!*ctx->pos) {
SET(JSONT_EOF);
return JSONT_EOF;
}
ctx->tok_start = ctx->pos;
switch (*ctx->pos) {
case '{':
INC();
SET(JSONT_LBRACE);
break;
case '}':
INC();
SET(JSONT_RBRACE);
break;
case '[':
INC();
SET(JSONT_LBRACKET);
break;
case ']':
INC();
SET(JSONT_RBRACKET);
break;
case ':':
INC();
SET(JSONT_COLON);
break;
case ',':
INC();
SET(JSONT_COMMA);
break;
case '"': {
/* Parse string */
INC();
ctx->tok_start = ctx->pos;
while (*ctx->pos && *ctx->pos != '"') {
if (*ctx->pos == '\\' && ctx->pos[1])
INC();
INC();
}
ctx->tok_end = ctx->pos;
if (*ctx->pos == '"')
INC();
SET(JSONT_STRING);
break;
}
case '-':
case '0' ... '9': {
/* Parse number */
if (*ctx->pos == '-')
INC();
while (*ctx->pos && isdigit(*ctx->pos))
INC();
if (*ctx->pos == '.') {
INC();
while (*ctx->pos && isdigit(*ctx->pos))
INC();
}
ctx->tok_end = ctx->pos;
SET(JSONT_NUMBER);
break;
}
case 't':
if (!strncmp(ctx->pos, "true", 4)) {
ctx->pos += 4;
ctx->tok_end = ctx->pos;
SET(JSONT_TRUE);
} else {
SET(JSONT_ERROR);
}
break;
case 'f':
if (!strncmp(ctx->pos, "false", 5)) {
ctx->pos += 5;
ctx->tok_end = ctx->pos;
SET(JSONT_FALSE);
} else {
SET(JSONT_ERROR);
}
break;
case 'n':
if (!strncmp(ctx->pos, "null", 4)) {
ctx->pos += 4;
ctx->tok_end = ctx->pos;
SET(JSONT_NULL);
} else {
SET(JSONT_ERROR);
}
break;
default:
SET(JSONT_ERROR);
break;
}
return ctx->tok;
}
/**
* parse_array() - Parse a JSON array
*
* @ctx: Parser context
* @prop: Property name for this array
* Return: 0 on success, negative error code on failure
*/
static int parse_array(struct json_parser *ctx, const char *prop)
{
u32 values[MAX_ARRAY_SIZE];
int index = 0;
int count = 0;
int ret;
/* Expect [ */
if (ctx->tok != JSONT_LBRACKET)
return -EINVAL;
next_token(ctx);
/* Handle empty array */
if (ctx->tok == JSONT_RBRACKET) {
next_token(ctx);
return fdt_property(ctx->fdt, prop, NULL, 0);
}
/* Check if this is an array of objects */
if (ctx->tok == JSONT_LBRACE) {
/* Array of objects - create a subnode for each */
while (ctx->tok != JSONT_RBRACKET) {
char name[64];
snprintf(name, sizeof(name), "%s-%d", prop, index++);
ret = parse_object(ctx, name);
if (ret)
return ret;
if (ctx->tok == JSONT_COMMA)
next_token(ctx);
else if (ctx->tok != JSONT_RBRACKET)
return -EINVAL;
}
next_token(ctx); /* Skip ] */
return 0;
}
/* Array of primitives - collect into cell array */
while (ctx->tok != JSONT_RBRACKET) {
if (ctx->tok == JSONT_NUMBER) {
char num_str[32];
int len = min((int)(ctx->tok_end - ctx->tok_start),
(int)sizeof(num_str) - 1);
if (count >= MAX_ARRAY_SIZE)
return -E2BIG;
memcpy(num_str, ctx->tok_start, len);
num_str[len] = '\0';
values[count++] = simple_strtoul(num_str, NULL, 0);
next_token(ctx);
} else if (ctx->tok == JSONT_STRING) {
/* String array - collect strings into a stringlist */
char *strings[MAX_ARRAY_SIZE];
char saved_chars[MAX_ARRAY_SIZE];
int str_lens[MAX_ARRAY_SIZE];
int count = 0;
int total_len = 0;
char *buf, *p;
int i;
/* Collect all strings, temporarily nul-terminating */
while (ctx->tok == JSONT_STRING) {
if (count >= MAX_ARRAY_SIZE)
return -E2BIG;
strings[count] = (char *)ctx->tok_start;
saved_chars[count] = *ctx->tok_end;
*(char *)ctx->tok_end = '\0';
str_lens[count] = strlen(strings[count]) + 1;
total_len += str_lens[count];
count++;
next_token(ctx);
if (ctx->tok == JSONT_COMMA)
next_token(ctx);
else if (ctx->tok != JSONT_RBRACKET)
return -EINVAL;
}
/* Build stringlist: concatenate all strings with nulls */
buf = malloc(total_len);
if (!buf)
return -ENOMEM;
p = buf;
for (i = 0; i < count; i++) {
memcpy(p, strings[i], str_lens[i]);
p += str_lens[i];
}
/* Create FDT stringlist property */
ret = fdt_property(ctx->fdt, prop, buf, total_len);
free(buf);
/* Restore all the saved characters */
for (i = 0; i < count; i++)
strings[i][str_lens[i] - 1] = saved_chars[i];
if (ret)
return ret;
next_token(ctx);
return 0;
} else {
return -EINVAL;
}
if (ctx->tok == JSONT_COMMA)
next_token(ctx);
else if (ctx->tok != JSONT_RBRACKET)
return -EINVAL;
}
next_token(ctx); /* Skip ] */
/* Write array as FDT property */
if (count == 1) {
/* Single element - use fdt_property_u32() */
ret = fdt_property_u32(ctx->fdt, prop, values[0]);
if (ret)
return ret;
} else if (count > 1) {
/* Multiple elements - convert to big-endian and write */
u32 cells[MAX_ARRAY_SIZE];
int i;
for (i = 0; i < count; i++)
cells[i] = cpu_to_fdt32(values[i]);
ret = fdt_property(ctx->fdt, prop, cells, count * sizeof(u32));
if (ret)
return ret;
}
return 0;
}
/**
* parse_value() - Parse a JSON value
*
* @ctx: Parser context
* @prop_name: Property name for this value
* Return: 0 on success, negative error code on failure
*/
static int parse_value(struct json_parser *ctx, const char *prop_name)
{
int ret;
switch (ctx->tok) {
case JSONT_STRING: {
char str[256];
int len = min((int)(ctx->tok_end - ctx->tok_start),
(int)sizeof(str) - 1);
memcpy(str, ctx->tok_start, len);
str[len] = '\0';
ret = fdt_property_string(ctx->fdt, prop_name, str);
if (ret)
return ret;
next_token(ctx);
break;
}
case JSONT_NUMBER: {
char num_str[32];
u64 val;
int len = min((int)(ctx->tok_end - ctx->tok_start),
(int)sizeof(num_str) - 1);
memcpy(num_str, ctx->tok_start, len);
num_str[len] = '\0';
val = simple_strtoull(num_str, NULL, 0);
/* Use u32 if it fits, otherwise u64 */
if (val <= 0xffffffff)
ret = fdt_property_u32(ctx->fdt, prop_name, (u32)val);
else
ret = fdt_property_u64(ctx->fdt, prop_name, val);
if (ret)
return ret;
next_token(ctx);
break;
}
case JSONT_TRUE:
ret = fdt_property_u32(ctx->fdt, prop_name, 1);
if (ret)
return ret;
next_token(ctx);
break;
case JSONT_FALSE:
ret = fdt_property_u32(ctx->fdt, prop_name, 0);
if (ret)
return ret;
next_token(ctx);
break;
case JSONT_NULL:
ret = fdt_property(ctx->fdt, prop_name, NULL, 0);
if (ret)
return ret;
next_token(ctx);
break;
case JSONT_LBRACE:
/* Nested object */
ret = parse_object(ctx, prop_name);
if (ret)
return ret;
break;
case JSONT_LBRACKET:
/* Array */
ret = parse_array(ctx, prop_name);
if (ret)
return ret;
break;
default:
return -EINVAL;
}
return 0;
}
/**
* parse_object() - Parse a JSON object
*
* @ctx: Parser context
* @node_name: Name for the device tree node (NULL for root)
* Return: 0 on success, negative error code on failure
*/
static int parse_object(struct json_parser *ctx, const char *node_name)
{
int ret;
/* Expect { */
if (ctx->tok != JSONT_LBRACE)
return -EINVAL;
/* Begin device tree node */
if (node_name) {
ret = fdt_begin_node(ctx->fdt, node_name);
if (ret)
return ret;
}
next_token(ctx);
/* Handle empty object */
if (ctx->tok == JSONT_RBRACE) {
next_token(ctx);
if (node_name) {
ret = fdt_end_node(ctx->fdt);
if (ret)
return ret;
}
return 0;
}
/* Parse key-value pairs */
while (ctx->tok != JSONT_RBRACE) {
char key[128];
int len;
/* Expect string key */
if (ctx->tok != JSONT_STRING)
return -EINVAL;
len = min((int)(ctx->tok_end - ctx->tok_start),
(int)sizeof(key) - 1);
memcpy(key, ctx->tok_start, len);
key[len] = '\0';
next_token(ctx);
/* Expect : */
if (ctx->tok != JSONT_COLON)
return -EINVAL;
next_token(ctx);
/* Parse value */
ret = parse_value(ctx, key);
if (ret)
return ret;
/* Expect , or } */
if (ctx->tok == JSONT_COMMA)
next_token(ctx);
else if (ctx->tok != JSONT_RBRACE)
return -EINVAL;
}
next_token(ctx); /* Skip } */
/* End device tree node */
if (node_name) {
ret = fdt_end_node(ctx->fdt);
if (ret)
return ret;
}
return 0;
}
/**
* parse_json_root() - Parse JSON and create FDT
*
* @ctx: Parser context (must be initialized with FDT buffer)
* Return: 0 on success, negative error code on failure
*/
static int parse_json_root(struct json_parser *ctx)
{
int ret;
/* Initialize FDT */
ret = fdt_create(ctx->fdt, ctx->fdt_size);
if (ret)
return ret;
ret = fdt_finish_reservemap(ctx->fdt);
if (ret)
return ret;
/* Begin root node */
ret = fdt_begin_node(ctx->fdt, "");
if (ret)
return ret;
/* Get first token */
next_token(ctx);
/* Parse the JSON (expecting an object at the root) */
if (ctx->tok == JSONT_LBRACE) {
/* Parse the root's contents directly into the root node */
next_token(ctx);
while (ctx->tok != JSONT_RBRACE) {
char key[128];
int len;
if (ctx->tok != JSONT_STRING)
return -EINVAL;
len = min((int)(ctx->tok_end - ctx->tok_start),
(int)sizeof(key) - 1);
memcpy(key, ctx->tok_start, len);
key[len] = '\0';
next_token(ctx);
if (ctx->tok != JSONT_COLON)
return -EINVAL;
next_token(ctx);
ret = parse_value(ctx, key);
if (ret)
return ret;
if (ctx->tok == JSONT_COMMA)
next_token(ctx);
else if (ctx->tok != JSONT_RBRACE)
return -EINVAL;
}
} else {
return -EINVAL;
}
/* End root node */
ret = fdt_end_node(ctx->fdt);
if (ret)
return ret;
/* Finalize FDT */
ret = fdt_finish(ctx->fdt);
if (ret)
return ret;
return 0;
}
int json_to_fdt(const char *json, struct abuf *buf)
{
struct json_parser ctx;
void *fdt_buf;
int fdt_size;
int ret;
if (!json || !buf)
return -EINVAL;
/* Estimate FDT size: JSON length * 2 should be plenty */
fdt_size = max((int)strlen(json) * 2, 4096);
abuf_init(buf);
if (!abuf_realloc(buf, fdt_size))
return -ENOMEM;
fdt_buf = abuf_data(buf);
/* Init parser */
memset(&ctx, 0, sizeof(ctx));
ctx.json = json;
ctx.pos = json;
ctx.fdt = fdt_buf;
ctx.fdt_size = fdt_size;
/* Parse JSON and create FDT */
ret = parse_json_root(&ctx);
if (ret)
goto err;
/* Adjust abuf size to actual FDT size */
abuf_realloc(buf, fdt_totalsize(fdt_buf));
log_debug("json %ld buf %ld\n", strlen(json), buf->size);
return 0;
err:
abuf_uninit(buf);
return ret;
}

View File

@@ -1,12 +1,16 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Tests for JSON pretty-printer
* Tests for JSON utilities including parser and FDT converter
*
* Copyright (C) 2025 Canonical Ltd
* Written by Simon Glass <simon.glass@canonical.com>
*/
#include <dm/ofnode.h>
#include <fdt_support.h>
#include <image.h>
#include <json.h>
#include <linux/libfdt.h>
#include <test/lib.h>
#include <test/test.h>
#include <test/ut.h>
@@ -209,3 +213,334 @@ static int lib_test_json_whitespace(struct unit_test_state *uts)
return 0;
}
LIB_TEST(lib_test_json_whitespace, UTF_CONSOLE);
/* JSON to FDT conversion tests */
static int lib_test_json_to_fdt_simple(struct unit_test_state *uts)
{
const char *json = "{\"name\":\"test\",\"value\":42}";
struct abuf buf;
void *fdt_buf;
ut_assertok(json_to_fdt(json, &buf));
fdt_buf = abuf_data(&buf);
/* Verify FDT is valid */
ut_assertok(fdt_check_header(fdt_buf));
/* Check string property */
ut_asserteq_str("test", fdt_getprop(fdt_buf, 0, "name", NULL));
/* Check integer property */
ut_asserteq(42, fdtdec_get_int(fdt_buf, 0, "value", 0));
abuf_uninit(&buf);
return 0;
}
LIB_TEST(lib_test_json_to_fdt_simple, 0);
static int lib_test_json_to_fdt_nested(struct unit_test_state *uts)
{
const char *json = "{\"outer\":{\"inner\":\"value\"}}";
struct abuf buf;
void *fdt_buf;
int node;
ut_assertok(json_to_fdt(json, &buf));
fdt_buf = abuf_data(&buf);
/* Verify FDT is valid */
ut_assertok(fdt_check_header(fdt_buf));
/* Find nested node */
node = fdt_path_offset(fdt_buf, "/outer");
ut_assert(node >= 0);
/* Check property in nested node */
ut_asserteq_str("value", fdt_getprop(fdt_buf, node, "inner", NULL));
abuf_uninit(&buf);
return 0;
}
LIB_TEST(lib_test_json_to_fdt_nested, 0);
static int lib_test_json_to_fdt_array(struct unit_test_state *uts)
{
const char *json = "{\"numbers\":[1,2,3]}";
struct abuf buf;
void *fdt_buf;
u32 arr[8];
int size;
oftree tree;
ofnode root;
ut_assertok(json_to_fdt(json, &buf));
fdt_buf = abuf_data(&buf);
/* Verify FDT is valid */
ut_assertok(fdt_check_header(fdt_buf));
/* Create oftree from FDT */
tree = oftree_from_fdt(fdt_buf);
ut_assert(oftree_valid(tree));
root = oftree_root(tree);
ut_assert(ofnode_valid(root));
/* Check array property */
ut_assertnonnull(ofnode_get_property(root, "numbers", &size));
size /= sizeof(u32);
ut_asserteq(3, size);
ut_assertok(ofnode_read_u32_array(root, "numbers", arr, size));
ut_asserteq(1, arr[0]);
ut_asserteq(2, arr[1]);
ut_asserteq(3, arr[2]);
abuf_uninit(&buf);
return 0;
}
LIB_TEST(lib_test_json_to_fdt_array, 0);
static int lib_test_json_to_fdt_string_array(struct unit_test_state *uts)
{
char json[] = "{\"tags\":[\"first\",\"second\",\"third\"]}";
struct abuf buf;
void *fdt_buf;
const char *str;
ofnode root;
oftree tree;
ut_assertok(json_to_fdt(json, &buf));
fdt_buf = abuf_data(&buf);
/* Verify FDT is valid */
ut_assertok(fdt_check_header(fdt_buf));
/* Create oftree from FDT */
tree = oftree_from_fdt(fdt_buf);
ut_assert(oftree_valid(tree));
root = oftree_root(tree);
ut_assert(ofnode_valid(root));
/* Check string array property */
ut_assertok(ofnode_read_string_index(root, "tags", 0, &str));
ut_asserteq_str("first", str);
ut_assertok(ofnode_read_string_index(root, "tags", 1, &str));
ut_asserteq_str("second", str);
ut_assertok(ofnode_read_string_index(root, "tags", 2, &str));
ut_asserteq_str("third", str);
abuf_uninit(&buf);
return 0;
}
LIB_TEST(lib_test_json_to_fdt_string_array, 0);
static int lib_test_json_to_fdt_bool(struct unit_test_state *uts)
{
const char *json = "{\"enabled\":true,\"disabled\":false}";
struct abuf buf;
void *fdt_buf;
ut_assertok(json_to_fdt(json, &buf));
fdt_buf = abuf_data(&buf);
/* Verify FDT is valid */
ut_assertok(fdt_check_header(fdt_buf));
/* Check boolean properties */
ut_asserteq(1, fdtdec_get_int(fdt_buf, 0, "enabled", 0));
ut_asserteq(0, fdtdec_get_int(fdt_buf, 0, "disabled", 0));
abuf_uninit(&buf);
return 0;
}
LIB_TEST(lib_test_json_to_fdt_bool, 0);
/* Test with realistic LUKS2 JSON metadata using ofnode API */
static int lib_test_json_to_fdt_luks2(struct unit_test_state *uts)
{
/* Simplified LUKS2 JSON metadata structure */
const char *luks2_json =
"{"
" \"keyslots\": {"
" \"0\": {"
" \"type\": \"luks2\","
" \"key_size\": 32,"
" \"area\": {"
" \"type\": \"raw\","
" \"offset\": \"32768\","
" \"size\": \"258048\""
" },"
" \"kdf\": {"
" \"type\": \"pbkdf2\","
" \"hash\": \"sha256\","
" \"iterations\": 1000,"
" \"salt\": \"aGVsbG93b3JsZA==\""
" }"
" },"
" \"1\": {"
" \"type\": \"luks2\","
" \"key_size\": 32,"
" \"area\": {"
" \"type\": \"raw\","
" \"offset\": \"290816\","
" \"size\": \"258048\""
" },"
" \"kdf\": {"
" \"type\": \"pbkdf2\","
" \"hash\": \"sha256\","
" \"iterations\": 2000,"
" \"salt\": \"YW5vdGhlcnNhbHQ=\""
" }"
" }"
" },"
" \"segments\": {"
" \"0\": {"
" \"type\": \"crypt\","
" \"offset\": \"16777216\","
" \"size\": \"dynamic\","
" \"iv_tweak\": \"0\","
" \"encryption\": \"aes-cbc-essiv:sha256\","
" \"sector_size\": 512"
" }"
" },"
" \"digests\": {"
" \"0\": {"
" \"type\": \"pbkdf2\","
" \"keyslots\": [0, 1],"
" \"segments\": [0],"
" \"hash\": \"sha256\","
" \"iterations\": 1000,"
" \"salt\": \"c2FsdHlzYWx0\""
" }"
" },"
" \"config\": {"
" \"json_size\": \"12288\","
" \"keyslots_size\": \"3145728\""
" }"
"}";
ofnode segments, segment0, digests, digest0, config;
ofnode root, keyslots, keyslot0, kdf;
struct abuf buf;
u32 arr[8];
int size;
void *fdt_buf;
oftree tree;
ut_assertok(json_to_fdt(luks2_json, &buf));
/* Verify FDT is valid */
fdt_buf = abuf_data(&buf);
ut_assertok(fdt_check_header(fdt_buf));
/* Create oftree from FDT */
tree = oftree_from_fdt(fdt_buf);
ut_assert(oftree_valid(tree));
/* Get root node */
root = oftree_root(tree);
ut_assert(ofnode_valid(root));
/* Navigate to keyslots node */
keyslots = ofnode_find_subnode(root, "keyslots");
ut_assert(ofnode_valid(keyslots));
/* Navigate to keyslot 0 */
keyslot0 = ofnode_find_subnode(keyslots, "0");
ut_assert(ofnode_valid(keyslot0));
/* Check keyslot type */
ut_asserteq_str("luks2", ofnode_read_string(keyslot0, "type"));
/* Check key_size */
ut_asserteq(32, ofnode_read_u32_default(keyslot0, "key_size", 0));
/* Navigate to KDF node */
kdf = ofnode_find_subnode(keyslot0, "kdf");
ut_assert(ofnode_valid(kdf));
/* Check KDF type */
ut_asserteq_str("pbkdf2", ofnode_read_string(kdf, "type"));
/* Check KDF hash */
ut_asserteq_str("sha256", ofnode_read_string(kdf, "hash"));
/* Check iterations */
ut_asserteq(1000, ofnode_read_u32_default(kdf, "iterations", 0));
/* Check salt (base64 string) */
ut_asserteq_str("aGVsbG93b3JsZA==", ofnode_read_string(kdf, "salt"));
/* Navigate to segments node */
segments = ofnode_find_subnode(root, "segments");
ut_assert(ofnode_valid(segments));
/* Navigate to segment 0 */
segment0 = ofnode_find_subnode(segments, "0");
ut_assert(ofnode_valid(segment0));
/* Check segment type */
ut_asserteq_str("crypt", ofnode_read_string(segment0, "type"));
/* Check encryption */
ut_asserteq_str("aes-cbc-essiv:sha256", ofnode_read_string(segment0, "encryption"));
/* Check offset (stored as string in JSON) */
ut_asserteq_str("16777216", ofnode_read_string(segment0, "offset"));
/* Check sector_size */
ut_asserteq(512, ofnode_read_u32_default(segment0, "sector_size", 0));
/* Navigate to digests node */
digests = ofnode_find_subnode(root, "digests");
ut_assert(ofnode_valid(digests));
/* Navigate to digest 0 */
digest0 = ofnode_find_subnode(digests, "0");
ut_assert(ofnode_valid(digest0));
/* Check digest type */
ut_asserteq_str("pbkdf2", ofnode_read_string(digest0, "type"));
/* Check keyslots array */
ut_assertnonnull(ofnode_get_property(digest0, "keyslots", &size));
size /= sizeof(u32);
ut_asserteq(2, size);
ut_assertok(ofnode_read_u32_array(digest0, "keyslots", arr, size));
ut_asserteq(0, arr[0]);
ut_asserteq(1, arr[1]);
/* Check segments array */
ut_assertnonnull(ofnode_get_property(digest0, "segments", &size));
ut_asserteq(4, size);
size /= sizeof(u32);
ut_assertok(ofnode_read_u32_array(digest0, "segments", arr, size));
ut_asserteq(0, arr[0]);
/* Navigate to config node */
config = ofnode_find_subnode(root, "config");
ut_assert(ofnode_valid(config));
/* Check json_size (stored as string in JSON) */
ut_asserteq_str("12288", ofnode_read_string(config, "json_size"));
/* Check keyslots_size (stored as string in JSON) */
ut_asserteq_str("3145728", ofnode_read_string(config, "keyslots_size"));
abuf_uninit(&buf);
return 0;
}
LIB_TEST(lib_test_json_to_fdt_luks2, 0);