Is it possible to have arbitrary data in a zephyr device tree node? - device-tree

I'd like to use the device tree to store some system level constants.
Is it possible to store and retrieve arbitrary values from the device tree?
Attempting to load these values fails to compile as build/zephyr/include/generated/devicetree_unfixed.h lacks the values for 'custom-num' or 'another-value'.
...
int custom_num = DT_PROP(DT_PATH(settings), custom_num);
printf("custom_num %d\n", custom_num);
...
...
zephyr/include/devicetree.h:81:17: error: 'DT_N_S_settings_P_custom_num' undeclared (first use in this function)
81 | #define DT_ROOT DT_N
| ^~~~
...
The devicetree overlay file:
/* SPDX-License-Identifier: Apache-2.0 */
/ {
aliases {
someuart-uart = &uart7;
};
settings {
custom-num = < 29992 >;
another-value = "some string";
};
};

The missing part here was the devicetree bindings, https://docs.zephyrproject.org/latest/guides/dts/bindings.html#, that a dt entry has to match in order to be correctly processed.
The tip to figure this out came from the zephyrproject issue tracker: https://github.com/zephyrproject-rtos/zephyr/issues/42404
First we have to adjust the overlay slightly to include a 'compatible' entry like:
/* SPDX-License-Identifier: Apache-2.0 */
/ {
aliases {
someuart-uart = &uart7;
};
settings {
compatible = "my-company,settings"; /* <--------- */
custom-num = < 29992 >;
another-value = "some string";
};
};
and then creating a file in the project's dts/bindings/ folder named, per the Zephyr recommendations after the compatible string:
dts/bindings/my-company,settings.yaml
that contains the format expected in a 'my-company,settings' entry:
compatible: "my-company,settings"
description: "Project specific settings and configuration"
properties:
custom-num:
type: int
required: true
another-value:
type: string
required: false

Related

bus error on usage of rusqlite with spatialite extension

I'm seeing a bus error on cargo run when attempting to load the spatialite extension with rusqlite:
Finished dev [unoptimized + debuginfo] target(s) in 1.19s
Running `target/debug/rust-spatialite-example`
[1] 33253 bus error cargo run --verbose
My suspicion is that there's a mismatch of sqlite version and spatialite and that they need to be built together rather than using the bundled feature of rusqlite, though it seems like that'd result in a different error?
Here's how things are set up:
Cargo.toml
[package]
name = "rust-spatialite-example"
version = "0.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rusqlite = { version = "0.28.0", features = ["load_extension", "bundled"] }
init.sql
CREATE TABLE place (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
SELECT AddGeometryColumn('place', 'geom', 4326, 'POINT', 'XY', 0);
SELECT CreateSpatialIndex('place', 'geom');
main.rs
use rusqlite::{Connection, Result, LoadExtensionGuard};
#[derive(Debug)]
struct Place {
id: i32,
name: String,
geom: String,
}
fn load_spatialite(conn: &Connection) -> Result<()> {
unsafe {
let _guard = LoadExtensionGuard::new(conn)?;
conn.load_extension("/opt/homebrew/Cellar/libspatialite/5.0.1_2/lib/mod_spatialite", None)
}
}
fn main() -> Result<()> {
let conn = Connection::open("./geo.db")?;
load_spatialite(&conn)?;
// ... sql statements that aren't executed
Ok(())
}
Running:
cat init.sql | spatialite geo.db
cargo run
The mod_spatialite path is correct (there's an expected SqliteFailure error when that path is wrong). I tried explicitly setting sqlite3_modspatialite_init as the entry point and the behavior stayed the same.

LOAD fails for Method component in USIM card via OTA SMS-DELIVER

I need to load a simple applet to my USIM card. Authentication is OK.
I'm using OTA SMS-DELIVER via SmartCard to install CAP file.
Using these:
https://github.com/simhacks/hello-stk.git
https://github.com/martinpaljak/ant-javacard
I couldnt find the right spec explaining "C482XXYY" in Header.cap, how it should be calculated,
this answer below helped me navigate, but for Method.cap loading - it still fails
https://community.oracle.com/tech/developers/discussion/1753814/globalpaltform-load-command-data-field
I've written this loader:
https://pastebin.com/pSXeDYyS
Every component in CAP file is in separate APDU with LOAD instruction.
First 5 is loaded successfully with DATA=00, SW=9000, however on Method it fails.
As you can see from my script, I've fixed Descriptor size to 0000 in Directory field.
And in Header C482xxyy, where xxyy is calculated properly, which is a sum of
all size fields in Directory, e.g.
016a = 0011+001f+000c+001e+0042+0018+006d+0032+0017+0000+0000
Question: Can't figure out why Method LOAD fails? By checking GP 2.1 spec, it doesnt
help me understand the problem. Concatenated components to < 255 block size also fails,
only separate components work for LOAD.
[+] Install for load
>> 80e602001207d07002ca449001000006ef04c60201850000c0000000
<< 009000
[+] LOAD - Header
>> 80e8000018c482016a010011decaffed010204000107d07002ca44900100c0000000
<< 009000
[+] LOAD - Directory
>> 80e800012202001f0011001f000c001e00420018006d003200170000000000040002002202010000c0000000
<< 009000
[+] LOAD - Import
>> 80e800022104001e02000107a0000000620101060210a0000000090003ffffffff891071000200c0000000
<< 009000
[+] LOAD - Applet
>> 80e800030f03000c0108d07002ca44900101002000c0000000
<< 009000
[+] LOAD - Class
>> 80e800041b06001843800301ff0007020000002f00398002008101010881000000c0000000
<< 009000
[!!!!] LOAD - Method (FAILED)
>> 80e800057007006d000911188c00048d00012c18197b0002037b00029210240303038b000388007a02318f00053d8c00062e1b8b00077a0120188b000860037a7a02228d00092d1d10076b101a8b000a321fae006b06188c000b7a06118d000c2c1903077b000d037b000d928b000e198b000f3b7a00c0000000
<< 9000 (should be 009000)
[-] LOAD - StaticField (FAILED due to last failed LOAD)
>> 80e80006350800.....
<< 6A86 (because prev. p2=05 is not loaded)
Applet source code:
package com.github.mrlnc.HelloSTK2;
import javacard.framework.*;
import sim.toolkit.*;
/*
Originally from: https://git.osmocom.org/sim/hello-stk/tree/hello-stk/src/org/toorcamp/HelloSTK/HelloSTK.java
*/
public class HelloSTK2 extends Applet implements ToolkitInterface, ToolkitConstants {
// DON'T DECLARE USELESS INSTANCE VARIABLES! They get saved to the EEPROM,
// which has a limited number of write cycles.
private byte helloMenuItem;
private HelloSTK2() {
// This is the interface to the STK applet registry (which is separate
// from the JavaCard applet registry!)
ToolkitRegistry reg = ToolkitRegistry.getEntry();
byte[] menuItemText = new byte[] { 'H', 'e', 'l', 'l', 'o'};
// Define the applet Menu Entry
helloMenuItem = reg.initMenuEntry(menuItemText, (short)0, (short)menuItemText.length,
PRO_CMD_SELECT_ITEM, false, (byte)0, (short)0);
}
// This method is called by the card when the applet is installed. You must
// instantiate your applet and register it here.
public static void install(byte[] bArray, short bOffset, byte bLength) {
HelloSTK2 applet = new HelloSTK2();
applet.register();
}
// This processes APDUs sent directly to the applet. For STK applets, this
// interface isn't really used.
public void process(APDU arg0) throws ISOException {
// ignore the applet select command dispached to the process
if (selectingApplet())
return;
}
// This processes STK events.
public void processToolkit(byte event) throws ToolkitException {
EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler();
if (event == EVENT_MENU_SELECTION) {
byte selectedItemId = envHdlr.getItemIdentifier();
if (selectedItemId == helloMenuItem) {
byte[] welcomeMsg = new byte[] { 'W', 'e', 'l', 'c', 'o', 'm', 'e' };
ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
proHdlr.initDisplayText((byte)0, DCS_8_BIT_DATA, welcomeMsg, (short)0,
(short)(welcomeMsg.length));
proHdlr.send();
}
}
}
}
UPDATE:
Raw CAP bytes in order (Header, Directory, Import, Applet, Class, Method, StaticField, ConstantPool, RefLocation, Descriptor)
['01000fdecaffed010204000105d07002ca44',
'02001f000f001f000c00280036001800aa000a001200000003000000000000030100', '04002803030107a0000000620101060210a0000000090003ffffffff8910710002000107a0000000620001',
'03000c0108d07002ca449001010039',
'06001843800301ff00070200000048005280020081010108810000', '0700aa000912188c00038d00012c08900b3d031048383d041065383d05106c383d06106c383d07106f382d18191a031a9210240303038b000288007a02318f00043d8c00052e1b8b00067a0120188b000760037a7a06248d00082d1d10076b4e1a8b0009321fae006b441007900b3d031057383d041065383d05106c383d061063383d07106f383d08106d383d100610653828048d000a2805150503071504031504928b000b15058b000c3b7a',
'08000a00000000000000000000', '050036000d02000000068109000381090b0680030001000000060000010380030103800303068108000381080c06810a0003810a1303810a16',
'0900120002372d000c05032c08040507090a330f05',
'0b0003000000']
UPDATE 2:
I'm sending via ENVELOPE commands. KIC1,KID1 encryptions are used. They work with RFM and some RAM commands like GET STATUS etc. Here is the code for INSTALL (for load) command:
def install_for_load(self, caps, exe_aid):
# 11.5.2.3.7 INSTALL Command Parameters, page 174
cap_data = "".join(caps)
apdu = "".join([
"80", # CLS
"e6", # INSTR
"02", # p1 0b000000_1_0 ; for load
"00", # p2
"%02x", # p3 ; Lc
])
# sec_aid = "a000000003000000" # GP211 Security Domain AID
data = "".join([
"%02x" % int(len(exe_aid)/2),
exe_aid,
"00", # security domain aid (same domain)
# "%02x" % int(len(sec_aid)/2), # security domain aid (same domain)
# sec_aid,
"00", # load file data block hash
"%02x", # load params length
])
load_params_tl = "".join([
"ef", # Load Parameters: System Specific Parameters tag
"%02x", # length of load_params
])
load_params_v = "".join([
"c6", # Non-volatile code Minimum Memory requirement. load params (mandatory)
"02", # length
"%04x" % int(len(cap_data)/2), # code size HelloSTK2.cap size.
# "c7", # volatile data minimum memory required (ram) - optional
# "02", # length
# "00ff",
# "c8", # non-volatile data minimum memory required - optional
# "02",
# "00ff"
])
load_params = load_params_tl % int(len(load_params_v)/2) + load_params_v
data = data % int(len(load_params)/2) + load_params
data += "00" # load token
apdu = apdu % int(len(data)/2) + data
apdu += "00c0000000" # C-MAC + Le
return apdu
You can have a look into the GlobalPlatform project loader.
It is LPGL, so directly using it as C code in other non LGPL projects is not possible.
[!!!!] LOAD - Method (FAILED)
80e800057007006d000911188c00048d00012c18197b0002037b00029210240303038b000388007a02318f00053d8c00062e1b8b00077a0120188b000860037a7a02228d00092d1d10076b101a8b000a321fae006b06188c000b7a06118d000c2c1903077b000d037b000d928b000e198b000f3b7a00c0000000
<< 9000 (should be 009000)
I assume that the log is the unwrapped logged before the commands are prepared for SCP80. A response of 9000 in OTA means usually "I received the command, but I don't know what to do with it". Is this 9000 command from the ENVELOPE response on the card? Are you sending the data over an SMPP connection?
Can you try your loading code with a simple HelloWorld Applet without any STK? It could be that your card is not supporting a version you are linking against, e.g. different ETSI releases. Then some method might not be supported and the loading fails.

Use libcryptsetup to open a plain encrypted partition

I have a deployed kiosk system which mounts an encrypted partition at boot time using crytpsetup and a key file on disk as such:
cryptsetup open --type plain --key-file /root/key.bin /dev/sda3 sda3
This yields a /dev/mapper/sda3 device which I can then mount for data access.
I am moving the key to a smart card and want to open the partition using libcryptsetup so the key is not exposed on the command line. Unfortunately, the only example given in the cryptsetup source is for LUKS.
I have tried to reverse engineer the cryptsetup source to get the correct library calls but have been frustrated by the complexity of the options.
Are there examples of other projects which use the library for plain encryption or perhaps a skeleton of the library calls required to duplicate the actions of the command line invocation?
The basic sequence of the library calls required for duplicating the actions on command line to open an encrypted partition using cryptsetup library will be as follows
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <libcryptsetup.h>
int activate_and_check_status(const char *path, const char *device_name)
{
struct crypt_device *cd;
struct crypt_active_device cad;
int r;
/*
* LUKS device activation example.
* It's sequence of sub-steps: device initialization, LUKS header load
* and the device activation itself.
*/
r = crypt_init(&cd, path);
if (r < 0 ) {
printf("crypt_init() failed for %s.\n", path);
return r;
}
/*
* crypt_load() is used to load the LUKS header from block device
* into crypt_device context.
*/
r = crypt_load(cd, /* crypt context */
CRYPT_LUKS1, /* requested type */
NULL); /* additional parameters (not used) */
if (r < 0) {
printf("crypt_load() failed on device %s.\n", crypt_get_device_name(cd));
crypt_free(cd);
return r;
}
/*
* Device activation creates device-mapper devie mapping with name device_name.
*/
r = crypt_activate_by_passphrase(cd, /* crypt context */
device_name, /* device name to activate */
CRYPT_ANY_SLOT,/* which slot use (ANY - try all) */
"foo", 3, /* passphrase */
CRYPT_ACTIVATE_READONLY); /* flags */
if (r < 0) {
printf("Device %s activation failed.\n", device_name);
crypt_free(cd);
return r;
}
printf("LUKS device %s/%s is active.\n", crypt_get_dir(), device_name);
printf("\tcipher used: %s\n", crypt_get_cipher(cd));
printf("\tcipher mode: %s\n", crypt_get_cipher_mode(cd));
printf("\tdevice UUID: %s\n", crypt_get_uuid(cd));
/*
* Get info about active device (query DM backend)
*/
r = crypt_get_active_device(cd, device_name, &cad);
if (r < 0) {
printf("Get info about active device %s failed.\n", device_name);
crypt_deactivate(cd, device_name);
crypt_free(cd);
return r;
}
printf("Active device parameters for %s:\n"
"\tDevice offset (in sectors): %" PRIu64 "\n"
"\tIV offset (in sectors) : %" PRIu64 "\n"
"\tdevice size (in sectors) : %" PRIu64 "\n"
"\tread-only flag : %s\n",
device_name, cad.offset, cad.iv_offset, cad.size,
cad.flags & CRYPT_ACTIVATE_READONLY ? "1" : "0");
crypt_free(cd);
return 0;
}
The API references for luks format, open, activate and deactivate with examples for cryptsetup is available at this link:
cryptsetup API

QBS: Explicitly setting qbs.profiles inside Products causing build to fail

My use-case is this:
I have a static library which I want to be available for some profiles (e.g. "gcc", "arm-gcc", "mips-gcc").
I also have an application which links to this library, but this applications should only build using a specific profile (e.g. "arm-gcc").
For this I am modifying the app-and-lib QBS example.
The lib.qbs file:
import qbs 1.0
Product {
qbs.profiles: ["gcc", "arm-gcc", "mips-gcc"] //I added only this line
type: "staticlibrary"
name: "mylib"
files: [
"lib.cpp",
"lib.h",
]
Depends { name: 'cpp' }
cpp.defines: ['CRUCIAL_DEFINE']
Export {
Depends { name: "cpp" }
cpp.includePaths: [product.sourceDirectory]
}
}
The app.qbs file:
import qbs 1.0
Product {
qbs.profiles: ["arm-gcc"] //I added only this line
type: "application"
consoleApplication: true
files : [ "main.cpp" ]
Depends { name: "cpp" }
Depends { name: "mylib" }
}
The app build fails. Qbs wrongly tries to link to the "gcc" version of the library instead of the "arm-gcc" version, as you can see in the log:
Build graph does not yet exist for configuration 'default'. Starting from scratch.
Resolving project for configuration default
Setting up build graph for configuration default
Building for configuration default
compiling lib.cpp [mylib {"profile":"gcc"}]
compiling lib.cpp [mylib {"profile":"arm-gcc"}]
compiling lib.cpp [mylib {"profile":"mips-gcc"}]
compiling main.cpp [app]
creating libmylib.a [mylib {"profile":"gcc"}]
creating libmylib.a [mylib {"profile":"mips-gcc"}]
creating libmylib.a [mylib {"profile":"arm-gcc"}]
linking app [app]
ERROR: /usr/bin/arm-linux-gnueabihf-g++ -o /home/user/programs/qbs/usr/local/share/qbs/examples/app-and-lib/default/app.7d104347/app /home/user/programs/qbs/usr/local/share/qbs/examples/app-and-lib/default/app.7d104347/3a52ce780950d4d9/main.cpp.o /home/user/programs/qbs/usr/local/share/qbs/examples/app-and-lib/default/mylib.eyJwcm9maWxlIjoiZ2NjIn0-.792f47ec/libmylib.a
ERROR: /home/user/programs/qbs/usr/local/share/qbs/examples/app-and-lib/default/mylib.eyJwcm9maWxlIjoiZ2NjIn0-.792f47ec/libmylib.a: error adding symbols: File format not recognized
collect2: error: ld returned 1 exit status
ERROR: Process failed with exit code 1.
The following products could not be built for configuration default:
app
The build fails only when selecting one profile in the app.qbs file, and this profile should not be the first profile in the qbs.profiles line in the lib.qbs file.
When selecting two or more profiles - the build succeeds.
My analysis:
I think this problem is related to multiplexing:
The lib.qbs contains more than one profile. This turns on multiplexing when building the library, which, in turn, adds additional 'multiplexConfigurationId' to the build-directory name (moduleloader.cpp).
The app.lib contains only one profile, so multiplexing is not turned on and the build-directory name does not get the extra string.
The problem can be solved by changing the code (moduleloader.cpp) so that multiplexing is turned even if there is only one profile i.e. with the following patch:
--- moduleloader.cpp 2018-10-24 16:17:43.633527397 +0300
+++ moduleloader.cpp.new 2018-10-24 16:18:27.541370544 +0300
## -872,7 +872,7 ##
= callWithTemporaryBaseModule<const MultiplexInfo>(dummyContext,
extractMultiplexInfoFromProduct);
- if (multiplexInfo.table.size() > 1)
+ if (multiplexInfo.table.size() > 0)
productItem->setProperty(StringConstants::multiplexedProperty(), VariantValue::trueValue());
VariantValuePtr productNameValue = VariantValue::create(productName);
## -891,7 +891,7 ##
const QString multiplexConfigurationId = multiplexInfo.toIdString(row);
const VariantValuePtr multiplexConfigurationIdValue
= VariantValue::create(multiplexConfigurationId);
- if (multiplexInfo.table.size() > 1 || aggregator) {
+ if (multiplexInfo.table.size() > 0 || aggregator) {
multiplexConfigurationIdValues.push_back(multiplexConfigurationIdValue);
item->setProperty(StringConstants::multiplexConfigurationIdProperty(),
multiplexConfigurationIdValue);
This worked for my use case. I don't know if it make sense in a broader view.
Finally, the questions:
Does it all make sense?
Is this a normal behavior?
Is this use-case simply not supported?
Is there a better solution?
Thanks in advance.
Yes, the default behavior with multiplexing is that the a non-multiplexed product depends on all variants of the dependency. In general, there is no way for a user to change that behavior, but there should be.
However, luckily for you, profiles are special:
Depends { name: "mylib"; profiles: "arm-gcc" }
This should fix your problem.

Cross platform way of testing whether a file is a directory

Currently I have some code like (condensed and removed a bunch of error checking):
dp = readdir(dir);
if (dp->d_type == DT_DIR) {
}
This works swimmingly on my Linux machine. However on another machine (looks like SunOS, sparc):
SunOS HOST 5.10 Generic_127127-11 sun4u sparc SUNW,Ultra-5_10
I get the following error at compile time:
error: structure has no member named `d_type'
error: `DT_DIR' undeclared (first use in this function)
I thought the dirent.h header was crossplatform (for POSIX machines). Any suggestions.
Ref http://www.nexenta.org/os/Porting_Codefixes:
The struct dirent definition in solaris does not contain the d_type field. You would need to make the changes as follows
if (de->d_type == DT_DIR)
{
return 0;
}
changes to
struct stat s; /*include sys/stat.h if necessary */
..
..
stat(de->d_name, &s);
if (s.st_mode & S_IFDIR)
{
return 0;
}
Since stat is also POSIX standard it should be more cross-platform. But you may want to use if ((s.st_mode & S_IFMT) == S_IFDIR) to follow the standard.

Resources