diff --git a/CLAUDE.md b/CLAUDE.md index 850db73..7542cff 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 diff --git a/src/hal/ambarella.c b/src/hal/ambarella.c index c73113d..0bd0622 100644 --- a/src/hal/ambarella.c +++ b/src/hal/ambarella.c @@ -1,5 +1,8 @@ #include "hal/ambarella.h" +#include +#include +#include #include #include "hal/common.h" @@ -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 } diff --git a/src/hal/ambarella.h b/src/hal/ambarella.h index 963c5b8..0b76ad6 100644 --- a/src/hal/ambarella.h +++ b/src/hal/ambarella.h @@ -3,9 +3,18 @@ #include #include +#include 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 */ diff --git a/src/hal/ambarella_test.c b/src/hal/ambarella_test.c index 46ca45e..d8c6518 100644 --- a/src/hal/ambarella_test.c +++ b/src/hal/ambarella_test.c @@ -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 #include #include #include @@ -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");