Skip to content
Merged
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
16 changes: 11 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,17 @@ is also what lets detection survive a kernel that will not hand over

Ambarella (`src/hal/ambarella.c`) stops at the machine string on purpose. The
exact part (S3L33M, S3L55M, S3L66, ...) is a word the DSP firmware writes,
reachable only through `/dev/iav`, so the report says `S3L`. Do not derive
total RAM from the boot log's `IAVMEM` line: Amboot computes it from a
compile-time `DRAM_SIZE`, and #266 has it ending at 512 MiB on a camera
reported as 256 MiB. That Dahua kernel also refuses `execve()` from writable
filesystems, so `/tmp/ipctool` fails there with "Success" and exit code 2.
reachable only through `/dev/iav`, so the report says `S3L`. Total RAM is the
top of the DSP carve-out, because `mem=` keeps the kernel far below it: 118 MiB
of 512 MiB on the camera in #266, where the DRAM marking confirmed the figure.
The carve-out comes from `/proc/device-tree/iavmem/reg`, or from the boot log's
`IAVMEM` line on kernels with no `/proc/device-tree`, which Dahua's 3.10.73 is.
That line is printed once, so if the ring buffer has dropped it the report falls
back to what the kernel sees. Stock Dahua firmware has no `/dev/i2c-*` either:
its own `sensor.ko` owns the bus, so no sensor is reported there, and reaching
for the controller registers would race that driver. The same kernel refuses
`execve()` from writable filesystems, so `/tmp/ipctool` fails with "Success"
and exit code 2.

Sensor I2C addresses in tables are the 8-bit (write) form; the default
`i2c_change_addr` shifts right by one for the kernel, and some HALs install
Expand Down
72 changes: 72 additions & 0 deletions src/hal/ambarella.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include "hal/ambarella.h"

#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "hal/common.h"
Expand Down Expand Up @@ -49,6 +52,75 @@ bool ambarella_detect_cpu(char *chip_name) {
return true;
}

#ifndef STANDALONE_LIBRARY
/* /iavmem is what the kernel itself parses, two big-endian cells. Dahua's
* 3.10.73 has no /proc/device-tree at all (#266), hence the log below. */
bool ambarella_iavmem_from_dt(const char *path, uint32_t *start,
uint32_t *size) {
uint32_t cells[2];
FILE *f = fopen(path, "rb");

if (!f)
return false;
bool res = fread(cells, sizeof(cells), 1, f) == 1;
fclose(f);
if (!res)
return false;

*start = ntohl(cells[0]);
*size = ntohl(cells[1]);
return true;
}

/* "Ambarella: IAVMEM = 0x07800000[ ],0x18800000". Printed once at
* boot, so a long-lived ring buffer may have dropped it. The newline is part
* of the match: without it a log cut off inside the size would still parse,
* as a smaller number. */
bool ambarella_iavmem_from_log(const char *log, uint32_t *start,
uint32_t *size) {
const char *line = strstr(log, "IAVMEM = ");
char end = 0;

if (!line)
return false;
return sscanf(line, "IAVMEM = 0x%x[%*[^]]],0x%x%c", start, size, &end) ==
3 &&
end == '\n';
}

static unsigned long ambarella_totalmem(unsigned long *media_mem) {
uint32_t start = 0, size = 0;
bool found = ambarella_iavmem_from_dt("/proc/device-tree/iavmem/reg",
&start, &size);

if (!found) {
char *log = NULL;
ssize_t len = get_dmesg_buf(&log);

/* klogctl() does not terminate what it returns, and the buffer is
* exactly the size of a full ring. */
char *text = log && len > 0 ? realloc(log, len + 1) : NULL;

if (text) {
log = text;
log[len] = '\0';
found = ambarella_iavmem_from_log(log, &start, &size);
}
free(log);
}

/* Nothing to go on, or a carve-out that wraps: what the kernel sees. */
if (!found || !size || start + size < start)
return kernel_mem();

*media_mem = size / 1024;
return (unsigned long)(start / 1024) + *media_mem;
}
#endif

void ambarella_setup_hal() {
possible_i2c_addrs = ambarella_possible_i2c_addrs;
#ifndef STANDALONE_LIBRARY
hal_totalmem = ambarella_totalmem;
#endif
}
9 changes: 9 additions & 0 deletions src/hal/ambarella.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

bool ambarella_machine_name(const char *cpuinfo, char *out, size_t outlen);
bool ambarella_detect_cpu(char *chip_name);
void ambarella_setup_hal();

/* The DSP carve-out, as a physical start and a size in bytes. It runs to the
* top of DRAM, which is the only place total RAM can be read from here: the
* kernel is capped well below it by mem= on the command line. */
bool ambarella_iavmem_from_dt(const char *path, uint32_t *start,
uint32_t *size);
bool ambarella_iavmem_from_log(const char *log, uint32_t *start,
uint32_t *size);

#endif /* HAL_AMBARELLA_H */
31 changes: 31 additions & 0 deletions src/hal/ambarella_test.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Hardware-free cover for the Ambarella machine-string parser. The strings
* are the DT_MACHINE_START names in the vendor kernel, and the S3L cpuinfo is
* the one from issue #266. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -62,6 +63,36 @@ int main() {
guarded + 1, 0));
CHECK(guarded[0] == 'x' && guarded[1] == 'y' && guarded[2] == 'z');

/* The reporter's board: 512 MiB, with 392 MiB of it the DSP's. */
uint32_t start = 0, size = 0;
CHECK(ambarella_iavmem_from_log(
"Ambarella: DRAMC = 0xdffe0000[0xef000000],0x00020000 0\n"
"Ambarella: IAVMEM = 0x07800000[ ],0x18800000\n"
"Ambarella: FBMEM = 0x00000000[ ],0x00000000\n",
&start, &size));
CHECK(start == 0x07800000 && size == 0x18800000);
CHECK(!ambarella_iavmem_from_log("Linux version 3.10.73\n", &start, &size));
/* Cut off inside the size: 0x1880 must not be taken for the answer. */
CHECK(!ambarella_iavmem_from_log(
"Ambarella: IAVMEM = 0x07800000[ ],0x1880", &start, &size));
CHECK(!ambarella_iavmem_from_log(
"Ambarella: IAVMEM = 0x07800000[ ],0x18800000", &start,
&size));

char path[] = "/tmp/ambarella_test.XXXXXX";
int fd = mkstemp(path);
const unsigned char reg[] = {0x0e, 0x40, 0x00, 0x00,
0x11, 0xc0, 0x00, 0x00};
CHECK(fd >= 0);
CHECK(write(fd, reg, sizeof(reg)) == (ssize_t)sizeof(reg));
close(fd);
CHECK(ambarella_iavmem_from_dt(path, &start, &size));
CHECK(start == 0x0e400000 && size == 0x11c00000);
CHECK(truncate(path, 4) == 0);
CHECK(!ambarella_iavmem_from_dt(path, &start, &size));
unlink(path);
CHECK(!ambarella_iavmem_from_dt(path, &start, &size));

if (failures)
return EXIT_FAILURE;
puts("ambarella_test: all cases passed");
Expand Down
Loading