diff --git a/general/package/legacy/ambarella-osdrv-s3l/ambarella-osdrv-s3l.mk b/general/package/legacy/ambarella-osdrv-s3l/ambarella-osdrv-s3l.mk index 79e63f6ca..0c896dc3a 100644 --- a/general/package/legacy/ambarella-osdrv-s3l/ambarella-osdrv-s3l.mk +++ b/general/package/legacy/ambarella-osdrv-s3l/ambarella-osdrv-s3l.mk @@ -4,11 +4,14 @@ # ################################################################################ -AMBARELLA_OSDRV_S3L_VERSION = -AMBARELLA_OSDRV_S3L_SITE = +AMBARELLA_OSDRV_S3L_SITE_METHOD = local +AMBARELLA_OSDRV_S3L_SITE = $(AMBARELLA_OSDRV_S3L_PKGDIR)/src AMBARELLA_OSDRV_S3L_LICENSE = MIT -AMBARELLA_OSDRV_S3L_LICENSE_FILES = LICENSE +# load_ucode puts the DSP microcode in place; the microcode itself is not shipped. +define AMBARELLA_OSDRV_S3L_BUILD_CMDS + $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(@D) load_ucode +endef define AMBARELLA_OSDRV_S3L_INSTALL_TARGET_CMDS $(INSTALL) -m 755 -d $(TARGET_DIR)/etc/sensors @@ -19,6 +22,7 @@ define AMBARELLA_OSDRV_S3L_INSTALL_TARGET_CMDS $(INSTALL) -m 755 -d $(TARGET_DIR)/usr/bin $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(AMBARELLA_OSDRV_S3L_PKGDIR)/files/script/load* + $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(@D)/load_ucode $(INSTALL) -m 755 -d $(TARGET_DIR)/usr/lib/sensors # $(INSTALL) -m 644 -t $(TARGET_DIR)/usr/lib/sensors $(AMBARELLA_OSDRV_S3L_PKGDIR)/files/sensor/*.so diff --git a/general/package/legacy/ambarella-osdrv-s3l/files/script/load_ambarella b/general/package/legacy/ambarella-osdrv-s3l/files/script/load_ambarella index 0a9fed01f..c51e468b7 100755 --- a/general/package/legacy/ambarella-osdrv-s3l/files/script/load_ambarella +++ b/general/package/legacy/ambarella-osdrv-s3l/files/script/load_ambarella @@ -7,11 +7,10 @@ # them and the nodes have to be made here. The major comes from /proc/devices; # the minors are fixed. # -# The microcode (orccode.bin, orcme.bin, default_binary.bin) and the load_ucode -# tool are Ambarella's and are not part of this package, and the stock tool is -# a glibc armhf binary that will not run on this musl image. Without them the -# drivers load and the DSP does not start, so no video. Drop working ones in -# place and this script uses them. +# The microcode (orccode.bin, orcme.bin, default_binary.bin) is Ambarella's and +# is not part of this package; load_ucode, which puts it in place, is built from +# the package's src/. Without the microcode the drivers load and the DSP does +# not start, so no video. Drop it in /lib/firmware and this script loads it. vendor=$(ipcinfo -v) ucode_dir=/lib/firmware diff --git a/general/package/legacy/ambarella-osdrv-s3l/src/Makefile b/general/package/legacy/ambarella-osdrv-s3l/src/Makefile new file mode 100644 index 000000000..8f9155cc9 --- /dev/null +++ b/general/package/legacy/ambarella-osdrv-s3l/src/Makefile @@ -0,0 +1,2 @@ +load_ucode: load_ucode.c + $(CC) $(CFLAGS) -Wall -Wextra -o $@ $< $(LDFLAGS) diff --git a/general/package/legacy/ambarella-osdrv-s3l/src/load_ucode.c b/general/package/legacy/ambarella-osdrv-s3l/src/load_ucode.c new file mode 100644 index 000000000..f69befe8f --- /dev/null +++ b/general/package/legacy/ambarella-osdrv-s3l/src/load_ucode.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: MIT +/* + * Load the Ambarella S3L DSP microcode through /dev/ucode. + * + * load_ucode DIR + * + * A drop-in for the vendor's load_ucode: dsp.ko describes where each file + * goes (UCODE_GET_INFO), the files are copied into the microcode window it + * maps, and the version the DSP will run is printed. The DSP itself is not + * started here; it boots when IAV first enters the idle state. + * + * The window is mapped uncached by dsp.ko, so no flush is needed. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UCODE_DEV "/dev/ucode" +#define UCODE_MAX_ITEMS 4 + +struct ucode_item { + uint32_t addr_offset; + char filename[24]; +}; + +struct ucode_info { + uint32_t map_size; + uint32_t nr_item; + struct ucode_item item[UCODE_MAX_ITEMS]; +}; + +struct ucode_version { + uint16_t year; + uint8_t month; + uint8_t day; + uint32_t edition_num; + uint32_t edition_ver; + uint32_t arch; +}; + +#define UCODE_GET_INFO _IOR('u', 0, struct ucode_info) +#define UCODE_GET_VERSION _IOR('u', 1, struct ucode_version) +#define UCODE_LOADED _IOW('u', 2, uint32_t) + +struct ucode_file { + uint8_t *data; + size_t size; +}; + +static int read_file(const char *dir, const char *name, size_t room, + struct ucode_file *f) +{ + char path[256]; + struct stat st; + size_t done = 0; + int fd, ret = -1; + + snprintf(path, sizeof(path), "%s/%s", dir, name); + if ((fd = open(path, O_RDONLY)) < 0 || fstat(fd, &st) < 0) { + fprintf(stderr, "load_ucode: %s: %s\n", path, strerror(errno)); + goto out; + } + if (st.st_size <= 0 || (size_t)st.st_size > room) { + fprintf(stderr, "load_ucode: %s is %lld bytes, needs 1..%zu\n", + path, (long long)st.st_size, room); + goto out; + } + f->size = st.st_size; + if (!(f->data = malloc(f->size))) { + fprintf(stderr, "load_ucode: %s: %s\n", path, strerror(errno)); + goto out; + } + + while (done < f->size) { + ssize_t n = read(fd, f->data + done, f->size - done); + if (n <= 0) { + fprintf(stderr, "load_ucode: reading %s: %s\n", path, + n ? strerror(errno) : "file shrank"); + goto out; + } + done += n; + } + ret = 0; +out: + if (fd >= 0) + close(fd); + return ret; +} + +int main(int argc, char **argv) +{ + struct ucode_info info; + struct ucode_version ver; + struct ucode_file files[UCODE_MAX_ITEMS] = { 0 }; + uint8_t *map; + int fd, ret = 1; + + if (argc != 2) { + fprintf(stderr, "Usage: %s DIR\n", argv[0]); + return 1; + } + + if ((fd = open(UCODE_DEV, O_RDWR)) < 0) { + fprintf(stderr, "load_ucode: %s: %s\n", UCODE_DEV, + strerror(errno)); + return 1; + } + + memset(&info, 0, sizeof(info)); + if (ioctl(fd, UCODE_GET_INFO, &info) < 0) { + fprintf(stderr, "load_ucode: UCODE_GET_INFO: %s\n", + strerror(errno)); + goto out; + } + if (!info.map_size || !info.nr_item || + info.nr_item > UCODE_MAX_ITEMS) { + fprintf(stderr, "load_ucode: bad layout: %u items in 0x%x\n", + info.nr_item, info.map_size); + goto out; + } + + /* Every file is read in full before the window is touched: a set that + * failed half way would leave old and new microcode mixed in it. */ + for (uint32_t i = 0; i < info.nr_item; i++) { + struct ucode_item *it = &info.item[i]; + /* Each file may run up to the next one's offset, the last one + * to the end of the window. */ + uint32_t end = info.map_size; + + for (uint32_t j = 0; j < info.nr_item; j++) + if (info.item[j].addr_offset > it->addr_offset && + info.item[j].addr_offset < end) + end = info.item[j].addr_offset; + + it->filename[sizeof(it->filename) - 1] = '\0'; + if (it->addr_offset >= info.map_size) { + fprintf(stderr, "load_ucode: %s at 0x%x is outside 0x%x\n", + it->filename, it->addr_offset, info.map_size); + goto out; + } + if (read_file(argv[1], it->filename, end - it->addr_offset, + &files[i])) + goto out; + } + + map = mmap(NULL, info.map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0); + if (map == MAP_FAILED) { + fprintf(stderr, "load_ucode: mmap 0x%x: %s\n", info.map_size, + strerror(errno)); + goto out; + } + + for (uint32_t i = 0; i < info.nr_item; i++) { + struct ucode_item *it = &info.item[i]; + + memcpy(map + it->addr_offset, files[i].data, files[i].size); + printf("%s: %zu bytes at +0x%06x\n", it->filename, + files[i].size, it->addr_offset); + } + + if (ioctl(fd, UCODE_LOADED, 0) < 0) { + fprintf(stderr, "load_ucode: UCODE_LOADED: %s\n", + strerror(errno)); + goto unmap; + } + + if (ioctl(fd, UCODE_GET_VERSION, &ver) == 0) + printf("ucode version %u/%u/%u %u.%u\n", ver.year, ver.month, + ver.day, ver.edition_num, ver.edition_ver); + ret = 0; + +unmap: + munmap(map, info.map_size); +out: + for (int i = 0; i < UCODE_MAX_ITEMS; i++) + free(files[i].data); + close(fd); + return ret; +}