diff --git a/README.md b/README.md index bf10a3e..902dfbc 100644 --- a/README.md +++ b/README.md @@ -763,7 +763,7 @@ Family ID 'rp2350-arm-s' can be downloaded in partition 0: ### create This command allows you to create partition tables, and additionally embed them into the block loop if ELF files (for example, for bootloaders). -By default, all partition tables are hashed, and you can also sign them. +By default, all partition tables are hashed, and you can also sign them. The schema for this JSON file is [here](json/schemas/partition-table-schema.json). ```text $ picotool help partition create @@ -907,7 +907,7 @@ The `otp` commands are for interacting with the RP2350 OTP Memory. They are not Note that the OTP Memory is One-Time-Programmable, which means that once a bit has been changed from 0 to 1, it cannot be changed back. Therefore, caution should be used when using these commands, as they risk bricking your RP2350 device. For example, if you set SECURE_BOOT_ENABLE but don't set a boot key, and disable the PICOBOOT interface, then your device will be unusable. -For the `list`, `set`, `get` and `load` commands, you can define your own OTP layout in a JSON file and pass that in with the `-i` argument. These rows will be added to the default rows when parsing. +For the `list`, `set`, `get` and `load` commands, you can define your own OTP layout in a JSON file and pass that in with the `-i` argument. These rows will be added to the default rows when parsing. The schema for this JSON file is [here](json/schemas/otp-contents-schema.json) ```text $ picotool help otp @@ -940,7 +940,7 @@ These commands will set/get specific rows of OTP. By default, they will write/re ### load -This command allows loading of a range of OTP rows onto the device. The source can be a binary file, or a JSON file such as the one output by `picotool sign`. +This command allows loading of a range of OTP rows onto the device. The source can be a binary file, or a JSON file such as the one output by `picotool sign`. The schema for this JSON file is [here](json/schemas/otp-schema.json) For example, if you wish to sign a binary and then test secure boot with it, you can run the following set of commands: ```text $ picotool sign hello_world.elf hello_world.signed.elf private.pem otp.json @@ -952,7 +952,7 @@ $ picotool reboot ### white-label This command allows for OTP white-labelling, which sets the USB configuration used by the device in BOOTSEL mode. -This can be configured from a JSON file, an example of which is in [sample-wl.json](sample-wl.json). +This can be configured from a JSON file, an example of which is in [sample-wl.json](json/sample-wl.json). The schema for this JSON file is [here](json/schemas/whitelabel-schema.json) ```text $ picotool help otp white-label @@ -990,7 +990,7 @@ OPTIONS: ``` ```text -$ picotool otp white-label -s 0x100 ../sample-wl.json +$ picotool otp white-label -s 0x100 sample-wl.json Setting attributes 20e0 0x2e8b, 0x000e, 0x0215, 0x0c09, 0x1090, 0x200c, 0x2615, 0x20e0, 0x310b, 0x3706, 0x3a04, 0x3c04, 0x3e21, 0x4f15, 0x5a0a, 0x5f0a, 0x007a, 0x00df, 0x6c34, 0xd83c, 0xdf4c, 0x0020, 0x0054, 0x0065, 0x0073, 0x0074, 0x0027, 0x0073, 0x0020, 0x0050, 0x0069, 0x0073, 0x6554, 0x7473, 0x5220, 0x3250, 0x3533, 0x3f30, 0x6f6e, 0x6e74, 0x6365, 0x7365, 0x6173, 0x6972, 0x796c, 0x6e61, 0x6d75, 0x6562, 0x0072, 0x6554, 0x7473, 0x6950, 0x4220, 0x6f6f, 0x0074, 0x6554, @@ -1032,7 +1032,7 @@ Device Descriptor: This command will run a binary on your device in order to set the OTP permissions, as these are not directly accessible from `picotool` on due to the default permissions settings required to fix errata XXX on RP2350. Because it runs a binary, the binary needs to be sign it if secure boot is enabled. The binary will print what it is doing over uart, which can be configured using the UART Configuration arguments. You can define your OTP permissions in a json file, an example of which -is in [sample-permissions.json](sample-permissions.json). +is in [sample-permissions.json](json/sample-permissions.json). The schema for this JSON file is [here](json/schemas/permissions-schema.json) ```text $ picotool help otp permissions @@ -1081,7 +1081,7 @@ OPTIONS: ``` ```text -$ picotool otp permissions --sign private.pem --tx 46 ../sample-permissions.json +$ picotool otp permissions --sign private.pem --tx 46 sample-permissions.json Picking file ./xip_ram_perms.elf page10 page10 = 0 diff --git a/default-pt.json b/default-pt.json deleted file mode 100644 index b057d7a..0000000 --- a/default-pt.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "version": [1, 0], - "unpartitioned": { - "families": ["absolute"], - "permissions": { - "secure": "rw", - "nonsecure": "rw", - "bootloader": "rw" - } - }, - "partitions": [ - { - "name": "A", - "id": 0, - "size": "2044K", - "families": ["rp2350-arm-s", "rp2350-riscv"], - "permissions": { - "secure": "rw", - "nonsecure": "rw", - "bootloader": "rw" - } - }, - { - "name": "B", - "id": 1, - "size": "2044K", - "families": ["rp2350-arm-s", "rp2350-riscv"], - "permissions": { - "secure": "rw", - "nonsecure": "rw", - "bootloader": "rw" - }, - "link": ["a", 0] - } - ] -} \ No newline at end of file diff --git a/json/default-pt.json b/json/default-pt.json new file mode 100644 index 0000000..83be3dc --- /dev/null +++ b/json/default-pt.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://raw.githubusercontent.com/raspberrypi/picotool/develop/json/schemas/partition-table-schema.json", + "version": [1, 0], + "unpartitioned": { + "families": ["absolute"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + "partitions": [ + { + "name": "A", + "id": 0, + "size": "2044K", + "families": ["rp2350-arm-s", "rp2350-riscv"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + { + "name": "B", + "id": 1, + "size": "2044K", + "families": ["rp2350-arm-s", "rp2350-riscv"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + }, + "link": ["a", 0] + } + ] +} diff --git a/sample-permissions.json b/json/sample-permissions.json similarity index 79% rename from sample-permissions.json rename to json/sample-permissions.json index fb125d1..0d01ff1 100644 --- a/sample-permissions.json +++ b/json/sample-permissions.json @@ -1,4 +1,5 @@ { + "$schema": "https://raw.githubusercontent.com/raspberrypi/picotool/develop/json/schemas/permissions-schema.json", "10": { "no_key_state": 0, "key_r": 0, diff --git a/sample-wl.json b/json/sample-wl.json similarity index 84% rename from sample-wl.json rename to json/sample-wl.json index 4a74dfd..d48274a 100644 --- a/sample-wl.json +++ b/json/sample-wl.json @@ -1,4 +1,5 @@ { + "$schema": "https://raw.githubusercontent.com/raspberrypi/picotool/develop/json/schemas/whitelabel-schema.json", "device": { "vid": "0x2e8b", "pid": "0x000e", @@ -22,4 +23,4 @@ "model": "My Test Pi", "board_id": "TPI-RP2350" } -} \ No newline at end of file +} diff --git a/json/schemas/otp-contents-schema.json b/json/schemas/otp-contents-schema.json new file mode 100644 index 0000000..aa4f7ac --- /dev/null +++ b/json/schemas/otp-contents-schema.json @@ -0,0 +1,77 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "OTP Contents", + "description": "Defined contents of the RP-series device OTP", + "type": "array", + "items": { + "description": "OTP Row", + "type": "object", + "properties": { + "crit": { + "description": "Critical Row (use three-of-eight vote encoding)", + "type": "boolean" + }, + "description": { + "description": "Row Description", + "type": "string" + }, + "ecc": { + "description": "ECC Row", + "type": "boolean" + }, + "fields": { + "description": "Fields within row", + "type": "array", + "items": { + "type": "object", + "properties": { + "description": { + "description": "Field Description", + "type": "string" + }, + "mask": { + "description": "Field Bit Mask", + "type": "integer" + }, + "name": { + "description": "Field Name", + "type": "string" + } + }, + "required": ["description", "mask", "name"], + "additionalProperties": false + } + }, + "mask": { + "description": "Row Bit Mask", + "type": "integer" + }, + "name": { + "description": "Row Name", + "type": "string" + }, + "redundancy": { + "description": "Number of redundant rows", + "type": "integer" + }, + "row": { + "description": "OTP Row", + "type": "integer" + }, + "seq_index": { + "description": "Sequence Index", + "type": "integer" + }, + "seq_length": { + "description": "Sequence Length", + "type": "integer" + }, + "seq_prefix": { + "description": "Sequence Prefix", + "type": "string" + } + }, + "required": ["crit", "description"], + "additionalProperties": false + } +} diff --git a/json/schemas/otp-schema.json b/json/schemas/otp-schema.json new file mode 100644 index 0000000..d59bc3a --- /dev/null +++ b/json/schemas/otp-schema.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "OTP Settings", + "description": "OTP Settings", + "type": "object", + "properties": {"$schema": {}}, + "patternProperties": { + "^\\d{1,2}:\\d{1,2}$": { + "description": "Generic OTP Row", + "type": "object", + "properties": { + "ecc": { + "description": "Protect with ECC", + "type": "boolean" + }, + "value": { + "description": "Value to write", + "type": ["array", "string", "integer"], + "pattern": "^0x[0-9a-fA-F]{1,6}$", + "items": { + "description": "Data Byte", + "type": ["string", "integer"], + "pattern": "^0x[0-9a-fA-F]{1,2}$" + } + } + }, + "additionalProperties": false, + "required": ["ecc", "value"] + }, + "^[\\d\\w_]+$": { + "description": "Defined OTP Row", + "type": ["object", "array", "string", "integer"], + "pattern": "^0x[0-9a-fA-F]{1,6}$", + "items": { + "description": "Data Byte", + "type": ["string", "integer"], + "pattern": "^0x[0-9a-fA-F]{1,2}$" + }, + "patternProperties": { + "^[\\d\\w_]+$": { + "description": "OTP Field", + "type": ["string", "integer"], + "pattern": "^0x[0-9a-fA-F]{1,6}$" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/json/schemas/partition-table-schema.json b/json/schemas/partition-table-schema.json new file mode 100644 index 0000000..92eadcd --- /dev/null +++ b/json/schemas/partition-table-schema.json @@ -0,0 +1,158 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Partition Table", + "description": "Layout of the partition table", + "type": "object", + "properties": { + "$schema": {}, + "version": { + "description": "Partition Table Version", + "type": "array", + "prefixItems": [ + { + "description": "Major Version", + "type": "integer", + "minimum": 0 + }, + { + "description": "Minor Version", + "type": "integer", + "minimum": 0 + } + ] + }, + "unpartitioned": { + "description": "Unpartitioned space UF2 families and permissions", + "type": "object", + "properties": { + "families": { + "description": "UF2 families accepted", + "type": "array", + "items": { + "enum": [ + "data", + "absolute", + "rp2040", + "rp2350-arm-s", + "rp2350-arm-ns", + "rp2350-riscv" + ] + } + }, + "permissions": {"$ref": "#/$defs/permissions"} + }, + "required": ["permissions", "families"], + "additionalProperties": false + }, + "partitions": { + "description": "Partitions", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Partition Name", + "type": "string" + }, + "id": { + "description": "Partition ID", + "type": ["integer", "string"], + "minimum": 0, + "exclusiveMaximum": 18446744073709551616, + "pattern": "^0x[0-9a-fA-F]{1,16}$", + "examples": [ + "0xDED3FFFF01234567", + 29, + "0xdeadbeef" + ] + }, + "start": { + "description": "Partition Start", + "type": ["integer", "string"], + "minimum": 0, + "pattern": "^\\d+(k|K)$" + }, + "size": { + "description": "Partition Size", + "type": ["integer", "string"], + "minimum": 0, + "pattern": "^\\d+(k|K)$" + }, + "families": { + "description": "UF2 families accepted", + "type": "array", + "items": { + "type": "string", + "pattern": "^data|absolute|rp2040|rp2350-arm-s|rp2350-arm-ns|rp2350-riscv|0x[0-9a-fA-F]{1,8}$", + "examples": [ + "data", + "absolute", + "rp2040", + "rp2350-arm-s", + "rp2350-arm-ns", + "rp2350-riscv" + ] + } + }, + "permissions": {"$ref": "#/$defs/permissions"}, + "link": { + "type": "array", + "prefixItems": [ + { + "description": "Link Type", + "enum": ["a", "owner" , "none"] + }, + { + "description": "Link Value", + "type": "integer" + } + ] + }, + "no_reboot_on_uf2_download": { + "description": "Don't reboot after UF2 is downloaded", + "type": "boolean" + }, + "ab_non_bootable_owner_affinity": { + "description": "Pick the non-bootable owner instead", + "type": "boolean" + }, + "ignored_during_riscv_boot": { + "description": "Ignore this partition during Risc-V boot", + "type": "boolean" + }, + "ignored_during_arm_boot": { + "description": "Ignore this partition during Arm boot", + "type": "boolean" + } + }, + "required": ["size", "permissions", "families"], + "additionalProperties": false + } + } + }, + "required": ["unpartitioned", "partitions"], + "additionalProperties": false, + "$defs": { + "permissions": { + "description": "Permissions", + "type": "object", + "properties": { + "secure": { + "description": "Secure Permissions", + "type": "string", + "pattern": "^(r|w){0,2}$" + }, + "nonsecure": { + "description": "Non-Secure Permissions", + "type": "string", + "pattern": "^(r|w){0,2}$" + }, + "bootloader": { + "description": "Bootloader Permissions", + "type": "string", + "pattern": "^(r|w){0,2}$" + } + } + } + } +} diff --git a/json/schemas/permissions-schema.json b/json/schemas/permissions-schema.json new file mode 100644 index 0000000..7956c62 --- /dev/null +++ b/json/schemas/permissions-schema.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "OTP Permissions", + "description": "Setup of OTP page permissions", + "type": "object", + "properties": {"$schema": {}}, + "patternProperties": { + "^[0-6][0-9]$": { + "description": "OTP Page Permissions", + "type": "object", + "properties": { + "no_key_state": { + "description": "State when at least one key is registered for this page and no matching key has been entered: 0 -> read_only, 1 -> inaccessible", + "type": "integer", + "minimum": 0, + "maximum": 1 + }, + "key_r": { + "description": "Index 1-6 of a hardware key which must be entered to grant read access, or 0 if no such key is required", + "type": "integer", + "minimum": 0, + "maximum": 6 + }, + "key_w": { + "description": "Index 1-6 of a hardware key which must be entered to grant write access, or 0 if no such key is required", + "type": "integer", + "minimum": 0, + "maximum": 6 + }, + "lock_bl": { + "description": "Dummy lock bits reserved for bootloaders (including the RP2350 USB bootloader) to store their own OTP access permissions: 0 -> read_write, 1 -> read_only, 2 -> Do not use (behaves the same as incaccessible), 3 -> inaccessible", + "type": "integer", + "minimum": 0, + "maximum": 3 + }, + "lock_ns": { + "description": "Lock state for Non-secure accesses to this page: 0 -> read_write, 1 -> read_only, 2 -> Do not use (behaves the same as incaccessible), 3 -> inaccessible", + "type": "integer", + "minimum": 0, + "maximum": 3 + }, + "lock_s": { + "description": "Lock state for Secure accesses to this page: 0 -> read_write, 1 -> read_only, 2 -> Do not use (behaves the same as incaccessible), 3 -> inaccessible", + "type": "integer", + "minimum": 0, + "maximum": 3 + } + } + } + }, + "additionalProperties": false +} diff --git a/json/schemas/whitelabel-schema.json b/json/schemas/whitelabel-schema.json new file mode 100644 index 0000000..d96011a --- /dev/null +++ b/json/schemas/whitelabel-schema.json @@ -0,0 +1,124 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "White Labelling", + "description": "White Labelling Configuration, see section 5.7 in the RP2350 datasheet for more details", + "type": "object", + "properties": { + "$schema": {}, + "device": { + "description": "Device Properties", + "type": "object", + "properties": { + "vid": { + "description": "Vendor ID", + "type": "string", + "pattern": "^0x[0-9a-fA-F]{4}$" + }, + "pid": { + "description": "Product ID", + "type": "string", + "pattern": "^0x[0-9a-fA-F]{4}$" + }, + "bcd": { + "description": "Device Revision", + "type": "number", + "minimum": 0, + "maximum": 99 + }, + "lang_id": { + "description": "Language ID", + "type": "string", + "pattern": "^0x[0-9a-fA-F]{4}$" + }, + "manufacturer": { + "description": "Manufacturer Name (can contain unicode)", + "type": "string", + "maxLength": 30 + }, + "product": { + "description": "Product Name (can contain unicode)", + "type": "string", + "maxLength": 30 + }, + "serial_number": { + "description": "Serial Number (can contain unicode)", + "type": "string", + "maxLength": 30 + }, + "max_power": { + "description": "Max power consumption, in 2mA units", + "type": ["integer", "string"], + "maximum": 255, + "pattern": "^0x[0-9a-fA-F]{1,2}$" + }, + "attributes": { + "description": "Device attributes: bit 7 must be 1, bit 6 is self-powered, bit 5 is remote wakeup, bits 0-4 must be 0", + "type": ["integer", "string"], + "minimum": 128, + "maximum": 224, + "pattern": "^0x[8aceACE]{1}0$" + } + }, + "dependentRequired": { + "max_power": ["attributes"], + "attributes": ["max_power"] + }, + "additionalProperties": false + }, + "scsi": { + "description": "SCSI Inquiry Values", + "type": "object", + "properties": { + "vendor": { + "description": "SCSI Vendor", + "type": "string", + "maxLength": 8 + }, + "product": { + "description": "SCSI Product", + "type": "string", + "maxLength": 16 + }, + "version": { + "description": "SCSI Version", + "type": "string", + "maxLength": 4 + } + }, + "additionalProperties": false + }, + "volume": { + "description": "MSD Volume Configuration", + "type": "object", + "properties": { + "label": { + "description": "Volume Label", + "type": "string", + "maxLength": 11 + }, + "redirect_url": { + "description": "INDEX.HTM Redirect URL", + "type": "string", + "maxLength": 127 + }, + "redirect_name": { + "description": "INDEX.HTM Redirect Name", + "type": "string", + "maxLength": 127 + }, + "model": { + "description": "INFO_UF2.TXT Model Name", + "type": "string", + "maxLength": 127 + }, + "board_id": { + "description": "INFO_UF2.TXT Board ID", + "type": "string", + "maxLength": 127 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/main.cpp b/main.cpp index 4000168..78ff0a1 100644 --- a/main.cpp +++ b/main.cpp @@ -7299,19 +7299,20 @@ bool otp_white_label_command::execute(device_map &devices) { // Check for separate max_power and attributes uint16_t val = 0; int hex_val = 0; - if (wl_json["device"].contains("max_power")) { + if (wl_json["device"].contains("max_power") && wl_json["device"].contains("attributes")) { if (!get_json_int(wl_json["device"]["max_power"], hex_val)) { fail(ERROR_FORMAT, "MaxPower must be an integer"); } val |= (hex_val << 8); - } - if (wl_json["device"].contains("attributes")) { + if (!get_json_int(wl_json["device"]["attributes"], hex_val)) { fail(ERROR_FORMAT, "Device Attributes must be an integer"); } else if (hex_val & 0b11111 || ~hex_val & 0x80) { fail(ERROR_FORMAT, "Device Attributes must have bit 7 set (0x80), and bits 4-0 clear"); } val |= hex_val; + } else if (wl_json["device"].contains("max_power") || wl_json["device"].contains("attributes")) { + fail(ERROR_INCOMPATIBLE, "Must specify both max_power and attributes in the JSON file"); } if (val) { fos << "Setting attributes " << hex_string(val, 4) << "\n";