Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions example/ipcinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,22 @@ static void print_chip_temperature() {
}

static void print_serial() {
char serial[512];
char serial[512] = {0};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the zero-fill and the !*serial test are redundant with found. Every reader writes at least 12 chars before returning true and returns false on every failure path, so if (!found) with a plain char serial[512] is behaviourally identical, and it makes clear which signal is authoritative.

The comment at 111-114 narrates the removed bug. The one sentence worth keeping in code is the last one (provisioning derives a MAC, so a miss must be silent on stdout and non-zero on exit); the rest belongs in the commit message.

bool found = false;

const char *vendor = getchipvendor();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-existing, but this PR puts ipcinfo -i on every V5 boot's rcS path, so flagging it: getchipvendor() runs setup_hal_hisi(), which writes 0 0 0 0 to /proc/sys/kernel/printk and on HISI_OT force-enables the sensor clock (CRG8464/CRG8472). ipcinfo never calls hal_cleanup() on any exit path (only sensors.c and i2cspi.c do), so the console stays silent and the CRG stays modified for the rest of boot.

One hal_cleanup() before each exit, or an atexit() registered after getchipname(), fixes it.

if (strstr(vendor, VENDOR_HISI) || strstr(vendor, VENDOR_GOKE))
hisi_ev300_get_die_id(serial, sizeof serial);
found = hisi_get_die_id(serial, sizeof serial);
#ifdef IPCHW_VENDOR_SSTAR
if (strstr(vendor, VENDOR_SSTAR))
sstar_get_die_id(serial, sizeof serial);
found = sstar_get_die_id(serial, sizeof serial);
#endif

if (!serial)
// The test used to be `if (!serial)`, the address of a stack array, which
// is never null -- a chip with no reader printed whatever the stack held.
// Provisioning scripts derive a MAC from this, so a miss has to be silent
// on stdout and non-zero on exit.
if (!found || !*serial)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth a line in the description: on master if (!serial) was never true, so -i always fell through with exit 0. Now a chip with no reader (V4A, INFINITY6C, anything non-HiSi/non-SStar) exits 1, and any flags after -i on the command line never run.

That is what ethaddr_provision wants and it matches -c/-f/-t, but it is a contract change for scripts that chain flags or test the exit code.

exit(EXIT_FAILURE);
puts(serial);
}
Expand Down
1 change: 1 addition & 0 deletions src/hal/hisi/hal_hisi.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@


bool hisi_ev300_get_die_id(char *buf, ssize_t len);
bool hisi_get_die_id(char *buf, ssize_t len);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two small things now that there is a single entry point:

  • hisi_ev300_get_die_id() keeps its own chip_generation != HISI_V4 guard and stays exported, but this dispatcher is now its only caller. Making it static and dropping the inner guard leaves one place encoding the routing. As it stands, a future case HISI_V4A: that reuses it would be silently dead.
  • A new API is the moment to take size_t, like sstar_get_die_id(), which is called side by side with it in print_serial().

void hisi_vi_information(sensor_ctx_t *ctx);
unsigned long hisi_totalmem(unsigned long *media_mem);
bool hisi_detect_cpu(char *chip_name, uint32_t SC_CTRL_base);
Expand Down
65 changes: 64 additions & 1 deletion src/hal/hisi/ispreg.c
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,69 @@ bool hisi_ev300_get_die_id(char *buf, ssize_t len) {
return true;
}

/* Per-die identity on V5 (HISI_OT).
*
* V5 has no counterpart to the V4 die-ID block at 0x12020400. The vendor keeps
* the die ID in OTP and reaches it through a bootrom call (otp_get_die_id() in
* gsl/drivers/share_drivers/share_drivers.c), but every OTP row is also
* shadowed into a register window at the same offset it occupies in OTP, so
* the 16 bytes of OTP_DIE_ID appear at OTP_SHADOW_BASE + 0xF0. Offsets from
* Hi3516CV610_SDK_V1.0.2.0:
* .../bsp/components/gsl/drivers/otp/otp.h OTP_DIE_ID 0xF0, 16 bytes
* .../bsp/components/gsl/include/platform.h OTP_SHADOW_BASE = 0x101E0000
*
* The window is readable from the non-secure side: the OEM's own hwconf.ko
* ioremaps OTP_SHADOW_BASE + 0x10C (OTP_VERSION_ID_REG, the ATE chip version)
* from an ordinary kernel module.
*
* Bytes are emitted in OTP order. The shadow words are little-endian, so byte
* i of what otp_get_die_id() would hand back is word[i / 4] >> (8 * (i % 4)).
*/
#define V5_OTP_SHADOW_BASE 0x101E0000u
#define V5_OTP_DIE_ID 0xF0
#define V5_DIE_ID_WORDS 4

static bool hisi_ot_get_die_id(char *buf, ssize_t len) {
if (len < V5_DIE_ID_WORDS * 8 + 1)
return false;

uint32_t id[V5_DIE_ID_WORDS];
uint32_t any_bit_set = 0;
uint32_t all_bits_set = 0xFFFFFFFF;
for (int i = 0; i < V5_DIE_ID_WORDS; i++) {
if (!mem_reg(V5_OTP_SHADOW_BASE + V5_OTP_DIE_ID + i * 4, &id[i],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking. This base is verified on one CV608, and the SDK it comes from covers CV608/CV610/CV613, but it runs on every HISI_OT part including 3516DV500 and 3519DV500, and hisi_chip_properties() now performs the read unconditionally on the plain ipctool path.

Two failure modes if 0x101E0000 is not the OTP shadow on one of those:

  • Unbacked window: mem_reg() only guards the mmap; the dereference at tools.c is unguarded and there is no SIGBUS handler, so the whole YAML report dies. See 81ccfa4 (the 3536CV100 bus-error fix) for the in-tree precedent.
  • Backed by some other stable register: it passes the all-zero/all-ones filter below and every board of that model reports the same id, which is the fleet-wide MAC collision this PR sets out to prevent.

Suggest gating the reader on the chip IDs the CV610 SDK covers until a DV500 or 3519DV500 has been measured. The read is harmless, so this is the whole test:

for a in 0x101E00F0 0x101E00F4 0x101E00F8 0x101E00FC 0x101E010C; do devmem $a 32; done

+0x10C should be a 0x24xxxx-style version id and the four die words should differ from the CV608 values in the description. I tried to run it on the lab 3519DV500 and CV608 today but both were unreachable.

OP_READ))
return false;
any_bit_set |= id[i];
all_bits_set &= id[i];
}

// An unfused or unreadable row reads all-zeroes or all-ones. Neither is an
// identity, and callers turn this string into a MAC address -- handing one
// out would give every board in a fleet the same address, so fail instead.
if (!any_bit_set || all_bits_set == 0xFFFFFFFF)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This combines all four words, so a shadow that reads e.g. {0xFFFFFFFF, 0xFFFFFFFF, 0, 0} (partially fused or partially locked) passes both tests and is emitted as an identity.

Rejecting a 16-byte value whose bytes are all 0x00 or 0xFF closes that without refusing a legitimate id that happens to contain a zero word. A per-word reject would be too strict for that reason.

return false;

char *ptr = buf;
for (int i = 0; i < V5_DIE_ID_WORDS; i++)
for (int b = 0; b < 4; b++)
ptr += snprintf(ptr, buf + len - ptr, "%02x",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: sixteen snprintf calls with running pointer arithmetic lean on the * 8 + 1 precheck above to keep buf + len - ptr non-negative, and a reader has to re-derive that. Copying the words into a uint8_t[16] and hex-encoding in one loop, as bootrom.c does, keeps the endian independence and lets the precheck become 2 * sizeof id + 1.

(id[i] >> (8 * b)) & 0xFF);

return true;
}

bool hisi_get_die_id(char *buf, ssize_t len) {
switch (chip_generation) {
case HISI_V4:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The V4 arm skips the all-zero/all-ones rejection that the V5 arm gets at line 757. hisi_ev300_get_die_id() returns true on a block of six zero words (the trailing-'0' strip never finds a non-zero digit, so it never breaks), and ipcinfo -i then prints 48 zeros with exit 0. Same for all-ones.

Since the invariant this PR introduces is "callers derive a MAC from this", the check belongs here in the dispatcher so both arms honour it.

return hisi_ev300_get_die_id(buf, len);
case HISI_OT:
return hisi_ot_get_die_id(buf, len);
default:
return false;
}
}

#define CV300_ISP_AF_CFG_ADDR 0x12200
struct CV300_ISP_AF_CFG {
bool en : 1;
Expand Down Expand Up @@ -927,7 +990,7 @@ struct PT_OFFSET {

void hisi_chip_properties(cJSON *j_inner) {
char buf[1024];
if (hisi_ev300_get_die_id(buf, sizeof buf)) {
if (hisi_get_die_id(buf, sizeof buf)) {
ADD_PARAM("id", buf);
}
}