diff --git a/config-rutx b/config-rutx index f369660a..ea04c4ee 100644 --- a/config-rutx +++ b/config-rutx @@ -1,6 +1,6 @@ CONFIG_TARGET_ipq40xx=y CONFIG_TARGET_ipq40xx_generic=y -CONFIG_TARGET_ipq40xx_generic_DEVICE_rutx10=y +CONFIG_TARGET_ipq40xx_generic_DEVICE_teltonika_rutx=y CONFIG_PACKAGE_kmod-6lowpan=y # CONFIG_KERNEL_CC_OPTIMIZE_FOR_PERFORMANCE is not set CONFIG_KERNEL_CC_OPTIMIZE_FOR_SIZE=y @@ -11,3 +11,8 @@ CONFIG_PACKAGE_kmod-ath10k-ct=y CONFIG_ATH10K-CT_LEDS=y CONFIG_PACKAGE_ath10k-firmware-qca4019-ct=y CONFIG_KERNEL_ARM_MODULE_PLTS=y +CONFIG_TARGET_SQUASHFS_BLOCK_SIZE=64 +CONFIG_KERNEL_SWAP=y +CONFIG_PREINITOPT=y +CONFIG_PACKAGE_kmod-r2ec=y +CONFIG_PACKAGE_uboot-ipq40xx=y diff --git a/root/package/kernel/r2ec/Makefile b/root/package/kernel/r2ec/Makefile new file mode 100644 index 00000000..d3998543 --- /dev/null +++ b/root/package/kernel/r2ec/Makefile @@ -0,0 +1,31 @@ +# +# Copyright (C) 2008-2012 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=r2ec +PKG_RELEASE:=1 +PKG_LICENSE:=GPL-2.0 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/r2ec + SUBMENU:=Other modules + TITLE:=STM32 R2EC (Router to Embedded board Communication) Driver + FILES:=$(PKG_BUILD_DIR)/r2ec.ko + AUTOLOAD:=$(call AutoLoad,30,r2ec,1) + KCONFIG:= +endef + +MAKE_OPTS:= $(KERNEL_MAKE_FLAGS) M="$(PKG_BUILD_DIR)" + +define Build/Compile + $(MAKE) -C "$(LINUX_DIR)" $(MAKE_OPTS) modules +endef + +$(eval $(call KernelPackage,r2ec)) diff --git a/root/package/kernel/r2ec/src/Makefile b/root/package/kernel/r2ec/src/Makefile new file mode 100644 index 00000000..9830ef2b --- /dev/null +++ b/root/package/kernel/r2ec/src/Makefile @@ -0,0 +1 @@ +obj-m += r2ec.o \ No newline at end of file diff --git a/root/package/kernel/r2ec/src/io.h b/root/package/kernel/r2ec/src/io.h new file mode 100644 index 00000000..478d7efd --- /dev/null +++ b/root/package/kernel/r2ec/src/io.h @@ -0,0 +1,60 @@ +#ifndef R2EC_IO_H +#define R2EC_IO_H + +#define NO_OF_GPIOS 37 + +enum proto_version { + PROTO_VERSION_1 = 0x01, + PROTO_VERSION_2 = 0x02 +}; + +enum cmd_type_id { + CMD_GPIO = 0x06, + CMD_PROTO = 0xFC, + CMD_FW = 0xFD, + CMD_BOOT = 0xFE +}; + +enum proto_id { + PROTO_GET_SUPPORTED = 0x03 +}; + +enum boot_id { + BOOT_START_APP = 0x03, + BOOT_STATE = 0xFD, + BOOT_VERSION = 0xFE +}; + +enum state_id { + NO_IMAGE_FOUND = 0x17, + BOOT_STARTED = 0x18, + WATCHDOG_RESET = 0x1B, + APPLICATION_START_FAIL = 0x99, + HARD_FAULT_ERROR = 0x9A, + APP_STARTED = 0xFC, + NO_DATA_AVAILABLE = 0xFF +}; + +enum ack_id { + STATUS_ACK = 0x7D, + STATUS_NACK = 0x7E +}; + +enum gpio_state { + GPIO_STATE_HIGH = 0x1E, + GPIO_STATE_LOW = 0x9F +}; + +enum gpio_mode { + GPIO_VALUE_SET_LOW = 0x00, + GPIO_VALUE_SET_HIGH = 0x01, + GPIO_VALUE_GET = 0x02, + GPIO_MODE_SET_OUTPUT = 0x04, + GPIO_MODE_SET_INPUT = 0x05 +}; + +enum fw_id { + FW_VERSION = 0x01 +}; + +#endif // R2EC_IO_H \ No newline at end of file diff --git a/root/package/kernel/r2ec/src/r2ec.c b/root/package/kernel/r2ec/src/r2ec.c new file mode 100644 index 00000000..6e3cffc0 --- /dev/null +++ b/root/package/kernel/r2ec/src/r2ec.c @@ -0,0 +1,679 @@ +#include +#include +#include +#include +#include + +#include "io.h" + +static const struct i2c_device_id r2ec_id[] = { + { "stm32v1", NO_OF_GPIOS }, + { } +}; +MODULE_DEVICE_TABLE(i2c, r2ec_id); + +static const struct of_device_id r2ec_of_table[] = { + { .compatible = "tlt,stm32v1" }, + { } +}; +MODULE_DEVICE_TABLE(of, r2ec_of_table); + +struct r2ec { + struct gpio_chip chip; + struct irq_chip irqchip; + struct i2c_client *client; + struct mutex i2c_lock; + struct mutex irq_lock; + int ic_ready; +}; + +struct r2ec_platform_data { + unsigned gpio_base; + + int (*setup)(struct i2c_client *client, int gpio, unsigned ngpio, + void *context); + + int (*teardown)(struct i2c_client *client, int gpio, unsigned ngpio, + void *context); + + void *context; +}; + +struct i2c_request { + uint8_t version; + uint16_t length; + uint8_t command; + uint8_t data[1]; + // uint8_t checksum; // invisible +} __attribute__((packed)); + +struct i2c_response { + uint8_t version; + uint8_t length; + uint8_t command; + uint8_t data[7]; + uint8_t checksum; +} __attribute__((packed)); + +static uint8_t calc_crc8(const uint8_t *data, size_t len) +{ + uint8_t crc = 0xFF; + int i, j; + + for (j = 0; j < len; j++) { + crc ^= data[j]; + + for (i = 0; i < 8; i++) { + crc = (crc & 0x80) ? (crc ^ 0xD5) << 1 : crc << 1; + } + } + + return crc; +} + +// generate outcoming mesage checksum and write i2c data +static int stm32_write(struct i2c_client *client, uint8_t ver, uint8_t cmd, + uint8_t *data, size_t len) +{ + struct i2c_request *req; + uint8_t tmp[sizeof(struct i2c_request) + len]; + const int tmp_len = sizeof(tmp); + int err; + + if (!client) { + printk(KERN_ERR "R2EC I2C client is not ready!\n"); + return -ENXIO; + } + + req = (struct i2c_request *)tmp; + req->version = ver; + req->length = 2 + len; // 2 + data_len + req->command = cmd; + + memcpy(req->data, data, len); + + req->data[len] = calc_crc8(tmp, tmp_len - 1); + + if ((err = i2c_master_send(client, tmp, tmp_len)) < 0) { + return err; + } + + return 0; +} + +// attempt to read i2c data +static int stm32_read(struct i2c_client *client, uint8_t *data, size_t len) +{ + char buffer[64] = { 0 }; + uint8_t checksum; + int err; + unsigned i; + + if (!client) { + printk(KERN_ERR "R2EC I2C client is not ready!\n"); + return -ENXIO; + } + + if ((err = i2c_master_recv(client, data, len)) < 0) { + return err; + } + + if (len == 1) { + return 0; + } + + // 0xFF - no data available + if (*(data + 3) == 0xFF) { + return -ENODATA; + } + + // generate checksum and verify + checksum = calc_crc8(data, len - 1); + + if (checksum != *(data + len - 1)) { + for (i = 0; i < len; i++) { + snprintf(buffer + strlen(buffer), sizeof(buffer), + "%02X ", *(data + i)); + } + + dev_err(&client->dev, "Checksum of incoming message " + "does not match!\n" + "Received: %s\n", buffer); + + return -EBADE; + } + + return 0; +} + +// attempt to retrieve supported protocol version, then retrieve device state +// and boot into application state +// this is done without interrupt, so there should be delay after writing +// request and before reading response for protocol versions up until v2 +static int stm32_prepare(struct r2ec *gpio, struct i2c_client *client) +{ + struct i2c_response rsp; + uint8_t data[1], recv[1], proto; + int ret; + + memset(&rsp, 0, sizeof(rsp)); + + data[0] = PROTO_GET_SUPPORTED; + + if ((ret = stm32_write(client, 1, CMD_PROTO, data, 1))) { + dev_err(&client->dev, + "stm32_prepare: proto version write failed (%d)\n", + ret); + return ret; + } + + // due compatibility reasons delay is needed between write/read + // operations + msleep(10); + + if ((ret = stm32_read(client, (uint8_t *)&rsp, sizeof(rsp)))) { + dev_err(&client->dev, + "stm32_prepare: proto version read failed (%d)\n", ret); + return ret; + } + + proto = rsp.data[1]; + + printk("STM32 supported protocol: %d\n", proto); + + data[0] = BOOT_STATE; + + if ((ret = stm32_write(client, proto, CMD_BOOT, data, 1))) { + dev_err(&client->dev, + "stm32_prepare: boot state write failed (%d)\n", ret); + return ret; + } + + if ((ret = stm32_read(client, recv, 1))) { + dev_err(&client->dev, + "stm32_prepare: boot state read failed (%d)\n", ret); + return ret; + } + + // device might be not ready aka in bootloader state + // we might need to ignore gpio_write status value + gpio->ic_ready = 0; + + // handle the following possible states reported either from + // bootloader or system: + switch (recv[0]) { + case NO_IMAGE_FOUND: + case APP_STARTED: + // device is ready, no need to ignore gpio_write status value + // note: on no_image_found, user-space flasher will reflash + // firmware and device will be rebooted + gpio->ic_ready = 1; + return 0; + case BOOT_STARTED: + case WATCHDOG_RESET: + case APPLICATION_START_FAIL: + case HARD_FAULT_ERROR: + case NO_DATA_AVAILABLE: + break; + default: + dev_err(&client->dev, "Device did not responded with correct " + "state! Actual response was 0x%02X. " + "Unable to get device state!\n", recv[0]); + break; + } + + data[0] = BOOT_START_APP; + + if ((ret = stm32_write(client, proto, CMD_BOOT, data, 1))) { + dev_err(&client->dev, + "stm32_prepare: boot start write failed (%d)\n", ret); + return ret; + } + + if ((ret = stm32_read(client, recv, 1))) { + dev_err(&client->dev, + "stm32_prepare: boot start read failed (%d)\n", ret); + return ret; + } + + if (recv[0] != STATUS_ACK && recv[0] != NO_DATA_AVAILABLE) { + dev_err(&client->dev, "Device did not responded with ACK. " + "Actual response was 0x%02X. " + "Unable to set device state!\n", recv[0]); + return -EIO; + } + + return 0; +} + +static int stm32_gpio_write(struct r2ec *gpio, int pin, int val) +{ + struct i2c_request *req; + size_t len = 2; + uint8_t tmp[sizeof(struct i2c_request) + len]; + //int err; + + if (!gpio->client) { + printk(KERN_ERR "R2EC I2C client is not ready!\n"); + return -ENXIO; + } + + req = (struct i2c_request *)tmp; + req->version = PROTO_VERSION_2; + req->length = 2 + len; // command + crc + data + req->command = CMD_GPIO; + req->data[0] = pin; + req->data[1] = val; + + i2c_master_send(gpio->client, tmp, sizeof(tmp)); +// if ((err = i2c_master_send(gpio->client, tmp, sizeof(tmp))) < 0) { +// if (err != -ENXIO) { +// return err; +// } + + // we need to ignore errors while device is not ready + // otherwise none of GPIOs/LEDs will be probed by the kernel +// if (!gpio->ic_ready) { +// err = 0; +// } +// +// return err; +// } + + return 0; +} + +static int stm32_gpio_read(struct r2ec *gpio, int pin, int val) +{ + struct i2c_request *req; + size_t len = 2; + uint8_t tmp[sizeof(struct i2c_request) + len]; + uint8_t recv[1]; + int err; + + if (!gpio->client) { + printk(KERN_ERR "R2EC I2C client is not ready!\n"); + return -ENXIO; + } + + req = (struct i2c_request *)tmp; + req->version = PROTO_VERSION_2; + req->length = 2 + len; // command + crc + data + req->command = CMD_GPIO; + req->data[0] = pin; + req->data[1] = val; + + if ((err = i2c_master_send(gpio->client, tmp, sizeof(tmp))) < 0) { + return err; + } + + if ((err = i2c_master_recv(gpio->client, recv, sizeof(recv))) < 0) { + return err; + } + + switch (recv[0]) { + case GPIO_STATE_HIGH: + return 1; + case GPIO_STATE_LOW: + return 0; + } + + return -EIO; +} + +static int r2ec_get(struct gpio_chip *chip, unsigned offset) +{ + struct r2ec *gpio = gpiochip_get_data(chip); + int value; + + mutex_lock(&gpio->i2c_lock); + value = stm32_gpio_read(gpio, offset, GPIO_VALUE_GET); + mutex_unlock(&gpio->i2c_lock); + + return value; +} + +static void r2ec_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct r2ec *gpio = gpiochip_get_data(chip); + int val = value ? GPIO_VALUE_SET_HIGH : GPIO_VALUE_SET_LOW; + + mutex_lock(&gpio->i2c_lock); + stm32_gpio_write(gpio, offset, val); + mutex_unlock(&gpio->i2c_lock); +} + +static int r2ec_input(struct gpio_chip *chip, unsigned offset) +{ + struct r2ec *gpio = gpiochip_get_data(chip); + int status; + + mutex_lock(&gpio->i2c_lock); + status = stm32_gpio_write(gpio, offset, GPIO_MODE_SET_INPUT); + mutex_unlock(&gpio->i2c_lock); + + return status; +} + +static int r2ec_output(struct gpio_chip *chip, unsigned offset, int value) +{ + struct r2ec *gpio = gpiochip_get_data(chip); + int status; + + mutex_lock(&gpio->i2c_lock); + status = stm32_gpio_write(gpio, offset, GPIO_MODE_SET_OUTPUT); + mutex_unlock(&gpio->i2c_lock); + + r2ec_set(chip, offset, value); + + return status; +} + +static void noop(struct irq_data *data) { } + +static int noop_wake(struct irq_data *data, unsigned on) +{ + return 0; +} + +static irqreturn_t r2ec_irq(int irq, void *data) +{ + struct r2ec *gpio = data; + unsigned i; + + for (i = 0; i < gpio->chip.ngpio; i++) { + handle_nested_irq(irq_find_mapping(gpio->chip.irq.domain, i)); + } + + return IRQ_HANDLED; +} + +static void r2ec_irq_bus_lock(struct irq_data *data) +{ + struct r2ec *gpio = irq_data_get_irq_chip_data(data); + mutex_lock(&gpio->irq_lock); +} + +static void r2ec_irq_bus_sync_unlock(struct irq_data *data) +{ + struct r2ec *gpio = irq_data_get_irq_chip_data(data); + mutex_unlock(&gpio->irq_lock); +} + +static int chip_label_match(struct gpio_chip *chip, void *data) +{ + return !strcmp(chip->label, data); +} + +static int get_stm32_version(struct device *dev, uint8_t type, char *buffer) +{ + struct gpio_chip *chip; + struct r2ec *gpio; + uint8_t recv[sizeof(struct i2c_response)]; + uint8_t data[1]; + + struct pt_fw_get_ver { + unsigned char command_ex; + unsigned char major; + unsigned char middle; + unsigned char minor; + unsigned char rev; + } __attribute__((packed)) *res; + + chip = gpiochip_find("stm32v1", chip_label_match); + if (!chip) { + printk(KERN_ERR "Unable to find R2EC gpio chip!\n"); + return -ENXIO; + } + + gpio = gpiochip_get_data(chip); + + if (!gpio->client) { + printk(KERN_ERR "R2EC I2C client is not ready!\n"); + return -ENXIO; + } + + data[0] = (type == CMD_FW) ? FW_VERSION : BOOT_VERSION; + + mutex_lock(&gpio->i2c_lock); + + if (stm32_write(gpio->client, PROTO_VERSION_2, type, data, 1)) { + printk(KERN_ERR "Unable transmit R2EC data!\n"); + goto done; + } + + if (stm32_read(gpio->client, recv, sizeof(recv))) { + printk(KERN_ERR "Unable receive R2EC data!\n"); + goto done; + } + + // device is ready now, running in application-mode + // this is called by autoflasher script first time + if (!gpio->ic_ready) { + gpio->ic_ready = 1; + } + + res = (struct pt_fw_get_ver *)(&recv[3]); + + sprintf(buffer, "%02d.%02d.%02d rev. %02d\n", + res->major, res->middle, res->minor, res->rev); + +done: + mutex_unlock(&gpio->i2c_lock); + return strlen(buffer); +} + +static ssize_t app_version_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + return get_stm32_version(dev, CMD_FW, buffer); +} + +static ssize_t boot_version_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + return get_stm32_version(dev, CMD_BOOT, buffer); +} + +static ssize_t reset_store(struct device *dev, struct device_attribute *attr, + const char *buff, size_t count) +{ + struct gpio_chip *chip; + struct r2ec *gpio; + uint8_t data[1]; + + chip = gpiochip_find("stm32v1", chip_label_match); + if (!chip) { + printk(KERN_ERR "Unable to find R2EC gpio chip!\n"); + return -ENXIO; + } + + gpio = gpiochip_get_data(chip); + + if (!gpio->client) { + printk(KERN_ERR "R2EC I2C client is not ready!\n"); + return -ENXIO; + } + + data[0] = BOOT_START_APP; + + mutex_lock(&gpio->i2c_lock); + if (stm32_write(gpio->client, PROTO_VERSION_2, CMD_BOOT, data, 1)) { + printk(KERN_ERR "Unable transmit R2EC data!\n"); + goto done; + } + +done: + mutex_unlock(&gpio->i2c_lock); + return 1; +} + +static struct device_attribute g_r2ec_kobj_attr[] = { + __ATTR_RO(app_version), + __ATTR_RO(boot_version), + __ATTR_WO(reset) +}; + +static struct attribute *g_r2ec_attrs[] = { + &g_r2ec_kobj_attr[0].attr, + &g_r2ec_kobj_attr[1].attr, + &g_r2ec_kobj_attr[2].attr, + NULL, +}; + +static struct attribute_group g_r2ec_attr_group = { .attrs = g_r2ec_attrs }; +static struct kobject *g_r2ec_kobj; + +static int r2ec_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct r2ec_platform_data *pdata = dev_get_platdata(&client->dev); + struct r2ec *gpio; + int status, i; + + gpio = devm_kzalloc(&client->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) { + return -ENOMEM; + } + + for (i = 0; i < 10; i++) { + if (!(status = stm32_prepare(gpio, client))) { + break; + } + + dev_err(&client->dev, + "Unable to initialize device, retrying...\n"); + + // give some time for next interation... + msleep(500); + } + + if (status) { + dev_err(&client->dev, "Unable to initialize device!\n"); + devm_kfree(&client->dev, gpio); + return status; + } + + mutex_init(&gpio->irq_lock); + mutex_init(&gpio->i2c_lock); + + lockdep_set_subclass(&gpio->i2c_lock, + i2c_adapter_depth(client->adapter)); + + gpio->chip.base = pdata ? pdata->gpio_base : -1; + gpio->chip.can_sleep = true; + gpio->chip.parent = &client->dev; + gpio->chip.owner = THIS_MODULE; + gpio->chip.get = r2ec_get; + gpio->chip.set = r2ec_set; + gpio->chip.direction_input = r2ec_input; + gpio->chip.direction_output = r2ec_output; + gpio->chip.ngpio = id->driver_data; + gpio->chip.label = client->name; + gpio->client = client; + + i2c_set_clientdata(client, gpio); + + status = devm_gpiochip_add_data(&client->dev, &gpio->chip, gpio); + if (status < 0) { + goto fail; + } + + if (client->irq) { + gpio->irqchip.name = "r2ec"; + gpio->irqchip.irq_enable = noop, + gpio->irqchip.irq_disable = noop, + gpio->irqchip.irq_ack = noop, + gpio->irqchip.irq_mask = noop, + gpio->irqchip.irq_unmask = noop, + gpio->irqchip.irq_set_wake = noop_wake, + gpio->irqchip.irq_bus_lock = r2ec_irq_bus_lock; + gpio->irqchip.irq_bus_sync_unlock = r2ec_irq_bus_sync_unlock; + + status = gpiochip_irqchip_add_nested(&gpio->chip, + &gpio->irqchip, + 0, handle_level_irq, + IRQ_TYPE_NONE); + if (status) { + dev_err(&client->dev, "cannot add irqchip\n"); + goto fail; + } + + status = devm_request_threaded_irq(&client->dev, client->irq, + NULL, r2ec_irq, + IRQF_ONESHOT | + IRQF_TRIGGER_FALLING | + IRQF_SHARED, + dev_name(&client->dev), + gpio); + if (status) { + goto fail; + } + } + + if (pdata && pdata->setup) { + status = pdata->setup(client, gpio->chip.base, gpio->chip.ngpio, + pdata->context); + + if (status < 0) { + dev_warn(&client->dev, "setup --> %d\n", status); + } + } + + g_r2ec_kobj = kobject_create_and_add("r2ec", NULL); + if (!g_r2ec_kobj) { + printk(KERN_ERR "Unable to create `r2ec` kobject!\n"); + goto fail; + } + + if (sysfs_create_group(g_r2ec_kobj, &g_r2ec_attr_group)) { + kobject_put(g_r2ec_kobj); + printk(KERN_ERR "Unable to create `r2ec` sysfs group!\n"); + goto fail; + } + + dev_info(&client->dev, "probed\n"); + return 0; + +fail: + devm_kfree(&client->dev, gpio); + dev_dbg(&client->dev, "probe error %d for %s\n", status, client->name); + return status; +} + +static int r2ec_remove(struct i2c_client *client) +{ + struct r2ec_platform_data *pdata = dev_get_platdata(&client->dev); + struct r2ec *gpio = i2c_get_clientdata(client); + int status = 0; + + if (!(pdata && pdata->teardown)) { + return status; + } + + status = pdata->teardown(client, gpio->chip.base, gpio->chip.ngpio, + pdata->context); + + if (status < 0) { + dev_err(&client->dev, "%s --> %d\n", "teardown", status); + } + + kobject_put(g_r2ec_kobj); + + return status; +} + +static struct i2c_driver r2ec_driver = { + .driver = { + .name = "r2ec", + .of_match_table = of_match_ptr(r2ec_of_table), + }, + .probe = r2ec_probe, + .remove = r2ec_remove, + .id_table = r2ec_id, +}; + +module_i2c_driver(r2ec_driver); + +MODULE_AUTHOR("Jokubas Maciulaitis "); +MODULE_DESCRIPTION("STM32F0 (R2EC) I2C GPIO Expander driver"); +MODULE_LICENSE("GPL v2"); diff --git a/root/scripts/mkits-rutx.sh b/root/scripts/mkits-rutx.sh new file mode 100755 index 00000000..7ee39edb --- /dev/null +++ b/root/scripts/mkits-rutx.sh @@ -0,0 +1,177 @@ +#!/bin/sh +# +# Licensed under the terms of the GNU GPL License version 2 or later. +# +# Author: Peter Tyser +# +# U-Boot firmware supports the booting of images in the Flattened Image +# Tree (FIT) format. The FIT format uses a device tree structure to +# describe a kernel image, device tree blob, ramdisk, etc. This script +# creates an Image Tree Source (.its file) which can be passed to the +# 'mkimage' utility to generate an Image Tree Blob (.itb file). The .itb +# file can then be booted by U-Boot (or other bootloaders which support +# FIT images). See doc/uImage.FIT/howto.txt in U-Boot source code for +# additional information on FIT images. +# + +usage() { + printf "Usage: %s -A arch -C comp -a addr -e entry" "$(basename "$0")" + printf " -v version -k kernel [-D name -n address -d dtb] -o its_file" + + printf "\n\t-A ==> set architecture to 'arch'" + printf "\n\t-C ==> set compression type 'comp'" + printf "\n\t-c ==> set config name 'config'" + printf "\n\t-a ==> set load address to 'addr' (hex)" + printf "\n\t-e ==> set entry point to 'entry' (hex)" + printf "\n\t-v ==> set kernel version to 'version'" + printf "\n\t-k ==> include kernel image 'kernel'" + printf "\n\t-D ==> human friendly Device Tree Blob 'name'" + printf "\n\t-n ==> fdt unit-address 'address'" + printf "\n\t-d ==> include Device Tree Blob 'dtb'" + printf "\n\t-o ==> create output file 'its_file'\n" + exit 1 +} + +FDTNUM=1 + +while getopts ":A:a:c:C:D:d:e:k:n:o:v:" OPTION +do + case $OPTION in + A ) ARCH=$OPTARG;; + a ) LOAD_ADDR=$OPTARG;; + c ) CONFIG=$OPTARG;; + C ) COMPRESS=$OPTARG;; + D ) DEVICE=$OPTARG;; + d ) DTB=$OPTARG;; + e ) ENTRY_ADDR=$OPTARG;; + k ) KERNEL=$OPTARG;; + n ) FDTNUM=$OPTARG;; + o ) OUTPUT=$OPTARG;; + v ) VERSION=$OPTARG;; + * ) echo "Invalid option passed to '$0' (options:$*)" + usage;; + esac +done + +# Make sure user entered all required parameters +if [ -z "${ARCH}" ] || [ -z "${COMPRESS}" ] || [ -z "${LOAD_ADDR}" ] || \ + [ -z "${ENTRY_ADDR}" ] || [ -z "${VERSION}" ] || [ -z "${KERNEL}" ] || \ + [ -z "${OUTPUT}" ] || [ -z "${CONFIG}" ]; then + usage +fi + +ARCH_UPPER=$(echo "$ARCH" | tr '[:lower:]' '[:upper:]') + +# Conditionally create fdt information +if [ -n "${DTB}" ]; then + if [ -f "$DTB" ]; then + FDT_NODE=" + fdt@$FDTNUM { + description = \"${ARCH_UPPER} OpenWrt ${DEVICE} device tree blob\"; + data = /incbin/(\"${DTB}\"); + type = \"flat_dt\"; + arch = \"${ARCH}\"; + compression = \"none\"; + hash@1 { + algo = \"crc32\"; + }; + hash@2 { + algo = \"sha1\"; + }; + }; +" + + CONFIG_NODE=" + ${CONFIG} { + description = \"OpenWrt\"; + kernel = \"kernel@1\"; + fdt = \"fdt@$FDTNUM\"; + }; +" + else + FDTNUM=0 + DEFAULT_CONFIG=$FDTNUM + + # for f in $(eval echo $DTB); do <= doesn't work on dash, here comes a workaround: + DTB_DIR="${DTB%%{*}" + DTB="${DTB##*{}" + DTB_CSV="${DTB##*{}" + DTB_CSV="${DTB%\}*}" + IFS=, + for f in $DTB_CSV; do + FDTNUM=$((FDTNUM+1)) + + ff="${DTB_DIR}/${f}" + [ -f "${ff}" ] || { + echo "ERROR: no DTBs found" >&2 + rm -f "${OUTPUT}" + exit 1 + } + + FDT_NODE="${FDT_NODE} + + fdt@$FDTNUM { + description = \"${f}\"; + data = /incbin/(\"${ff}\"); + type = \"flat_dt\"; + arch = \"${ARCH}\"; + compression = \"none\"; + hash@1 { + algo = \"crc32\"; + }; + hash@2 { + algo = \"sha1\"; + }; + }; +" + # extract XYZ from image-qcom-ipq4018-rutx-XYZ.dtb + f=${f##*-} + f=${f%.*} + + CONFIG_NODE="${CONFIG_NODE} + + conf_mdtb@${FDTNUM} { + description = \"${f}\"; + kernel = \"kernel@1\"; + fdt = \"fdt@$FDTNUM\"; + }; +" + done + fi +fi + +# Create a default, fully populated DTS file +DATA="/dts-v1/; + +/ { + description = \"${ARCH_UPPER} OpenWrt FIT (Flattened Image Tree)\"; + #address-cells = <1>; + + images { + kernel@1 { + description = \"${ARCH_UPPER} OpenWrt Linux-${VERSION}\"; + data = /incbin/(\"${KERNEL}\"); + type = \"kernel\"; + arch = \"${ARCH}\"; + os = \"linux\"; + compression = \"${COMPRESS}\"; + load = <${LOAD_ADDR}>; + entry = <${ENTRY_ADDR}>; + hash@1 { + algo = \"crc32\"; + }; + hash@2 { + algo = \"sha1\"; + }; + }; +${FDT_NODE} + }; + + configurations { + default = \"${DEFAULT_CONFIG}\"; +${CONFIG_NODE} + }; +};" + +# Write .its file to disk +echo "$DATA" > "${OUTPUT}" diff --git a/root/scripts/mkits.sh b/root/scripts/mkits.sh deleted file mode 100755 index 5a585dc6..00000000 --- a/root/scripts/mkits.sh +++ /dev/null @@ -1,301 +0,0 @@ -#!/bin/sh -# -# Licensed under the terms of the GNU GPL License version 2 or later. -# -# Author: Peter Tyser -# -# U-Boot firmware supports the booting of images in the Flattened Image -# Tree (FIT) format. The FIT format uses a device tree structure to -# describe a kernel image, device tree blob, ramdisk, etc. This script -# creates an Image Tree Source (.its file) which can be passed to the -# 'mkimage' utility to generate an Image Tree Blob (.itb file). The .itb -# file can then be booted by U-Boot (or other bootloaders which support -# FIT images). See doc/uImage.FIT/howto.txt in U-Boot source code for -# additional information on FIT images. -# - -usage() { - printf "Usage: %s -A arch -C comp -a addr -e entry" "$(basename "$0")" - printf " -v version -k kernel [-D name -n address -d dtb] -o its_file" - - printf "\n\t-A ==> set architecture to 'arch'" - printf "\n\t-C ==> set compression type 'comp'" - printf "\n\t-c ==> set config name 'config'" - printf "\n\t-a ==> set load address to 'addr' (hex)" - printf "\n\t-e ==> set entry point to 'entry' (hex)" - printf "\n\t-f ==> set device tree compatible string" - printf "\n\t-i ==> include initrd Blob 'initrd'" - printf "\n\t-v ==> set kernel version to 'version'" - printf "\n\t-k ==> include kernel image 'kernel'" - printf "\n\t-D ==> human friendly Device Tree Blob 'name'" - printf "\n\t-n ==> fdt unit-address 'address'" - printf "\n\t-d ==> include Device Tree Blob 'dtb'" - printf "\n\t-r ==> include RootFS blob 'rootfs'" - printf "\n\t-H ==> specify hash algo instead of SHA1" - printf "\n\t-l ==> legacy mode character (@ etc otherwise -)" - printf "\n\t-o ==> create output file 'its_file'" - printf "\n\t-O ==> create config with dt overlay 'name:dtb'" - printf "\n\t\t(can be specified more than once)\n" - exit 1 -} - -REFERENCE_CHAR='-' -FDTNUM=1 -ROOTFSNUM=1 -INITRDNUM=1 -HASH=sha1 -LOADABLES= -DTOVERLAY= -DTADDR= - -while getopts ":A:a:c:C:D:d:e:f:i:k:l:n:o:O:v:r:H:" OPTION -do - case $OPTION in - A ) ARCH=$OPTARG;; - a ) LOAD_ADDR=$OPTARG;; - c ) CONFIG=$OPTARG;; - C ) COMPRESS=$OPTARG;; - D ) DEVICE=$OPTARG;; - d ) DTB=$OPTARG;; - e ) ENTRY_ADDR=$OPTARG;; - f ) COMPATIBLE=$OPTARG;; - i ) INITRD=$OPTARG;; - k ) KERNEL=$OPTARG;; - l ) REFERENCE_CHAR=$OPTARG;; - n ) FDTNUM=$OPTARG;; - o ) OUTPUT=$OPTARG;; - O ) DTOVERLAY="$DTOVERLAY ${OPTARG}";; - r ) ROOTFS=$OPTARG;; - H ) HASH=$OPTARG;; - v ) VERSION=$OPTARG;; - * ) echo "Invalid option passed to '$0' (options:$*)" - usage;; - esac -done - -# Make sure user entered all required parameters -if [ -z "${ARCH}" ] || [ -z "${COMPRESS}" ] || [ -z "${LOAD_ADDR}" ] || \ - [ -z "${ENTRY_ADDR}" ] || [ -z "${VERSION}" ] || [ -z "${KERNEL}" ] || \ - [ -z "${OUTPUT}" ] || [ -z "${CONFIG}" ]; then - usage -fi - -ARCH_UPPER=$(echo "$ARCH" | tr '[:lower:]' '[:upper:]') - -if [ -n "${COMPATIBLE}" ]; then - COMPATIBLE_PROP="compatible = \"${COMPATIBLE}\";" -fi - -# Conditionally create fdt information -if [ -n "${DTB}" ]; then - if [ -f "$DTB" ]; then - [ "$DTOVERLAY" ] && { - dtbsize=$(wc -c "$DTB" | cut -d' ' -f1) - DTADDR=$(printf "0x%08x" $(($LOAD_ADDR - $dtbsize)) ) - } - FDT_NODE=" - fdt${REFERENCE_CHAR}$FDTNUM { - description = \"${ARCH_UPPER} OpenWrt ${DEVICE} device tree blob\"; - ${COMPATIBLE_PROP} - data = /incbin/(\"${DTB}\"); - type = \"flat_dt\"; - ${DTADDR:+load = <${DTADDR}>;} - arch = \"${ARCH}\"; - compression = \"none\"; - hash@1 { - algo = \"crc32\"; - }; - hash@2 { - algo = \"${HASH}\"; - }; - }; -" - FDT_PROP="fdt = \"fdt${REFERENCE_CHAR}$FDTNUM\";" - CONFIG_NODE=" - ${CONFIG} { - description = \"OpenWrt ${DEVICE}\"; - kernel = \"kernel${REFERENCE_CHAR}1\"; - ${FDT_PROP} - ${LOADABLES:+loadables = ${LOADABLES};} - ${COMPATIBLE_PROP} - ${INITRD_PROP} - }; -" - else - FDTNUM=0 - DEFAULT_CONFIG=$FDTNUM - - # for f in $(eval echo $DTB); do <= doesn't work on dash, here comes a workaround: - DTB_DIR="${DTB%%{*}" - DTB="${DTB##*{}" - DTB_CSV="${DTB##*{}" - DTB_CSV="${DTB%\}*}" - IFS=, - for f in $DTB_CSV; do - FDTNUM=$((FDTNUM+1)) - - ff="${DTB_DIR}/${f}" - [ -f "${ff}" ] || { - echo "ERROR: no DTBs found" >&2 - rm -f "${OUTPUT}" - exit 1 - } - [ "$DTOVERLAY" ] && { - dtbsize=$(wc -c "$ff" | cut -d' ' -f1) - DTADDR=$(printf "0x%08x" $(($LOAD_ADDR - $dtbsize)) ) - } - FDT_NODE="${FDT_NODE} - fdt${REFERENCE_CHAR}$FDTNUM { - description = \"${f}\"; - ${COMPATIBLE_PROP} - data = /incbin/(\"${ff}\"); - type = \"flat_dt\"; - ${DTADDR:+load = <${DTADDR}>;} - arch = \"${ARCH}\"; - compression = \"none\"; - hash@1 { - algo = \"crc32\"; - }; - hash@2 { - algo = \"${HASH}\"; - }; - }; -" - FDT_PROP="fdt = \"fdt${REFERENCE_CHAR}$FDTNUM\";" - CONFIG_NODE="${CONFIG_NODE} - - conf_mdtb@${FDTNUM} { - description = \"${f}\"; - kernel = \"kernel${REFERENCE_CHAR}1\"; - ${FDT_PROP} - ${LOADABLES:+loadables = ${LOADABLES};} - ${COMPATIBLE_PROP} - ${INITRD_PROP} - }; -" - done - fi -fi - -if [ -n "${INITRD}" ]; then - INITRD_NODE=" - initrd${REFERENCE_CHAR}$INITRDNUM { - description = \"${ARCH_UPPER} OpenWrt ${DEVICE} initrd\"; - ${COMPATIBLE_PROP} - data = /incbin/(\"${INITRD}\"); - type = \"ramdisk\"; - arch = \"${ARCH}\"; - os = \"linux\"; - hash@1 { - algo = \"crc32\"; - }; - hash@2 { - algo = \"${HASH}\"; - }; - }; -" - INITRD_PROP="ramdisk=\"initrd${REFERENCE_CHAR}${INITRDNUM}\";" -fi - - -if [ -n "${ROOTFS}" ]; then - dd if="${ROOTFS}" of="${ROOTFS}.pagesync" bs=4096 conv=sync - ROOTFS_NODE=" - rootfs-$ROOTFSNUM { - description = \"${ARCH_UPPER} OpenWrt ${DEVICE} rootfs\"; - ${COMPATIBLE_PROP} - data = /incbin/(\"${ROOTFS}.pagesync\"); - type = \"filesystem\"; - arch = \"${ARCH}\"; - compression = \"none\"; - hash@1 { - algo = \"crc32\"; - }; - hash@2 { - algo = \"${HASH}\"; - }; - }; -" - LOADABLES="${LOADABLES:+$LOADABLES, }\"rootfs${REFERENCE_CHAR}${ROOTFSNUM}\"" -fi - -# add DT overlay blobs -FDTOVERLAY_NODE="" -OVCONFIGS="" -[ "$DTOVERLAY" ] && for overlay in $DTOVERLAY ; do - overlay_blob=${overlay##*:} - ovname=${overlay%%:*} - ovnode="fdt-$ovname" - ovsize=$(wc -c "$overlay_blob" | cut -d' ' -f1) - echo "$ovname ($overlay_blob) : $ovsize" >&2 - DTADDR=$(printf "0x%08x" $(($DTADDR - $ovsize))) - FDTOVERLAY_NODE="$FDTOVERLAY_NODE - - $ovnode { - description = \"${ARCH_UPPER} OpenWrt ${DEVICE} device tree overlay $ovname\"; - ${COMPATIBLE_PROP} - data = /incbin/(\"${overlay_blob}\"); - type = \"flat_dt\"; - arch = \"${ARCH}\"; - load = <${DTADDR}>; - compression = \"none\"; - hash@1 { - algo = \"crc32\"; - }; - hash@2 { - algo = \"${HASH}\"; - }; - }; -" - OVCONFIGS="$OVCONFIGS - - config-$ovname { - description = \"OpenWrt ${DEVICE} with $ovname\"; - kernel = \"kernel${REFERENCE_CHAR}1\"; - fdt = \"fdt${REFERENCE_CHAR}$FDTNUM\", \"$ovnode\"; - ${LOADABLES:+loadables = ${LOADABLES};} - ${COMPATIBLE_PROP} - ${INITRD_PROP} - }; - " -done - -# Create a default, fully populated DTS file -DATA="/dts-v1/; - -/ { - description = \"${ARCH_UPPER} OpenWrt FIT (Flattened Image Tree)\"; - #address-cells = <1>; - - images { - kernel${REFERENCE_CHAR}1 { - description = \"${ARCH_UPPER} OpenWrt Linux-${VERSION}\"; - data = /incbin/(\"${KERNEL}\"); - type = \"kernel\"; - arch = \"${ARCH}\"; - os = \"linux\"; - compression = \"${COMPRESS}\"; - load = <${LOAD_ADDR}>; - entry = <${ENTRY_ADDR}>; - hash@1 { - algo = \"crc32\"; - }; - hash@2 { - algo = \"$HASH\"; - }; - }; -${INITRD_NODE} -${FDT_NODE} -${FDTOVERLAY_NODE} -${ROOTFS_NODE} - }; - - configurations { - default = \"${CONFIG}\"; - ${CONFIG_NODE} - ${OVCONFIGS} - }; -};" - -# Write .its file to disk -echo "$DATA" > "${OUTPUT}" diff --git a/root/target/linux/generic/backport-5.4/070-v5.5-MIPS-BPF-Restore-MIPS32-cBPF-JIT.patch b/root/target/linux/generic/backport-5.4/070-v5.5-MIPS-BPF-Restore-MIPS32-cBPF-JIT.patch new file mode 100644 index 00000000..027d0253 --- /dev/null +++ b/root/target/linux/generic/backport-5.4/070-v5.5-MIPS-BPF-Restore-MIPS32-cBPF-JIT.patch @@ -0,0 +1,1650 @@ +From 36366e367ee93ced84fddb8fae6675e12985f5a4 Mon Sep 17 00:00:00 2001 +From: Paul Burton +Date: Thu, 5 Dec 2019 10:23:18 -0800 +Subject: [PATCH] MIPS: BPF: Restore MIPS32 cBPF JIT + +Commit 716850ab104d ("MIPS: eBPF: Initial eBPF support for MIPS32 +architecture.") enabled our eBPF JIT for MIPS32 kernels, whereas it has +previously only been availailable for MIPS64. It was my understanding at +the time that the BPF test suite was passing & JITing a comparable +number of tests to our cBPF JIT [1], but it turns out that was not the +case. + +The eBPF JIT has a number of problems on MIPS32: + +- Most notably various code paths still result in emission of MIPS64 + instructions which will cause reserved instruction exceptions & kernel + panics when run on MIPS32 CPUs. + +- The eBPF JIT doesn't account for differences between the O32 ABI used + by MIPS32 kernels versus the N64 ABI used by MIPS64 kernels. Notably + arguments beyond the first 4 are passed on the stack in O32, and this + is entirely unhandled when JITing a BPF_CALL instruction. Stack space + must be reserved for arguments even if they all fit in registers, and + the callee is free to assume that stack space has been reserved for + its use - with the eBPF JIT this is not the case, so calling any + function can result in clobbering values on the stack & unpredictable + behaviour. Function arguments in eBPF are always 64-bit values which + is also entirely unhandled - the JIT still uses a single (32-bit) + register per argument. As a result all function arguments are always + passed incorrectly when JITing a BPF_CALL instruction, leading to + kernel crashes or strange behavior. + +- The JIT attempts to bail our on use of ALU64 instructions or 64-bit + memory access instructions. The code doing this at the start of + build_one_insn() incorrectly checks whether BPF_OP() equals BPF_DW, + when it should really be checking BPF_SIZE() & only doing so when + BPF_CLASS() is one of BPF_{LD,LDX,ST,STX}. This results in false + positives that cause more bailouts than intended, and that in turns + hides some of the problems described above. + +- The kernel's cBPF->eBPF translation makes heavy use of 64-bit eBPF + instructions that the MIPS32 eBPF JIT bails out on, leading to most + cBPF programs not being JITed at all. + +Until these problems are resolved, revert the removal of the cBPF JIT +performed by commit 716850ab104d ("MIPS: eBPF: Initial eBPF support for +MIPS32 architecture."). Together with commit f8fffebdea75 ("MIPS: BPF: +Disable MIPS32 eBPF JIT") this restores MIPS32 BPF JIT behavior back to +the same state it was prior to the introduction of the broken eBPF JIT +support. + +[1] https://lore.kernel.org/linux-mips/MWHPR2201MB13583388481F01A422CE7D66D4410@MWHPR2201MB1358.namprd22.prod.outlook.com/ + +Signed-off-by: Paul Burton +Fixes: 716850ab104d ("MIPS: eBPF: Initial eBPF support for MIPS32 architecture.") +Cc: Daniel Borkmann +Cc: Hassan Naveed +Cc: Tony Ambardar +Cc: bpf@vger.kernel.org +Cc: netdev@vger.kernel.org +Cc: linux-mips@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +--- + arch/mips/Kconfig | 1 + + arch/mips/net/Makefile | 1 + + arch/mips/net/bpf_jit.c | 1270 +++++++++++++++++++++++++++++++++++ + arch/mips/net/bpf_jit_asm.S | 285 ++++++++ + 4 files changed, 1557 insertions(+) + create mode 100644 arch/mips/net/bpf_jit.c + create mode 100644 arch/mips/net/bpf_jit_asm.S + +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -46,6 +46,7 @@ config MIPS + select HAVE_ARCH_TRACEHOOK + select HAVE_ARCH_TRANSPARENT_HUGEPAGE if CPU_SUPPORTS_HUGEPAGES + select HAVE_ASM_MODVERSIONS ++ select HAVE_CBPF_JIT if !64BIT && !CPU_MICROMIPS + select HAVE_EBPF_JIT if 64BIT && !CPU_MICROMIPS && TARGET_ISA_REV >= 2 + select HAVE_CONTEXT_TRACKING + select HAVE_COPY_THREAD_TLS +--- a/arch/mips/net/Makefile ++++ b/arch/mips/net/Makefile +@@ -1,4 +1,5 @@ + # SPDX-License-Identifier: GPL-2.0-only + # MIPS networking code + ++obj-$(CONFIG_MIPS_CBPF_JIT) += bpf_jit.o bpf_jit_asm.o + obj-$(CONFIG_MIPS_EBPF_JIT) += ebpf_jit.o +--- /dev/null ++++ b/arch/mips/net/bpf_jit.c +@@ -0,0 +1,1270 @@ ++/* ++ * Just-In-Time compiler for BPF filters on MIPS ++ * ++ * Copyright (c) 2014 Imagination Technologies Ltd. ++ * Author: Markos Chandras ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; version 2 of the License. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bpf_jit.h" ++ ++/* ABI ++ * r_skb_hl SKB header length ++ * r_data SKB data pointer ++ * r_off Offset ++ * r_A BPF register A ++ * r_X BPF register X ++ * r_skb *skb ++ * r_M *scratch memory ++ * r_skb_len SKB length ++ * ++ * On entry (*bpf_func)(*skb, *filter) ++ * a0 = MIPS_R_A0 = skb; ++ * a1 = MIPS_R_A1 = filter; ++ * ++ * Stack ++ * ... ++ * M[15] ++ * M[14] ++ * M[13] ++ * ... ++ * M[0] <-- r_M ++ * saved reg k-1 ++ * saved reg k-2 ++ * ... ++ * saved reg 0 <-- r_sp ++ * ++ * ++ * Packet layout ++ * ++ * <--------------------- len ------------------------> ++ * <--skb-len(r_skb_hl)-->< ----- skb->data_len ------> ++ * ---------------------------------------------------- ++ * | skb->data | ++ * ---------------------------------------------------- ++ */ ++ ++#define ptr typeof(unsigned long) ++ ++#define SCRATCH_OFF(k) (4 * (k)) ++ ++/* JIT flags */ ++#define SEEN_CALL (1 << BPF_MEMWORDS) ++#define SEEN_SREG_SFT (BPF_MEMWORDS + 1) ++#define SEEN_SREG_BASE (1 << SEEN_SREG_SFT) ++#define SEEN_SREG(x) (SEEN_SREG_BASE << (x)) ++#define SEEN_OFF SEEN_SREG(2) ++#define SEEN_A SEEN_SREG(3) ++#define SEEN_X SEEN_SREG(4) ++#define SEEN_SKB SEEN_SREG(5) ++#define SEEN_MEM SEEN_SREG(6) ++/* SEEN_SK_DATA also implies skb_hl an skb_len */ ++#define SEEN_SKB_DATA (SEEN_SREG(7) | SEEN_SREG(1) | SEEN_SREG(0)) ++ ++/* Arguments used by JIT */ ++#define ARGS_USED_BY_JIT 2 /* only applicable to 64-bit */ ++ ++#define SBIT(x) (1 << (x)) /* Signed version of BIT() */ ++ ++/** ++ * struct jit_ctx - JIT context ++ * @skf: The sk_filter ++ * @prologue_bytes: Number of bytes for prologue ++ * @idx: Instruction index ++ * @flags: JIT flags ++ * @offsets: Instruction offsets ++ * @target: Memory location for the compiled filter ++ */ ++struct jit_ctx { ++ const struct bpf_prog *skf; ++ unsigned int prologue_bytes; ++ u32 idx; ++ u32 flags; ++ u32 *offsets; ++ u32 *target; ++}; ++ ++ ++static inline int optimize_div(u32 *k) ++{ ++ /* power of 2 divides can be implemented with right shift */ ++ if (!(*k & (*k-1))) { ++ *k = ilog2(*k); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static inline void emit_jit_reg_move(ptr dst, ptr src, struct jit_ctx *ctx); ++ ++/* Simply emit the instruction if the JIT memory space has been allocated */ ++#define emit_instr(ctx, func, ...) \ ++do { \ ++ if ((ctx)->target != NULL) { \ ++ u32 *p = &(ctx)->target[ctx->idx]; \ ++ uasm_i_##func(&p, ##__VA_ARGS__); \ ++ } \ ++ (ctx)->idx++; \ ++} while (0) ++ ++/* ++ * Similar to emit_instr but it must be used when we need to emit ++ * 32-bit or 64-bit instructions ++ */ ++#define emit_long_instr(ctx, func, ...) \ ++do { \ ++ if ((ctx)->target != NULL) { \ ++ u32 *p = &(ctx)->target[ctx->idx]; \ ++ UASM_i_##func(&p, ##__VA_ARGS__); \ ++ } \ ++ (ctx)->idx++; \ ++} while (0) ++ ++/* Determine if immediate is within the 16-bit signed range */ ++static inline bool is_range16(s32 imm) ++{ ++ return !(imm >= SBIT(15) || imm < -SBIT(15)); ++} ++ ++static inline void emit_addu(unsigned int dst, unsigned int src1, ++ unsigned int src2, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, addu, dst, src1, src2); ++} ++ ++static inline void emit_nop(struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, nop); ++} ++ ++/* Load a u32 immediate to a register */ ++static inline void emit_load_imm(unsigned int dst, u32 imm, struct jit_ctx *ctx) ++{ ++ if (ctx->target != NULL) { ++ /* addiu can only handle s16 */ ++ if (!is_range16(imm)) { ++ u32 *p = &ctx->target[ctx->idx]; ++ uasm_i_lui(&p, r_tmp_imm, (s32)imm >> 16); ++ p = &ctx->target[ctx->idx + 1]; ++ uasm_i_ori(&p, dst, r_tmp_imm, imm & 0xffff); ++ } else { ++ u32 *p = &ctx->target[ctx->idx]; ++ uasm_i_addiu(&p, dst, r_zero, imm); ++ } ++ } ++ ctx->idx++; ++ ++ if (!is_range16(imm)) ++ ctx->idx++; ++} ++ ++static inline void emit_or(unsigned int dst, unsigned int src1, ++ unsigned int src2, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, or, dst, src1, src2); ++} ++ ++static inline void emit_ori(unsigned int dst, unsigned src, u32 imm, ++ struct jit_ctx *ctx) ++{ ++ if (imm >= BIT(16)) { ++ emit_load_imm(r_tmp, imm, ctx); ++ emit_or(dst, src, r_tmp, ctx); ++ } else { ++ emit_instr(ctx, ori, dst, src, imm); ++ } ++} ++ ++static inline void emit_daddiu(unsigned int dst, unsigned int src, ++ int imm, struct jit_ctx *ctx) ++{ ++ /* ++ * Only used for stack, so the imm is relatively small ++ * and it fits in 15-bits ++ */ ++ emit_instr(ctx, daddiu, dst, src, imm); ++} ++ ++static inline void emit_addiu(unsigned int dst, unsigned int src, ++ u32 imm, struct jit_ctx *ctx) ++{ ++ if (!is_range16(imm)) { ++ emit_load_imm(r_tmp, imm, ctx); ++ emit_addu(dst, r_tmp, src, ctx); ++ } else { ++ emit_instr(ctx, addiu, dst, src, imm); ++ } ++} ++ ++static inline void emit_and(unsigned int dst, unsigned int src1, ++ unsigned int src2, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, and, dst, src1, src2); ++} ++ ++static inline void emit_andi(unsigned int dst, unsigned int src, ++ u32 imm, struct jit_ctx *ctx) ++{ ++ /* If imm does not fit in u16 then load it to register */ ++ if (imm >= BIT(16)) { ++ emit_load_imm(r_tmp, imm, ctx); ++ emit_and(dst, src, r_tmp, ctx); ++ } else { ++ emit_instr(ctx, andi, dst, src, imm); ++ } ++} ++ ++static inline void emit_xor(unsigned int dst, unsigned int src1, ++ unsigned int src2, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, xor, dst, src1, src2); ++} ++ ++static inline void emit_xori(ptr dst, ptr src, u32 imm, struct jit_ctx *ctx) ++{ ++ /* If imm does not fit in u16 then load it to register */ ++ if (imm >= BIT(16)) { ++ emit_load_imm(r_tmp, imm, ctx); ++ emit_xor(dst, src, r_tmp, ctx); ++ } else { ++ emit_instr(ctx, xori, dst, src, imm); ++ } ++} ++ ++static inline void emit_stack_offset(int offset, struct jit_ctx *ctx) ++{ ++ emit_long_instr(ctx, ADDIU, r_sp, r_sp, offset); ++} ++ ++static inline void emit_subu(unsigned int dst, unsigned int src1, ++ unsigned int src2, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, subu, dst, src1, src2); ++} ++ ++static inline void emit_neg(unsigned int reg, struct jit_ctx *ctx) ++{ ++ emit_subu(reg, r_zero, reg, ctx); ++} ++ ++static inline void emit_sllv(unsigned int dst, unsigned int src, ++ unsigned int sa, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, sllv, dst, src, sa); ++} ++ ++static inline void emit_sll(unsigned int dst, unsigned int src, ++ unsigned int sa, struct jit_ctx *ctx) ++{ ++ /* sa is 5-bits long */ ++ if (sa >= BIT(5)) ++ /* Shifting >= 32 results in zero */ ++ emit_jit_reg_move(dst, r_zero, ctx); ++ else ++ emit_instr(ctx, sll, dst, src, sa); ++} ++ ++static inline void emit_srlv(unsigned int dst, unsigned int src, ++ unsigned int sa, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, srlv, dst, src, sa); ++} ++ ++static inline void emit_srl(unsigned int dst, unsigned int src, ++ unsigned int sa, struct jit_ctx *ctx) ++{ ++ /* sa is 5-bits long */ ++ if (sa >= BIT(5)) ++ /* Shifting >= 32 results in zero */ ++ emit_jit_reg_move(dst, r_zero, ctx); ++ else ++ emit_instr(ctx, srl, dst, src, sa); ++} ++ ++static inline void emit_slt(unsigned int dst, unsigned int src1, ++ unsigned int src2, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, slt, dst, src1, src2); ++} ++ ++static inline void emit_sltu(unsigned int dst, unsigned int src1, ++ unsigned int src2, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, sltu, dst, src1, src2); ++} ++ ++static inline void emit_sltiu(unsigned dst, unsigned int src, ++ unsigned int imm, struct jit_ctx *ctx) ++{ ++ /* 16 bit immediate */ ++ if (!is_range16((s32)imm)) { ++ emit_load_imm(r_tmp, imm, ctx); ++ emit_sltu(dst, src, r_tmp, ctx); ++ } else { ++ emit_instr(ctx, sltiu, dst, src, imm); ++ } ++ ++} ++ ++/* Store register on the stack */ ++static inline void emit_store_stack_reg(ptr reg, ptr base, ++ unsigned int offset, ++ struct jit_ctx *ctx) ++{ ++ emit_long_instr(ctx, SW, reg, offset, base); ++} ++ ++static inline void emit_store(ptr reg, ptr base, unsigned int offset, ++ struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, sw, reg, offset, base); ++} ++ ++static inline void emit_load_stack_reg(ptr reg, ptr base, ++ unsigned int offset, ++ struct jit_ctx *ctx) ++{ ++ emit_long_instr(ctx, LW, reg, offset, base); ++} ++ ++static inline void emit_load(unsigned int reg, unsigned int base, ++ unsigned int offset, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, lw, reg, offset, base); ++} ++ ++static inline void emit_load_byte(unsigned int reg, unsigned int base, ++ unsigned int offset, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, lb, reg, offset, base); ++} ++ ++static inline void emit_half_load(unsigned int reg, unsigned int base, ++ unsigned int offset, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, lh, reg, offset, base); ++} ++ ++static inline void emit_half_load_unsigned(unsigned int reg, unsigned int base, ++ unsigned int offset, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, lhu, reg, offset, base); ++} ++ ++static inline void emit_mul(unsigned int dst, unsigned int src1, ++ unsigned int src2, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, mul, dst, src1, src2); ++} ++ ++static inline void emit_div(unsigned int dst, unsigned int src, ++ struct jit_ctx *ctx) ++{ ++ if (ctx->target != NULL) { ++ u32 *p = &ctx->target[ctx->idx]; ++ uasm_i_divu(&p, dst, src); ++ p = &ctx->target[ctx->idx + 1]; ++ uasm_i_mflo(&p, dst); ++ } ++ ctx->idx += 2; /* 2 insts */ ++} ++ ++static inline void emit_mod(unsigned int dst, unsigned int src, ++ struct jit_ctx *ctx) ++{ ++ if (ctx->target != NULL) { ++ u32 *p = &ctx->target[ctx->idx]; ++ uasm_i_divu(&p, dst, src); ++ p = &ctx->target[ctx->idx + 1]; ++ uasm_i_mfhi(&p, dst); ++ } ++ ctx->idx += 2; /* 2 insts */ ++} ++ ++static inline void emit_dsll(unsigned int dst, unsigned int src, ++ unsigned int sa, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, dsll, dst, src, sa); ++} ++ ++static inline void emit_dsrl32(unsigned int dst, unsigned int src, ++ unsigned int sa, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, dsrl32, dst, src, sa); ++} ++ ++static inline void emit_wsbh(unsigned int dst, unsigned int src, ++ struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, wsbh, dst, src); ++} ++ ++/* load pointer to register */ ++static inline void emit_load_ptr(unsigned int dst, unsigned int src, ++ int imm, struct jit_ctx *ctx) ++{ ++ /* src contains the base addr of the 32/64-pointer */ ++ emit_long_instr(ctx, LW, dst, imm, src); ++} ++ ++/* load a function pointer to register */ ++static inline void emit_load_func(unsigned int reg, ptr imm, ++ struct jit_ctx *ctx) ++{ ++ if (IS_ENABLED(CONFIG_64BIT)) { ++ /* At this point imm is always 64-bit */ ++ emit_load_imm(r_tmp, (u64)imm >> 32, ctx); ++ emit_dsll(r_tmp_imm, r_tmp, 16, ctx); /* left shift by 16 */ ++ emit_ori(r_tmp, r_tmp_imm, (imm >> 16) & 0xffff, ctx); ++ emit_dsll(r_tmp_imm, r_tmp, 16, ctx); /* left shift by 16 */ ++ emit_ori(reg, r_tmp_imm, imm & 0xffff, ctx); ++ } else { ++ emit_load_imm(reg, imm, ctx); ++ } ++} ++ ++/* Move to real MIPS register */ ++static inline void emit_reg_move(ptr dst, ptr src, struct jit_ctx *ctx) ++{ ++ emit_long_instr(ctx, ADDU, dst, src, r_zero); ++} ++ ++/* Move to JIT (32-bit) register */ ++static inline void emit_jit_reg_move(ptr dst, ptr src, struct jit_ctx *ctx) ++{ ++ emit_addu(dst, src, r_zero, ctx); ++} ++ ++/* Compute the immediate value for PC-relative branches. */ ++static inline u32 b_imm(unsigned int tgt, struct jit_ctx *ctx) ++{ ++ if (ctx->target == NULL) ++ return 0; ++ ++ /* ++ * We want a pc-relative branch. We only do forward branches ++ * so tgt is always after pc. tgt is the instruction offset ++ * we want to jump to. ++ ++ * Branch on MIPS: ++ * I: target_offset <- sign_extend(offset) ++ * I+1: PC += target_offset (delay slot) ++ * ++ * ctx->idx currently points to the branch instruction ++ * but the offset is added to the delay slot so we need ++ * to subtract 4. ++ */ ++ return ctx->offsets[tgt] - ++ (ctx->idx * 4 - ctx->prologue_bytes) - 4; ++} ++ ++static inline void emit_bcond(int cond, unsigned int reg1, unsigned int reg2, ++ unsigned int imm, struct jit_ctx *ctx) ++{ ++ if (ctx->target != NULL) { ++ u32 *p = &ctx->target[ctx->idx]; ++ ++ switch (cond) { ++ case MIPS_COND_EQ: ++ uasm_i_beq(&p, reg1, reg2, imm); ++ break; ++ case MIPS_COND_NE: ++ uasm_i_bne(&p, reg1, reg2, imm); ++ break; ++ case MIPS_COND_ALL: ++ uasm_i_b(&p, imm); ++ break; ++ default: ++ pr_warn("%s: Unhandled branch conditional: %d\n", ++ __func__, cond); ++ } ++ } ++ ctx->idx++; ++} ++ ++static inline void emit_b(unsigned int imm, struct jit_ctx *ctx) ++{ ++ emit_bcond(MIPS_COND_ALL, r_zero, r_zero, imm, ctx); ++} ++ ++static inline void emit_jalr(unsigned int link, unsigned int reg, ++ struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, jalr, link, reg); ++} ++ ++static inline void emit_jr(unsigned int reg, struct jit_ctx *ctx) ++{ ++ emit_instr(ctx, jr, reg); ++} ++ ++static inline u16 align_sp(unsigned int num) ++{ ++ /* Double word alignment for 32-bit, quadword for 64-bit */ ++ unsigned int align = IS_ENABLED(CONFIG_64BIT) ? 16 : 8; ++ num = (num + (align - 1)) & -align; ++ return num; ++} ++ ++static void save_bpf_jit_regs(struct jit_ctx *ctx, unsigned offset) ++{ ++ int i = 0, real_off = 0; ++ u32 sflags, tmp_flags; ++ ++ /* Adjust the stack pointer */ ++ if (offset) ++ emit_stack_offset(-align_sp(offset), ctx); ++ ++ tmp_flags = sflags = ctx->flags >> SEEN_SREG_SFT; ++ /* sflags is essentially a bitmap */ ++ while (tmp_flags) { ++ if ((sflags >> i) & 0x1) { ++ emit_store_stack_reg(MIPS_R_S0 + i, r_sp, real_off, ++ ctx); ++ real_off += SZREG; ++ } ++ i++; ++ tmp_flags >>= 1; ++ } ++ ++ /* save return address */ ++ if (ctx->flags & SEEN_CALL) { ++ emit_store_stack_reg(r_ra, r_sp, real_off, ctx); ++ real_off += SZREG; ++ } ++ ++ /* Setup r_M leaving the alignment gap if necessary */ ++ if (ctx->flags & SEEN_MEM) { ++ if (real_off % (SZREG * 2)) ++ real_off += SZREG; ++ emit_long_instr(ctx, ADDIU, r_M, r_sp, real_off); ++ } ++} ++ ++static void restore_bpf_jit_regs(struct jit_ctx *ctx, ++ unsigned int offset) ++{ ++ int i, real_off = 0; ++ u32 sflags, tmp_flags; ++ ++ tmp_flags = sflags = ctx->flags >> SEEN_SREG_SFT; ++ /* sflags is a bitmap */ ++ i = 0; ++ while (tmp_flags) { ++ if ((sflags >> i) & 0x1) { ++ emit_load_stack_reg(MIPS_R_S0 + i, r_sp, real_off, ++ ctx); ++ real_off += SZREG; ++ } ++ i++; ++ tmp_flags >>= 1; ++ } ++ ++ /* restore return address */ ++ if (ctx->flags & SEEN_CALL) ++ emit_load_stack_reg(r_ra, r_sp, real_off, ctx); ++ ++ /* Restore the sp and discard the scrach memory */ ++ if (offset) ++ emit_stack_offset(align_sp(offset), ctx); ++} ++ ++static unsigned int get_stack_depth(struct jit_ctx *ctx) ++{ ++ int sp_off = 0; ++ ++ ++ /* How may s* regs do we need to preserved? */ ++ sp_off += hweight32(ctx->flags >> SEEN_SREG_SFT) * SZREG; ++ ++ if (ctx->flags & SEEN_MEM) ++ sp_off += 4 * BPF_MEMWORDS; /* BPF_MEMWORDS are 32-bit */ ++ ++ if (ctx->flags & SEEN_CALL) ++ sp_off += SZREG; /* Space for our ra register */ ++ ++ return sp_off; ++} ++ ++static void build_prologue(struct jit_ctx *ctx) ++{ ++ int sp_off; ++ ++ /* Calculate the total offset for the stack pointer */ ++ sp_off = get_stack_depth(ctx); ++ save_bpf_jit_regs(ctx, sp_off); ++ ++ if (ctx->flags & SEEN_SKB) ++ emit_reg_move(r_skb, MIPS_R_A0, ctx); ++ ++ if (ctx->flags & SEEN_SKB_DATA) { ++ /* Load packet length */ ++ emit_load(r_skb_len, r_skb, offsetof(struct sk_buff, len), ++ ctx); ++ emit_load(r_tmp, r_skb, offsetof(struct sk_buff, data_len), ++ ctx); ++ /* Load the data pointer */ ++ emit_load_ptr(r_skb_data, r_skb, ++ offsetof(struct sk_buff, data), ctx); ++ /* Load the header length */ ++ emit_subu(r_skb_hl, r_skb_len, r_tmp, ctx); ++ } ++ ++ if (ctx->flags & SEEN_X) ++ emit_jit_reg_move(r_X, r_zero, ctx); ++ ++ /* ++ * Do not leak kernel data to userspace, we only need to clear ++ * r_A if it is ever used. In fact if it is never used, we ++ * will not save/restore it, so clearing it in this case would ++ * corrupt the state of the caller. ++ */ ++ if (bpf_needs_clear_a(&ctx->skf->insns[0]) && ++ (ctx->flags & SEEN_A)) ++ emit_jit_reg_move(r_A, r_zero, ctx); ++} ++ ++static void build_epilogue(struct jit_ctx *ctx) ++{ ++ unsigned int sp_off; ++ ++ /* Calculate the total offset for the stack pointer */ ++ ++ sp_off = get_stack_depth(ctx); ++ restore_bpf_jit_regs(ctx, sp_off); ++ ++ /* Return */ ++ emit_jr(r_ra, ctx); ++ emit_nop(ctx); ++} ++ ++#define CHOOSE_LOAD_FUNC(K, func) \ ++ ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative : func) : \ ++ func##_positive) ++ ++static int build_body(struct jit_ctx *ctx) ++{ ++ const struct bpf_prog *prog = ctx->skf; ++ const struct sock_filter *inst; ++ unsigned int i, off, condt; ++ u32 k, b_off __maybe_unused; ++ u8 (*sk_load_func)(unsigned long *skb, int offset); ++ ++ for (i = 0; i < prog->len; i++) { ++ u16 code; ++ ++ inst = &(prog->insns[i]); ++ pr_debug("%s: code->0x%02x, jt->0x%x, jf->0x%x, k->0x%x\n", ++ __func__, inst->code, inst->jt, inst->jf, inst->k); ++ k = inst->k; ++ code = bpf_anc_helper(inst); ++ ++ if (ctx->target == NULL) ++ ctx->offsets[i] = ctx->idx * 4; ++ ++ switch (code) { ++ case BPF_LD | BPF_IMM: ++ /* A <- k ==> li r_A, k */ ++ ctx->flags |= SEEN_A; ++ emit_load_imm(r_A, k, ctx); ++ break; ++ case BPF_LD | BPF_W | BPF_LEN: ++ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4); ++ /* A <- len ==> lw r_A, offset(skb) */ ++ ctx->flags |= SEEN_SKB | SEEN_A; ++ off = offsetof(struct sk_buff, len); ++ emit_load(r_A, r_skb, off, ctx); ++ break; ++ case BPF_LD | BPF_MEM: ++ /* A <- M[k] ==> lw r_A, offset(M) */ ++ ctx->flags |= SEEN_MEM | SEEN_A; ++ emit_load(r_A, r_M, SCRATCH_OFF(k), ctx); ++ break; ++ case BPF_LD | BPF_W | BPF_ABS: ++ /* A <- P[k:4] */ ++ sk_load_func = CHOOSE_LOAD_FUNC(k, sk_load_word); ++ goto load; ++ case BPF_LD | BPF_H | BPF_ABS: ++ /* A <- P[k:2] */ ++ sk_load_func = CHOOSE_LOAD_FUNC(k, sk_load_half); ++ goto load; ++ case BPF_LD | BPF_B | BPF_ABS: ++ /* A <- P[k:1] */ ++ sk_load_func = CHOOSE_LOAD_FUNC(k, sk_load_byte); ++load: ++ emit_load_imm(r_off, k, ctx); ++load_common: ++ ctx->flags |= SEEN_CALL | SEEN_OFF | ++ SEEN_SKB | SEEN_A | SEEN_SKB_DATA; ++ ++ emit_load_func(r_s0, (ptr)sk_load_func, ctx); ++ emit_reg_move(MIPS_R_A0, r_skb, ctx); ++ emit_jalr(MIPS_R_RA, r_s0, ctx); ++ /* Load second argument to delay slot */ ++ emit_reg_move(MIPS_R_A1, r_off, ctx); ++ /* Check the error value */ ++ emit_bcond(MIPS_COND_EQ, r_ret, 0, b_imm(i + 1, ctx), ++ ctx); ++ /* Load return register on DS for failures */ ++ emit_reg_move(r_ret, r_zero, ctx); ++ /* Return with error */ ++ emit_b(b_imm(prog->len, ctx), ctx); ++ emit_nop(ctx); ++ break; ++ case BPF_LD | BPF_W | BPF_IND: ++ /* A <- P[X + k:4] */ ++ sk_load_func = sk_load_word; ++ goto load_ind; ++ case BPF_LD | BPF_H | BPF_IND: ++ /* A <- P[X + k:2] */ ++ sk_load_func = sk_load_half; ++ goto load_ind; ++ case BPF_LD | BPF_B | BPF_IND: ++ /* A <- P[X + k:1] */ ++ sk_load_func = sk_load_byte; ++load_ind: ++ ctx->flags |= SEEN_OFF | SEEN_X; ++ emit_addiu(r_off, r_X, k, ctx); ++ goto load_common; ++ case BPF_LDX | BPF_IMM: ++ /* X <- k */ ++ ctx->flags |= SEEN_X; ++ emit_load_imm(r_X, k, ctx); ++ break; ++ case BPF_LDX | BPF_MEM: ++ /* X <- M[k] */ ++ ctx->flags |= SEEN_X | SEEN_MEM; ++ emit_load(r_X, r_M, SCRATCH_OFF(k), ctx); ++ break; ++ case BPF_LDX | BPF_W | BPF_LEN: ++ /* X <- len */ ++ ctx->flags |= SEEN_X | SEEN_SKB; ++ off = offsetof(struct sk_buff, len); ++ emit_load(r_X, r_skb, off, ctx); ++ break; ++ case BPF_LDX | BPF_B | BPF_MSH: ++ /* X <- 4 * (P[k:1] & 0xf) */ ++ ctx->flags |= SEEN_X | SEEN_CALL | SEEN_SKB; ++ /* Load offset to a1 */ ++ emit_load_func(r_s0, (ptr)sk_load_byte, ctx); ++ /* ++ * This may emit two instructions so it may not fit ++ * in the delay slot. So use a0 in the delay slot. ++ */ ++ emit_load_imm(MIPS_R_A1, k, ctx); ++ emit_jalr(MIPS_R_RA, r_s0, ctx); ++ emit_reg_move(MIPS_R_A0, r_skb, ctx); /* delay slot */ ++ /* Check the error value */ ++ emit_bcond(MIPS_COND_NE, r_ret, 0, ++ b_imm(prog->len, ctx), ctx); ++ emit_reg_move(r_ret, r_zero, ctx); ++ /* We are good */ ++ /* X <- P[1:K] & 0xf */ ++ emit_andi(r_X, r_A, 0xf, ctx); ++ /* X << 2 */ ++ emit_b(b_imm(i + 1, ctx), ctx); ++ emit_sll(r_X, r_X, 2, ctx); /* delay slot */ ++ break; ++ case BPF_ST: ++ /* M[k] <- A */ ++ ctx->flags |= SEEN_MEM | SEEN_A; ++ emit_store(r_A, r_M, SCRATCH_OFF(k), ctx); ++ break; ++ case BPF_STX: ++ /* M[k] <- X */ ++ ctx->flags |= SEEN_MEM | SEEN_X; ++ emit_store(r_X, r_M, SCRATCH_OFF(k), ctx); ++ break; ++ case BPF_ALU | BPF_ADD | BPF_K: ++ /* A += K */ ++ ctx->flags |= SEEN_A; ++ emit_addiu(r_A, r_A, k, ctx); ++ break; ++ case BPF_ALU | BPF_ADD | BPF_X: ++ /* A += X */ ++ ctx->flags |= SEEN_A | SEEN_X; ++ emit_addu(r_A, r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_SUB | BPF_K: ++ /* A -= K */ ++ ctx->flags |= SEEN_A; ++ emit_addiu(r_A, r_A, -k, ctx); ++ break; ++ case BPF_ALU | BPF_SUB | BPF_X: ++ /* A -= X */ ++ ctx->flags |= SEEN_A | SEEN_X; ++ emit_subu(r_A, r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_MUL | BPF_K: ++ /* A *= K */ ++ /* Load K to scratch register before MUL */ ++ ctx->flags |= SEEN_A; ++ emit_load_imm(r_s0, k, ctx); ++ emit_mul(r_A, r_A, r_s0, ctx); ++ break; ++ case BPF_ALU | BPF_MUL | BPF_X: ++ /* A *= X */ ++ ctx->flags |= SEEN_A | SEEN_X; ++ emit_mul(r_A, r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_DIV | BPF_K: ++ /* A /= k */ ++ if (k == 1) ++ break; ++ if (optimize_div(&k)) { ++ ctx->flags |= SEEN_A; ++ emit_srl(r_A, r_A, k, ctx); ++ break; ++ } ++ ctx->flags |= SEEN_A; ++ emit_load_imm(r_s0, k, ctx); ++ emit_div(r_A, r_s0, ctx); ++ break; ++ case BPF_ALU | BPF_MOD | BPF_K: ++ /* A %= k */ ++ if (k == 1) { ++ ctx->flags |= SEEN_A; ++ emit_jit_reg_move(r_A, r_zero, ctx); ++ } else { ++ ctx->flags |= SEEN_A; ++ emit_load_imm(r_s0, k, ctx); ++ emit_mod(r_A, r_s0, ctx); ++ } ++ break; ++ case BPF_ALU | BPF_DIV | BPF_X: ++ /* A /= X */ ++ ctx->flags |= SEEN_X | SEEN_A; ++ /* Check if r_X is zero */ ++ emit_bcond(MIPS_COND_EQ, r_X, r_zero, ++ b_imm(prog->len, ctx), ctx); ++ emit_load_imm(r_ret, 0, ctx); /* delay slot */ ++ emit_div(r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_MOD | BPF_X: ++ /* A %= X */ ++ ctx->flags |= SEEN_X | SEEN_A; ++ /* Check if r_X is zero */ ++ emit_bcond(MIPS_COND_EQ, r_X, r_zero, ++ b_imm(prog->len, ctx), ctx); ++ emit_load_imm(r_ret, 0, ctx); /* delay slot */ ++ emit_mod(r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_OR | BPF_K: ++ /* A |= K */ ++ ctx->flags |= SEEN_A; ++ emit_ori(r_A, r_A, k, ctx); ++ break; ++ case BPF_ALU | BPF_OR | BPF_X: ++ /* A |= X */ ++ ctx->flags |= SEEN_A; ++ emit_ori(r_A, r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_XOR | BPF_K: ++ /* A ^= k */ ++ ctx->flags |= SEEN_A; ++ emit_xori(r_A, r_A, k, ctx); ++ break; ++ case BPF_ANC | SKF_AD_ALU_XOR_X: ++ case BPF_ALU | BPF_XOR | BPF_X: ++ /* A ^= X */ ++ ctx->flags |= SEEN_A; ++ emit_xor(r_A, r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_AND | BPF_K: ++ /* A &= K */ ++ ctx->flags |= SEEN_A; ++ emit_andi(r_A, r_A, k, ctx); ++ break; ++ case BPF_ALU | BPF_AND | BPF_X: ++ /* A &= X */ ++ ctx->flags |= SEEN_A | SEEN_X; ++ emit_and(r_A, r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_LSH | BPF_K: ++ /* A <<= K */ ++ ctx->flags |= SEEN_A; ++ emit_sll(r_A, r_A, k, ctx); ++ break; ++ case BPF_ALU | BPF_LSH | BPF_X: ++ /* A <<= X */ ++ ctx->flags |= SEEN_A | SEEN_X; ++ emit_sllv(r_A, r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_RSH | BPF_K: ++ /* A >>= K */ ++ ctx->flags |= SEEN_A; ++ emit_srl(r_A, r_A, k, ctx); ++ break; ++ case BPF_ALU | BPF_RSH | BPF_X: ++ ctx->flags |= SEEN_A | SEEN_X; ++ emit_srlv(r_A, r_A, r_X, ctx); ++ break; ++ case BPF_ALU | BPF_NEG: ++ /* A = -A */ ++ ctx->flags |= SEEN_A; ++ emit_neg(r_A, ctx); ++ break; ++ case BPF_JMP | BPF_JA: ++ /* pc += K */ ++ emit_b(b_imm(i + k + 1, ctx), ctx); ++ emit_nop(ctx); ++ break; ++ case BPF_JMP | BPF_JEQ | BPF_K: ++ /* pc += ( A == K ) ? pc->jt : pc->jf */ ++ condt = MIPS_COND_EQ | MIPS_COND_K; ++ goto jmp_cmp; ++ case BPF_JMP | BPF_JEQ | BPF_X: ++ ctx->flags |= SEEN_X; ++ /* pc += ( A == X ) ? pc->jt : pc->jf */ ++ condt = MIPS_COND_EQ | MIPS_COND_X; ++ goto jmp_cmp; ++ case BPF_JMP | BPF_JGE | BPF_K: ++ /* pc += ( A >= K ) ? pc->jt : pc->jf */ ++ condt = MIPS_COND_GE | MIPS_COND_K; ++ goto jmp_cmp; ++ case BPF_JMP | BPF_JGE | BPF_X: ++ ctx->flags |= SEEN_X; ++ /* pc += ( A >= X ) ? pc->jt : pc->jf */ ++ condt = MIPS_COND_GE | MIPS_COND_X; ++ goto jmp_cmp; ++ case BPF_JMP | BPF_JGT | BPF_K: ++ /* pc += ( A > K ) ? pc->jt : pc->jf */ ++ condt = MIPS_COND_GT | MIPS_COND_K; ++ goto jmp_cmp; ++ case BPF_JMP | BPF_JGT | BPF_X: ++ ctx->flags |= SEEN_X; ++ /* pc += ( A > X ) ? pc->jt : pc->jf */ ++ condt = MIPS_COND_GT | MIPS_COND_X; ++jmp_cmp: ++ /* Greater or Equal */ ++ if ((condt & MIPS_COND_GE) || ++ (condt & MIPS_COND_GT)) { ++ if (condt & MIPS_COND_K) { /* K */ ++ ctx->flags |= SEEN_A; ++ emit_sltiu(r_s0, r_A, k, ctx); ++ } else { /* X */ ++ ctx->flags |= SEEN_A | ++ SEEN_X; ++ emit_sltu(r_s0, r_A, r_X, ctx); ++ } ++ /* A < (K|X) ? r_scrach = 1 */ ++ b_off = b_imm(i + inst->jf + 1, ctx); ++ emit_bcond(MIPS_COND_NE, r_s0, r_zero, b_off, ++ ctx); ++ emit_nop(ctx); ++ /* A > (K|X) ? scratch = 0 */ ++ if (condt & MIPS_COND_GT) { ++ /* Checking for equality */ ++ ctx->flags |= SEEN_A | SEEN_X; ++ if (condt & MIPS_COND_K) ++ emit_load_imm(r_s0, k, ctx); ++ else ++ emit_jit_reg_move(r_s0, r_X, ++ ctx); ++ b_off = b_imm(i + inst->jf + 1, ctx); ++ emit_bcond(MIPS_COND_EQ, r_A, r_s0, ++ b_off, ctx); ++ emit_nop(ctx); ++ /* Finally, A > K|X */ ++ b_off = b_imm(i + inst->jt + 1, ctx); ++ emit_b(b_off, ctx); ++ emit_nop(ctx); ++ } else { ++ /* A >= (K|X) so jump */ ++ b_off = b_imm(i + inst->jt + 1, ctx); ++ emit_b(b_off, ctx); ++ emit_nop(ctx); ++ } ++ } else { ++ /* A == K|X */ ++ if (condt & MIPS_COND_K) { /* K */ ++ ctx->flags |= SEEN_A; ++ emit_load_imm(r_s0, k, ctx); ++ /* jump true */ ++ b_off = b_imm(i + inst->jt + 1, ctx); ++ emit_bcond(MIPS_COND_EQ, r_A, r_s0, ++ b_off, ctx); ++ emit_nop(ctx); ++ /* jump false */ ++ b_off = b_imm(i + inst->jf + 1, ++ ctx); ++ emit_bcond(MIPS_COND_NE, r_A, r_s0, ++ b_off, ctx); ++ emit_nop(ctx); ++ } else { /* X */ ++ /* jump true */ ++ ctx->flags |= SEEN_A | SEEN_X; ++ b_off = b_imm(i + inst->jt + 1, ++ ctx); ++ emit_bcond(MIPS_COND_EQ, r_A, r_X, ++ b_off, ctx); ++ emit_nop(ctx); ++ /* jump false */ ++ b_off = b_imm(i + inst->jf + 1, ctx); ++ emit_bcond(MIPS_COND_NE, r_A, r_X, ++ b_off, ctx); ++ emit_nop(ctx); ++ } ++ } ++ break; ++ case BPF_JMP | BPF_JSET | BPF_K: ++ ctx->flags |= SEEN_A; ++ /* pc += (A & K) ? pc -> jt : pc -> jf */ ++ emit_load_imm(r_s1, k, ctx); ++ emit_and(r_s0, r_A, r_s1, ctx); ++ /* jump true */ ++ b_off = b_imm(i + inst->jt + 1, ctx); ++ emit_bcond(MIPS_COND_NE, r_s0, r_zero, b_off, ctx); ++ emit_nop(ctx); ++ /* jump false */ ++ b_off = b_imm(i + inst->jf + 1, ctx); ++ emit_b(b_off, ctx); ++ emit_nop(ctx); ++ break; ++ case BPF_JMP | BPF_JSET | BPF_X: ++ ctx->flags |= SEEN_X | SEEN_A; ++ /* pc += (A & X) ? pc -> jt : pc -> jf */ ++ emit_and(r_s0, r_A, r_X, ctx); ++ /* jump true */ ++ b_off = b_imm(i + inst->jt + 1, ctx); ++ emit_bcond(MIPS_COND_NE, r_s0, r_zero, b_off, ctx); ++ emit_nop(ctx); ++ /* jump false */ ++ b_off = b_imm(i + inst->jf + 1, ctx); ++ emit_b(b_off, ctx); ++ emit_nop(ctx); ++ break; ++ case BPF_RET | BPF_A: ++ ctx->flags |= SEEN_A; ++ if (i != prog->len - 1) ++ /* ++ * If this is not the last instruction ++ * then jump to the epilogue ++ */ ++ emit_b(b_imm(prog->len, ctx), ctx); ++ emit_reg_move(r_ret, r_A, ctx); /* delay slot */ ++ break; ++ case BPF_RET | BPF_K: ++ /* ++ * It can emit two instructions so it does not fit on ++ * the delay slot. ++ */ ++ emit_load_imm(r_ret, k, ctx); ++ if (i != prog->len - 1) { ++ /* ++ * If this is not the last instruction ++ * then jump to the epilogue ++ */ ++ emit_b(b_imm(prog->len, ctx), ctx); ++ emit_nop(ctx); ++ } ++ break; ++ case BPF_MISC | BPF_TAX: ++ /* X = A */ ++ ctx->flags |= SEEN_X | SEEN_A; ++ emit_jit_reg_move(r_X, r_A, ctx); ++ break; ++ case BPF_MISC | BPF_TXA: ++ /* A = X */ ++ ctx->flags |= SEEN_A | SEEN_X; ++ emit_jit_reg_move(r_A, r_X, ctx); ++ break; ++ /* AUX */ ++ case BPF_ANC | SKF_AD_PROTOCOL: ++ /* A = ntohs(skb->protocol */ ++ ctx->flags |= SEEN_SKB | SEEN_OFF | SEEN_A; ++ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, ++ protocol) != 2); ++ off = offsetof(struct sk_buff, protocol); ++ emit_half_load(r_A, r_skb, off, ctx); ++#ifdef CONFIG_CPU_LITTLE_ENDIAN ++ /* This needs little endian fixup */ ++ if (cpu_has_wsbh) { ++ /* R2 and later have the wsbh instruction */ ++ emit_wsbh(r_A, r_A, ctx); ++ } else { ++ /* Get first byte */ ++ emit_andi(r_tmp_imm, r_A, 0xff, ctx); ++ /* Shift it */ ++ emit_sll(r_tmp, r_tmp_imm, 8, ctx); ++ /* Get second byte */ ++ emit_srl(r_tmp_imm, r_A, 8, ctx); ++ emit_andi(r_tmp_imm, r_tmp_imm, 0xff, ctx); ++ /* Put everyting together in r_A */ ++ emit_or(r_A, r_tmp, r_tmp_imm, ctx); ++ } ++#endif ++ break; ++ case BPF_ANC | SKF_AD_CPU: ++ ctx->flags |= SEEN_A | SEEN_OFF; ++ /* A = current_thread_info()->cpu */ ++ BUILD_BUG_ON(FIELD_SIZEOF(struct thread_info, ++ cpu) != 4); ++ off = offsetof(struct thread_info, cpu); ++ /* $28/gp points to the thread_info struct */ ++ emit_load(r_A, 28, off, ctx); ++ break; ++ case BPF_ANC | SKF_AD_IFINDEX: ++ /* A = skb->dev->ifindex */ ++ case BPF_ANC | SKF_AD_HATYPE: ++ /* A = skb->dev->type */ ++ ctx->flags |= SEEN_SKB | SEEN_A; ++ off = offsetof(struct sk_buff, dev); ++ /* Load *dev pointer */ ++ emit_load_ptr(r_s0, r_skb, off, ctx); ++ /* error (0) in the delay slot */ ++ emit_bcond(MIPS_COND_EQ, r_s0, r_zero, ++ b_imm(prog->len, ctx), ctx); ++ emit_reg_move(r_ret, r_zero, ctx); ++ if (code == (BPF_ANC | SKF_AD_IFINDEX)) { ++ BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4); ++ off = offsetof(struct net_device, ifindex); ++ emit_load(r_A, r_s0, off, ctx); ++ } else { /* (code == (BPF_ANC | SKF_AD_HATYPE) */ ++ BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2); ++ off = offsetof(struct net_device, type); ++ emit_half_load_unsigned(r_A, r_s0, off, ctx); ++ } ++ break; ++ case BPF_ANC | SKF_AD_MARK: ++ ctx->flags |= SEEN_SKB | SEEN_A; ++ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4); ++ off = offsetof(struct sk_buff, mark); ++ emit_load(r_A, r_skb, off, ctx); ++ break; ++ case BPF_ANC | SKF_AD_RXHASH: ++ ctx->flags |= SEEN_SKB | SEEN_A; ++ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, hash) != 4); ++ off = offsetof(struct sk_buff, hash); ++ emit_load(r_A, r_skb, off, ctx); ++ break; ++ case BPF_ANC | SKF_AD_VLAN_TAG: ++ ctx->flags |= SEEN_SKB | SEEN_A; ++ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, ++ vlan_tci) != 2); ++ off = offsetof(struct sk_buff, vlan_tci); ++ emit_half_load_unsigned(r_A, r_skb, off, ctx); ++ break; ++ case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: ++ ctx->flags |= SEEN_SKB | SEEN_A; ++ emit_load_byte(r_A, r_skb, PKT_VLAN_PRESENT_OFFSET(), ctx); ++ if (PKT_VLAN_PRESENT_BIT) ++ emit_srl(r_A, r_A, PKT_VLAN_PRESENT_BIT, ctx); ++ if (PKT_VLAN_PRESENT_BIT < 7) ++ emit_andi(r_A, r_A, 1, ctx); ++ break; ++ case BPF_ANC | SKF_AD_PKTTYPE: ++ ctx->flags |= SEEN_SKB; ++ ++ emit_load_byte(r_tmp, r_skb, PKT_TYPE_OFFSET(), ctx); ++ /* Keep only the last 3 bits */ ++ emit_andi(r_A, r_tmp, PKT_TYPE_MAX, ctx); ++#ifdef __BIG_ENDIAN_BITFIELD ++ /* Get the actual packet type to the lower 3 bits */ ++ emit_srl(r_A, r_A, 5, ctx); ++#endif ++ break; ++ case BPF_ANC | SKF_AD_QUEUE: ++ ctx->flags |= SEEN_SKB | SEEN_A; ++ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, ++ queue_mapping) != 2); ++ BUILD_BUG_ON(offsetof(struct sk_buff, ++ queue_mapping) > 0xff); ++ off = offsetof(struct sk_buff, queue_mapping); ++ emit_half_load_unsigned(r_A, r_skb, off, ctx); ++ break; ++ default: ++ pr_debug("%s: Unhandled opcode: 0x%02x\n", __FILE__, ++ inst->code); ++ return -1; ++ } ++ } ++ ++ /* compute offsets only during the first pass */ ++ if (ctx->target == NULL) ++ ctx->offsets[i] = ctx->idx * 4; ++ ++ return 0; ++} ++ ++void bpf_jit_compile(struct bpf_prog *fp) ++{ ++ struct jit_ctx ctx; ++ unsigned int alloc_size, tmp_idx; ++ ++ if (!bpf_jit_enable) ++ return; ++ ++ memset(&ctx, 0, sizeof(ctx)); ++ ++ ctx.offsets = kcalloc(fp->len + 1, sizeof(*ctx.offsets), GFP_KERNEL); ++ if (ctx.offsets == NULL) ++ return; ++ ++ ctx.skf = fp; ++ ++ if (build_body(&ctx)) ++ goto out; ++ ++ tmp_idx = ctx.idx; ++ build_prologue(&ctx); ++ ctx.prologue_bytes = (ctx.idx - tmp_idx) * 4; ++ /* just to complete the ctx.idx count */ ++ build_epilogue(&ctx); ++ ++ alloc_size = 4 * ctx.idx; ++ ctx.target = module_alloc(alloc_size); ++ if (ctx.target == NULL) ++ goto out; ++ ++ /* Clean it */ ++ memset(ctx.target, 0, alloc_size); ++ ++ ctx.idx = 0; ++ ++ /* Generate the actual JIT code */ ++ build_prologue(&ctx); ++ build_body(&ctx); ++ build_epilogue(&ctx); ++ ++ /* Update the icache */ ++ flush_icache_range((ptr)ctx.target, (ptr)(ctx.target + ctx.idx)); ++ ++ if (bpf_jit_enable > 1) ++ /* Dump JIT code */ ++ bpf_jit_dump(fp->len, alloc_size, 2, ctx.target); ++ ++ fp->bpf_func = (void *)ctx.target; ++ fp->jited = 1; ++ ++out: ++ kfree(ctx.offsets); ++} ++ ++void bpf_jit_free(struct bpf_prog *fp) ++{ ++ if (fp->jited) ++ module_memfree(fp->bpf_func); ++ ++ bpf_prog_unlock_free(fp); ++} +--- /dev/null ++++ b/arch/mips/net/bpf_jit_asm.S +@@ -0,0 +1,285 @@ ++/* ++ * bpf_jib_asm.S: Packet/header access helper functions for MIPS/MIPS64 BPF ++ * compiler. ++ * ++ * Copyright (C) 2015 Imagination Technologies Ltd. ++ * Author: Markos Chandras ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; version 2 of the License. ++ */ ++ ++#include ++#include ++#include ++#include "bpf_jit.h" ++ ++/* ABI ++ * ++ * r_skb_hl skb header length ++ * r_skb_data skb data ++ * r_off(a1) offset register ++ * r_A BPF register A ++ * r_X PF register X ++ * r_skb(a0) *skb ++ * r_M *scratch memory ++ * r_skb_le skb length ++ * r_s0 Scratch register 0 ++ * r_s1 Scratch register 1 ++ * ++ * On entry: ++ * a0: *skb ++ * a1: offset (imm or imm + X) ++ * ++ * All non-BPF-ABI registers are free for use. On return, we only ++ * care about r_ret. The BPF-ABI registers are assumed to remain ++ * unmodified during the entire filter operation. ++ */ ++ ++#define skb a0 ++#define offset a1 ++#define SKF_LL_OFF (-0x200000) /* Can't include linux/filter.h in assembly */ ++ ++ /* We know better :) so prevent assembler reordering etc */ ++ .set noreorder ++ ++#define is_offset_negative(TYPE) \ ++ /* If offset is negative we have more work to do */ \ ++ slti t0, offset, 0; \ ++ bgtz t0, bpf_slow_path_##TYPE##_neg; \ ++ /* Be careful what follows in DS. */ ++ ++#define is_offset_in_header(SIZE, TYPE) \ ++ /* Reading from header? */ \ ++ addiu $r_s0, $r_skb_hl, -SIZE; \ ++ slt t0, $r_s0, offset; \ ++ bgtz t0, bpf_slow_path_##TYPE; \ ++ ++LEAF(sk_load_word) ++ is_offset_negative(word) ++FEXPORT(sk_load_word_positive) ++ is_offset_in_header(4, word) ++ /* Offset within header boundaries */ ++ PTR_ADDU t1, $r_skb_data, offset ++ .set reorder ++ lw $r_A, 0(t1) ++ .set noreorder ++#ifdef CONFIG_CPU_LITTLE_ENDIAN ++# if MIPS_ISA_REV >= 2 ++ wsbh t0, $r_A ++ rotr $r_A, t0, 16 ++# else ++ sll t0, $r_A, 24 ++ srl t1, $r_A, 24 ++ srl t2, $r_A, 8 ++ or t0, t0, t1 ++ andi t2, t2, 0xff00 ++ andi t1, $r_A, 0xff00 ++ or t0, t0, t2 ++ sll t1, t1, 8 ++ or $r_A, t0, t1 ++# endif ++#endif ++ jr $r_ra ++ move $r_ret, zero ++ END(sk_load_word) ++ ++LEAF(sk_load_half) ++ is_offset_negative(half) ++FEXPORT(sk_load_half_positive) ++ is_offset_in_header(2, half) ++ /* Offset within header boundaries */ ++ PTR_ADDU t1, $r_skb_data, offset ++ lhu $r_A, 0(t1) ++#ifdef CONFIG_CPU_LITTLE_ENDIAN ++# if MIPS_ISA_REV >= 2 ++ wsbh $r_A, $r_A ++# else ++ sll t0, $r_A, 8 ++ srl t1, $r_A, 8 ++ andi t0, t0, 0xff00 ++ or $r_A, t0, t1 ++# endif ++#endif ++ jr $r_ra ++ move $r_ret, zero ++ END(sk_load_half) ++ ++LEAF(sk_load_byte) ++ is_offset_negative(byte) ++FEXPORT(sk_load_byte_positive) ++ is_offset_in_header(1, byte) ++ /* Offset within header boundaries */ ++ PTR_ADDU t1, $r_skb_data, offset ++ lbu $r_A, 0(t1) ++ jr $r_ra ++ move $r_ret, zero ++ END(sk_load_byte) ++ ++/* ++ * call skb_copy_bits: ++ * (prototype in linux/skbuff.h) ++ * ++ * int skb_copy_bits(sk_buff *skb, int offset, void *to, int len) ++ * ++ * o32 mandates we leave 4 spaces for argument registers in case ++ * the callee needs to use them. Even though we don't care about ++ * the argument registers ourselves, we need to allocate that space ++ * to remain ABI compliant since the callee may want to use that space. ++ * We also allocate 2 more spaces for $r_ra and our return register (*to). ++ * ++ * n64 is a bit different. The *caller* will allocate the space to preserve ++ * the arguments. So in 64-bit kernels, we allocate the 4-arg space for no ++ * good reason but it does not matter that much really. ++ * ++ * (void *to) is returned in r_s0 ++ * ++ */ ++#ifdef CONFIG_CPU_LITTLE_ENDIAN ++#define DS_OFFSET(SIZE) (4 * SZREG) ++#else ++#define DS_OFFSET(SIZE) ((4 * SZREG) + (4 - SIZE)) ++#endif ++#define bpf_slow_path_common(SIZE) \ ++ /* Quick check. Are we within reasonable boundaries? */ \ ++ LONG_ADDIU $r_s1, $r_skb_len, -SIZE; \ ++ sltu $r_s0, offset, $r_s1; \ ++ beqz $r_s0, fault; \ ++ /* Load 4th argument in DS */ \ ++ LONG_ADDIU a3, zero, SIZE; \ ++ PTR_ADDIU $r_sp, $r_sp, -(6 * SZREG); \ ++ PTR_LA t0, skb_copy_bits; \ ++ PTR_S $r_ra, (5 * SZREG)($r_sp); \ ++ /* Assign low slot to a2 */ \ ++ PTR_ADDIU a2, $r_sp, DS_OFFSET(SIZE); \ ++ jalr t0; \ ++ /* Reset our destination slot (DS but it's ok) */ \ ++ INT_S zero, (4 * SZREG)($r_sp); \ ++ /* \ ++ * skb_copy_bits returns 0 on success and -EFAULT \ ++ * on error. Our data live in a2. Do not bother with \ ++ * our data if an error has been returned. \ ++ */ \ ++ /* Restore our frame */ \ ++ PTR_L $r_ra, (5 * SZREG)($r_sp); \ ++ INT_L $r_s0, (4 * SZREG)($r_sp); \ ++ bltz v0, fault; \ ++ PTR_ADDIU $r_sp, $r_sp, 6 * SZREG; \ ++ move $r_ret, zero; \ ++ ++NESTED(bpf_slow_path_word, (6 * SZREG), $r_sp) ++ bpf_slow_path_common(4) ++#ifdef CONFIG_CPU_LITTLE_ENDIAN ++# if MIPS_ISA_REV >= 2 ++ wsbh t0, $r_s0 ++ jr $r_ra ++ rotr $r_A, t0, 16 ++# else ++ sll t0, $r_s0, 24 ++ srl t1, $r_s0, 24 ++ srl t2, $r_s0, 8 ++ or t0, t0, t1 ++ andi t2, t2, 0xff00 ++ andi t1, $r_s0, 0xff00 ++ or t0, t0, t2 ++ sll t1, t1, 8 ++ jr $r_ra ++ or $r_A, t0, t1 ++# endif ++#else ++ jr $r_ra ++ move $r_A, $r_s0 ++#endif ++ ++ END(bpf_slow_path_word) ++ ++NESTED(bpf_slow_path_half, (6 * SZREG), $r_sp) ++ bpf_slow_path_common(2) ++#ifdef CONFIG_CPU_LITTLE_ENDIAN ++# if MIPS_ISA_REV >= 2 ++ jr $r_ra ++ wsbh $r_A, $r_s0 ++# else ++ sll t0, $r_s0, 8 ++ andi t1, $r_s0, 0xff00 ++ andi t0, t0, 0xff00 ++ srl t1, t1, 8 ++ jr $r_ra ++ or $r_A, t0, t1 ++# endif ++#else ++ jr $r_ra ++ move $r_A, $r_s0 ++#endif ++ ++ END(bpf_slow_path_half) ++ ++NESTED(bpf_slow_path_byte, (6 * SZREG), $r_sp) ++ bpf_slow_path_common(1) ++ jr $r_ra ++ move $r_A, $r_s0 ++ ++ END(bpf_slow_path_byte) ++ ++/* ++ * Negative entry points ++ */ ++ .macro bpf_is_end_of_data ++ li t0, SKF_LL_OFF ++ /* Reading link layer data? */ ++ slt t1, offset, t0 ++ bgtz t1, fault ++ /* Be careful what follows in DS. */ ++ .endm ++/* ++ * call skb_copy_bits: ++ * (prototype in linux/filter.h) ++ * ++ * void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, ++ * int k, unsigned int size) ++ * ++ * see above (bpf_slow_path_common) for ABI restrictions ++ */ ++#define bpf_negative_common(SIZE) \ ++ PTR_ADDIU $r_sp, $r_sp, -(6 * SZREG); \ ++ PTR_LA t0, bpf_internal_load_pointer_neg_helper; \ ++ PTR_S $r_ra, (5 * SZREG)($r_sp); \ ++ jalr t0; \ ++ li a2, SIZE; \ ++ PTR_L $r_ra, (5 * SZREG)($r_sp); \ ++ /* Check return pointer */ \ ++ beqz v0, fault; \ ++ PTR_ADDIU $r_sp, $r_sp, 6 * SZREG; \ ++ /* Preserve our pointer */ \ ++ move $r_s0, v0; \ ++ /* Set return value */ \ ++ move $r_ret, zero; \ ++ ++bpf_slow_path_word_neg: ++ bpf_is_end_of_data ++NESTED(sk_load_word_negative, (6 * SZREG), $r_sp) ++ bpf_negative_common(4) ++ jr $r_ra ++ lw $r_A, 0($r_s0) ++ END(sk_load_word_negative) ++ ++bpf_slow_path_half_neg: ++ bpf_is_end_of_data ++NESTED(sk_load_half_negative, (6 * SZREG), $r_sp) ++ bpf_negative_common(2) ++ jr $r_ra ++ lhu $r_A, 0($r_s0) ++ END(sk_load_half_negative) ++ ++bpf_slow_path_byte_neg: ++ bpf_is_end_of_data ++NESTED(sk_load_byte_negative, (6 * SZREG), $r_sp) ++ bpf_negative_common(1) ++ jr $r_ra ++ lbu $r_A, 0($r_s0) ++ END(sk_load_byte_negative) ++ ++fault: ++ jr $r_ra ++ addiu $r_ret, zero, 1 diff --git a/root/target/linux/generic/backport-5.4/071-bpf-dont-allow-vmlinux-btf-to-be-used-in-map_create-and-prog_load.patch b/root/target/linux/generic/backport-5.4/071-bpf-dont-allow-vmlinux-btf-to-be-used-in-map_create-and-prog_load.patch new file mode 100644 index 00000000..a1042739 --- /dev/null +++ b/root/target/linux/generic/backport-5.4/071-bpf-dont-allow-vmlinux-btf-to-be-used-in-map_create-and-prog_load.patch @@ -0,0 +1,80 @@ +Index: linux-5.4.147/kernel/bpf/syscall.c +=================================================================== +--- linux-5.4.147.orig/kernel/bpf/syscall.c ++++ linux-5.4.147/kernel/bpf/syscall.c +@@ -593,6 +593,11 @@ static int map_create(union bpf_attr *at + err = PTR_ERR(btf); + goto free_map; + } ++ if (btf_is_kernel(btf)) { ++ btf_put(btf); ++ err = -EACCES; ++ goto free_map; ++ } + + err = map_check_btf(map, btf, attr->btf_key_type_id, + attr->btf_value_type_id); +Index: linux-5.4.147/kernel/bpf/verifier.c +=================================================================== +--- linux-5.4.147.orig/kernel/bpf/verifier.c ++++ linux-5.4.147/kernel/bpf/verifier.c +@@ -6959,6 +6959,11 @@ static int check_btf_info(struct bpf_ver + btf = btf_get_by_fd(attr->prog_btf_fd); + if (IS_ERR(btf)) + return PTR_ERR(btf); ++ if (btf_is_kernel(btf)) { ++ btf_put(btf); ++ return -EACCES; ++ } ++ + env->prog->aux->btf = btf; + + err = check_btf_func(env, attr, uattr); +Index: linux-5.4.147/include/linux/btf.h +=================================================================== +--- linux-5.4.147.orig/include/linux/btf.h ++++ linux-5.4.147/include/linux/btf.h +@@ -47,6 +47,7 @@ void btf_type_seq_show(const struct btf + struct seq_file *m); + int btf_get_fd_by_id(u32 id); + u32 btf_id(const struct btf *btf); ++bool btf_is_kernel(const struct btf *btf); + bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s, + const struct btf_member *m, + u32 expected_offset, u32 expected_size); +Index: linux-5.4.147/kernel/bpf/btf.c +=================================================================== +--- linux-5.4.147.orig/kernel/bpf/btf.c ++++ linux-5.4.147/kernel/bpf/btf.c +@@ -212,6 +212,7 @@ struct btf { + refcount_t refcnt; + u32 id; + struct rcu_head rcu; ++ bool kernel_btf; + }; + + enum verifier_phase { +@@ -352,6 +353,11 @@ static bool btf_type_nosize(const struct + btf_type_is_func(t) || btf_type_is_func_proto(t); + } + ++bool btf_is_kernel(const struct btf *btf) ++{ ++ return btf->kernel_btf; ++} ++ + static bool btf_type_nosize_or_null(const struct btf_type *t) + { + return !t || btf_type_nosize(t); +Index: linux-5.4.147/include/uapi/linux/bpf.h +=================================================================== +--- linux-5.4.147.orig/include/uapi/linux/bpf.h ++++ linux-5.4.147/include/uapi/linux/bpf.h +@@ -3275,6 +3275,7 @@ struct bpf_btf_info { + __aligned_u64 btf; + __u32 btf_size; + __u32 id; ++ __u32 kernel_btf; + } __attribute__((aligned(8))); + + /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed diff --git a/root/target/linux/generic/backport-5.4/097-bpf-fix-integer-overflow-in-arg-calculation.patch b/root/target/linux/generic/backport-5.4/097-bpf-fix-integer-overflow-in-arg-calculation.patch new file mode 100644 index 00000000..1f14f015 --- /dev/null +++ b/root/target/linux/generic/backport-5.4/097-bpf-fix-integer-overflow-in-arg-calculation.patch @@ -0,0 +1,53 @@ +From: Bui Quang Minh @ 2021-01-26 8:26 UTC (permalink / raw) + To: ast, daniel, davem, kuba, hawk, john.fastabend, andrii, kafai, + songliubraving, yhs, kpsingh, jakub, lmb + Cc: netdev, bpf, linux-kernel, minhquangbui99 + +In 32-bit architecture, the result of sizeof() is a 32-bit integer so +the expression becomes the multiplication between 2 32-bit integer which +can potentially leads to integer overflow. As a result, +bpf_map_area_alloc() allocates less memory than needed. + +Fix this by casting 1 operand to u64. + +Signed-off-by: Bui Quang Minh +--- + kernel/bpf/devmap.c | 4 ++-- + net/core/sock_map.c | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +Index: linux-5.4.147/kernel/bpf/devmap.c +=================================================================== +--- linux-5.4.147.orig/kernel/bpf/devmap.c ++++ linux-5.4.147/kernel/bpf/devmap.c +@@ -94,7 +94,7 @@ static struct hlist_head *dev_map_create + int i; + struct hlist_head *hash; + +- hash = bpf_map_area_alloc(entries * sizeof(*hash), numa_node); ++ hash = bpf_map_area_alloc((u64) entries * sizeof(*hash), numa_node); + if (hash != NULL) + for (i = 0; i < entries; i++) + INIT_HLIST_HEAD(&hash[i]); +@@ -159,7 +159,7 @@ static int dev_map_init_map(struct bpf_d + + spin_lock_init(&dtab->index_lock); + } else { +- dtab->netdev_map = bpf_map_area_alloc(dtab->map.max_entries * ++ dtab->netdev_map = bpf_map_area_alloc((u64) dtab->map.max_entries * + sizeof(struct bpf_dtab_netdev *), + dtab->map.numa_node); + if (!dtab->netdev_map) +Index: linux-5.4.147/net/core/sock_map.c +=================================================================== +--- linux-5.4.147.orig/net/core/sock_map.c ++++ linux-5.4.147/net/core/sock_map.c +@@ -48,7 +48,7 @@ static struct bpf_map *sock_map_alloc(un + if (err) + goto free_stab; + +- stab->sks = bpf_map_area_alloc(stab->map.max_entries * ++ stab->sks = bpf_map_area_alloc((u64) stab->map.max_entries * + sizeof(struct sock *), + stab->map.numa_node); + if (stab->sks) diff --git a/root/target/linux/generic/backport-5.4/500-ovl-check-permission-to-open-real-file.patch b/root/target/linux/generic/backport-5.4/500-ovl-check-permission-to-open-real-file.patch new file mode 100644 index 00000000..61751f68 --- /dev/null +++ b/root/target/linux/generic/backport-5.4/500-ovl-check-permission-to-open-real-file.patch @@ -0,0 +1,48 @@ +From 05acefb4872dae89e772729efb194af754c877e8 Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Tue, 2 Jun 2020 22:20:26 +0200 +Subject: ovl: check permission to open real file + +Call inode_permission() on real inode before opening regular file on one of +the underlying layers. + +In some cases ovl_permission() already checks access to an underlying file, +but it misses the metacopy case, and possibly other ones as well. + +Removing the redundant permission check from ovl_permission() should be +considered later. + +Signed-off-by: Miklos Szeredi +--- + fs/overlayfs/file.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +Index: linux-5.4.147/fs/overlayfs/file.c +=================================================================== +--- linux-5.4.147.orig/fs/overlayfs/file.c ++++ linux-5.4.147/fs/overlayfs/file.c +@@ -34,10 +34,22 @@ static struct file *ovl_open_realfile(co + struct file *realfile; + const struct cred *old_cred; + int flags = file->f_flags | OVL_OPEN_FLAGS; ++ int acc_mode = ACC_MODE(flags); ++ int err; ++ ++ if (flags & O_APPEND) ++ acc_mode |= MAY_APPEND; + + old_cred = ovl_override_creds(inode->i_sb); +- realfile = open_with_fake_path(&file->f_path, flags, realinode, +- current_cred()); ++ err = inode_permission(realinode, MAY_OPEN | acc_mode); ++ if (err) { ++ realfile = ERR_PTR(err); ++ } else if (!inode_owner_or_capable(realinode)) { ++ realfile = ERR_PTR(-EPERM); ++ } else { ++ realfile = open_with_fake_path(&file->f_path, flags, realinode, ++ current_cred()); ++ } + revert_creds(old_cred); + + pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n", diff --git a/root/target/linux/generic/backport-5.4/501-ovl-switch-to-mounter-creds-in-readdir.patch b/root/target/linux/generic/backport-5.4/501-ovl-switch-to-mounter-creds-in-readdir.patch new file mode 100644 index 00000000..be4a7643 --- /dev/null +++ b/root/target/linux/generic/backport-5.4/501-ovl-switch-to-mounter-creds-in-readdir.patch @@ -0,0 +1,73 @@ +From 48bd024b8a40d73ad6b086de2615738da0c7004f Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Tue, 2 Jun 2020 22:20:25 +0200 +Subject: ovl: switch to mounter creds in readdir + +In preparation for more permission checking, override credentials for +directory operations on the underlying filesystems. + +Signed-off-by: Miklos Szeredi +--- + fs/overlayfs/readdir.c | 27 +++++++++++++++++++++------ + 1 file changed, 21 insertions(+), 6 deletions(-) + +Index: linux-5.4.147/fs/overlayfs/readdir.c +=================================================================== +--- linux-5.4.147.orig/fs/overlayfs/readdir.c ++++ linux-5.4.147/fs/overlayfs/readdir.c +@@ -732,8 +732,10 @@ static int ovl_iterate(struct file *file + struct ovl_dir_file *od = file->private_data; + struct dentry *dentry = file->f_path.dentry; + struct ovl_cache_entry *p; ++ const struct cred *old_cred; + int err; + ++ old_cred = ovl_override_creds(dentry->d_sb); + if (!ctx->pos) + ovl_dir_reset(file); + +@@ -747,17 +749,20 @@ static int ovl_iterate(struct file *file + (ovl_same_sb(dentry->d_sb) && + (ovl_is_impure_dir(file) || + OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) { +- return ovl_iterate_real(file, ctx); ++ err = ovl_iterate_real(file, ctx); ++ } else { ++ err = iterate_dir(od->realfile, ctx); + } +- return iterate_dir(od->realfile, ctx); ++ goto out; + } + + if (!od->cache) { + struct ovl_dir_cache *cache; + + cache = ovl_cache_get(dentry); ++ err = PTR_ERR(cache); + if (IS_ERR(cache)) +- return PTR_ERR(cache); ++ goto out; + + od->cache = cache; + ovl_seek_cursor(od, ctx->pos); +@@ -769,7 +774,7 @@ static int ovl_iterate(struct file *file + if (!p->ino) { + err = ovl_cache_update_ino(&file->f_path, p); + if (err) +- return err; ++ goto out; + } + if (!dir_emit(ctx, p->name, p->len, p->ino, p->type)) + break; +@@ -777,7 +782,10 @@ static int ovl_iterate(struct file *file + od->cursor = p->l_node.next; + ctx->pos++; + } +- return 0; ++ err = 0; ++out: ++ revert_creds(old_cred); ++ return err; + } + + static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin) diff --git a/root/target/linux/generic/backport-5.4/502-ovl-verify-permissions-in-ovl_path_open.patch b/root/target/linux/generic/backport-5.4/502-ovl-verify-permissions-in-ovl_path_open.patch new file mode 100644 index 00000000..7ea95909 --- /dev/null +++ b/root/target/linux/generic/backport-5.4/502-ovl-verify-permissions-in-ovl_path_open.patch @@ -0,0 +1,61 @@ +From 56230d956739b9cb1cbde439d76227d77979a04d Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Tue, 2 Jun 2020 22:20:26 +0200 +Subject: ovl: verify permissions in ovl_path_open() + +Check permission before opening a real file. + +ovl_path_open() is used by readdir and copy-up routines. + +ovl_permission() theoretically already checked copy up permissions, but it +doesn't hurt to re-do these checks during the actual copy-up. + +For directory reading ovl_permission() only checks access to topmost +underlying layer. Readdir on a merged directory accesses layers below the +topmost one as well. Permission wasn't checked for these layers. + +Note: modifying ovl_permission() to perform this check would be far more +complex and hence more bug prone. The result is less precise permissions +returned in access(2). If this turns out to be an issue, we can revisit +this bug. + +Signed-off-by: Miklos Szeredi +--- + fs/overlayfs/util.c | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +Index: linux-5.4.147/fs/overlayfs/util.c +=================================================================== +--- linux-5.4.147.orig/fs/overlayfs/util.c ++++ linux-5.4.147/fs/overlayfs/util.c +@@ -475,7 +475,29 @@ bool ovl_is_whiteout(struct dentry *dent + + struct file *ovl_path_open(struct path *path, int flags) + { +- return dentry_open(path, flags | O_NOATIME, current_cred()); ++ struct inode *inode = d_inode(path->dentry); ++ int err, acc_mode; ++ ++ switch (flags & O_ACCMODE) { ++ case O_RDONLY: ++ acc_mode = MAY_READ; ++ break; ++ case O_WRONLY: ++ acc_mode = MAY_WRITE; ++ break; ++ default: ++ BUG(); ++ } ++ ++ err = inode_permission(inode, acc_mode | MAY_OPEN); ++ if (err) ++ return ERR_PTR(err); ++ ++ /* O_NOATIME is an optimization, don't fail if not permitted */ ++ if (inode_owner_or_capable(inode)) ++ flags |= O_NOATIME; ++ ++ return dentry_open(path, flags, current_cred()); + } + + /* Caller should hold ovl_inode->lock */ diff --git a/root/target/linux/generic/backport-5.4/503-ovl-do-not-fail-because-of_O_NOACTIME.patch b/root/target/linux/generic/backport-5.4/503-ovl-do-not-fail-because-of_O_NOACTIME.patch new file mode 100644 index 00000000..03c96315 --- /dev/null +++ b/root/target/linux/generic/backport-5.4/503-ovl-do-not-fail-because-of_O_NOACTIME.patch @@ -0,0 +1,46 @@ +From b6650dab404c701d7fe08a108b746542a934da84 Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Mon, 14 Dec 2020 15:26:14 +0100 +Subject: ovl: do not fail because of O_NOATIME + +In case the file cannot be opened with O_NOATIME because of lack of +capabilities, then clear O_NOATIME instead of failing. + +Remove WARN_ON(), since it would now trigger if O_NOATIME was cleared. +Noticed by Amir Goldstein. + +Signed-off-by: Miklos Szeredi +--- + fs/overlayfs/file.c | 11 +++-------- + 1 file changed, 3 insertions(+), 8 deletions(-) + +Index: linux-5.4.147/fs/overlayfs/file.c +=================================================================== +--- linux-5.4.147.orig/fs/overlayfs/file.c ++++ linux-5.4.147/fs/overlayfs/file.c +@@ -44,9 +44,10 @@ static struct file *ovl_open_realfile(co + err = inode_permission(realinode, MAY_OPEN | acc_mode); + if (err) { + realfile = ERR_PTR(err); +- } else if (!inode_owner_or_capable(realinode)) { +- realfile = ERR_PTR(-EPERM); + } else { ++ if (!inode_owner_or_capable(realinode)) ++ flags &= ~O_NOATIME; ++ + realfile = open_with_fake_path(&file->f_path, flags, realinode, + current_cred()); + } +@@ -66,12 +67,6 @@ static int ovl_change_flags(struct file + struct inode *inode = file_inode(file); + int err; + +- flags |= OVL_OPEN_FLAGS; +- +- /* If some flag changed that cannot be changed then something's amiss */ +- if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK)) +- return -EIO; +- + flags &= OVL_SETFL_MASK; + + if (((flags ^ file->f_flags) & O_APPEND) && IS_APPEND(inode)) diff --git a/root/target/linux/generic/backport-5.4/700-ignore-pppoe-when-dst-does-not-match-dev-address.patch b/root/target/linux/generic/backport-5.4/700-ignore-pppoe-when-dst-does-not-match-dev-address.patch new file mode 100644 index 00000000..e562f5cb --- /dev/null +++ b/root/target/linux/generic/backport-5.4/700-ignore-pppoe-when-dst-does-not-match-dev-address.patch @@ -0,0 +1,17 @@ +Index: linux-5.4.124/drivers/net/ppp/pppoe.c +=================================================================== +--- linux-5.4.124.orig/drivers/net/ppp/pppoe.c ++++ linux-5.4.124/drivers/net/ppp/pppoe.c +@@ -502,6 +502,12 @@ static int pppoe_disc_rcv(struct sk_buff + if (ph->code != PADT_CODE) + goto abort; + ++ // compare the dst addr to the current net device addr and ignore packet if not matching ++ // otherwise it will terminate the connection ++ if(!ether_addr_equal(eth_hdr(skb)->h_dest, dev->dev_addr)) { ++ goto abort; ++ } ++ + pn = pppoe_pernet(dev_net(dev)); + po = get_item(pn, ph->sid, eth_hdr(skb)->h_source, dev->ifindex); + if (po) diff --git a/root/target/linux/generic/pending-5.4/481-mtd-spi-nor-Add-default-and-support-for-missing-flashes.patch b/root/target/linux/generic/pending-5.4/481-mtd-spi-nor-Add-default-and-support-for-missing-flashes.patch new file mode 100644 index 00000000..eb007da1 --- /dev/null +++ b/root/target/linux/generic/pending-5.4/481-mtd-spi-nor-Add-default-and-support-for-missing-flashes.patch @@ -0,0 +1,50 @@ +Index: linux-5.4.124/drivers/mtd/spi-nor/spi-nor.c +=================================================================== +--- linux-5.4.124.orig/drivers/mtd/spi-nor/spi-nor.c ++++ linux-5.4.124/drivers/mtd/spi-nor/spi-nor.c +@@ -2241,6 +2241,9 @@ static const struct flash_info spi_nor_i + .fixups = &gd25q256_fixups, + }, + ++ /* Zbit */ ++ { "zb25vq128", INFO(0xC84018, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ + /* Intel/Numonyx -- xxxs33b */ + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, +@@ -2405,6 +2408,7 @@ static const struct flash_info spi_nor_i + { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, + { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, + { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, ++ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0) }, + + { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, + { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, +@@ -2508,10 +2512,14 @@ static const struct flash_info spi_nor_i + { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + + /* XTX Technology (Shenzhen) Limited */ ++ { "XT25F128A", INFO(0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "xt25f128b", INFO(0x0B4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { }, + }; + ++static const struct flash_info spi_nor_unknown_id = ++ { "undefined", INFO(0x0, 0, 64 * 1024, 256, SECT_4K) }; ++ + static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + { + int tmp; +@@ -2542,9 +2550,12 @@ static const struct flash_info *spi_nor_ + return &spi_nor_ids[tmp]; + } + } ++ return &spi_nor_unknown_id; // for Teltonikia RUT devices ++ /* + dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", + SPI_NOR_MAX_ID_LEN, id); + return ERR_PTR(-ENODEV); ++ */ + } + + static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, diff --git a/root/target/linux/generic/pending-5.4/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch b/root/target/linux/generic/pending-5.4/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch new file mode 100644 index 00000000..d3156f5c --- /dev/null +++ b/root/target/linux/generic/pending-5.4/483-mtd-spinand-add-support-for-xtx-xt26g0xa.patch @@ -0,0 +1,198 @@ +From a07e31adf2753cad2fd9790db5bfc047c81e8152 Mon Sep 17 00:00:00 2001 +From: Felix Matouschek +Date: Fri, 2 Jul 2021 20:31:23 +0200 +Subject: [PATCH] mtd: spinand: Add support for XTX XT26G0xA + +Add support for XTX Technology XT26G01AXXXXX, XTX26G02AXXXXX and +XTX26G04AXXXXX SPI NAND. + +These are 3V, 1G/2G/4Gbit serial SLC NAND flash devices with on-die ECC +(8bit strength per 512bytes). + +Tested on Teltonika RUTX10 flashed with OpenWrt. + +Datasheets available at +http://www.xtxtech.com/download/?AId=225 +https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf + +Signed-off-by: Felix Matouschek +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/xtx.c | 122 ++++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 125 insertions(+), 1 deletion(-) + create mode 100644 drivers/mtd/nand/spi/xtx.c + +Index: linux-5.4.132/drivers/mtd/nand/spi/Makefile +=================================================================== +--- linux-5.4.132.orig/drivers/mtd/nand/spi/Makefile ++++ linux-5.4.132/drivers/mtd/nand/spi/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o ++spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +Index: linux-5.4.132/drivers/mtd/nand/spi/core.c +=================================================================== +--- linux-5.4.132.orig/drivers/mtd/nand/spi/core.c ++++ linux-5.4.132/drivers/mtd/nand/spi/core.c +@@ -758,6 +758,7 @@ static const struct spinand_manufacturer + ¶gon_spinand_manufacturer, + &toshiba_spinand_manufacturer, + &winbond_spinand_manufacturer, ++ &xtx_spinand_manufacturer, + }; + + static int spinand_manufacturer_detect(struct spinand_device *spinand) +Index: linux-5.4.132/drivers/mtd/nand/spi/xtx.c +=================================================================== +--- /dev/null ++++ linux-5.4.132/drivers/mtd/nand/spi/xtx.c +@@ -0,0 +1,134 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Author: ++ * Felix Matouschek ++ */ ++ ++#include ++#include ++#include ++ ++#define SPINAND_MFR_XTX 0x0B ++ ++#define XT26G0XA_STATUS_ECC_MASK GENMASK(5, 2) ++#define XT26G0XA_STATUS_ECC_NO_DETECTED (0 << 2) ++#define XT26G0XA_STATUS_ECC_8_CORRECTED (3 << 4) ++#define XT26G0XA_STATUS_ECC_UNCOR_ERROR (2 << 4) ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ region->offset = 8; ++ region->length = 40; ++ ++ return 0; ++} ++ ++static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section) ++ return -ERANGE; ++ ++ region->offset = 1; ++ region->length = 7; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = { ++ .ecc = xt26g0xa_ooblayout_ecc, ++ .free = xt26g0xa_ooblayout_free, ++}; ++ ++static int xt26g0xa_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & XT26G0XA_STATUS_ECC_MASK) { ++ case XT26G0XA_STATUS_ECC_NO_DETECTED: ++ return 0; ++ case XT26G0XA_STATUS_ECC_8_CORRECTED: ++ return 8; ++ case XT26G0XA_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ default: /* (1 << 2) through (7 << 2) are 1-7 corrected errors */ ++ return (status & XT26G0XA_STATUS_ECC_MASK) >> 2; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct spinand_info xtx_spinand_table[] = { ++ SPINAND_INFO("XT26G01A", 0xE1, ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&xt26g0xa_ooblayout, ++ xt26g0xa_ecc_get_status)), ++ SPINAND_INFO("XT26G02A", 0xE2, ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&xt26g0xa_ooblayout, ++ xt26g0xa_ecc_get_status)), ++ SPINAND_INFO("XT26G04A", 0xE3, ++ NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&xt26g0xa_ooblayout, ++ xt26g0xa_ecc_get_status)), ++}; ++ ++static int xtx_spinand_detect(struct spinand_device *spinand) ++{ ++ u8 *id = spinand->id.data; ++ int ret; ++ ++ if (id[1] != SPINAND_MFR_XTX) ++ return 0; ++ ++ ret = spinand_match_and_init(spinand, xtx_spinand_table, ++ ARRAY_SIZE(xtx_spinand_table), id[2]); ++ if (ret) ++ return ret; ++ ++ return 1; ++} ++ ++static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = { ++ .detect = xtx_spinand_detect, ++}; ++ ++const struct spinand_manufacturer xtx_spinand_manufacturer = { ++ .id = SPINAND_MFR_XTX, ++ .name = "XTX", ++ .ops = &xtx_spinand_manuf_ops, ++}; +Index: linux-5.4.132/include/linux/mtd/spinand.h +=================================================================== +--- linux-5.4.132.orig/include/linux/mtd/spinand.h ++++ linux-5.4.132/include/linux/mtd/spinand.h +@@ -230,6 +230,7 @@ extern const struct spinand_manufacturer + extern const struct spinand_manufacturer paragon_spinand_manufacturer; + extern const struct spinand_manufacturer toshiba_spinand_manufacturer; + extern const struct spinand_manufacturer winbond_spinand_manufacturer; ++extern const struct spinand_manufacturer xtx_spinand_manufacturer; + + /** + * struct spinand_op_variants - SPI NAND operation variants diff --git a/root/target/linux/ipq40xx/base-files/bin/backup b/root/target/linux/ipq40xx/base-files/bin/backup new file mode 100755 index 00000000..0b83b6ed --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/bin/backup @@ -0,0 +1,80 @@ +#!/bin/sh + +main() { + if [ $1 == "check_backup" ]; then + check_backup + elif [ $1 == "apply_backup" ]; then + apply_backup + fi +} + +reverse_string() { + local input="$1" + local reverse="" + + local len=${#input} + local i=$(($len-1)) + while [ $i -ge 0 ]; do + reverse="$reverse${input:$i:1}" + i=$((i-1)) + done + + echo "$reverse" +} + +check_backup() { + local size + local write_new_config_ok='1' + + local this_device_code=$(uci -q get system.system.device_code) + local this_device_code_len=${#this_device_code} + + local device_code_in_the_new_config=$(cat /tmp/new_config_dir/etc/config/system | grep device_code | cut -d ' ' -f3) + device_code_in_the_new_config=${device_code_in_the_new_config:1} + device_code_in_the_new_config=${device_code_in_the_new_config%?} + local device_code_in_new_config_len=${#device_code_in_the_new_config} + + local this_device_name=${this_device_code:0:4} + [ "$this_device_name" = "RUT2" ] && size=8 || size=7 + + this_device_code=${this_device_code:0:$size} + device_code_in_the_new_config=${device_code_in_the_new_config:0:$size} + + local this_device_fw_version=$(cat /etc/version) + + local fw_version_in_new_config=$(cat /tmp/new_config_dir/etc/config/system | grep device_fw_version | cut -d ' ' -f3) + fw_version_in_new_config=${fw_version_in_new_config:1} + fw_version_in_new_config=${fw_version_in_new_config%?} + local fw_version_in_new_config_len=${#fw_version_in_new_config} + + this_device_fw_version=$(reverse_string $this_device_fw_version) + this_device_fw_version=$(echo $this_device_fw_version | awk -F _ '{print $1}') + this_device_fw_version=$(reverse_string $this_device_fw_version) + + fw_version_in_new_config=$(reverse_string $fw_version_in_new_config) + fw_version_in_new_config=$(echo $fw_version_in_new_config | awk -F _ '{print $1}') + fw_version_in_new_config=$(reverse_string $fw_version_in_new_config) + + if [ $this_device_code_len -ne 12 ] || [ $device_code_in_new_config_len -ne 12 ] || [ "$this_device_code" != "$device_code_in_the_new_config" ]; then + write_new_config_ok='0' + fi + + if [ $fw_version_in_new_config_len -lt 12 ] || [ $(expr ${this_device_fw_version} \< ${fw_version_in_new_config}) -eq 1 ]; then + write_new_config_ok='0' + fi + + echo "$write_new_config_ok" +} + +apply_backup() { + /etc/init.d/simcard reload >/dev/null 2>/dev/null + rm /tmp/new_config_dir/etc/config/hwinfo /tmp/new_config_dir/etc/inittab 2>/dev/null + sed -i "s/\/home\/root/\/root/" /tmp/new_config_dir/etc/passwd + sed -i "s/\/bin\/sh/\/bin\/ash/" /tmp/new_config_dir/etc/passwd + cp -rf /tmp/new_config_dir/etc/ / 2>/dev/null + cp -r /rom/etc/uci-defaults/* /etc/uci-defaults/ 2>/dev/null +} + +main $1 + +exit 0 \ No newline at end of file diff --git a/root/target/linux/ipq40xx/base-files/bin/board_detect b/root/target/linux/ipq40xx/base-files/bin/board_detect new file mode 100755 index 00000000..840bd017 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/bin/board_detect @@ -0,0 +1,14 @@ +#!/bin/sh + +CFG=$1 + +[ -n "$CFG" ] || CFG=/etc/board.json + +[ -d "/etc/board.d/" -a ! -s "$CFG" ] && { + for a in $(ls /etc/board.d/*); do + [ -x $a ] || continue; + $(. $a) + done +} + +[ -s "$CFG" ] || return 1 diff --git a/root/target/linux/ipq40xx/base-files/bin/board_modem b/root/target/linux/ipq40xx/base-files/bin/board_modem new file mode 100755 index 00000000..7628fc86 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/bin/board_modem @@ -0,0 +1,307 @@ +#!/bin/sh +# +# Copyright (c) 2015 The Linux Foundation. All rights reserved. +# Copyright (c) 2011-2015 OpenWrt.org +# + +. /lib/functions/uci-defaults.sh +. /lib/functions/system.sh + +CFG=/etc/board.json + +# do not run on preinit/early init +[ "$EARLY_INIT" ] && return + +strstr() { + [ "${1#*$2*}" = "$1" ] && return 1 + return 0 +} + +print_array() { + json_add_array $1 + case "$1" in + 4G) + for element in $2 + do + json_add_string "" "$(echo $element)" + done + ;; + 3G) + for element in $2 + do + json_add_string "" "wcdma_$(echo $element)" + done + ;; + 2G) + for element in $2 + do + json_add_string "" "$(echo $element)" + done + ;; + esac + json_close_array +} + +gather_band_capabilities() { + # Same logic as unhandler.c + ###################### EG06 ######################### + if strstr $revision_from_unhandler "EG06E"; then #EG06E + lte_bands="1 3 5 7 8 20 28 32 38 40 41" #B + trysg_bands="850 900 1800 2100" #MHz + dug_bands="" + elif strstr $revision_from_unhandler "EG06A"; then #EG06A + lte_bands="2 4 5 7 12 13 25 26 29 30 66" + trysg_bands="850 1700 1900" + dug_bands="" + ###################### EC25 ######################### + elif strstr $revision_from_unhandler "EC25EF"; then #EC25E + lte_bands="1 3 5 7 8 20 38 40 41" + trysg_bands="850 900 2100" + dug_bands="900 1800" #MHz + elif strstr $revision_from_unhandler "EC25EC"; then #EC25EC + lte_bands="1 3 7 8 20 28" + trysg_bands="900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "EC25AUX"; then #EC25AUX + lte_bands="1 2 3 4 5 7 8 28 40" + trysg_bands="850 900 1700 1900 2100" + dug_bands="850 900 1800 1900" + elif strstr $revision_from_unhandler "EC25AFA"; then #EC25A + lte_bands="2 4 12" + trysg_bands="850 1700 1900" + dug_bands="" + elif strstr $revision_from_unhandler "EC25V"; then #EC25V + lte_bands="4 13" + trysg_bands="" + dug_bands="" + elif strstr $revision_from_unhandler "EC25AFX"; then #EC25AFX + lte_bands="2 4 5 12 13 14 66 71" + trysg_bands="850 1700 1900" + dug_bands="" + elif strstr $revision_from_unhandler "EC25AFF"; then #EC25AF + lte_bands="2 4 5 12 13 14 66 71" + trysg_bands="850 1700 1900" + dug_bands="" + elif strstr $revision_from_unhandler "EC25AUTF"; then #EC25AUT + lte_bands="1 3 5 7 28" + trysg_bands="850 2100" + dug_bands="" + elif strstr $revision_from_unhandler "EC25AUTL"; then #EC25AUTL + lte_bands="3 7 28" + trysg_bands="" + dug_bands="" + elif strstr $revision_from_unhandler "EC25AUF"; then #EC25AU + lte_bands="1 2 3 4 5 7 8 28 40" + trysg_bands="850 900 1900 2100" + dug_bands="850 900 1800 1900" + elif strstr $revision_from_unhandler "EC25J"; then #EC25J + lte_bands="1 3 8 18 19 26 41" + trysg_bands="800 900 2100" + dug_bands="" + elif strstr $revision_from_unhandler "EC25EUX"; then #EC25EUX + lte_bands="1 3 7 8 20 28 38 40 41" + trysg_bands="900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "EC25EUF"; then #EC25EU + lte_bands="1 3 7 8 20 28 38 40 41" + trysg_bands="900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "EC25EUG"; then #EC25EU + lte_bands="1 3 7 8 20 28 38 40 41" + trysg_bands="900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "EC25MX"; then #EC25MX + lte_bands="2 4 5 7 28 66" + trysg_bands="850 1700 1900" + dug_bands="" + ###################### EC21 ######################### + elif strstr $revision_from_unhandler "EC21EUX"; then #EC21EUX + lte_bands="1 3 7 8 20 28" + trysg_bands="900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "EC21EU"; then #EC21EU + lte_bands="1 3 7 8 20 28" + trysg_bands="900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "EC21EC"; then #EC21EC + lte_bands="1 3 7 8 20 28" + trysg_bands="900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "EC21E"; then #EC21E + lte_bands="1 3 5 7 8 20" + trysg_bands="850 900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "EC21V"; then #EC21V + lte_bands="4 13" + trysg_bands="" + dug_bands="" + elif strstr $revision_from_unhandler "EC21KL"; then #EC21KL + lte_bands="1 3 5 7 8" + trysg_bands="" + dug_bands="" + elif strstr $revision_from_unhandler "EC21J"; then #EC21J + lte_bands="1 3 8 18 19 26" + trysg_bands="" + dug_bands="" + elif strstr $revision_from_unhandler "EC21AUX"; then #EC21AUX + lte_bands="1 2 3 4 5 7 8 28 40" + trysg_bands="850 900 1700 1900 2100" + dug_bands="850 900 1800 1900" + elif strstr $revision_from_unhandler "EC21AUT"; then #EC21AUT + lte_bands="1 3 5 7 28" + trysg_bands="850 2100" + dug_bands="" + elif strstr $revision_from_unhandler "EC21AU"; then #EC21AU + lte_bands="1 2 3 4 5 7 8 28 40" + trysg_bands="850 900 1900 2100" + dug_bands="850 900 1800 1900" + elif strstr $revision_from_unhandler "EC21A"; then #EC21A + lte_bands="2 4 12" + trysg_bands="850 1700 1900" + dug_bands="" + ###################### EG25 ######################### + elif strstr $revision_from_unhandler "EG25G"; then #EG25G + lte_bands="1 2 3 4 5 7 8 12 13 18 19 20 25 26 28 38 39 40 41" + trysg_bands="800 850 900 1700 1900 2100" + dug_bands="850 900 1800 1900" + elif strstr $revision_from_unhandler "EG12EA"; then #EG12EA + lte_bands="1 3 5 7 8 20 28 38 40 41" + trysg_bands="850 900 1800 2100" + dug_bands="" + elif strstr $revision_from_unhandler "EG12NA"; then #EG12NA + lte_bands="2 4 5 7 12 13 14 17 25 26 29 30 41 66 71" + trysg_bands="850 1700 1900" + dug_bands="" + elif strstr $revision_from_unhandler "BG96"; then #BG96M + lte_bands="1 2 3 4 5 8 12 13 18 19 20 26 28 39" + trysg_bands="" + dug_bands="850 900 1800 1900" + ##################### SLM750 ######################## + elif strstr $revision_from_unhandler "750VE"; then #SLM750VE + lte_bands="1 3 5 7 8 20 40" + trysg_bands="850 900 2100" + dug_bands="900 1800" + elif strstr $revision_from_unhandler "750VAU"; then #SLM750VAU + lte_bands="1 3 5 7 8 28 40" + trysg_bands="850 900 2100" + dug_bands="850 900 1800" + elif strstr $revision_from_unhandler "750VA"; then #SLM750VA + lte_bands="2 4 5 12 13 17 18 25 26 41" + trysg_bands="850 1700 1900" + dug_bands="850 1900" + elif strstr $revision_from_unhandler "750VJ"; then #SLM750VJ + lte_bands="1 3 8 18 19 26 41" + trysg_bands="800 900 2100" + dug_bands="" + elif strstr $revision_from_unhandler "750VSA"; then #SLM750VSA + lte_bands="2 4 5 7 8 28 40" + trysg_bands="850 900 1900" + dug_bands="850 900 1900" + ###################### UC20 ######################### + elif strstr $revision_from_unhandler "UC20E"; then #UC20E + lte_bands="" + trysg_bands="900 2100" + dug_bands="850 900 1800 1900" + elif strstr $revision_from_unhandler "UC20G"; then #UC20G + lte_bands="" + trysg_bands="800 850 900 1900 2100" + dug_bands="850 900 1800 1900" + elif strstr $revision_from_unhandler "UC20A"; then #UC20A + lte_bands="" + trysg_bands="850 1900" + dug_bands="" + else + lte_bands="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28" + trysg_bands="700 800 850 900 1500 1700 2600" + dug_bands="1700 1800 1900 2100" + fi +} + +validate_service_modes() { + json_get_keys service_modes service_modes + + found_modes="$(printf "$service_modes" | awk '!seen[$0]++'| wc -l)" + [ "$found_modes" -eq 0 ] && { + return 0 + } + + return 1 +} + +#~ Get model name for RUTX products +setup_modem() { + local key="$1" + local object_num="$2" + local id gps boudrate type desc control product vendor stop_bits + + json_select "$object_num" + json_get_vars id product + + if [ "$id" = "$id_from_unhandler" ]; then + + [ -z "$product" ] || \ + { + [ -f "/sys/bus/usb/devices/$id/idVendor" ] && [ -f "/sys/bus/usb/devices/$id/idProduct" ] || { + json_select .. + return 1 + } + + validate_service_modes && { + gather_band_capabilities + json_select_object service_modes + [ -z "$lte_bands" ] || print_array "4G" "$lte_bands" + [ -z "$trysg_bands" ] || print_array "3G" "$trysg_bands" + [ -z "$dug_bands" ] || print_array "2G" "$dug_bands" + json_select .. + } + json_select .. + return 1 + } + + vendor="$(cat "/sys/bus/usb/devices/$id/idVendor")" + product="$(cat "/sys/bus/usb/devices/$id/idProduct")" + + [ -f "/lib/network/wwan/$vendor:$product" ] && { + devicename="$id" + + gather_band_capabilities + + json_set_namespace defaults old_cb + json_load "$(cat /lib/network/wwan/$vendor:$product)" + json_get_vars gps boudrate type desc control stop_bits + json_set_namespace "$old_cb" + + [ "${devicename%%:*}" = "$devicename" ] && { + json_add_string vendor "$vendor" + json_add_string product "$product" + json_add_string gps "$gps" + json_add_string stop_bits "$stop_bits" + json_add_string boudrate "$boudrate" + json_add_string type "$type" + json_add_string desc "$desc" + json_add_string control "$control" + json_add_object service_modes + [ -z "$lte_bands" ] || print_array "4G" "$lte_bands" + [ -z "$trysg_bands" ] || print_array "3G" "$trysg_bands" + [ -z "$dug_bands" ] || print_array "2G" "$dug_bands" + json_close_object + } + } + fi + json_select .. +} + +[ -s "${CFG}" ] || exit 1 + +id_from_unhandler="$1" +revision_from_unhandler="$2" + +lock /var/run/board_modem.lock + +board_config_update +json_for_each_item setup_modem modems +board_config_flush + +lock -u /var/run/board_modem.lock + +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/bin/board_track b/root/target/linux/ipq40xx/base-files/bin/board_track new file mode 100755 index 00000000..3f620e73 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/bin/board_track @@ -0,0 +1,36 @@ +#!/bin/sh + +. /lib/functions/uci-defaults.sh + +CFG=/etc/board.json +SLP=30 + +check_modem() { + json_select "$2" + json_get_vars id + + [ -z "$id" ] && { + json_select .. + return 0 + } + + ttys=$(ls -d /sys/bus/usb/devices/$id/${id}*/tty?*) + + [ -n "$ttys" ] || { #FAILED TO FIND MODEM + mctl -s + sleep 1 + mctl -p + json_select .. + return 1 + } + + #MODEM UP + json_select .. +} + +board_config_update +while true; do + json_for_each_item check_modem modems + sleep $SLP + [ $SLP -lt 300 ] && SLP=$((SLP+30)) +done diff --git a/root/target/linux/ipq40xx/base-files/bin/config_generate b/root/target/linux/ipq40xx/base-files/bin/config_generate new file mode 100755 index 00000000..acd08754 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/bin/config_generate @@ -0,0 +1,665 @@ +#!/bin/sh + +CFG=/etc/board.json + +. /usr/share/libubox/jshn.sh + +[ -s $CFG ] || /bin/board_detect || exit 1 + +[ -s /etc/config/network ] && \ +[ -s /etc/config/system ] && \ +[ -s /etc/config/hwinfo ] && \ +[ -s /etc/config/blesem ] && \ +exit 0 + +generate_static_network() { + uci -q batch <<-EOF + delete network.loopback + set network.loopback='interface' + set network.loopback.ifname='lo' + set network.loopback.proto='static' + set network.loopback.ipaddr='127.0.0.1' + set network.loopback.netmask='255.0.0.0' + EOF + [ -e /proc/sys/net/ipv6 ] && { + uci -q batch <<-EOF + delete network.globals + set network.globals='globals' + set network.globals.ula_prefix='auto' + EOF + } + + if json_is_a dsl object; then + json_select dsl + if json_is_a atmbridge object; then + json_select atmbridge + local vpi vci encaps payload nameprefix + json_get_vars vpi vci encaps payload nameprefix + uci -q batch <<-EOF + delete network.atm + set network.atm='atm-bridge' + set network.atm.vpi='$vpi' + set network.atm.vci='$vci' + set network.atm.encaps='$encaps' + set network.atm.payload='$payload' + set network.atm.nameprefix='$nameprefix' + EOF + json_select .. + fi + + if json_is_a modem object; then + json_select modem + local type annex firmware tone xfer_mode + json_get_vars type annex firmware tone xfer_mode + uci -q batch <<-EOF + delete network.dsl + set network.dsl='dsl' + set network.dsl.annex='$annex' + set network.dsl.firmware='$firmware' + set network.dsl.tone='$tone' + set network.dsl.xfer_mode='$xfer_mode' + EOF + json_select .. + fi + json_select .. + fi +} + +addr_offset=2 +generate_network() { + local keys var val ifname macaddr proto type ipaddr netmask + + uci -q batch <<-EOF + delete "network.$1" + set network.$1='interface' + EOF + + json_select network + json_select "$1" + json_get_keys keys + for var in $keys; do + json_get_var val "$var" + [ "${var#_*}" = "$var" ] && { + eval "$var=\"\$val\"" + uci -q set "network.$1.$var=$val" + } + done + json_select .. + json_select .. + + #~ [ -n "$ifname" ] || return + + # force bridge for multi-interface devices (and lan) + case "$1:$ifname" in + *\ * | lan:*) + type="bridge" + uci -q set "network.$1.type=$type" + ;; + esac + + if [ -n "$macaddr" ]; then + for name in $ifname; do + uci -q batch <<-EOF + delete network.$1_${name/./_}_dev + set network.$1_${name/./_}_dev='device' + set network.$1_${name/./_}_dev.name='$name' + set network.$1_${name/./_}_dev.macaddr='$macaddr' + EOF + done + fi + + case "$proto" in + static) + local ipad + case "$1" in + lan) + ipad=${ipaddr:-"192.168.1.1"} + ;; + *) ipad=${ipaddr:-"192.168.$((addr_offset++)).1"} ;; + esac + + netm=${netmask:-"255.255.255.0"} + + uci -q batch <<-EOF + set network.$1.proto='static' + set network.$1.ipaddr='$ipad' + set network.$1.netmask='$netm' + EOF + [ -e /proc/sys/net/ipv6 ] && uci set network.$1.ip6assign='60' + ;; + + dhcp) + # fixup IPv6 slave interface if parent is a bridge + [ "$type" = "bridge" ] && ifname="br-$1" + + uci set network.$1.proto='dhcp' + uci set network.$1.metric='1' + + [ -e /proc/sys/net/ipv6 ] && { + uci -q batch <<-EOF + delete network.${1}6 + set network.${1}6='interface' + set network.${1}6.ifname='$ifname' + set network.${1}6.proto='dhcpv6' + set network.${1}6.metric='1' + EOF + } + ;; + + pppoe) + uci -q batch <<-EOF + set network.$1.proto='pppoe' + set network.$1.username='username' + set network.$1.password='password' + EOF + [ -e /proc/sys/net/ipv6 ] && { + uci -q batch <<-EOF + set network.$1.ipv6='1' + delete network.${1}6 + set network.${1}6='interface' + set network.${1}6.ifname='@${1}' + set network.${1}6.proto='dhcpv6' + EOF + } + ;; + esac +} + +add_modem_section() { + local id="$1" + local num="$2" + local simcount="$3" + local builtin="$4" + + for count in $(seq "$simcount"); do + interface="mob${num}s${count}a1" + local proto="wwan" + + # just like this for now + # probably we should merge connm with wwan + [ -e /dev/smd9 ] && { + proto="connm" + } + + uci -q batch <<-EOF + delete network.$interface + set network.$interface='interface' + set network.$interface.proto='$proto' + set network.$interface.modem='$id' + set network.$interface.metric='$((num+1))' + set network.$interface.sim='${count}' + set network.$interface.pdp='1' + EOF + + # just like this for now + # probably we should merge connm with wwan + [ -e /dev/smd9 ] && { + uci set network.$interface.ifname='rmnet0' + } + + update_firewall_zone "wan" "$interface" + create_multiwan_iface "$interface" "$num" + add_simcard_config "$id" "${count}" "${count}" "$builtin" + add_sim_switch_config "$id" "${count}" + add_quota_limit_config "$interface" + done + add_sms_storage_config "$id" +} + +generate_dynamic_lte() { +[ -f /lib/functions/modem.sh ] || return +. /lib/functions/modem.sh + + local interface num id simcount builtin + + #creating simcard sections from board.json file + if json_is_a modems array; then + json_get_keys modems modems + json_select modems + + num=1 + + for modem in $modems; do + json_select "$modem" + json_get_vars id simcount builtin + json_select .. + add_modem_section "$id" "$num" "$simcount" "$builtin" + num=$(( num + 1 )) + done + + json_select .. + else + ## because of RUTX8 have no default modem + # after this script runs out simcard config + # must not be empty due to external modems could appear to config + echo " " >> /etc/config/simcard + fi + + #creating simcard sections from conneted via USB + for a in `ls /sys/bus/usb/devices`; do + local vendor product + [ -f "/sys/bus/usb/devices/$a/idVendor" ] && [ -f "/sys/bus/usb/devices/$a/idProduct" ] || continue + vendor=$(cat "/sys/bus/usb/devices/$a/idVendor") + product=$(cat "/sys/bus/usb/devices/$a/idProduct") + [ -f "/lib/network/wwan/${vendor}:${product}" ] && { + add_simcard_config "$a" "1" "0" "" + } + done +} + +generate_switch_vlans_ports() { + local switch="$1" + local port ports role roles num attr val + + # + # autogenerate vlans + # + + if json_is_a roles array; then + json_get_keys roles roles + json_select roles + + for role in $roles; do + json_select "$role" + json_get_vars ports + json_select .. + + uci -q batch <<-EOF + add network switch_vlan + set network.@switch_vlan[-1].device='$switch' + set network.@switch_vlan[-1].vlan='$role' + set network.@switch_vlan[-1].ports='$ports' + EOF + done + + json_select .. + fi + + + # + # write port specific settings + # + + if json_is_a ports array; then + json_get_keys ports ports + json_select ports + + for port in $ports; do + json_select "$port" + json_get_vars num + + if json_is_a attr object; then + json_get_keys attr attr + json_select attr + uci -q batch <<-EOF + add network switch_port + set network.@switch_port[-1].device='$switch' + set network.@switch_port[-1].port=$num + EOF + + for attr in $attr; do + json_get_var val "$attr" + uci -q set network.@switch_port[-1].$attr="$val" + done + json_select .. + fi + json_select .. + done + + json_select .. + fi +} + +generate_switch() { + local key="$1" + local vlans + + json_select switch + json_select "$key" + json_get_vars enable reset blinkrate cpu_port \ + ar8xxx_mib_type ar8xxx_mib_poll_interval + + uci -q batch <<-EOF + add network switch + set network.@switch[-1].name='$key' + set network.@switch[-1].reset='$reset' + set network.@switch[-1].enable_vlan='$enable' + set network.@switch[-1].blinkrate='$blinkrate' + set network.@switch[-1].ar8xxx_mib_type='$ar8xxx_mib_type' + set network.@switch[-1].ar8xxx_mib_poll_interval='$ar8xxx_mib_poll_interval' + EOF + + generate_switch_vlans_ports "$1" + + json_select .. + json_select .. +} + + +generate_static_system() { + param=$(/sbin/mnf_info "--name") + hostname=${param:0:6} + uci -q batch <<-EOF + delete system.@system[0] + set system.system='system' + set system.@system[-1].hostname='Teltonika-$hostname.com' + set system.@system[-1].timezone='UTC' + set system.@system[-1].ttylogin='0' + set system.@system[-1].log_size='128' + set system.@system[-1].urandom_seed='0' + + delete system.ntp + set system.ntp='timeserver' + set system.ntp.enabled='0' + set system.ntp.enable_server='0' + add_list system.ntp.server='0.pool.ntp.org' + add_list system.ntp.server='1.pool.ntp.org' + add_list system.ntp.server='2.pool.ntp.org' + add_list system.ntp.server='3.pool.ntp.org' + + delete system.debug + set system.debug='debug' + set system.debug.sms_utils_debug_level='4' + EOF + + if json_is_a system object; then + json_select system + local hostname + if json_get_var hostname hostname; then + uci -q set "system.@system[-1].hostname=$hostname" + fi + + if json_is_a ntpserver array; then + local keys key + json_get_keys keys ntpserver + json_select ntpserver + uci -q delete "system.ntp.server" + + for key in $keys; do + local server + if json_get_var server "$key"; then + uci -q add_list "system.ntp.server=$server" + fi + done + json_select .. + fi + json_select .. + fi +} + +generate_rssimon() { + local key="$1" + local cfg="rssid_$key" + local refresh threshold + + json_select rssimon + json_select "$key" + json_get_vars refresh threshold + json_select .. + json_select .. + + uci -q batch <<-EOF + delete system.$cfg + set system.$cfg='rssid' + set system.$cfg.dev='$key' + set system.$cfg.refresh='$refresh' + set system.$cfg.threshold='$threshold' + EOF +} + +generate_led() { + local key="$1" + local cfg="led_$key" + + json_select led + json_select "$key" + json_get_vars name sysfs type trigger default + + uci -q batch <<-EOF + delete system.$cfg + set system.$cfg='led' + set system.$cfg.name='$name' + set system.$cfg.sysfs='$sysfs' + set system.$cfg.trigger='$trigger' + set system.$cfg.default='$default' + EOF + + case "$type" in + gpio) + local gpio inverted + json_get_vars gpio inverted + uci -q batch <<-EOF + set system.$cfg.trigger='gpio' + set system.$cfg.gpio='$gpio' + set system.$cfg.inverted='$inverted' + EOF + ;; + + netdev) + local device mode + json_get_vars device mode + uci -q batch <<-EOF + set system.$cfg.trigger='netdev' + set system.$cfg.mode='$mode' + set system.$cfg.dev='$device' + EOF + ;; + + usb) + local device + json_get_vars device + uci -q batch <<-EOF + set system.$cfg.trigger='usbdev' + set system.$cfg.interval='50' + set system.$cfg.dev='$device' + EOF + ;; + + usbport) + local ports port + json_get_values ports ports + uci set system.$cfg.trigger='usbport' + for port in $ports; do + uci add_list system.$cfg.port=$port + done + ;; + + rssi) + local iface minq maxq offset factor + json_get_vars iface minq maxq offset factor + uci -q batch <<-EOF + set system.$cfg.trigger='rssi' + set system.$cfg.iface='rssid_$iface' + set system.$cfg.minq='$minq' + set system.$cfg.maxq='$maxq' + set system.$cfg.offset='$offset' + set system.$cfg.factor='$factor' + EOF + ;; + + switch) + local port_mask speed_mask mode + json_get_vars port_mask speed_mask mode + uci -q batch <<-EOF + set system.$cfg.port_mask='$port_mask' + set system.$cfg.speed_mask='$speed_mask' + set system.$cfg.mode='$mode' + EOF + ;; + + portstate) + local port_state + json_get_vars port_state + uci -q batch <<-EOF + set system.$cfg.port_state='$port_state' + EOF + ;; + + timer|oneshot) + local delayon delayoff + json_get_vars delayon delayoff + uci -q batch <<-EOF + set system.$cfg.trigger='$type' + set system.$cfg.delayon='$delayon' + set system.$cfg.delayoff='$delayoff' + EOF + ;; + esac + + json_select .. + json_select .. +} + +generate_gpioswitch() { + local cfg="$1" + + json_select gpioswitch + json_select "$cfg" + local name pin default + json_get_vars name pin default + uci -q batch <<-EOF + delete system.$cfg + set system.$cfg='gpio_switch' + set system.$cfg.name='$name' + set system.$cfg.gpio_pin='$pin' + set system.$cfg.value='$default' + EOF + json_select .. + json_select .. +} + +generate_hwinfo() { + local parameter="$1" + local temp + + json_select hwinfo + json_get_var temp "$parameter" + json_select .. + + uci -q batch <<-EOF + set hwinfo.hwinfo='hwinfo' + set hwinfo.hwinfo.$parameter='$temp' + EOF +} + +generate_bluetooth() { + uci -q batch <<-EOF + set blesem.general='section' + set blesem.general.enabled='0' + set blesem.settings='app' + set blesem.settings.refresh_time='30000' + EOF +} + +add_firewall_zone() { + local ifname + + json_select network + json_select "$1" + json_get_vars ifname + json_select .. + json_select .. + + fw3 -q network "$1" || fw3 -q device "$ifname" && return + + uci -q batch <<-EOF + add firewall zone + set firewall.@zone[-1].name='$1' + set firewall.@zone[-1].network='$1' + set firewall.@zone[-1].input='REJECT' + set firewall.@zone[-1].output='ACCEPT' + set firewall.@zone[-1].forward='REJECT' + + add firewall forwarding + set firewall.@forwarding[-1].src='$1' + set firewall.@forwarding[-1].dest='wan' + + add firewall rule + set firewall.@rule[-1].name='Allow-DNS-$1' + set firewall.@rule[-1].src='$1' + set firewall.@rule[-1].dest_port='53' + set firewall.@rule[-1].proto='tcp udp' + set firewall.@rule[-1].target='ACCEPT' + + add firewall rule + set firewall.@rule[-1].name='Allow-DHCP-$1' + set firewall.@rule[-1].src='$1' + set firewall.@rule[-1].dest_port='67' + set firewall.@rule[-1].proto='udp' + set firewall.@rule[-1].family='ipv4' + set firewall.@rule[-1].target='ACCEPT' + EOF +} + +add_dhcp() { + + json_select network + json_select "$1" + json_get_vars _dhcp + json_select .. + json_select .. + + [ "$_dhcp" = "1" ] || return + uci -q batch <<-EOF + set dhcp.$1='dhcp' + set dhcp.$1.interface='$1' + set dhcp.$1.start='100' + set dhcp.$1.limit='150' + set dhcp.$1.leasetime='1h' + EOF +} + +json_init +json_load "$(cat ${CFG})" + +umask 077 + +if [ ! -s /etc/config/network ]; then + touch /etc/config/network + generate_static_network + + json_get_keys keys network + for key in $keys; do + generate_network $key + add_firewall_zone "$key" + add_dhcp "$key" + done + + json_get_keys keys switch + for key in $keys; do generate_switch $key; done + + generate_dynamic_lte +fi + +if [ ! -s /etc/config/system ]; then + touch /etc/config/system + generate_static_system + + json_get_keys keys rssimon + for key in $keys; do generate_rssimon $key; done + + json_get_keys keys gpioswitch + for key in $keys; do generate_gpioswitch $key; done + + json_get_keys keys led + for key in $keys; do generate_led $key; done +fi + +if [ ! -s /etc/config/hwinfo ]; then + touch /etc/config/hwinfo + json_get_keys keys hwinfo + for key in $keys; do generate_hwinfo $key; done +fi + +if [ ! -s /etc/config/blesem ]; then + bluetooth="" + json_select hwinfo + json_get_vars bluetooth + [ "$bluetooth" -eq 1 ] && { + touch /etc/config/blesem + touch /etc/config/ble_devices + generate_bluetooth + } +fi + +uci commit diff --git a/root/target/linux/ipq40xx/base-files/bin/ipcalc.sh b/root/target/linux/ipq40xx/base-files/bin/ipcalc.sh new file mode 100755 index 00000000..5d5eac3e --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/bin/ipcalc.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +awk -f - $* <limit) end=limit + + print "IP="int2ip(ipaddr) + print "NETMASK="int2ip(netmask) + print "BROADCAST="int2ip(broadcast) + print "NETWORK="int2ip(network) + print "PREFIX="32-bitcount(compl32(netmask)) + + # range calculations: + # ipcalc + + if (ARGC > 3) { + print "START="int2ip(start) + print "END="int2ip(end) + } +} +EOF diff --git a/root/target/linux/ipq40xx/base-files/bin/ipk-sig.sh b/root/target/linux/ipq40xx/base-files/bin/ipk-sig.sh new file mode 100755 index 00000000..016f5f56 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/bin/ipk-sig.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +sig="/tmp/.$(cat /proc/sys/kernel/random/uuid).sig" + +check() { + local ipk="$1" + [ -z "$ipk" ] && return 1 + + tar -xzOf "$ipk" ./control+data.sig > $sig || return 2 + + tar -xzOf "$ipk" ./control.tar.gz ./data.tar.gz | usign -V -m - -P /etc/opkg/keys -x $sig || return 3 +} + +check $1 +status=$? +rm -f $sig +return $status diff --git a/root/target/linux/ipq40xx/base-files/etc/board.d/02_network b/root/target/linux/ipq40xx/base-files/etc/board.d/02_network deleted file mode 100755 index 9ac46265..00000000 --- a/root/target/linux/ipq40xx/base-files/etc/board.d/02_network +++ /dev/null @@ -1,174 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2015 The Linux Foundation. All rights reserved. -# Copyright (c) 2011-2015 OpenWrt.org -# - -. /lib/functions/uci-defaults.sh -. /lib/functions/system.sh - -ipq40xx_setup_interfaces() -{ - local board="$1" - - case "$board" in - 8dev,habanero-dvk|\ - 8dev,jalapeno|\ - alfa-network,ap120c-ac|\ - engenius,emr3500|\ - engenius,ens620ext) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ;; - aruba,ap-303|\ - aruba,ap-365|\ - avm,fritzrepeater-1200|\ - dlink,dap-2610 |\ - engenius,eap1300|\ - engenius,emd1|\ - meraki,mr33|\ - netgear,ex6100v2|\ - netgear,ex6150v2|\ - zyxel,wre6606) - ucidef_set_interface_lan "eth0" - ;; - aruba,ap-303h|\ - teltonika,rutx) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0u@eth0" "2:lan:1" "3:lan:2" "4:lan:3" "0u@eth1" "5:wan" - ;; - asus,map-ac2200|\ - cilab,meshpoint-one|\ - openmesh,a42|\ - openmesh,a62) - ucidef_set_interfaces_lan_wan "eth1" "eth0" - ;; - asus,rt-ac58u|\ - p2w,r619ac-128m|\ - p2w,r619ac|\ - zyxel,nbg6617) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0u@eth0" "1:lan:4" "2:lan:3" "3:lan:2" "4:lan:1" - ;; - avm,fritzbox-4040|\ - linksys,ea6350v3|\ - linksys,ea8300) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" - ;; - avm,fritzbox-7530) - ucidef_add_switch "switch0" \ - "0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" - ;; - avm,fritzrepeater-3000|\ - compex,wpj419|\ - compex,wpj428|\ - engenius,eap2200) - ucidef_set_interface_lan "eth0 eth1" - ;; - buffalo,wtr-m2133hp) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0u@eth0" "2:lan:3" "3:lan:2" "4:lan:1" - ;; - cellc,rtl30vw) - ucidef_set_interface_lan "eth0" - ucidef_add_switch "switch0" \ - "0u@eth0" "3:lan" "4:lan" - ;; - ezviz,cs-w3-wd1200g-eup) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0u@eth0" "2:lan:3" "3:lan:2" "4:lan:1" "0u@eth1" "5:wan" - ;; - glinet,gl-b1300 |\ - glinet,gl-s1300) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0u@eth0" "3:lan" "4:lan" - ;; - mobipromo,cm520-79f) - ucidef_add_switch "switch0" \ - "0u@eth0" "3:lan:2" "4:lan:1" - ucidef_set_interface_wan "eth1" - ;; - qxwlan,e2600ac-c1 |\ - qxwlan,e2600ac-c2) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0u@eth0" "3:lan" "4:lan" "0u@eth1" "5:wan" - ;; - unielec,u4019-32m) - ucidef_set_interfaces_lan_wan "eth0" "eth1" - ucidef_add_switch "switch0" \ - "0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "0u@eth1" "5:wan" - ;; - *) - echo "Unsupported hardware. Network interfaces not initialized" - ;; - esac -} - -ipq40xx_setup_macs() -{ - local board="$1" - local lan_mac="" - local wan_mac="" - local label_mac="" - - case "$board" in - 8dev,habanero-dvk) - label_mac=$(mtd_get_mac_binary "ART" 0x1006) - ;; - asus,rt-ac58u|\ - p2w,r619ac-128m|\ - p2w,r619ac) - CI_UBIPART=UBI_DEV - wan_mac=$(mtd_get_mac_binary_ubi Factory 0x1006) - lan_mac=$(mtd_get_mac_binary_ubi Factory 0x5006) - label_mac=$wan_mac - ;; - cilab,meshpoint-one) - label_mac=$(mtd_get_mac_binary "ART" 0x1006) - ;; - dlink,dap-2610) - lan_mac=$(mtd_get_mac_ascii bdcfg lanmac) - label_mac=$lan_mac - ;; - engenius,eap2200|\ - engenius,emd1) - lan_mac=$(mtd_get_mac_ascii 0:APPSBLENV ethaddr) - label_mac=$lan_mac - ;; - engenius,emr3500) - wan_mac=$(mtd_get_mac_ascii 0:APPSBLENV wanaddr) - lan_mac=$(mtd_get_mac_ascii 0:APPSBLENV ethaddr) - label_mac=$wan_mac - ;; - engenius,ens620ext) - wan_mac=$(mtd_get_mac_ascii u-boot-env ethaddr) - lan_mac=$(macaddr_add "$wan_mac" 1) - ;; - ezviz,cs-w3-wd1200g-eup) - label_mac=$(mtd_get_mac_binary "ART" 0x6) - ;; - linksys,ea6350v3) - wan_mac=$(mtd_get_mac_ascii devinfo hw_mac_addr) - lan_mac=$(macaddr_add "$wan_mac" 1) - ;; - esac - - [ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" $lan_mac - [ -n "$wan_mac" ] && ucidef_set_interface_macaddr "wan" $wan_mac - [ -n "$label_mac" ] && ucidef_set_label_macaddr $label_mac -} - -board_config_update -board=$(board_name) -ipq40xx_setup_interfaces $board -ipq40xx_setup_macs $board -board_config_flush - -exit 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/board.d/1-board_json b/root/target/linux/ipq40xx/base-files/etc/board.d/1-board_json new file mode 100755 index 00000000..6d26ba42 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/board.d/1-board_json @@ -0,0 +1,209 @@ +#!/bin/sh +# +# Copyright (c) 2015 The Linux Foundation. All rights reserved. +# Copyright (c) 2011-2015 OpenWrt.org +# + +. /lib/functions/uci-defaults.sh +. /lib/functions/teltonika-defaults.sh +. /lib/functions/system.sh + +setup_json() { + local model="$1" + + case "$model" in + RUTX08*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0u@eth0" "2:lan:1" "3:lan:2" "4:lan:3" "0u@eth1" "5:wan" + ucidef_set_hwinfo usb ethernet ios + ;; + RUTX09*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0u@eth0" "2:lan:1" "3:lan:2" "4:lan:3" "0u@eth1" "5:wan" + ucidef_add_static_modem_info "$model" "3-1" "2" "gps_out" + ucidef_set_hwinfo dual_sim usb gps mobile ethernet ios + ;; + RUTX10*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0u@eth0" "2:lan:1" "3:lan:2" "4:lan:3" "0u@eth1" "5:wan" + ucidef_set_hwinfo bluetooth usb wifi dual_band_ssid ethernet ios + ;; + RUTX11*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0u@eth0" "2:lan:1" "3:lan:2" "4:lan:3" "0u@eth1" "5:wan" + ucidef_add_static_modem_info "$model" "3-1" "2" "gps_out" + ucidef_set_hwinfo dual_sim usb gps mobile wifi dual_band_ssid bluetooth ethernet ios + ;; + RUTXR1*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "0u@eth1" "5:wan" + ucidef_add_static_modem_info "$model" "3-1" "2" + ucidef_set_hwinfo dual_sim usb mobile wifi dual_band_ssid ethernet sfp_port + ucidef_set_release_version "2.3.1" + ;; + RUTX12*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "0u@eth1" "5:wan" + # builtin and primary should be first modem + ucidef_add_static_modem_info "$model" "3-1" "1" "primary" "gps_out" + ucidef_add_static_modem_info "$model" "1-1.2" "1" + ucidef_set_hwinfo usb gps mobile wifi dual_band_ssid bluetooth ethernet ios + ucidef_set_release_version "2.3.1" + ;; + RUTX14*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "0u@eth1" "5:wan" + ucidef_add_static_modem_info "$model" "1-1" "2" "gps_out" + ucidef_set_hwinfo usb gps dual_sim mobile wifi dual_band_ssid bluetooth ethernet ios at_sim + ucidef_set_release_version "2.6.1" + ;; + RUTX18*) + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_switch "switch0" \ + "0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "0u@eth1" "5:wan" + ucidef_add_static_modem_info "$model" "2-1" "2" "gps_out" + ucidef_set_hwinfo usb gps dual_sim mobile wifi dual_band_ssid bluetooth ethernet ios + ;; + TRB2*) + ucidef_set_led_switch "lan" "LAN" "eth_led" "switch0" "0x04" + ucidef_set_interface_lan "eth0" + ucidef_add_static_modem_info "$model" "1-1.4" "2" "gps_out" + ucidef_add_serial_capabilities "rs232 rs485" \ + "300 600 1200 2400 4800 9600 14400 19200 38400 56000 57600 115200 \ + 230400 460800 921600 1000000 3000000" \ + "7 8" + ucidef_set_hwinfo dual_sim mobile gps ethernet ios + ;; + RUT200* |\ + RUT241*) + ucidef_set_led_switch "lan" "LAN" "eth1_led" "switch0" "0x2" + ucidef_set_led_switch "wan" "WAN" "eth2_led" "switch0" "0x1" + ucidef_add_switch "switch0" "1:lan" "0:wan" "6@eth0" + ucidef_set_interface_macaddr "lan" "$(mtd_get_mac_binary config 0x0)" + ucidef_set_interface_macaddr "wan" "$(macaddr_add "$(mtd_get_mac_binary config 0x0)" 1)" + ucidef_add_static_modem_info "$model" "1-1" "1" + [ "${model:7:1}" = "1" ] && ucidef_set_hwinfo mobile wifi \ + ethernet || ucidef_set_hwinfo mobile wifi ethernet ios + ;; + RUT2*) + ucidef_set_led_switch "lan" "LAN" "lan_led" "switch0" "0x04" + ucidef_set_led_netdev "wan" "WAN" "wan_led" "eth1" + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_static_modem_info "$model" "1-1" "1" + [ "${model:6:1}" = "1" ] && ucidef_set_hwinfo mobile wifi \ + ethernet || ucidef_set_hwinfo mobile wifi ethernet ios + ;; + RUT300*) + ucidef_set_led_switch "lan1" "LAN1" "eth1_led" "switch0" "0x02" + ucidef_set_led_switch "lan2" "LAN2" "eth2_led" "switch0" "0x10" + ucidef_set_led_switch "lan3" "LAN3" "eth3_led" "switch0" "0x08" + ucidef_set_led_switch "lan4" "LAN4" "eth4_led" "switch0" "0x04" + ucidef_set_led_netdev "wan" "WAN" "wan_led" "eth1" + ucidef_set_interface_wan "eth1" + ucidef_add_switch "switch0" \ + "0@eth0" "1:lan:1" "2:lan:4" "3:lan:3" "4:lan:2" + ucidef_set_hwinfo usb ethernet ios + ;; + RUT360*) + ucidef_set_led_switch "lan" "LAN" "eth1_led" "switch0" "0x10" + ucidef_set_led_netdev "wan" "WAN" "eth2_led" "eth1" + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_add_static_modem_info "$model" "1-1" "1" + ucidef_set_hwinfo mobile wifi dual_band_ssid ethernet ios + ;; + RUT950*) + ucidef_set_led_switch "lan1" "LAN1" "eth1_led" "switch0" "0x10" + ucidef_set_led_switch "lan2" "LAN2" "eth2_led" "switch0" "0x08" + ucidef_set_led_switch "lan3" "LAN3" "eth3_led" "switch0" "0x04" + ucidef_set_led_netdev "wan" "WAN" "wan_led" "eth1" + ucidef_set_interface_wan "eth1" + ucidef_add_switch "switch0" "0@eth0" "2:lan:3" "3:lan:2" "4:lan:1" + ucidef_add_static_modem_info "$model" "1-1" "2" + [ "${model:7:2}" = "06" ] && ucidef_set_hwinfo dual_sim mobile \ + wifi ethernet || ucidef_set_hwinfo dual_sim mobile wifi ethernet ios + ;; + RUT955*) + ucidef_set_led_switch "lan1" "LAN1" "eth1_led" "switch0" "0x10" + ucidef_set_led_switch "lan2" "LAN2" "eth2_led" "switch0" "0x08" + ucidef_set_led_switch "lan3" "LAN3" "eth3_led" "switch0" "0x04" + ucidef_set_led_netdev "wan" "WAN" "wan_led" "eth1" + ucidef_set_interface_wan "eth1" + ucidef_add_switch "switch0" "0@eth0" "2:lan:3" "3:lan:2" "4:lan:1" + ucidef_add_static_modem_info "$model" "1-1.4" "2" "gps_out" + [ "${model:7:2}" = "06" ] && ucidef_set_hwinfo dual_sim usb gps \ + mobile wifi ethernet || ucidef_set_hwinfo dual_sim usb gps \ + mobile wifi ethernet ios + ucidef_add_serial_capabilities "rs232" \ + "200 300 600 1200 1800 2400 4800 9600 19200 38400 57600 115200 \ + 230400 460800 500000 576000" \ + "5 6 7 8" + ucidef_add_serial_capabilities "rs485" \ + "300 600 1200 1800 2400 4800 9600 19200 38400 57600 115200 230400 \ + 460800 500000 576000 921600 1000000 1152000 1500000 2000000 \ + 2500000 3000000" \ + "8" + ;; + TRB140*) + ucidef_set_interface_lan "eth0 rndis0" + ucidef_add_trb14x_lte_modem "$model" + ucidef_add_nand_info "$model" + [ "${model:7:1}" = "2" ] && ucidef_set_hwinfo mobile ethernet || \ + ucidef_set_hwinfo mobile ethernet ios + ;; + TRB141*) + ucidef_set_interface_lan "rndis0" + ucidef_add_trb14x_lte_modem "$model" + ucidef_add_nand_info "$model" + ucidef_set_hwinfo mobile ios + ;; + TRB142* |\ + TRB145*) + ucidef_set_interface_lan "rndis0" + ucidef_add_trb14x_lte_modem "$model" + ucidef_add_nand_info "$model" + [ "${model:7:1}" = "2" ] && ucidef_set_hwinfo mobile || \ + ucidef_set_hwinfo mobile ios + [ "${model:5:1}" = "2" ] && local rs="rs232" + ucidef_add_serial_capabilities "${rs:-rs485}" \ + "300 600 1200 2400 4800 9600 19200 38400 57600 115200 460800" \ + "5 6 7 8" + [ "${model:5:2}" = "23" -o "${model:5:2}" = "52" ] && \ + ucidef_set_release_version "7.1" + ;; + TCR100*) + ucidef_set_led_switch "lan" "LAN" "eth1_led" "switch0" "0x10" + ucidef_set_led_netdev "wan" "WAN" "eth2_led" "eth1" + ucidef_set_interfaces_lan_wan "eth0" "eth1" + ucidef_set_interface guest proto static type bridge \ + guest 1 _wireless true _dhcp true + ucidef_add_static_modem_info "$model" "1-1" "1" + ucidef_set_hwinfo mobile wifi dual_band_ssid wps ethernet + ;; + *) + echo "Unsupported hardware. Network interfaces not intialized" + ;; + esac +} + +#~ Get model name for RUTX products +if ! model="$(mnf_info --name)" 2>/dev/null; then + model="$(mnf_info --prod-code)" 2>/dev/null +fi + +platform="$(cat /proc/device-tree/platform)" 2>/dev/null + +board_config_update +setup_json "$model" + +ucidef_set_board_platform "$platform" + +board_config_flush + +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/fstab b/root/target/linux/ipq40xx/base-files/etc/fstab new file mode 100644 index 00000000..637a6340 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/fstab @@ -0,0 +1,2 @@ +# +/dev/mtdblock15 /log jffs2 defaults 0 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata b/root/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata index adc17c7e..88319423 100644 --- a/root/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata +++ b/root/target/linux/ipq40xx/base-files/etc/hotplug.d/firmware/11-ath10k-caldata @@ -1,246 +1,67 @@ #!/bin/sh +ath10kcal_die() { + echo "ath10cal: " "$*" + exit 1 +} + +ath10kcal_extract() { + local part=$1 + local offset=$2 + local count=$3 + local mtd + + mtd=$(find_mtd_chardev $part) + [ -n "$mtd" ] || \ + ath10kcal_die "no mtd device found for partition $part" + + dd if=$mtd of=/lib/firmware/$FIRMWARE iflag=skip_bytes bs=$count skip=$offset count=1 2>/dev/null || \ + ath10kcal_die "failed to extract calibration data from $mtd" +} + +ensure_correct_art() { + # NOTE(rytis): Hardcoded hashes for 64 KiB file, filled with zeroes (md5b0) and ones (md5b1). + local md5b0="fcd6bcb56c1689fcef28b57c22475bad" + local md5b1="ecb99e6ffea7be1e5419350f725da86b" + local artdir="/dev/mtd12" + local bindir="/usr/share/art/art_rutx.bin" + local md5art="$(md5sum $artdir)" + md5art="${md5art%% *}" + local devicename="$(mnf_info -n)" + devicename="${devicename:0:6}" + + if [ "$devicename" != "RUTX08" ] && [ "$devicename" != "RUTX09" ]; then + if [ "$md5art" == "$md5b0" ] || [ "$md5art" == "$md5b1" ] && [ -e "$bindir" ]; then + mtd write $bindir $artdir + fi + else + if [ "$md5art" != "$md5b0" ] && [ "$md5art" != "$md5b1" ]; then + mtd erase $artdir + fi + fi +} + [ -e /lib/firmware/$FIRMWARE ] && exit 0 -. /lib/functions/caldata.sh +. /lib/functions.sh +. /lib/functions/system.sh board=$(board_name) +ensure_correct_art + case "$FIRMWARE" in -"ath10k/cal-pci-0000:01:00.0.bin") - case "$board" in - meraki,mr33) - caldata_extract_ubi "ART" 0x9000 0x844 - caldata_valid "4408" || caldata_extract "ART" 0x9000 0x844 - ath10k_patch_mac $(macaddr_add $(get_mac_binary "/sys/bus/i2c/devices/0-0050/eeprom" 0x66) +1) - ;; - esac - ;; -"ath10k/pre-cal-pci-0000:01:00.0.bin") - case "$board" in - asus,map-ac2200) - caldata_extract_ubi "Factory" 0x9000 0x2f20 - ln -sf /lib/firmware/ath10k/pre-cal-pci-0000\:00\:00.0.bin \ - /lib/firmware/ath10k/QCA9888/hw2.0/board.bin - ;; - avm,fritzrepeater-3000) - /usr/bin/fritz_cal_extract -i 1 -s 0x3D000 -e 0x212 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C800 -e 0x212 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C000 -e 0x212 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3D000 -e 0x212 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C800 -e 0x212 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C000 -e 0x212 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") - ;; - buffalo,wtr-m2133hp) - caldata_extract "ART" 0x9000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_binary ORGDATA 0x32) - ;; - engenius,eap2200 |\ - openmesh,a62) - caldata_extract "0:ART" 0x9000 0x2f20 - ;; - linksys,ea8300) - caldata_extract "ART" 0x9000 0x2f20 - # OEM assigns 4 sequential MACs - ath10k_patch_mac $(macaddr_setbit_la $(macaddr_add "$(cat /sys/class/net/eth0/address)" 4)) - ;; - esac - ;; "ath10k/pre-cal-ahb-a000000.wifi.bin") case "$board" in - 8dev,habanero-dvk |\ - 8dev,jalapeno |\ - alfa-network,ap120c-ac |\ - cilab,meshpoint-one |\ - ezviz,cs-w3-wd1200g-eup |\ - glinet,gl-b1300 |\ - glinet,gl-s1300 |\ - linksys,ea6350v3 |\ - mobipromo,cm520-79f |\ - p2w,r619ac-128m |\ - p2w,r619ac |\ - qcom,ap-dk01.1-c1) - caldata_extract "ART" 0x1000 0x2f20 - ;; - aruba,ap-303 |\ - aruba,ap-303h |\ - aruba,ap-365) - caldata_extract "ART" 0x1000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_binary mfginfo 0x1D) - ;; - asus,map-ac2200) - caldata_extract_ubi "Factory" 0x1000 0x2f20 - ;; - asus,rt-ac58u) - CI_UBIPART=UBI_DEV - caldata_extract_ubi "Factory" 0x1000 0x2f20 - ;; - avm,fritzbox-4040) - /usr/bin/fritz_cal_extract -i 1 -s 0x400 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader_config") - ;; - avm,fritzbox-7530 |\ - avm,fritzrepeater-1200 |\ - avm,fritzrepeater-3000) - /usr/bin/fritz_cal_extract -i 1 -s 0x3C000 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C800 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3D000 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C000 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C800 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3D000 -e 0x207 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") - ;; - buffalo,wtr-m2133hp) - caldata_extract "ART" 0x1000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_binary ORGDATA 0x26) - ;; - cellc,rtl30vw |\ - compex,wpj419 |\ - compex,wpj428 |\ - engenius,eap1300 |\ - engenius,eap2200 |\ - openmesh,a42 |\ - openmesh,a62 |\ - qxwlan,e2600ac-c1 |\ - qxwlan,e2600ac-c2 |\ - unielec,u4019-32m) - caldata_extract "0:ART" 0x1000 0x2f20 - ;; - dlink,dap-2610) - caldata_extract "ART" 0x1000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_ascii bdcfg wlanmac) - ;; - engenius,emd1) - caldata_extract "0:ART" 0x1000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_ascii 0:APPSBLENV wlanaddr) - ;; - engenius,emr3500) - caldata_extract "0:ART" 0x1000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_ascii 0:APPSBLENV ethaddr) - ;; - engenius,ens620ext) - caldata_extract "ART" 0x1000 0x2f20 - ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii u-boot-env ethaddr) +2) - ;; - linksys,ea8300) - caldata_extract "ART" 0x1000 0x2f20 - ath10k_patch_mac $(macaddr_add "$(cat /sys/class/net/eth0/address)" 2) - ;; - meraki,mr33) - caldata_extract_ubi "ART" 0x1000 0x2f20 - caldata_valid "202f" || caldata_extract "ART" 0x1000 0x2f20 - ath10k_patch_mac $(macaddr_add $(get_mac_binary "/sys/bus/i2c/devices/0-0050/eeprom" 0x66) +2) - ;; - netgear,ex6100v2 |\ - netgear,ex6150v2) - caldata_extract "ART" 0x1000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_binary dnidata 0x0) - ;; teltonika,rutx) - caldata_extract "0:ART" 0x1000 0x2f20 - ath10k_patch_mac $(macaddr_add $(mtd_get_mac_binary "0:CONFIG" 0x0) 2) - ;; - zyxel,nbg6617 |\ - zyxel,wre6606) - caldata_extract "ART" 0x1000 0x2f20 - ath10k_patch_mac $(macaddr_add $(cat /sys/class/net/eth0/address) -2) + ath10kcal_extract "0:ART" 4096 12064 ;; esac ;; "ath10k/pre-cal-ahb-a800000.wifi.bin") case "$board" in - 8dev,habanero-dvk |\ - 8dev,jalapeno |\ - alfa-network,ap120c-ac |\ - cilab,meshpoint-one |\ - ezviz,cs-w3-wd1200g-eup |\ - glinet,gl-b1300 |\ - glinet,gl-s1300 |\ - linksys,ea6350v3 |\ - mobipromo,cm520-79f |\ - p2w,r619ac-128m |\ - p2w,r619ac |\ - qcom,ap-dk01.1-c1) - caldata_extract "ART" 0x5000 0x2f20 - ;; - aruba,ap-303 |\ - aruba,ap-303h |\ - aruba,ap-365) - caldata_extract "ART" 0x5000 0x2f20 - ath10k_patch_mac $(macaddr_add $(mtd_get_mac_binary mfginfo 0x1D) +1) - ;; - asus,map-ac2200) - caldata_extract_ubi "Factory" 0x5000 0x2f20 - ;; - asus,rt-ac58u) - CI_UBIPART=UBI_DEV - caldata_extract_ubi "Factory" 0x5000 0x2f20 - ;; - avm,fritzbox-4040) - /usr/bin/fritz_cal_extract -i 1 -s 0x400 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader_config") - ;; - avm,fritzbox-7530 |\ - avm,fritzrepeater-1200 |\ - avm,fritzrepeater-3000) - /usr/bin/fritz_cal_extract -i 1 -s 0x3C800 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3D000 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C000 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader0") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C800 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3D000 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") || \ - /usr/bin/fritz_cal_extract -i 1 -s 0x3C000 -e 0x208 -l 12064 -o /lib/firmware/$FIRMWARE $(find_mtd_chardev "urlader1") - ;; - buffalo,wtr-m2133hp) - caldata_extract "ART" 0x5000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_binary ORGDATA 0x2c) - ;; - cellc,rtl30vw |\ - compex,wpj419 |\ - compex,wpj428 |\ - engenius,eap1300 |\ - engenius,eap2200 |\ - openmesh,a42 |\ - openmesh,a62 |\ - qxwlan,e2600ac-c1 |\ - qxwlan,e2600ac-c2 |\ - unielec,u4019-32m) - caldata_extract "0:ART" 0x5000 0x2f20 - ;; - dlink,dap-2610) - caldata_extract "ART" 0x5000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_ascii bdcfg wlanmac_a) - ;; - engenius,emd1) - caldata_extract "0:ART" 0x5000 0x2f20 - ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:APPSBLENV wlanaddr) +1) - ;; - engenius,emr3500) - caldata_extract "0:ART" 0x5000 0x2f20 - ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii 0:APPSBLENV ethaddr) +1) - ;; - engenius,ens620ext) - caldata_extract "ART" 0x5000 0x2f20 - ath10k_patch_mac $(macaddr_add $(mtd_get_mac_ascii u-boot-env ethaddr) +3) - ;; - linksys,ea8300) - caldata_extract "ART" 0x5000 0x2f20 - ath10k_patch_mac $(macaddr_add "$(cat /sys/class/net/eth0/address)" 3) - ;; - meraki,mr33) - caldata_extract_ubi "ART" 0x5000 0x2f20 - caldata_valid "202f" || caldata_extract "ART" 0x5000 0x2f20 - ath10k_patch_mac $(macaddr_add $(get_mac_binary "/sys/bus/i2c/devices/0-0050/eeprom" 0x66) +3) - ;; - netgear,ex6100v2 |\ - netgear,ex6150v2) - caldata_extract "ART" 0x5000 0x2f20 - ath10k_patch_mac $(mtd_get_mac_binary dnidata 0xc) - ;; teltonika,rutx) - caldata_extract "0:ART" 0x1000 0x2f20 - ath10k_patch_mac $(macaddr_add $(mtd_get_mac_binary "0:CONFIG" 0x0) 3) - ;; - zyxel,nbg6617 |\ - zyxel,wre6606) - caldata_extract "ART" 0x5000 0x2f20 - ath10k_patch_mac $(macaddr_add $(cat /sys/class/net/eth0/address) -1) + ath10kcal_extract "0:ART" 20480 12064 ;; esac ;; diff --git a/root/target/linux/ipq40xx/base-files/etc/hotplug.d/ieee80211/09_fix_wifi_mac b/root/target/linux/ipq40xx/base-files/etc/hotplug.d/ieee80211/09_fix_wifi_mac new file mode 100644 index 00000000..4c4893f4 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/hotplug.d/ieee80211/09_fix_wifi_mac @@ -0,0 +1,23 @@ +#!/bin/ash + +[ "$ACTION" == "add" ] || exit 0 + +PHYNBR=${DEVPATH##*/phy} + +[ -n $PHYNBR ] || exit 0 + +. /lib/functions.sh +. /lib/functions/system.sh + +board=$(board_name) + +case "$board" in + teltonika,rutx) + mac_addr=$(mtd_get_mac_binary 0:CONFIG 0) + if [ "$mac_addr" != "ff:ff:ff:ff:ff:ff" ]; then + echo $(macaddr_add $mac_addr $(($PHYNBR + 2)) ) > /sys${DEVPATH}/macaddress + fi + ;; + *) + ;; +esac diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/boot b/root/target/linux/ipq40xx/base-files/etc/init.d/boot new file mode 100755 index 00000000..85549882 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/boot @@ -0,0 +1,70 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006-2011 OpenWrt.org + +START=10 +STOP=90 + +uci_apply_defaults() { + . /lib/functions/system.sh + + cd /etc/uci-defaults || return 0 + files="$(find . -type f | sort)" + [ -z "$files" ] && return 0 + mkdir -p /tmp/.uci + for file in $files; do + ( . "./$file" ) && rm -f "$file" + done + uci commit +} + +boot() { + [ -f /proc/mounts ] || /sbin/mount_root + [ -f /proc/jffs2_bbc ] && echo "S" > /proc/jffs2_bbc + + mkdir -p /var/run + mkdir -p /var/log + mkdir -p /var/lock + mkdir -p /var/state + mkdir -p /var/tmp + mkdir -p /tmp/.uci + chmod 0700 /tmp/.uci + touch /var/log/wtmp + touch /var/log/lastlog + mkdir -p /tmp/resolv.conf.d + touch /tmp/resolv.conf.d/resolv.conf.auto + ln -sf /tmp/resolv.conf.d/resolv.conf.auto /tmp/resolv.conf + grep -q debugfs /proc/filesystems && /bin/mount -o noatime -t debugfs debugfs /sys/kernel/debug + grep -q bpf /proc/filesystems && /bin/mount -o nosuid,nodev,noexec,noatime,mode=0700 -t bpf bpffs /sys/fs/bpf + grep -q pstore /proc/filesystems && /bin/mount -o noatime -t pstore pstore /sys/fs/pstore + + # mount all entries in fstab + /bin/mount -a & + + # /log directory might be created on preinit + # symlink /storage to /log on TRB14X devices + [ -d /storage -a ! -h /log ] && { + rm -rf /log + ln -sf /storage /log + } + + # Wifi --- + param=$(/sbin/mnf_info "--name") + router_name=${param:0:6} + if [ $router_name == "RUTX08" ] || [ $router_name == "RUTX09" ]; then + rm /etc/modules.d/ath10k + fi + + /bin/board_detect + + /sbin/kmodloader + + /bin/config_generate + uci_apply_defaults + + [ -f "/etc/config/teltonika" ] && rm /etc/config/teltonika + + # temporary hack until configd exists + /sbin/reload_config + # leave finished boot script indication + touch /var/run/boot-done +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/done b/root/target/linux/ipq40xx/base-files/etc/init.d/done new file mode 100755 index 00000000..b64e6e4d --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/done @@ -0,0 +1,13 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=95 +boot() { + mount_root done + rm -f /sysupgrade.tgz && sync + + # process user commands + [ -f /etc/rc.local ] && { + sh /etc/rc.local + } +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/gpio_switch b/root/target/linux/ipq40xx/base-files/etc/init.d/gpio_switch new file mode 100755 index 00000000..24d790b0 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/gpio_switch @@ -0,0 +1,66 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2015 OpenWrt.org + +START=94 +STOP=10 +USE_PROCD=1 + + +load_gpio_switch() +{ + local name + local gpio_pin + local value + + config_get gpio_pin "$1" gpio_pin + config_get name "$1" name + config_get value "$1" value 0 + + [ -z "$gpio_pin" ] && { + echo >&2 "Skipping gpio_switch '$name' due to missing gpio_pin" + return 1 + } + + local gpio_path + if [ -n "$(echo "$gpio_pin" | grep -E "^[0-9]+$")" ]; then + gpio_path="/sys/class/gpio/gpio${gpio_pin}" + + # export GPIO pin for access + [ -d "$gpio_path" ] || { + echo "$gpio_pin" >/sys/class/gpio/export + # we need to wait a bit until the GPIO appears + [ -d "$gpio_path" ] || sleep 1 + } + + # direction attribute only exists if the kernel supports changing the + # direction of a GPIO + if [ -e "${gpio_path}/direction" ]; then + # set the pin to output with high or low pin value + { [ "$value" = "0" ] && echo "low" || echo "high"; } \ + >"$gpio_path/direction" + else + { [ "$value" = "0" ] && echo "0" || echo "1"; } \ + >"$gpio_path/value" + fi + else + gpio_path="/sys/class/gpio/${gpio_pin}" + + [ -d "$gpio_path" ] && { + { [ "$value" = "0" ] && echo "0" || echo "1"; } \ + >"$gpio_path/value" + } + fi +} + +service_triggers() +{ + procd_add_reload_trigger "system" +} + +start_service() +{ + [ -e /sys/class/gpio/ ] && { + config_load system + config_foreach load_gpio_switch gpio_switch + } +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/led b/root/target/linux/ipq40xx/base-files/etc/init.d/led new file mode 100755 index 00000000..277fb4e7 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/led @@ -0,0 +1,140 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2008 OpenWrt.org + +START=96 + +load_led() { + local name + local sysfs + local trigger + local dev + local ports + local mode + local default + local delayon + local delayoff + local interval + + config_get sysfs $1 sysfs + config_get name $1 name "$sysfs" + config_get trigger $1 trigger "none" + config_get dev $1 dev + config_get ports $1 port + config_get mode $1 mode + config_get_bool default $1 default "0" + config_get delayon $1 delayon + config_get delayoff $1 delayoff + config_get interval $1 interval "50" + config_get port_state $1 port_state + config_get delay $1 delay "150" + config_get message $1 message "" + config_get gpio $1 gpio "0" + config_get inverted $1 inverted "0" + + if [ "$trigger" = "rssi" ]; then + # handled by rssileds userspace process + return + fi + + [ "$trigger" = "usbdev" ] && { + # Backward compatibility: translate to the new trigger + trigger="usbport" + # Translate port of root hub, e.g. 4-1 -> usb4-port1 + ports=$(echo "$dev" | sed -n 's/^\([0-9]*\)-\([0-9]*\)$/usb\1-port\2/p') + # Translate port of extra hub, e.g. 2-2.4 -> 2-2-port4 + [ -z "$ports" ] && ports=$(echo "$dev" | sed -n 's/\./-port/p') + } + + [ -e /sys/class/leds/${sysfs}/brightness ] && { + echo "setting up led ${name}" + + printf "%s %s %d\n" \ + "$sysfs" \ + "$(sed -ne 's/^.*\[\(.*\)\].*$/\1/p' /sys/class/leds/${sysfs}/trigger)" \ + "$(cat /sys/class/leds/${sysfs}/brightness)" \ + >> /var/run/led.state + + [ "$default" = 0 ] && + echo 0 >/sys/class/leds/${sysfs}/brightness + + echo $trigger > /sys/class/leds/${sysfs}/trigger 2> /dev/null + ret="$?" + + [ $default = 1 ] && + cat /sys/class/leds/${sysfs}/max_brightness > /sys/class/leds/${sysfs}/brightness + + [ $ret = 0 ] || { + echo >&2 "Skipping trigger '$trigger' for led '$name' due to missing kernel module" + return 1 + } + case "$trigger" in + "netdev") + [ -n "$dev" ] && { + echo $dev > /sys/class/leds/${sysfs}/device_name + for m in $mode; do + [ -e "/sys/class/leds/${sysfs}/$m" ] && \ + echo 1 > /sys/class/leds/${sysfs}/$m + done + echo $interval > /sys/class/leds/${sysfs}/interval + } + ;; + + "timer"|"oneshot") + [ -n "$delayon" ] && \ + echo $delayon > /sys/class/leds/${sysfs}/delay_on + [ -n "$delayoff" ] && \ + echo $delayoff > /sys/class/leds/${sysfs}/delay_off + ;; + + "usbport") + local p + + for p in $ports; do + echo 1 > /sys/class/leds/${sysfs}/ports/$p + done + ;; + + "port_state") + [ -n "$port_state" ] && \ + echo $port_state > /sys/class/leds/${sysfs}/port_state + ;; + + "gpio") + echo $gpio > /sys/class/leds/${sysfs}/gpio + echo $inverted > /sys/class/leds/${sysfs}/inverted + ;; + + switch[0-9]*) + local port_mask speed_mask + + config_get port_mask $1 port_mask + [ -n "$port_mask" ] && \ + echo $port_mask > /sys/class/leds/${sysfs}/port_mask + config_get speed_mask $1 speed_mask + [ -n "$speed_mask" ] && \ + echo $speed_mask > /sys/class/leds/${sysfs}/speed_mask + [ -n "$mode" ] && \ + echo "$mode" > /sys/class/leds/${sysfs}/mode + ;; + esac + } +} + +start() { + [ -e /sys/class/leds/ ] && { + [ -s /var/run/led.state ] && { + local led trigger brightness + while read led trigger brightness; do + [ -e "/sys/class/leds/$led/trigger" ] && \ + echo "$trigger" > "/sys/class/leds/$led/trigger" + + [ -e "/sys/class/leds/$led/brightness" ] && \ + echo "$brightness" > "/sys/class/leds/$led/brightness" + done < /var/run/led.state + rm /var/run/led.state + } + + config_load system + config_foreach load_led led + } +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/modem_tracker b/root/target/linux/ipq40xx/base-files/etc/init.d/modem_tracker new file mode 100755 index 00000000..3a3d06bc --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/modem_tracker @@ -0,0 +1,39 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2021 Teltonika Networks + +START=96 + +USE_PROCD=1 + +PROG=/bin/board_track +NAME=board_track +PIDCOUNT=1 + +start_service() { + config_load system + + config_get modem_track system modem_track 1 + + [ "$modem_track" != 0 ] || return + + local pid_file="/var/run/${NAME}.${PIDCOUNT}.pid" + + procd_open_instance + procd_set_param command "$PROG" + procd_set_param file /etc/config/system + + procd_set_param respawn + + procd_set_param stdout 1 + procd_set_param pidfile $pid_file + procd_close_instance +} + +reload_service() { + stop + start +} + +service_triggers() { + procd_add_reload_trigger "system" +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/mount_nand b/root/target/linux/ipq40xx/base-files/etc/init.d/mount_nand new file mode 100755 index 00000000..6f2e5c1b --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/mount_nand @@ -0,0 +1,29 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006-2011 OpenWrt.org + +START=10 +STOP=90 + +NAND_PART="nand_ubi" + +boot() { + # check if spi-nand is present + local mtd=$(grep -i -m 1 "$NAND_PART" /proc/mtd | cut -c 4- | cut -d ':' -f1) + [ -z "$mtd" ] && { + return 0 + } + + [ ! -e /nand_storage ] && { + mkdir /nand_storage + } + + # attach mtd device, automatically format if empty + ubiattach /dev/ubi_ctrl -m "$mtd" + + # create default volume, if volume not present + [ ! -e /dev/ubi0_0 ] && { + ubimkvol /dev/ubi0 -m -N nand_storage + } + + mount -t ubifs -o sync,noatime,rw ubi0:nand_storage /nand_storage/ +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/ntpserver b/root/target/linux/ipq40xx/base-files/etc/init.d/ntpserver new file mode 100755 index 00000000..78095bd4 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/ntpserver @@ -0,0 +1,28 @@ +#!/bin/sh /etc/rc.common + +# Copyright (C) 2021 Teltonika + +START=50 +STOP=51 +USE_PROCD=1 + +service_triggers() +{ + procd_add_reload_trigger "ntpserver" +} + +start_service() { + . /lib/functions.sh + local enabled + + config_load ntpserver + config_get enabled general enabled "0" + + [ "$enabled" -gt 0 ] || return + + logger -t "ntpd" "Starting NTP server" + procd_open_instance + procd_set_param respawn 0 + procd_set_param command "ntpd" -ln + procd_close_instance +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/pcrypt-crconf b/root/target/linux/ipq40xx/base-files/etc/init.d/pcrypt-crconf new file mode 100755 index 00000000..1ba5bbd1 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/pcrypt-crconf @@ -0,0 +1,30 @@ +#!/bin/sh /etc/rc.common +# (C) 2013 openwrt.org + +START=81 + +boot() { + SHA_ALGS="sha1-neon sha224-neon sha256-neon sha384-neon sha512-neon md5-generic" + + for alg in $SHA_ALGS; do + crconf add driver "authenc(hmac($alg),cbc(aes-generic))" type 3 + crconf add driver "pcrypt(authenc(hmac($alg),cbc(aes-generic)))" type 3 + done + + for alg in $SHA_ALGS; do + crconf add driver "authenc(hmac($alg),cbc(des3_ede-generic))" type 3 + crconf add driver "pcrypt(authenc(hmac($alg),cbc(des3_ede-generic)))" type 3 + done +} + +start() { + return 0 +} + +restart() { + return 0 +} + +stop() { + return 0 +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/powerctl b/root/target/linux/ipq40xx/base-files/etc/init.d/powerctl new file mode 100755 index 00000000..7b346fc5 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/powerctl @@ -0,0 +1,32 @@ +#!/bin/sh /etc/rc.common + +START=98 + +ipq40xx_power_auto() { + # change scaling governor as ondemand to enable clock scaling based on system load + echo "performance" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor + + # set scaling min freq as 200 MHz + echo "716000" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq + + # Change sampling rate for frequency scaling decisions to 1s, from 10 ms + #echo "1000000" > /sys/devices/system/cpu/cpufreq/ondemand/sampling_rate + + # Change sampling rate for frequency down scaling decision to 10s + #echo 10 > /sys/devices/system/cpu/cpufreq/ondemand/sampling_down_factor + + # Change the CPU load threshold above which frequency is up-scaled to + # turbo frequency,to 50% + #echo 50 > /sys/devices/system/cpu/cpufreq/ondemand/up_threshold +} + +start() { + . /lib/functions.sh + + local board=$(board_name) + case "$board" in + teltonika,rutx | ap-dk01.1-c1 | ap-dk01.1-c2 | ap-dk04.1-c1 | ap-dk04.1-c2 | ap-dk04.1-c3 | \ + ap-dk04.1-c4 | ap-dk04.1-c5 | ap-dk05.1-c1 | ap-dk06.1-c1 | ap-dk07.1-c1 | ap-dk07.1-c2 | ap-dk07.1-c3) + ipq40xx_power_auto ;; + esac +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/sysctl b/root/target/linux/ipq40xx/base-files/etc/init.d/sysctl new file mode 100755 index 00000000..01416a5c --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/sysctl @@ -0,0 +1,46 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=11 + +apply_defaults() { + local mem="$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)" + local min_free frag_low_thresh frag_high_thresh + + if [ "$mem" -gt 65536 ]; then # 128M + min_free=16384 + elif [ "$mem" -gt 32768 ]; then # 64M + #Too high for RUT3 device, lets make 2048 + #min_free=8192 + min_free=2048 + else + min_free=1024 + frag_low_thresh=393216 + frag_high_thresh=524288 + fi + + sysctl -qw vm.min_free_kbytes="$min_free" + + [ "$frag_low_thresh" ] && sysctl -qw \ + net.ipv4.ipfrag_low_thresh="$frag_low_thresh" \ + net.ipv4.ipfrag_high_thresh="$frag_high_thresh" \ + net.ipv6.ip6frag_low_thresh="$frag_low_thresh" \ + net.ipv6.ip6frag_high_thresh="$frag_high_thresh" \ + net.netfilter.nf_conntrack_frag6_low_thresh="$frag_low_thresh" \ + net.netfilter.nf_conntrack_frag6_high_thresh="$frag_high_thresh" + + # first set default, then all interfaces to avoid races with appearing interfaces + if [ -d /proc/sys/net/ipv6/conf ]; then + echo 0 > /proc/sys/net/ipv6/conf/default/accept_ra + for iface in /proc/sys/net/ipv6/conf/*/accept_ra; do + echo 0 > "$iface" + done + fi +} + +start() { + apply_defaults + for CONF in /etc/sysctl.d/*.conf /etc/sysctl.conf; do + [ -f "$CONF" ] && sysctl -e -p "$CONF" >&- + done +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/sysfixtime b/root/target/linux/ipq40xx/base-files/etc/init.d/sysfixtime new file mode 100755 index 00000000..47d7af61 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/sysfixtime @@ -0,0 +1,34 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2013-2014 OpenWrt.org + +START=00 +STOP=90 + +RTC_DEV=/dev/rtc0 +HWCLOCK=/sbin/hwclock + +boot() { +# start && exit 0 + + local maxtime="$(maxtime)" + local curtime="$(date +%s)" + [ $curtime -lt $maxtime ] && date -s @$maxtime +} + +start() { + boot +} + +stop() { + [ -e "$RTC_DEV" ] && [ -e "$HWCLOCK" ] && $HWCLOCK -w -u -f $RTC_DEV && \ + logger -t sysfixtime "saved '$(date)' to $RTC_DEV" +} + +maxtime() { + local file newest + + for file in $( find /etc -type f ) ; do + [ -z "$newest" -o "$newest" -ot "$file" ] && newest=$file + done + [ "$newest" ] && date -r "$newest" +%s +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/system b/root/target/linux/ipq40xx/base-files/etc/init.d/system new file mode 100755 index 00000000..0e467091 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/system @@ -0,0 +1,45 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2014 OpenWrt.org + +START=10 +USE_PROCD=1 + +validate_system_section() { + uci_load_validate system system "$1" "$2" \ + 'hostname:string:%%NAME%%' \ + 'conloglevel:uinteger' \ + 'buffersize:uinteger' \ + 'timezone:string:UTC' \ + 'zonename:string' +} + +system_config() { + [ "$2" = 0 ] || { + echo "validation failed" + return 1 + } + + echo "$hostname" > /proc/sys/kernel/hostname + [ -z "$conloglevel" -a -z "$buffersize" ] || dmesg ${conloglevel:+-n $conloglevel} ${buffersize:+-s $buffersize} + echo "$timezone" > /tmp/TZ + [ -n "$zonename" ] && [ -f "/usr/share/zoneinfo/${zonename// /_}" ] \ + && ln -sf "/usr/share/zoneinfo/${zonename// /_}" /tmp/localtime \ + && rm -f /tmp/TZ + + # apply timezone to kernel + hwclock -u --systz +} + +reload_service() { + config_load system + config_foreach validate_system_section system system_config +} + +service_triggers() { + procd_add_reload_trigger "system" + procd_add_validation validate_system_section +} + +start_service() { + reload_service +} diff --git a/root/target/linux/ipq40xx/base-files/etc/init.d/umount b/root/target/linux/ipq40xx/base-files/etc/init.d/umount new file mode 100755 index 00000000..b764ae15 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/init.d/umount @@ -0,0 +1,13 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +STOP=90 + +restart() { + : +} + +stop() { + sync + /bin/umount -a -d -r +} diff --git a/root/target/linux/ipq40xx/base-files/etc/uci-defaults/01_mnf-info b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/01_mnf-info new file mode 100644 index 00000000..8d1d13a7 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/01_mnf-info @@ -0,0 +1,26 @@ +#!/bin/sh + +SECTION="hwinfo" +CONFIG="hwinfo" + +get_hw_info() { + param=$(/sbin/mnf_info "--name") + [ -n "$param" -a ${#param} == 12 ] && uci set "system"."@system[0]"."device_code"=$param + + hostname=${param:0:6} + [ -n "$hostname" -a ${#hostname} == 6 -a -z "$(uci -q get system.@system[0].hostname)" ] && uci set "system"."@system[0]"."hostname"=Teltonika-$hostname.com + + routername=${param:0:6} + [ -n "$routername" -a ${#routername} == 6 -a -z "$(uci -q get system.@system[0].routername)" ] && uci set "system"."@system[0]"."routername"=$routername + + version=$(cat /etc/version) + [ -n "$version" ] && uci set "system"."@system[0]"."device_fw_version"=$version + + uci commit "system" +} + +# get device information +echo "Reading device information" +get_hw_info + +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/uci-defaults/02_simcard b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/02_simcard new file mode 100644 index 00000000..7e8dbc6a --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/02_simcard @@ -0,0 +1,28 @@ +#!/bin/sh +. /lib/functions.sh +. /lib/functions/board.sh + + +set_pin() { + local sim="$1" + local modem position num builtin + + config_get modem "$sim" modem + builtin=$(is_builtin_modem "$modem") + [ "$builtin" = "0" ] && return + + config_get position "$sim" position + #If modem builtin and primary then position num 3 or 4 else 1 or 2 + [ "$builtin" = "2" ] && num=$((position + 2)) || num=$position + + pin=`/sbin/mnf_info --simpin $num` + [ -z "$pin" ] && return + + uci -q set "simcard"."$sim"."pincode"="$pin" +} + +config_load simcard +config_foreach set_pin sim +uci commit simcard + +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/uci-defaults/03_side-widget b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/03_side-widget new file mode 100644 index 00000000..36c60051 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/03_side-widget @@ -0,0 +1,119 @@ +#!/bin/sh +. /lib/functions.sh + +CONFIG="widget" +MOBILE_CONFIG="/etc/config/simcard" +RMS_CONFIG="/etc/config/rms_connect_mqtt" +WIFI_CONFIG="/etc/config/wireless" +ETHERNET=$(uci -q get network.@switch[0].name) +ethernet_widget_exists=0 +mobile_widget_exists=0 +system_widget_exists=0 +wifi_widget_exists=0 +rms_widget_exists=0 +position=1 +radio0_network=1 +radio1_network=1 + +check_for_widgets() { + local widget="$1" + local type + config_get type "$widget" type + + if [ "$type" = "ethernet" ]; then + ethernet_widget_exists=1 + elif [ "$type" = "mobile" ]; then + mobile_widget_exists=1 + elif [ "$type" = "system" ]; then + system_widget_exists=1 + elif [ "$type" = "wifi" ]; then + wifi_widget_exists=1 + elif [ "$type" = "rms" ]; then + rms_widget_exists=1 + fi +} + +setup_wifi_fields() { + local iface_section="$1" + local device + config_get device "$iface_section" device + + section=$(uci -q add widget widget) + + if [ "$device" == "radio0" ]; then + uci -q set $CONFIG."${section}.id"=$device."network${radio0_network}" + let "radio0_network=radio0_network + 1" + elif [ "$device" == "radio1" ]; then + uci -q set $CONFIG."${section}.id"=$device."network${radio1_network}" + let "radio1_network=radio1_network + 1" + fi + uci -q set $CONFIG."${section}.type=wifi" + uci -q set $CONFIG."${section}.position=${position}" + uci -q set $CONFIG."${section}.enabled=1" + let "position=position + 1" +} + +check_if_config_exists() { + if [ ! -f "/etc/config/${CONFIG}" ]; then + touch "/etc/config/${CONFIG}" + fi +} + +setup_side_widget() { + config_load widget + config_foreach check_for_widgets widget + + if [ $ethernet_widget_exists -eq 0 -a -n "$ETHERNET" ]; then + check_if_config_exists + section=$(uci -q add widget widget) + uci -q set $CONFIG."${section}.id=widget1" + uci -q set $CONFIG."${section}.type=ethernet" + uci -q set $CONFIG."${section}.position=${position}" + uci -q set $CONFIG."${section}.enabled=1" + let "position=position + 1" + fi + + if [ $mobile_widget_exists -eq 0 -a -s "$MOBILE_CONFIG" -a -f "$MOBILE_CONFIG" ]; then + check_if_config_exists + modem_count=$(jsonfilter -q -i /etc/board.json -e '@["modems"][*]' | wc -l) + for i in $(seq $modem_count) + do + section=$(uci -q add widget widget) + uci -q set $CONFIG."${section}.id=mobile-widget$i" + uci -q set $CONFIG."${section}.type=mobile" + uci -q set $CONFIG."${section}.position=${position}" + uci -q set $CONFIG."${section}.enabled=1" + let "position=position + 1" + done + fi + + if [ $system_widget_exists -eq 0 ]; then + check_if_config_exists + section=$(uci -q add widget widget) + uci -q set $CONFIG."${section}.id=widget3" + uci -q set $CONFIG."${section}.type=system" + uci -q set $CONFIG."${section}.position=${position}" + uci -q set $CONFIG."${section}.enabled=1" + let "position=position + 1" + fi + + if [ $wifi_widget_exists -eq 0 -a -f "$WIFI_CONFIG" ]; then + check_if_config_exists + config_load wireless + config_foreach setup_wifi_fields wifi-iface + fi + + if [ $rms_widget_exists -eq 0 -a -f "$RMS_CONFIG" ]; then + check_if_config_exists + section=$(uci -q add widget widget) + uci -q set $CONFIG."${section}.id=widget5" + uci -q set $CONFIG."${section}.type=rms" + uci -q set $CONFIG."${section}.position=${position}" + uci -q set $CONFIG."${section}.enabled=1" + let "position=position + 1" + fi + + uci -q commit $CONFIG +} +setup_side_widget +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/uci-defaults/04_migrate-vlan b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/04_migrate-vlan new file mode 100644 index 00000000..7b96fa48 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/04_migrate-vlan @@ -0,0 +1,34 @@ +#!/bin/sh +. /lib/functions.sh + +move_network(){ + local section=$1 + local device ports vlan + + config_get ports "$section" ports + config_get device "$section" device + config_get vlan "$section" vlan + + [ "$vlan" -eq 1 ] && { + if list_contains "ports" "0t" && list_contains "ports" "5"; then + uci_set network "$section" ports "${ports/0t/0}" + uci_set network "$section" vlan "2" + uci_remove network "$section" vid + fi + } + + [ "$vlan" -eq 2 ] && { + if list_contains ports "0t"; then + echo "Contains" + uci_set network "$section" ports "${ports/0t/0}" + uci_set network "$section" vlan "1" + uci_remove network "$section" vid + fi + } +} + +config_load network +config_foreach move_network switch_vlan +uci_commit network + +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/uci-defaults/05_add_mctl_cfg b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/05_add_mctl_cfg new file mode 100644 index 00000000..431c38ae --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/05_add_mctl_cfg @@ -0,0 +1,16 @@ +#!/bin/sh + +[ -n "$(uci -q get system.modem.disable)" ] || { + for m in /sys/class/gpio/modem*_power; do + local label="$(basename $m | awk -F_ '{print $1}')" + uci set "system.${label}=mctl" + uci set "system.${label}.disable=0" + + # modem is turned on in preinit but others are not + [ "${label}" != "modem" ] && /sbin/mctl -p -m "${label}" + done + + uci commit system +} + +exit 0 \ No newline at end of file diff --git a/root/target/linux/ipq40xx/base-files/etc/uci-defaults/13_migrate-groups b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/13_migrate-groups new file mode 100755 index 00000000..c4b6e3d1 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/13_migrate-groups @@ -0,0 +1,35 @@ +#!/bin/sh +. /lib/functions.sh + +CONFIG="/etc/config/user_groups" + +migrate_tel_list() { + local tel="$1" + local new_section="$2" + + uci -q add_list user_groups."$new_section".tel="$tel" +} + +migrate_group() { + local group="$1" + local name + + config_get name "$group" name "" + local new_section=`uci -q add user_groups phone` + uci -q set user_groups."$new_section".name="$name" + config_list_foreach "$group" tel migrate_tel_list "$new_section" + + uci -q delete sms_utils."$group" +} + +if [ ! -e "$CONFIG" ]; then + touch "$CONFIG" +fi + +config_load sms_utils +config_foreach migrate_group group + +uci -q commit user_groups +uci -q commit sms_utils + +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/uci-defaults/40_strongswan_to_ipsec b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/40_strongswan_to_ipsec new file mode 100755 index 00000000..88497cbc --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/40_strongswan_to_ipsec @@ -0,0 +1,140 @@ +#!/bin/sh + +[ -e /etc/config/strongswan ] || exit 0 + +. /lib/functions.sh +ENABLED=0 + +move_config() { + + local name=$1 + + #get old config + config_get_bool enabled "$1" "enabled" "0" + config_get keyexchange "$1" "keyexchange" + config_get aggressive "$1" "aggressive" + config_get ipsec_type "$1" "ipsec_type" + config_get my_identifier_type "$1" "my_identifier_type" + config_get my_identifier "$1" "my_identifier" + config_get leftfirewall "$1" "leftfirewall" + config_get forceencaps "$1" "forceencaps" + config_get dpdaction "$1" "dpdaction" "none" + config_get dpddelay "$1" "dpddelay" + config_get dpdtimeout "$1" "dpdtimeout" + config_get psk_key "$1" "psk_key" + config_get right "$1" "right" + config_get rightfirewall "$1" "rightfirewall" + config_get ike_encryption_algorithm "$1" "ike_encryption_algorithm" + config_get ike_authentication_algorithm "$1" "ike_authentication_algorithm" + config_get ike_dh_group "$1" "ike_dh_group" + config_get ikelifetime "$1" "ikelifetime" + config_get esp_encryption_algorithm "$1" "esp_encryption_algorithm" + config_get esp_hash_algorithm "$1" "esp_hash_algorithm" + config_get esp_pfs_group "$1" "esp_pfs_group" + config_get keylife "$1" "keylife" + config_get leftsubnet "$1" "leftsubnet" + config_get rightsubnet "$1" "rightsubnet" + config_get leftprotoport "$1" "leftprotoport" + config_get rightprotoport "$1" "rightprotoport" + + [ "$enabled" = 1 ] && ENABLED=1 + #set new config structure + + config_set + uci_add "ipsec" "proposal" "${name}_ph1" + uci_set "ipsec" $CONFIG_SECTION "encryption_algorithm" "$ike_encryption_algorithm" + uci_set "ipsec" $CONFIG_SECTION "hash_algorithm" "$ike_authentication_algorithm" + uci_set "ipsec" $CONFIG_SECTION "dh_group" "$ike_dh_group" + + uci_add "ipsec" "proposal" "${name}_ph2" + uci_set "ipsec" $CONFIG_SECTION "encryption_algorithm" "$esp_encryption_algorithm" + uci_set "ipsec" $CONFIG_SECTION "hash_algorithm" "$esp_hash_algorithm" + uci_set "ipsec" $CONFIG_SECTION "dh_group" "$esp_pfs_group" + + uci_add "ipsec" "connection" "${name}_c" + uci_set "ipsec" $CONFIG_SECTION "mode" "start" + uci_set "ipsec" $CONFIG_SECTION "type" "$ipsec_type" + uci_set "ipsec" $CONFIG_SECTION "local_subnet" "$leftsubnet" + uci_set "ipsec" $CONFIG_SECTION "remote_subnet" "$rightsubnet" + uci_set "ipsec" $CONFIG_SECTION "remote_firewall" "$rightfirewall" + uci_set "ipsec" $CONFIG_SECTION "keyexchange" "$keyexchange" + uci_set "ipsec" $CONFIG_SECTION "aggressive" "$aggressive" + uci_set "ipsec" $CONFIG_SECTION "ikelifetime" "$ikelifetime" + uci_set "ipsec" $CONFIG_SECTION "lifetime" "$lifetime" + [ -n "$dpdaction" ] && { + uci_set "ipsec" $CONFIG_SECTION "_dpd" "1" + uci_set "ipsec" $CONFIG_SECTION "dpdaction" "$dpdaction" + uci_set "ipsec" $CONFIG_SECTION "dpddelay" "$dpddelay" + } + uci_set "ipsec" $CONFIG_SECTION "crypto_proposal" "${name}_ph2" + uci_set "ipsec" $CONFIG_SECTION "leftprotoport" "$leftprotoport" + uci_set "ipsec" $CONFIG_SECTION "rightprotoport" "$rightprotoport" + uci_set "ipsec" $CONFIG_SECTION "lifetime" "$keylife" + uci_set "ipsec" $CONFIG_SECTION "forceencaps" "$forceencaps" + + uci_add "ipsec" "remote" "${name}" + uci_set "ipsec" $CONFIG_SECTION "enabled" "$enabled" + uci_set "ipsec" $CONFIG_SECTION "gateway" "$right" + uci_set "ipsec" $CONFIG_SECTION "remote_identifier" "%any" + uci_set "ipsec" $CONFIG_SECTION "authentication_method" "psk" + uci_set "ipsec" $CONFIG_SECTION "pre_shared_key" "$psk_key" + uci_set "ipsec" $CONFIG_SECTION "local_identifier" "$my_identifier" + uci_set "ipsec" $CONFIG_SECTION "crypto_proposal" "${name}_ph1" + + [ "$ipsec_type" = "tunnel" ] && uci -q add_list "ipsec"."${CONFIG_SECTION}"."tunnel"="${name}_c" + [ "$ipsec_type" = "transport" ] && uci -q add_list "ipsec"."${CONFIG_SECTION}"."transport"="${name}_c" + + uci_commit "ipsec" +} + +config_load 'strongswan' +config_foreach move_config 'conn' + +[ "$ENABLED" = 1 ] && { + uci_add firewall rule + uci_set firewall "$CONFIG_SECTION" src 'wan' + uci_set firewall "$CONFIG_SECTION" name 'Allow-IPsec-ESP' + uci_set firewall "$CONFIG_SECTION" target 'ACCEPT' + uci_set firewall "$CONFIG_SECTION" vpn_type 'IPsec' + uci_set firewall "$CONFIG_SECTION" proto 'esp' + + uci_add firewall rule + uci_set firewall "$CONFIG_SECTION" dest_port '4500' + uci_set firewall "$CONFIG_SECTION" src 'wan' + uci_set firewall "$CONFIG_SECTION" name 'Allow-IPsec-NAT-T' + uci_set firewall "$CONFIG_SECTION" target 'ACCEPT' + uci_set firewall "$CONFIG_SECTION" vpn_type 'IPsec' + uci_set firewall "$CONFIG_SECTION" proto 'udp' + + uci_add firewall rule + uci_set firewall "$CONFIG_SECTION" dest_port '500' + uci_set firewall "$CONFIG_SECTION" src 'wan' + uci_set firewall "$CONFIG_SECTION" name 'Allow-IPsec-IKE' + uci_set firewall "$CONFIG_SECTION" target 'ACCEPT' + uci_set firewall "$CONFIG_SECTION" vpn_type 'IPsec' + uci_set firewall "$CONFIG_SECTION" proto 'udp' + + uci_add firewall rule + uci_set firewall "$CONFIG_SECTION" src 'wan' + uci_set firewall "$CONFIG_SECTION" name 'Allow-IPsec-Forward' + uci_set firewall "$CONFIG_SECTION" extra '-m policy --dir in --pol ipsec' + uci_set firewall "$CONFIG_SECTION" target 'ACCEPT' + uci_set firewall "$CONFIG_SECTION" vpn_type 'IPsec' + uci_set firewall "$CONFIG_SECTION" dest '*' + uci_set firewall "$CONFIG_SECTION" proto 'all' + + uci_add firewall redirect + uci_set firewall "$CONFIG_SECTION" proto any + uci_set firewall "$CONFIG_SECTION" name Exclude-IPsec-from-NAT + uci_set firewall "$CONFIG_SECTION" extra '-m policy --dir out --pol ipsec' + uci_set firewall "$CONFIG_SECTION" vpn_type IPsec + uci_set firewall "$CONFIG_SECTION" target ACCEPT + uci_set firewall "$CONFIG_SECTION" dest wan + uci_set firewall "$CONFIG_SECTION" enabled 1 + + uci_commit firewall +} + +rm -f /etc/config/strongswan + +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/etc/uci-defaults/50_migrate_pptp_l2tp_sstp b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/50_migrate_pptp_l2tp_sstp new file mode 100644 index 00000000..2c470f93 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/etc/uci-defaults/50_migrate_pptp_l2tp_sstp @@ -0,0 +1,82 @@ +#!/bin/sh + +. /lib/functions.sh +#this script move configuration from 1.x firmware +#it should rename some sections and add option disabled to network config + +move_config() { + local section="$1" + local file="$2" + + config_get_bool enabled "$1" "enabled" "0" + config_get _name "$1" "_name" + config_get _type "$1" "_type" + [ -z "$_type" ] && config_get _type "$1" "type" + [ "$enabled" -eq 1 ] && [ "$_type" = "client" ] && { + uci set network."$1".auto=1 + uci_commit "${config}" + } + [ "${_type}_${_name}" = "$section" ] && return + uci_rename "$file" "$section" "${_type:+${_type}_}${_name}" + uci_commit "${config}" +} + +move_network() { + local section="$1" + local net_proto net_name net_type net_enabled + + config_get net_proto "$section" "proto" + + case "$net_proto" in + pptp|\ + l2tp) + config_get net_name "$1" "_name" + config_get_bool net_enabled "$1" "enabled" + [ "client_${net_name}" = "$section" ] && return + [ -n "$net_enabled" ] && { + [ "$net_enabled" = "1" ] && uci_set network "$section" disabled 0 || uci_set network "$section" disabled 1 + uci_remove network "$section" enabled + } + uci_rename network "$section" "${net_type:+${net_type}_}${net_name}" + uci_commit network + + ;; + sstp) + config_get_bool net_enabled "$1" "enabled" + [ -z "$net_enabled" ] && return + [ "$net_enabled" = "1" ] && uci_set network "$section" disabled 0 || uci_set network "$section" disabled 1 + uci_remove network "$section" enabled + uci_commit network + ;; + *) + return + ;; + esac +} + +# removes client configurations, because they reside in network and with vuci became unncessary +remove_network() { + local section="$1" + local current_config="$2" + local sectionType + config_get sectionType "$1" "type" + if [ "$sectionType" = "client" ]; then + uci_remove "$current_config" "$section" + uci_commit "$current_config" + fi +} + +configs="pptpd xl2tpd" + +for config in $configs; do + [ -s /etc/config/${config} ] || continue + config_load "$config" + config_foreach move_config "service" "$config" + config_foreach remove_network "service" "$config" +done + +[ -s /etc/config/network ] || exit 0 +config_load "network" +config_foreach move_network "interface" + +exit 0 diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/board.sh b/root/target/linux/ipq40xx/base-files/lib/functions/board.sh new file mode 100644 index 00000000..2f38cbdc --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/board.sh @@ -0,0 +1,40 @@ + +. /usr/share/libubox/jshn.sh + +# when device contains 2 internal modems, this function will return '2' if +# selected modem(inc_id) is builtin and primary. +# And if it's only builtin, then '1' +is_builtin_modem() { + local inc_id="$1" + local modem modems id builtin primary + + json_init + json_load_file "/etc/board.json" + + json_get_keys modems modems + json_select modems + + for modem in $modems; do + json_select "$modem" + json_get_vars id builtin primary + + [ "$id" = "$inc_id" ] && { + [ -n "$builtin" ] && { + [ -n "$primary" ] && { + echo 2 + return + } + + echo 1 + return + } + + echo 0 + return + } + + json_select .. + done + + echo 0 +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/caldata.sh b/root/target/linux/ipq40xx/base-files/lib/functions/caldata.sh new file mode 100644 index 00000000..2177cf84 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/caldata.sh @@ -0,0 +1,171 @@ +# Copyright (C) 2019 OpenWrt.org + +. /lib/functions.sh +. /lib/functions/system.sh + +caldata_dd() { + local source=$1 + local target=$2 + local count=$(($3)) + local offset=$(($4)) + + dd if=$source of=$target iflag=skip_bytes,fullblock bs=$count skip=$offset count=1 2>/dev/null + return $? +} + +caldata_die() { + echo "caldata: " "$*" + exit 1 +} + +caldata_extract() { + local part=$1 + local offset=$(($2)) + local count=$(($3)) + local mtd + + mtd=$(find_mtd_chardev $part) + [ -n "$mtd" ] || caldata_die "no mtd device found for partition $part" + + caldata_dd $mtd /lib/firmware/$FIRMWARE $count $offset || \ + caldata_die "failed to extract calibration data from $mtd" +} + +caldata_extract_ubi() { + local part=$1 + local offset=$(($2)) + local count=$(($3)) + local ubidev + local ubi + + . /lib/upgrade/nand.sh + + ubidev=$(nand_find_ubi $CI_UBIPART) + ubi=$(nand_find_volume $ubidev $part) + [ -n "$ubi" ] || caldata_die "no UBI volume found for $part" + + caldata_dd /dev/$ubi /lib/firmware/$FIRMWARE $count $offset || \ + caldata_die "failed to extract calibration data from $ubi" +} + +caldata_extract_reverse() { + local part=$1 + local offset=$2 + local count=$(($3)) + local mtd + local reversed + local caldata + + mtd=$(find_mtd_chardev "$part") + reversed=$(hexdump -v -s $offset -n $count -e '/1 "%02x "' $mtd) + + for byte in $reversed; do + caldata="\x${byte}${caldata}" + done + + printf "%b" "$caldata" > /lib/firmware/$FIRMWARE +} + +caldata_from_file() { + local source=$1 + local offset=$(($2)) + local count=$(($3)) + local target=$4 + + [ -n "$target" ] || target=/lib/firmware/$FIRMWARE + + caldata_dd $source $target $count $offset || \ + caldata_die "failed to extract calibration data from $source" +} + +caldata_sysfsload_from_file() { + local source=$1 + local offset=$(($2)) + local count=$(($3)) + local target_dir="/sys/$DEVPATH" + local target="$target_dir/data" + + [ -d "$target_dir" ] || \ + caldata_die "no sysfs dir to write: $target" + + echo 1 > "$target_dir/loading" + caldata_dd $source $target $count $offset + if [ $? != 0 ]; then + echo 1 > "$target_dir/loading" + caldata_die "failed to extract calibration data from $source" + else + echo 0 > "$target_dir/loading" + fi +} + +caldata_valid() { + local expected="$1" + local target=$2 + + [ -n "$target" ] || target=/lib/firmware/$FIRMWARE + + magic=$(hexdump -v -n 2 -e '1/1 "%02x"' $target) + [ "$magic" = "$expected" ] + return $? +} + +caldata_patch_chksum() { + local mac=$1 + local mac_offset=$(($2)) + local chksum_offset=$(($3)) + local target=$4 + local xor_mac + local xor_fw_mac + local xor_fw_chksum + + xor_mac=${mac//:/} + xor_mac="${xor_mac:0:4} ${xor_mac:4:4} ${xor_mac:8:4}" + + xor_fw_mac=$(hexdump -v -n 6 -s $mac_offset -e '/1 "%02x"' /lib/firmware/$FIRMWARE) + xor_fw_mac="${xor_fw_mac:0:4} ${xor_fw_mac:4:4} ${xor_fw_mac:8:4}" + + xor_fw_chksum=$(hexdump -v -n 2 -s $chksum_offset -e '/1 "%02x"' /lib/firmware/$FIRMWARE) + xor_fw_chksum=$(xor $xor_fw_chksum $xor_fw_mac $xor_mac) + + printf "%b" "\x${xor_fw_chksum:0:2}\x${xor_fw_chksum:2:2}" | \ + dd of=$target conv=notrunc bs=1 seek=$chksum_offset count=2 +} + +caldata_patch_mac() { + local mac=$1 + local mac_offset=$(($2)) + local chksum_offset=$3 + local target=$4 + + [ -z "$mac" -o -z "$mac_offset" ] && return + + [ -n "$target" ] || target=/lib/firmware/$FIRMWARE + + [ -n "$chksum_offset" ] && caldata_patch_chksum "$mac" "$mac_offset" "$chksum_offset" "$target" + + macaddr_2bin $mac | dd of=$target conv=notrunc oflag=seek_bytes bs=6 seek=$mac_offset count=1 || \ + caldata_die "failed to write MAC address to eeprom file" +} + +ath9k_patch_mac() { + local mac=$1 + local target=$2 + + caldata_patch_mac "$mac" 0x2 "" "$target" +} + +ath9k_patch_mac_crc() { + local mac=$1 + local mac_offset=$2 + local chksum_offset=$((mac_offset - 10)) + local target=$4 + + caldata_patch_mac "$mac" "$mac_offset" "$chksum_offset" "$target" +} + +ath10k_patch_mac() { + local mac=$1 + local target=$2 + + caldata_patch_mac "$mac" 0x6 0x2 "$target" +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/leds.sh b/root/target/linux/ipq40xx/base-files/lib/functions/leds.sh new file mode 100644 index 00000000..a7532faa --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/leds.sh @@ -0,0 +1,94 @@ +# Copyright (C) 2013 OpenWrt.org + +get_dt_led_path() { + local ledpath + local basepath="/proc/device-tree" + local nodepath="$basepath/aliases/led-$1" + + [ -f "$nodepath" ] && ledpath=$(cat "$nodepath") + [ -n "$ledpath" ] && ledpath="$basepath$ledpath" + + echo "$ledpath" +} + +get_dt_led() { + local label + local ledpath=$(get_dt_led_path $1) + + [ -n "$ledpath" ] && \ + label=$(cat "$ledpath/label" 2>/dev/null) || \ + label=$(cat "$ledpath/chan-name" 2>/dev/null) || \ + label=$(basename "$ledpath") + + echo "$label" +} + +led_set_attr() { + [ -f "/sys/class/leds/$1/$2" ] && echo "$3" > "/sys/class/leds/$1/$2" +} + +led_timer() { + led_set_attr $1 "trigger" "timer" + led_set_attr $1 "delay_on" "$2" + led_set_attr $1 "delay_off" "$3" +} + +led_on() { + led_set_attr $1 "trigger" "none" + led_set_attr $1 "brightness" 255 +} + +led_off() { + led_set_attr $1 "trigger" "none" + led_set_attr $1 "brightness" 0 +} + +status_led_restore_trigger() { + local trigger + local ledpath=$(get_dt_led_path $1) + + [ -n "$ledpath" ] && \ + trigger=$(cat "$ledpath/linux,default-trigger" 2>/dev/null) + + [ -n "$trigger" ] && \ + led_set_attr "$(get_dt_led $1)" "trigger" "$trigger" +} + +status_led_set_timer() { + led_timer $status_led "$1" "$2" + [ -n "$status_led2" ] && led_timer $status_led2 "$1" "$2" +} + +status_led_set_heartbeat() { + led_set_attr $status_led "trigger" "heartbeat" +} + +status_led_on() { + led_on $status_led + [ -n "$status_led2" ] && led_on $status_led2 +} + +status_led_off() { + led_off $status_led + [ -n "$status_led2" ] && led_off $status_led2 +} + +status_led_blink_slow() { + led_timer $status_led 1000 1000 +} + +status_led_blink_fast() { + led_timer $status_led 100 100 +} + +status_led_blink_preinit() { + led_timer $status_led 100 100 +} + +status_led_blink_failsafe() { + led_timer $status_led 50 50 +} + +status_led_blink_preinit_regular() { + led_timer $status_led 200 200 +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/migrate.sh b/root/target/linux/ipq40xx/base-files/lib/functions/migrate.sh new file mode 100644 index 00000000..634f9560 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/migrate.sh @@ -0,0 +1,196 @@ +#!/bin/sh + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +CFG_PATH="/etc/config/" + +get_section_name() { + local ___var="$1" + [ "$#" -ge 1 ] && shift + local ___type="$1" + [ "$#" -ge 1 ] && shift + local section cfgtype + + [ -z "$CONFIG_SECTIONS" ] && return 0 + for section in ${CONFIG_SECTIONS}; do + config_get cfgtype "$section" TYPE + [ -n "$___type" -a "x$cfgtype" != "x$___type" ] && continue + eval export "${___var}=\${section}" + return 0 + done +} + +_set_option() { + local option="$1" + local value="$2" + + uci_set "$_NEW_CONFIG" "$_NEW_SEC_NAME" "$option" "$value" +} + +_set_list_option() { + local option="$1" + local value="$2" + + for element in $value; do + uci_add_list "$_NEW_CONFIG" "$_NEW_SEC_NAME" "$option" "$element" + done +} + +_del_uci_element() { + local section="$1" + local option="$2" + + uci_remove "$_OLD_CONFIG" "$section" "$option" +} +_option_cond_cb() { + local value=$3 + + json_select $2 + json_get_var old 1 + json_get_var new 2 + + [ "$old" = "$value" ] && _COND_VALUE="$new" + + json_select .. +} + +_parse_condition(){ + local value="$1" + + _COND_VALUE= + json_for_each_item _option_cond_cb "if" "$value" +} + +_option_rule_cb(){ + local rule="$1" + local option="$2" + local value + + [ -n "$rule" ] || return 0 + + json_select "$option" + json_get_vars new_name "if" default cb type + + if [ -n "$cb" ]; then + eval "$cb \"\$option\" \"\$_OLD_SEC_NAME\" \"\$_NEW_SEC_NAME\"" + [ "$?" -eq 0 ] && { + json_select .. + return 0 + } + + value="$_OPTION_VALUE" + else + config_get value $_OLD_SEC_NAME "$option" "$default" + fi + + [ -n "$if" ] && { + _parse_condition "$value" + value="${_COND_VALUE:-${value:-$default}}" + } + + if [ -n "$type" -a "$type" = "list" ]; then + _set_list_option "${new_name:-$option}" "$value" + else + _set_option "${new_name:-$option}" "$value" + fi + + json_select .. +} + +_init_section() { + local sec_t + + json_get_vars old_name new_name new_type old_type + [ -n "$old_name" -o -n "$old_type" ] || return 1 + + if [ -z "$old_name" ]; then + get_section_name _OLD_SEC_NAME "$old_type" + else + _OLD_SEC_NAME=$old_name + fi + + _NEW_SEC_NAME=$new_name + _OLD_SEC_TYPE=$old_type + _NEW_SEC_TYPE=${new_type:-$old_type} + [ -n "$_NEW_SEC_TYPE" ] || \ + eval "_NEW_SEC_TYPE=\$CONFIG_${_OLD_SEC_NAME}_TYPE" + + if [ -n "$_NEW_SEC_NAME" ]; then + uci set "$_NEW_CONFIG"."$_NEW_SEC_NAME"="$_NEW_SEC_TYPE" + else + _NEW_SEC_NAME="$(uci -q add "$_NEW_CONFIG" "$_NEW_SEC_TYPE")" + fi + + [ -n "$_NEW_SEC_NAME" ] || return 1 +} + +_section_rule_cb(){ + local rule="$1" + local section="$2" + local value + + [ -n "$rule" ] || return 0 + + json_select "$section" + json_get_vars cb old_name new_name new_type old_type remove + [ -n "$cb" ] && { + eval "$cb \"\$old_name\" \"\$new_name\" \"\$old_type\" \"\$new_type\"" + [ "$?" -eq 0 ] && { + json_select .. + return 0 + } + } + + _init_section + [ $? -ne 0 ] && { + logger -t "Migration" "Unable to init section" + json_select .. + + return 1 + } + + json_for_each_item _option_rule_cb options + json_select .. + [ -n "$remove" ] && { + _del_uci_element "$old_name" + uci_commit "$_OLD_CONFIG" + } +} + +_init_config() { + json_select config + json_get_vars old_name new_name + + [ -n "$old_name" ] || return 1 + [ -f "$CFG_PATH$old_name" ] || return 1 + [ -f "$CFG_PATH$new_name" ] || touch $CFG_PATH$new_name + + config_load "$old_name" + _NEW_CONFIG="${new_name:-$old_name}" + _OLD_CONFIG="$old_name" + + json_select .. +} + +migrate() { + local remove + local json_file="$1" + + [ -f "$json_file" ] || return 0 + + json_init + json_load_file "$json_file" + json_select + _init_config + [ $? -ne 0 ] && { + logger -t "Migration" "Unable to load config" + return 1 + } + + json_for_each_item _section_rule_cb sections + uci_commit "$_NEW_CONFIG" + + json_get_vars remove + [ -n "$remove" ] && rm "$CFG_PATH$_OLD_CONFIG" +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/migrations.sh b/root/target/linux/ipq40xx/base-files/lib/functions/migrations.sh new file mode 100644 index 00000000..d43ea350 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/migrations.sh @@ -0,0 +1,67 @@ +. /lib/functions.sh + +migrate_led_sysfs() { + local cfg="$1"; shift + local tuples="$@" + local sysfs + local name + + config_get sysfs ${cfg} sysfs + config_get name ${cfg} name + + [ -z "${sysfs}" ] && return + + for tuple in ${tuples}; do + local old=${tuple%=*} + local new=${tuple#*=} + local new_sysfs + + new_sysfs=$(echo ${sysfs} | sed "s/${old}/${new}/") + + [ "${new_sysfs}" = "${sysfs}" ] && continue + + uci set system.${cfg}.sysfs="${new_sysfs}" + + logger -t led-migration "sysfs option of LED \"${name}\" updated to ${new_sysfs}" + done; +} + +remove_devicename_led_sysfs() { + local cfg="$1"; shift + local exceptions="$@" + local sysfs + local name + local new_sysfs + + config_get sysfs ${cfg} sysfs + config_get name ${cfg} name + + # only continue if two or more colons are present + echo "${sysfs}" | grep -q ":.*:" || return + + for exception in ${exceptions}; do + # no change if exceptions provided as argument are found for devicename + echo "${sysfs}" | grep -q "^${exception}:" && return + done + + new_sysfs=$(echo ${sysfs} | sed "s/^[^:]*://") + + uci set system.${cfg}.sysfs="${new_sysfs}" + + logger -t led-migration "sysfs option of LED \"${name}\" updated to ${new_sysfs}" +} + +migrate_leds() { + config_load system + config_foreach migrate_led_sysfs led "$@" +} + +remove_devicename_leds() { + config_load system + config_foreach remove_devicename_led_sysfs led "$@" +} + +migrations_apply() { + local realm="$1" + [ -n "$(uci changes ${realm})" ] && uci -q commit ${realm} +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/mobile.sh b/root/target/linux/ipq40xx/base-files/lib/functions/mobile.sh new file mode 100644 index 00000000..2476644e --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/mobile.sh @@ -0,0 +1,247 @@ +#Mobile configuration management lib + +. /usr/share/libubox/jshn.sh +. /lib/functions.sh + +gsm_soft_reset() { + gsmctl -n -A at+cfun=4 + sleep 2 + gsmctl -n -A at+cfun=1 +} + +qmi_error_handle() { + local error="$1" + local modem_id="$2" + + $(echo "$error" | grep -q "error") && { + echo "$error" + } + + $(echo "$error" | grep -q "Client IDs exhausted") && { + echo "ClientIdsExhausted! reseting counter..." + proto_notify_error "$interface" NO_CID + uqmi -s -d "$device" --sync + return 1 + } + +# Reik papildyt ERROR handlinima +# $(echo "$error" | grep -q "multiple-connection-to-same-pdn-not-allowed") && { +# echo "Reseting due dublicated connection..." +# qmicli -p -d "$device" --uim-sim-power-off=1 +# qmicli -p -d "$device" --uim-sim-power-on=1 +# return 1 +# } + +# $(echo "$error" | grep -q "Transaction timed out") && { +# echo "Device not responding, restarting module" +# gsmctl -O $modem_id -A at+cfun=1,1 +# } +# +# $(echo "$error" | grep -q 'verbose call end reason (2,236)') && { + # echo "Failed to start network, clearing all cids" + # qmicli -p -d "$device" --wds-noop --device-open-sync + # return 1 +# } + + $(echo "$error" | grep -q "Call Failed") && { + echo "Device not responding, restarting module" + sleep 10 + gsm_soft_reset + return 1 + } + + $(echo "$error" | grep -q "Policy Mismatch") && { + echo "Reseting network..." + gsm_soft_reset + return 1 + } + + $(echo "$error" | grep -q "Failed to connect to service") && { + echo "Device not responding, restarting module" + gsmctl -A at+cfun=1,1 + return 1 + } + + $(echo "$error" | grep -q "error") && { + echo "$error" + } + + return 0 +} + +passthrough_mode= +get_passthrough() { + config_get primary "$1" primary + [ "$primary" = "1" ] && { + config_get sim "$1" position; + passthrough_mode=$(uci -q get network.mob1s${sim}a1.passthrough_mode 2>/dev/null); + } +} + +setup_bridge_v4() { + local dev="$1" + local dhcp_param_file="/tmp/dnsmasq.d/bridge" + echo "$parameters4" + + json_load "$parameters4" + json_select "ipv4" + json_get_var bridge_ipaddr ip + json_get_var bridge_mask subnet + json_get_var bridge_gateway gateway + json_get_var bridge_dns1 dns1 + json_get_var bridge_dns2 dns2 + + json_init + json_add_string name "${interface}_4" + json_add_string ifname "$dev" + json_add_string proto "none" + json_add_object "data" + ubus call network add_dynamic "$(json_dump)" + IFACE4="${interface}_4" + + json_init + json_add_string interface "${interface}_4" + json_add_string zone "lan" + ubus call network.interface set_data "$(json_dump)" + + json_init + json_add_string interface "${interface}" + json_add_string bridge_ipaddr "$bridge_ipaddr" + ubus call network.interface set_data "$(json_dump)" + + json_init + json_add_string modem "$modem" + json_add_string sim "$sim" + ubus call network.interface."${interface}_4" set_data "$(json_dump)" + json_close_object + + ip route add default dev "$dev" table 42 + ip route add default dev br-lan table 43 + ip route add "$bridge_ipaddr" dev br-lan + + ip rule add pref 5042 from "$bridge_ipaddr" lookup 42 + ip rule add pref 5043 iif "$dev" lookup 43 + #sysctl -w net.ipv4.conf.br-lan.proxy_arp=1 #2>/dev/null + ip neighbor add proxy "$bridge_gateway" dev br-lan + + iptables -A postrouting_rule -m comment --comment "Bridge mode" -o "$dev" -j ACCEPT -tnat + + config_load simcard + config_foreach get_passthrough sim + + > $dhcp_param_file + [ -z "$mac" ] && mac="*:*:*:*:*:*" + [ "$passthrough_mode" != "no_dhcp" ] && { + echo "dhcp-range=tag:mobbridge,$bridge_ipaddr,static,$bridge_mask,${leasetime:-1h}" > "$dhcp_param_file" + echo "shared-network=br-lan,$bridge_ipaddr" >> "$dhcp_param_file" + echo "dhcp-host=$mac,set:mobbridge,$bridge_ipaddr" >> "$dhcp_param_file" + echo "dhcp-option=tag:mobbridge,br-lan,3,$bridge_gateway" >> "$dhcp_param_file" + echo "dhcp-option=tag:mobbridge,br-lan,6,$bridge_dns1,$bridge_dns2" >> "$dhcp_param_file" + echo "server=$bridge_dns1" >> "$dhcp_param_file" + echo "server=$bridge_dns2" >> "$dhcp_param_file" + } + /etc/init.d/dnsmasq reload + swconfig dev 'switch0' set soft_reset 5 & +} + +setup_static_v4() { + local dev="$1" + echo "Setting up $dev V4 static" + echo "$parameters4" + + json_load "$parameters4" + json_select "ipv4" + json_get_var ip_4 ip + json_get_var dns1_4 dns1 + json_get_var dns2_4 dns2 + + json_init + json_add_string name "${interface}_4" + json_add_string ifname "$dev" + json_add_string proto static + json_add_string gateway "0.0.0.0" + + json_add_array ipaddr + json_add_string "" "$ip_4" + json_close_array + + json_add_array dns + [ -n "$dns1_4" ] && json_add_string "" "$dns1_4" + [ -n "$dns2_4" ] && json_add_string "" "$dns2_4" + json_close_array + + [ -n "$ip4table" ] && json_add_string ip4table "$ip4table" + proto_add_dynamic_defaults + + ubus call network add_dynamic "$(json_dump)" +} + +setup_dhcp_v4() { + local dev="$1" + echo "Setting up $dev V4 DCHP" + json_init + json_add_string name "${interface}_4" + json_add_string ifname "$dev" + json_add_string proto "dhcp" + json_add_string script "/lib/netifd/dhcp_mobile.script" + [ -n "$ip4table" ] && json_add_string ip4table "$ip4table" + proto_add_dynamic_defaults + [ -n "$zone" ] && json_add_string zone "$zone" + ubus call network add_dynamic "$(json_dump)" +} + +setup_dhcp_v6() { + local dev="$1" + echo "Setting up $dev V6 DHCP" + json_init + json_add_string name "${interface}_6" + json_add_string ifname "$dev" + json_add_string proto "dhcpv6" + [ -n "$ip6table" ] && json_add_string ip6table "$ip6table" + json_add_boolean ignore_valid 1 + proto_add_dynamic_defaults + # RFC 7278: Extend an IPv6 /64 Prefix to LAN + json_add_string extendprefix 1 + [ -n "$zone" ] && json_add_string zone "$zone" + ubus call network add_dynamic "$(json_dump)" +} + +setup_static_v6() { + local dev="$1" + echo "Setting up $dev V6 static" + echo "$parameters6" + + json_load "$parameters6" + json_select "ipv6" + json_get_var ip6_with_prefix ip + ip_6="${ip6_with_prefix%/*}" + ip_prefix_length="${ip6_with_prefix#*/}" + json_get_var ip6_gateway_with_prefix gateway + gateway_6="${ip6_gateway_with_prefix%/*}" + json_get_var dns1_6 dns1 + json_get_var dns2_6 dns2 + + json_init + json_add_string name "${interface}_6" + json_add_string ifname "$dev" + json_add_string proto static + json_add_string ip6gw "$gateway_6" + + json_add_array ip6prefix + json_add_string "" "$ip6_with_prefix" + json_close_array + + json_add_array ip6addr + json_add_string "" "$ip6_with_prefix" + json_close_array + + json_add_array dns + [ -n "$dns1_6" ] && json_add_string "" "$dns1_6" + [ -n "$dns2_6" ] && json_add_string "" "$dns2_6" + json_close_array + + [ -n "$ip6table" ] && json_add_string ip6table "$ip6table" + proto_add_dynamic_defaults + + ubus call network add_dynamic "$(json_dump)" +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/network.sh b/root/target/linux/ipq40xx/base-files/lib/functions/network.sh new file mode 100644 index 00000000..a5d8d263 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/network.sh @@ -0,0 +1,313 @@ +# 1: destination variable +# 2: interface +# 3: path +# 4: separator +# 5: limit +__network_ifstatus() { + local __tmp + + [ -z "$__NETWORK_CACHE" ] && { + __tmp="$(ubus call network.interface dump 2>&1)" + case "$?" in + 4) : ;; + 0) export __NETWORK_CACHE="$__tmp" ;; + *) echo "$__tmp" >&2 ;; + esac + } + + __tmp="$(jsonfilter ${4:+-F "$4"} ${5:+-l "$5"} -s "${__NETWORK_CACHE:-{}}" -e "$1=@.interface${2:+[@.interface='$2']}$3")" + + [ -z "$__tmp" ] && \ + unset "$1" && \ + return 1 + + eval "$__tmp" +} + +# determine first IPv4 address of given logical interface +# 1: destination variable +# 2: interface +network_get_ipaddr() { + __network_ifstatus "$1" "$2" "['ipv4-address'][0].address"; +} + +# determine first IPv6 address of given logical interface +# 1: destination variable +# 2: interface +network_get_ipaddr6() { + __network_ifstatus "$1" "$2" "['ipv6-address'][0].address" || \ + __network_ifstatus "$1" "$2" "['ipv6-prefix-assignment'][0]['local-address'].address" || \ + return 1 +} + +# determine first IPv4 subnet of given logical interface +# 1: destination variable +# 2: interface +network_get_subnet() { + __network_ifstatus "$1" "$2" "['ipv4-address'][0]['address','mask']" "/" +} + +# determine first IPv6 subnet of given logical interface +# 1: destination variable +# 2: interface +network_get_subnet6() { + local __nets __addr + + if network_get_subnets6 __nets "$2"; then + # Attempt to return first non-fe80::/10, non-fc::/7 range + for __addr in $__nets; do + case "$__addr" in fe[8ab]?:*|f[cd]??:*) + continue + esac + export "$1=$__addr" + return 0 + done + + # Attempt to return first non-fe80::/10 range + for __addr in $__nets; do + case "$__addr" in fe[8ab]?:*) + continue + esac + export "$1=$__addr" + return 0 + done + + # Return first item + for __addr in $__nets; do + export "$1=$__addr" + return 0 + done + fi + + unset "$1" + return 1 +} + +# determine first IPv6 prefix of given logical interface +# 1: destination variable +# 2: interface +network_get_prefix6() { + __network_ifstatus "$1" "$2" "['ipv6-prefix'][0]['address','mask']" "/" +} + +# determine all IPv4 addresses of given logical interface +# 1: destination variable +# 2: interface +network_get_ipaddrs() { + __network_ifstatus "$1" "$2" "['ipv4-address'][*].address" +} + +# determine all IPv6 addresses of given logical interface +# 1: destination variable +# 2: interface +network_get_ipaddrs6() { + local __addr + local __list="" + + if __network_ifstatus "__addr" "$2" "['ipv6-address'][*].address"; then + for __addr in $__addr; do + __list="${__list:+$__list }${__addr}" + done + fi + + if __network_ifstatus "__addr" "$2" "['ipv6-prefix-assignment'][*]['local-address'].address"; then + for __addr in $__addr; do + __list="${__list:+$__list }${__addr}" + done + fi + + if [ -n "$__list" ]; then + export "$1=$__list" + return 0 + fi + + unset "$1" + return 1 +} + +# determine all IP addresses of given logical interface +# 1: destination variable +# 2: interface +network_get_ipaddrs_all() { + local __addr __addr6 + + network_get_ipaddrs __addr "$2" + network_get_ipaddrs6 __addr6 "$2" + + if [ -n "$__addr" -o -n "$__addr6" ]; then + export "$1=${__addr:+$__addr }$__addr6" + return 0 + fi + + unset "$1" + return 1 +} + +# determine all IPv4 subnets of given logical interface +# 1: destination variable +# 2: interface +network_get_subnets() { + __network_ifstatus "$1" "$2" "['ipv4-address'][*]['address','mask']" "/ " +} + +# determine all IPv6 subnets of given logical interface +# 1: destination variable +# 2: interface +network_get_subnets6() { + local __addr __mask + local __list="" + + if __network_ifstatus "__addr" "$2" "['ipv6-address'][*]['address','mask']" "/ "; then + for __addr in $__addr; do + __list="${__list:+$__list }${__addr}" + done + fi + + if __network_ifstatus "__addr" "$2" "['ipv6-prefix-assignment'][*]['local-address'].address" && \ + __network_ifstatus "__mask" "$2" "['ipv6-prefix-assignment'][*].mask"; then + for __addr in $__addr; do + __list="${__list:+$__list }${__addr}/${__mask%% *}" + __mask="${__mask#* }" + done + fi + + if [ -n "$__list" ]; then + export "$1=$__list" + return 0 + fi + + unset "$1" + return 1 +} + +# determine all IPv6 prefixes of given logical interface +# 1: destination variable +# 2: interface +network_get_prefixes6() { + __network_ifstatus "$1" "$2" "['ipv6-prefix'][*]['address','mask']" "/ " +} + +# determine IPv4 gateway of given logical interface +# 1: destination variable +# 2: interface +# 3: consider inactive gateway if "true" (optional) +network_get_gateway() { + __network_ifstatus "$1" "$2" ".route[@.target='0.0.0.0' && !@.table].nexthop" "" 1 && \ + return 0 + + [ "$3" = 1 -o "$3" = "true" ] && \ + __network_ifstatus "$1" "$2" ".inactive.route[@.target='0.0.0.0' && !@.table].nexthop" "" 1 +} + +# determine IPv6 gateway of given logical interface +# 1: destination variable +# 2: interface +# 3: consider inactive gateway if "true" (optional) +network_get_gateway6() { + __network_ifstatus "$1" "$2" ".route[@.target='::' && !@.table].nexthop" "" 1 && \ + return 0 + + [ "$3" = 1 -o "$3" = "true" ] && \ + __network_ifstatus "$1" "$2" ".inactive.route[@.target='::' && !@.table].nexthop" "" 1 +} + +# determine the DNS servers of the given logical interface +# 1: destination variable +# 2: interface +# 3: consider inactive servers if "true" (optional) +network_get_dnsserver() { + __network_ifstatus "$1" "$2" "['dns-server'][*]" && return 0 + + [ "$3" = 1 -o "$3" = "true" ] && \ + __network_ifstatus "$1" "$2" ".inactive['dns-server'][*]" +} + +# determine the domains of the given logical interface +# 1: destination variable +# 2: interface +# 3: consider inactive domains if "true" (optional) +network_get_dnssearch() { + __network_ifstatus "$1" "$2" "['dns-search'][*]" && return 0 + + [ "$3" = 1 -o "$3" = "true" ] && \ + __network_ifstatus "$1" "$2" ".inactive['dns-search'][*]" +} + +# 1: destination variable +# 2: addr +# 3: inactive +# 4: limit +__network_wan() +{ + limit=1 + [ -n "$4" ] && limit="$4" + __network_ifstatus "$1" "" \ + "[@.route[@.target='$2' && !@.table]].interface" "" $limit && \ + return 0 + + [ "$3" = 1 -o "$3" = "true" ] && \ + __network_ifstatus "$1" "" \ + "[@.inactive.route[@.target='$2' && !@.table]].interface" "" $limit +} + +# find the logical interface which holds the current IPv4 default route +# 1: destination variable +# 2: consider inactive default routes if "true" (optional) +network_find_wan() { __network_wan "$1" "0.0.0.0" "$2" "$3"; } + +# find the logical interface which holds the current IPv6 default route +# 1: destination variable +# 2: consider inactive default routes if "true" (optional) +network_find_wan6() { __network_wan "$1" "::" "$2"; } + +# test whether the given logical interface is running +# 1: interface +network_is_up() +{ + local __up + __network_ifstatus "__up" "$1" ".up" && [ "$__up" = 1 ] +} + +# determine the protocol of the given logical interface +# 1: destination variable +# 2: interface +network_get_protocol() { __network_ifstatus "$1" "$2" ".proto"; } + +# determine the uptime of the given logical interface +# 1: destination variable +# 2: interface +network_get_uptime() { __network_ifstatus "$1" "$2" ".uptime"; } + +# determine the metric of the given logical interface +# 1: destination variable +# 2: interface +network_get_metric() { __network_ifstatus "$1" "$2" ".metric"; } + +# determine the layer 3 linux network device of the given logical interface +# 1: destination variable +# 2: interface +network_get_device() { __network_ifstatus "$1" "$2" ".l3_device"; } + +# determine the layer 2 linux network device of the given logical interface +# 1: destination variable +# 2: interface +network_get_physdev() { __network_ifstatus "$1" "$2" ".device"; } + +# defer netifd actions on the given linux network device +# 1: device name +network_defer_device() +{ + ubus call network.device set_state \ + "$(printf '{ "name": "%s", "defer": true }' "$1")" 2>/dev/null +} + +# continue netifd actions on the given linux network device +# 1: device name +network_ready_device() +{ + ubus call network.device set_state \ + "$(printf '{ "name": "%s", "defer": false }' "$1")" 2>/dev/null +} + +# flush the internal value cache to force re-reading values from ubus +network_flush_cache() { unset __NETWORK_CACHE; } diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/preinit.sh b/root/target/linux/ipq40xx/base-files/lib/functions/preinit.sh new file mode 100644 index 00000000..591e810a --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/preinit.sh @@ -0,0 +1,87 @@ +# Copyright (C) 2006-2013 OpenWrt.org +# Copyright (C) 2010 Vertical Communications + +boot_hook_splice_start() { + export -n PI_HOOK_SPLICE=1 +} + +boot_hook_splice_finish() { + local hook + for hook in $PI_STACK_LIST; do + local v; eval "v=\${${hook}_splice:+\$${hook}_splice }$hook" + export -n "${hook}=${v% }" + export -n "${hook}_splice=" + done + export -n PI_HOOK_SPLICE= +} + +boot_hook_init() { + local hook="${1}_hook" + export -n "PI_STACK_LIST=${PI_STACK_LIST:+$PI_STACK_LIST }$hook" + export -n "$hook=" +} + +boot_hook_add() { + local hook="${1}_hook${PI_HOOK_SPLICE:+_splice}" + local func="${2}" + + [ -n "$func" ] && { + local v; eval "v=\$$hook" + export -n "$hook=${v:+$v }$func" + } +} + +boot_hook_shift() { + local hook="${1}_hook" + local rvar="${2}" + + local v; eval "v=\$$hook" + [ -n "$v" ] && { + local first="${v%% *}" + + [ "$v" != "${v#* }" ] && \ + export -n "$hook=${v#* }" || \ + export -n "$hook=" + + export -n "$rvar=$first" + return 0 + } + + return 1 +} + +boot_run_hook() { + local hook="$1" + local func + + while boot_hook_shift "$hook" func; do + local ran; eval "ran=\$PI_RAN_$func" + [ -n "$ran" ] || { + export -n "PI_RAN_$func=1" + $func "$1" "$2" + } + done +} + +pivot() { # + /bin/mount -o noatime,move /proc $1/proc && \ + pivot_root $1 $1$2 && { + /bin/mount -o noatime,move $2/dev /dev + /bin/mount -o noatime,move $2/tmp /tmp + /bin/mount -o noatime,move $2/sys /sys 2>&- + /bin/mount -o noatime,move $2/overlay /overlay 2>&- + return 0 + } +} + +fopivot() { # + /bin/mount -o noatime,lowerdir=/,upperdir=$1,workdir=$2 -t overlay "overlayfs:$1" /mnt + pivot /mnt $3 +} + +ramoverlay() { + mkdir -p /tmp/root + /bin/mount -t tmpfs -o noatime,mode=0755 root /tmp/root + mkdir -p /tmp/root/root /tmp/root/work + fopivot /tmp/root/root /tmp/root/work /rom 1 +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/service.sh b/root/target/linux/ipq40xx/base-files/lib/functions/service.sh new file mode 100644 index 00000000..3d08e143 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/service.sh @@ -0,0 +1,103 @@ +# +# service: simple wrapper around start-stop-daemon +# +# Usage: service ACTION EXEC ARGS... +# +# Action: +# -C check if EXEC is alive +# -S start EXEC, passing it ARGS as its arguments +# -K kill EXEC, sending it a TERM signal if not specified otherwise +# +# Environment variables exposed: +# SERVICE_DAEMONIZE run EXEC in background +# SERVICE_WRITE_PID create a pid-file and use it for matching +# SERVICE_MATCH_EXEC use EXEC command-line for matching (default) +# SERVICE_MATCH_NAME use EXEC process name for matching +# SERVICE_USE_PID assume EXEC create its own pid-file and use it for matching +# SERVICE_NAME process name to use (default to EXEC file part) +# SERVICE_PID_FILE pid file to use (default to /var/run/$SERVICE_NAME.pid) +# SERVICE_SIG signal to send when using -K +# SERVICE_SIG_RELOAD default signal used when reloading +# SERVICE_SIG_STOP default signal used when stopping +# SERVICE_STOP_TIME time to wait for a process to stop gracefully before killing it +# SERVICE_UID user EXEC should be run as +# SERVICE_GID group EXEC should be run as +# +# SERVICE_DEBUG don't do anything, but show what would be done +# SERVICE_QUIET don't print anything +# + +SERVICE_QUIET=1 +SERVICE_SIG_RELOAD="HUP" +SERVICE_SIG_STOP="TERM" +SERVICE_STOP_TIME=5 +SERVICE_MATCH_EXEC=1 + +service() { + local ssd + local exec + local name + local start + ssd="${SERVICE_DEBUG:+echo }start-stop-daemon${SERVICE_QUIET:+ -q}" + case "$1" in + -C) + ssd="$ssd -K -t" + ;; + -S) + ssd="$ssd -S${SERVICE_DAEMONIZE:+ -b}${SERVICE_WRITE_PID:+ -m}" + start=1 + ;; + -K) + ssd="$ssd -K${SERVICE_SIG:+ -s $SERVICE_SIG}" + ;; + *) + echo "service: unknown ACTION '$1'" 1>&2 + return 1 + esac + shift + exec="$1" + [ -n "$exec" ] || { + echo "service: missing argument" 1>&2 + return 1 + } + [ -x "$exec" ] || { + echo "service: file '$exec' is not executable" 1>&2 + return 1 + } + name="${SERVICE_NAME:-${exec##*/}}" + [ -z "$SERVICE_USE_PID$SERVICE_WRITE_PID$SERVICE_PID_FILE" ] \ + || ssd="$ssd -p ${SERVICE_PID_FILE:-/var/run/$name.pid}" + [ -z "$SERVICE_MATCH_NAME" ] || ssd="$ssd -n $name" + ssd="$ssd${SERVICE_UID:+ -c $SERVICE_UID${SERVICE_GID:+:$SERVICE_GID}}" + [ -z "$SERVICE_MATCH_EXEC$start" ] || ssd="$ssd -x $exec" + shift + $ssd${1:+ -- "$@"} +} + +service_check() { + service -C "$@" +} + +service_signal() { + SERVICE_SIG="${SERVICE_SIG:-USR1}" service -K "$@" +} + +service_start() { + service -S "$@" +} + +service_stop() { + local try + SERVICE_SIG="${SERVICE_SIG:-$SERVICE_SIG_STOP}" service -K "$@" || return 1 + while [ $((try++)) -lt $SERVICE_STOP_TIME ]; do + service -C "$@" || return 0 + sleep 1 + done + SERVICE_SIG="KILL" service -K "$@" + sleep 1 + ! service -C "$@" +} + +service_reload() { + SERVICE_SIG="${SERVICE_SIG:-$SERVICE_SIG_RELOAD}" service -K "$@" +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/system.sh b/root/target/linux/ipq40xx/base-files/lib/functions/system.sh new file mode 100644 index 00000000..80e41718 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/system.sh @@ -0,0 +1,226 @@ +# Copyright (C) 2006-2013 OpenWrt.org + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +get_mac_binary() { + local path="$1" + local offset="$2" + + if ! [ -e "$path" ]; then + echo "get_mac_binary: file $path not found!" >&2 + return + fi + + hexdump -v -n 6 -s $offset -e '5/1 "%02x:" 1/1 "%02x"' $path 2>/dev/null +} + +get_mac_label_dt() { + local basepath="/proc/device-tree" + local macdevice="$(cat "$basepath/aliases/label-mac-device" 2>/dev/null)" + local macaddr + + [ -n "$macdevice" ] || return + + macaddr=$(get_mac_binary "$basepath/$macdevice/mac-address" 0 2>/dev/null) + [ -n "$macaddr" ] || macaddr=$(get_mac_binary "$basepath/$macdevice/local-mac-address" 0 2>/dev/null) + + echo $macaddr +} + +get_mac_label_json() { + local cfg="/etc/board.json" + local macaddr + + [ -s "$cfg" ] || return + + json_init + json_load "$(cat $cfg)" + if json_is_a system object; then + json_select system + json_get_var macaddr label_macaddr + json_select .. + fi + + echo $macaddr +} + +get_mac_label() { + local macaddr=$(get_mac_label_dt) + + [ -n "$macaddr" ] || macaddr=$(get_mac_label_json) + + echo $macaddr +} + +find_mtd_chardev() { + local INDEX=$(find_mtd_index "$1") + local PREFIX=/dev/mtd + + [ -d /dev/mtd ] && PREFIX=/dev/mtd/ + echo "${INDEX:+$PREFIX$INDEX}" +} + +mtd_get_mac_ascii() { + local mtdname="$1" + local key="$2" + local part + local mac_dirty + + part=$(find_mtd_part "$mtdname") + if [ -z "$part" ]; then + echo "mtd_get_mac_ascii: partition $mtdname not found!" >&2 + return + fi + + mac_dirty=$(strings "$part" | sed -n 's/^'"$key"'=//p') + + # "canonicalize" mac + [ -n "$mac_dirty" ] && macaddr_canonicalize "$mac_dirty" +} + +mtd_get_mac_text() { + local mtdname=$1 + local offset=$(($2)) + local part + local mac_dirty + + part=$(find_mtd_part "$mtdname") + if [ -z "$part" ]; then + echo "mtd_get_mac_text: partition $mtdname not found!" >&2 + return + fi + + if [ -z "$offset" ]; then + echo "mtd_get_mac_text: offset missing!" >&2 + return + fi + + mac_dirty=$(dd if="$part" bs=1 skip="$offset" count=17 2>/dev/null) + + # "canonicalize" mac + [ -n "$mac_dirty" ] && macaddr_canonicalize "$mac_dirty" +} + +mtd_get_mac_binary() { + local mtdname="$1" + local offset="$2" + local part + + part=$(find_mtd_part "$mtdname") + get_mac_binary "$part" "$offset" +} + +mtd_get_mac_binary_ubi() { + local mtdname="$1" + local offset="$2" + + . /lib/upgrade/nand.sh + + local ubidev=$(nand_find_ubi $CI_UBIPART) + local part=$(nand_find_volume $ubidev $1) + + get_mac_binary "/dev/$part" "$offset" +} + +mtd_get_part_size() { + local part_name=$1 + local first dev size erasesize name + while read dev size erasesize name; do + name=${name#'"'}; name=${name%'"'} + if [ "$name" = "$part_name" ]; then + echo $((0x$size)) + break + fi + done < /proc/mtd +} + +macaddr_add() { + local mac=$1 + local val=$2 + local oui=${mac%:*:*:*} + local nic=${mac#*:*:*:} + + nic=$(printf "%06x" $((0x${nic//:/} + val & 0xffffff)) | sed 's/^\(.\{2\}\)\(.\{2\}\)\(.\{2\}\)/\1:\2:\3/') + echo $oui:$nic +} + +macaddr_geteui() { + local mac=$1 + local sep=$2 + + echo ${mac:9:2}$sep${mac:12:2}$sep${mac:15:2} +} + +macaddr_setbit() { + local mac=$1 + local bit=${2:-0} + + [ $bit -gt 0 -a $bit -le 48 ] || return + + printf "%012x" $(( 0x${mac//:/} | 2**(48-bit) )) | sed -e 's/\(.\{2\}\)/\1:/g' -e 's/:$//' +} + +macaddr_unsetbit() { + local mac=$1 + local bit=${2:-0} + + [ $bit -gt 0 -a $bit -le 48 ] || return + + printf "%012x" $(( 0x${mac//:/} & ~(2**(48-bit)) )) | sed -e 's/\(.\{2\}\)/\1:/g' -e 's/:$//' +} + +macaddr_setbit_la() { + macaddr_setbit $1 7 +} + +macaddr_unsetbit_mc() { + local mac=$1 + + printf "%02x:%s" $((0x${mac%%:*} & ~0x01)) ${mac#*:} +} + +macaddr_random() { + local randsrc=$(get_mac_binary /dev/urandom 0) + + echo "$(macaddr_unsetbit_mc "$(macaddr_setbit_la "${randsrc}")")" +} + +macaddr_2bin() { + local mac=$1 + + echo -ne \\x${mac//:/\\x} +} + +macaddr_canonicalize() { + local mac="$1" + local canon="" + + mac=$(echo -n $mac | tr -d \") + [ ${#mac} -gt 17 ] && return + [ -n "${mac//[a-fA-F0-9\.: -]/}" ] && return + + for octet in ${mac//[\.:-]/ }; do + case "${#octet}" in + 1) + octet="0${octet}" + ;; + 2) + ;; + 4) + octet="${octet:0:2} ${octet:2:2}" + ;; + 12) + octet="${octet:0:2} ${octet:2:2} ${octet:4:2} ${octet:6:2} ${octet:8:2} ${octet:10:2}" + ;; + *) + return + ;; + esac + canon=${canon}${canon:+ }${octet} + done + + [ ${#canon} -ne 17 ] && return + + printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${canon// / 0x} 2>/dev/null +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/teltonika-defaults.sh b/root/target/linux/ipq40xx/base-files/lib/functions/teltonika-defaults.sh new file mode 100755 index 00000000..5cb17a99 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/teltonika-defaults.sh @@ -0,0 +1,284 @@ +#!/bin/ash + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +ucidef_add_nand_info() { + local model="$1" + + model=${model:0:7} + + json_select_object nand + + case "$model" in + TRB1412) + json_add_int blocksize 128 + json_add_int pagesize 2048 + json_add_int subpagesize 2048 + ;; + TRB1422 |\ + TRB1423 |\ + TRB1452) + json_add_int blocksize 256 + json_add_int pagesize 4096 + json_add_int subpagesize 4096 + ;; + TRB14*0) + json_add_int blocksize 128 + json_add_int pagesize 2048 + json_add_int subpagesize 2048 + ;; + TRB14*1) + json_add_int blocksize 256 + json_add_int pagesize 4096 + json_add_int subpagesize 4096 + ;; + esac + + json_select .. +} + +ucidef_add_static_modem_info() { + #Parameters: model usb_id sim_count other_params + local model usb_id count + local modem_counter=0 + local sim_count=1 + + model="$1" + usb_id="$2" + + [ -n "$3" ] && sim_count="$3" + + json_get_keys count modems + [ -n "$count" ] && modem_counter="$(echo "$count" | wc -w)" + + json_select_array "modems" + json_add_object + json_add_string id "$usb_id" + json_add_string num "$((modem_counter + 1))" + json_add_boolean builtin 1 + json_add_int simcount "$sim_count" + + for i in "$@"; do + case "$i" in + primary) + json_add_boolean primary 1 + ;; + gps_out) + json_add_boolean gps_out 1 + ;; + esac + done + + json_close_object + json_select .. +} + +ucidef_add_trb14x_lte_modem() { + print_array() { + json_add_array $1 + for element in $2 + do + json_add_string "" "$(echo $element)" + done + json_close_array + } + #Parameters: model primary + local model vendor product boudrate gps type desc control region modem_counter + modem_counter=1 + json_select_array "modems" + + model="$1" + + model=${model:0:7} + + case "$model" in + TRB1422) + vendor=05c6 + product=9215 + ;; + TRB1412 |\ + TRB1423 |\ + TRB1452 |\ + TRB140*) + vendor=2c7c + product=0125 + ;; + TRB14*) + vendor=2c7c + product=0121 + ;; + esac + + case "$model" in + TRB1412 |\ + TRB14*0) + region="EU" + [ "$product" = "0121" ] && { + lte_bands="1 3 7 8 20 28" + trysg_bands="wcdma_2100 wcdma_900" + dug_bands="gsm_1800 gsm_900" + } + [ "$product" = "0125" ] && { + lte_bands="1 3 7 8 20 28 38 40 41" + trysg_bands="wcdma_2100 wcdma_900" + dug_bands="gsm_1800 gsm_900" + } + ;; + TRB1422) + region="CE" + lte_bands="1 3 5 8 34 38 39 40 41" + trysg_bands="bc-0-a-system bc-0-b-system wcdma_2100 wcdma_900" + dug_bands="gsm_1800 gsm_900" + ;; + TRB1423 |\ + TRB1452 |\ + TRB14*1) + region="AU" + [ "$product" = "0121" ] && { + lte_bands="1 2 3 4 5 7 8 28 40" + trysg_bands="wcdma_2100 wcdma_1900 wcdma_900 wcdma_850" + dug_bands="gsm_1800 gsm_900 gsm_850 gsm_1900" + } + [ "$product" = "0125" ] && { + lte_bands="1 2 3 4 5 7 8 28 40" + trysg_bands="wcdma_2100 wcdma_1900 wcdma_900 wcdma_850" + dug_bands="gsm_1800 gsm_900 gsm_850 gsm_1900" + } + ;; + esac + + [ -f "/lib/network/wwan/$vendor:$product" ] && { + devicename="3-1" + json_set_namespace defaults old_cb + json_load "$(cat /lib/network/wwan/$vendor:$product)" + json_get_vars gps boudrate type desc control stop_bits + json_set_namespace $old_cb + + [ "${devicename%%:*}" = "$devicename" ] && { + json_add_object + json_add_string id "$devicename" + json_add_string num "$modem_counter" + json_add_string vendor "$vendor" + json_add_string product "$product" + json_add_string stop_bits "$stop_bits" + json_add_string gps "$gps" + json_add_string boudrate "$boudrate" + json_add_string type "$type" + json_add_string desc "$desc" + json_add_string region "$region" + json_add_string control "$control" + json_add_int simcount 1 + json_add_boolean builtin 1 + [ -n "$2" ] && json_add_boolean primary 1 + json_add_object service_modes + [ -z "$lte_bands" ] || print_array "4G" "$lte_bands" + [ -z "$trysg_bands" ] || print_array "3G" "$trysg_bands" + [ -z "$dug_bands" ] || print_array "2G" "$dug_bands" + json_close_object + json_close_object + } + } + json_select .. +} + +ucidef_add_serial_capabilities() { + json_select_array serial + json_add_object + [ -n "$1" ] && { + json_select_array devices + for d in $1; do + json_add_string "" $d + done + json_select .. + } + + json_select_array bauds + for b in $2; do + json_add_string "" $b + done + json_select .. + + json_select_array data_bits + for n in $3; do + json_add_string "" $n + done + json_select .. + json_close_object + json_select .. +} + +ucidef_set_hwinfo() { + local function + local dual_sim=0 + local wifi=0 + local dual_band_ssid=0 + local wps=0 + local mobile=0 + local gps=0 + local usb=0 + local bluetooth=0 + local ethernet=0 + local sfp_port=0 + local ios=0 + + for function in "$@"; do + case "$function" in + dual_sim) + dual_sim=1 + ;; + wifi) + wifi=1 + ;; + dual_band_ssid) + dual_band_ssid=1 + ;; + wps) + wps=1 + ;; + mobile) + mobile=1 + ;; + gps) + gps=1 + ;; + usb) + usb=1 + ;; + bluetooth) + bluetooth=1 + ;; + ethernet) + ethernet=1 + ;; + sfp_port) + sfp_port=1 + ;; + ios) + ios=1 + ;; + at_sim) + at_sim=1 + ;; + esac + done + + json_select_object hwinfo + json_add_boolean dual_sim "$dual_sim" + json_add_boolean usb "$usb" + json_add_boolean bluetooth "$bluetooth" + json_add_boolean wifi "$wifi" + json_add_boolean dual_band_ssid "$dual_band_ssid" + json_add_boolean wps "$wps" + json_add_boolean mobile "$mobile" + json_add_boolean gps "$gps" + json_add_boolean ethernet "$ethernet" + json_add_boolean sfp_port "$sfp_port" + json_add_boolean ios "$ios" + json_add_boolean at_sim "$at_sim" + json_select .. +} + +ucidef_set_release_version() { + json_add_string release_version "$1" +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/teltonika-functions.sh b/root/target/linux/ipq40xx/base-files/lib/functions/teltonika-functions.sh new file mode 100755 index 00000000..5381f81f --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/teltonika-functions.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +. /usr/share/libubox/jshn.sh + +is_ios_enabled() { + local ios + json_load_file "/etc/board.json" && \ + json_select hwinfo && \ + json_get_var ios ios && [ "$ios" = "1" ] +} diff --git a/root/target/linux/ipq40xx/base-files/lib/functions/uci-defaults.sh b/root/target/linux/ipq40xx/base-files/lib/functions/uci-defaults.sh new file mode 100644 index 00000000..75381262 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/functions/uci-defaults.sh @@ -0,0 +1,626 @@ +#!/bin/ash + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +json_select_array() { + local _json_no_warning=1 + + json_select "$1" + [ $? = 0 ] && return + + json_add_array "$1" + json_close_array + + json_select "$1" +} + +json_select_object() { + local _json_no_warning=1 + + json_select "$1" + [ $? = 0 ] && return + + json_add_object "$1" + json_close_object + + json_select "$1" +} + +ucidef_set_interface() { + local network=$1; shift + + [ -z "$network" ] && return + + json_select_object network + json_select_object "$network" + + while [ -n "$1" ]; do + local opt=$1; shift + local val=$1; shift + + [ -n "$opt" -a -n "$val" ] || break + case "$val" in + true) json_add_boolean "$opt" "1" ;; + false) json_add_boolean "$opt" "0" ;; + *) json_add_string "$opt" "$val" ;; + esac + done + + if ! json_is_a proto string; then + case "$network" in + lan) json_add_string proto static ;; + wan) json_add_string proto dhcp ;; + *) json_add_string proto none ;; + esac + fi + + json_select .. + json_select .. +} + +ucidef_set_board_id() { + json_select_object model + json_add_string id "$1" + json_select .. +} + +ucidef_set_board_platform() { + json_select_object model + json_add_string platform "$1" + json_select .. +} + +ucidef_set_model_name() { + json_select_object model + json_add_string name "$1" + json_select .. +} + +ucidef_set_compat_version() { + json_select_object system + json_add_string compat_version "${1:-1.0}" + json_select .. +} + +ucidef_set_interface_lan() { + ucidef_set_interface "lan" ifname "$1" proto "${2:-static}" +} + +ucidef_set_interface_wan() { + ucidef_set_interface "wan" ifname "$1" proto "${2:-dhcp}" +} + +ucidef_set_interfaces_lan_wan() { + local lan_if="$1" + local wan_if="$2" + + ucidef_set_interface_lan "$lan_if" + ucidef_set_interface_wan "$wan_if" +} + +_ucidef_add_switch_port() { + # inherited: $num $device $need_tag $want_untag $role $index $prev_role + # inherited: $n_cpu $n_ports $n_vlan $cpu0 $cpu1 $cpu2 $cpu3 $cpu4 $cpu5 + + n_ports=$((n_ports + 1)) + + json_select_array ports + json_add_object + json_add_int num "$num" + [ -n "$device" ] && json_add_string device "$device" + [ -n "$need_tag" ] && json_add_boolean need_tag "$need_tag" + [ -n "$want_untag" ] && json_add_boolean want_untag "$want_untag" + [ -n "$role" ] && json_add_string role "$role" + [ -n "$index" ] && json_add_int index "$index" + json_close_object + json_select .. + + # record pointer to cpu entry for lookup in _ucidef_finish_switch_roles() + [ -n "$device" ] && { + export "cpu$n_cpu=$n_ports" + n_cpu=$((n_cpu + 1)) + } + + # create/append object to role list + [ -n "$role" ] && { + json_select_array roles + + if [ "$role" != "$prev_role" ]; then + json_add_object + json_add_string role "$role" + json_add_string ports "$num" + json_close_object + + prev_role="$role" + n_vlan=$((n_vlan + 1)) + else + json_select_object "$n_vlan" + json_get_var port ports + json_add_string ports "$port $num" + json_select .. + fi + + json_select .. + } +} + +_ucidef_finish_switch_roles() { + # inherited: $name $n_cpu $n_vlan $cpu0 $cpu1 $cpu2 $cpu3 $cpu4 $cpu5 + local index role roles num device need_tag want_untag port ports + + json_select switch + json_select "$name" + json_get_keys roles roles + json_select .. + json_select .. + + for index in $roles; do + eval "port=\$cpu$(((index - 1) % n_cpu))" + + json_select switch + json_select "$name" + json_select ports + json_select "$port" + json_get_vars num device need_tag want_untag + json_select .. + json_select .. + + if [ ${need_tag:-0} -eq 1 -o ${want_untag:-0} -ne 1 ]; then + num="${num}t" + device="${device}.${index}" + fi + + json_select roles + json_select "$index" + json_get_vars role ports + json_add_string ports "$ports $num" + json_add_string device "$device" + json_select .. + json_select .. + json_select .. + json_select .. + + json_select_object network + local devices + + json_select_object "$role" + # attach previous interfaces (for multi-switch devices) + json_get_var devices ifname + if ! list_contains devices "$device"; then + devices="${devices:+$devices }$device" + fi + json_select .. + json_select .. + + ucidef_set_interface "$role" ifname "$devices" + done +} + +ucidef_set_ar8xxx_switch_mib() { + local name="$1" + local type="$2" + local interval="$3" + + json_select_object switch + json_select_object "$name" + json_add_int ar8xxx_mib_type $type + json_add_int ar8xxx_mib_poll_interval $interval + json_select .. + json_select .. +} + +ucidef_add_switch() { + local name="$1"; shift + local port num role device index need_tag prev_role + local cpu0 cpu1 cpu2 cpu3 cpu4 cpu5 + local n_cpu=0 n_vlan=0 n_ports=0 + + json_select_object switch + json_select_object "$name" + json_add_boolean enable 1 + json_add_boolean reset 1 + + for port in "$@"; do + case "$port" in + [0-9]*@*) + num="${port%%@*}" + device="${port##*@}" + need_tag=0 + want_untag=0 + [ "${num%t}" != "$num" ] && { + num="${num%t}" + need_tag=1 + } + [ "${num%u}" != "$num" ] && { + num="${num%u}" + want_untag=1 + } + ;; + [0-9]*:*:[0-9]*) + num="${port%%:*}" + index="${port##*:}" + role="${port#[0-9]*:}"; role="${role%:*}" + ;; + [0-9]*:*) + num="${port%%:*}" + role="${port##*:}" + ;; + esac + + if [ -n "$num" ] && [ -n "$device$role" ]; then + _ucidef_add_switch_port + fi + + unset num device role index need_tag want_untag + done + json_select .. + json_select .. + + _ucidef_finish_switch_roles +} + +ucidef_add_switch_attr() { + local name="$1" + local key="$2" + local val="$3" + + json_select_object switch + json_select_object "$name" + + case "$val" in + true|false) [ "$val" != "true" ]; json_add_boolean "$key" $? ;; + [0-9]) json_add_int "$key" "$val" ;; + *) json_add_string "$key" "$val" ;; + esac + + json_select .. + json_select .. +} + +ucidef_add_switch_port_attr() { + local name="$1" + local port="$2" + local key="$3" + local val="$4" + local ports i num + + json_select_object switch + json_select_object "$name" + + json_get_keys ports ports + json_select_array ports + + for i in $ports; do + json_select "$i" + json_get_var num num + + if [ -n "$num" ] && [ $num -eq $port ]; then + json_select_object attr + + case "$val" in + true|false) [ "$val" != "true" ]; json_add_boolean "$key" $? ;; + [0-9]) json_add_int "$key" "$val" ;; + *) json_add_string "$key" "$val" ;; + esac + + json_select .. + fi + + json_select .. + done + + json_select .. + json_select .. + json_select .. +} + +ucidef_set_interface_macaddr() { + local network="$1" + local macaddr="$2" + + ucidef_set_interface "$network" macaddr "$macaddr" +} + +ucidef_add_atm_bridge() { + local vpi="$1" + local vci="$2" + local encaps="$3" + local payload="$4" + local nameprefix="$5" + + json_select_object dsl + json_select_object atmbridge + json_add_int vpi "$vpi" + json_add_int vci "$vci" + json_add_string encaps "$encaps" + json_add_string payload "$payload" + json_add_string nameprefix "$nameprefix" + json_select .. + json_select .. +} + +ucidef_add_adsl_modem() { + local annex="$1" + local firmware="$2" + + json_select_object dsl + json_select_object modem + json_add_string type "adsl" + json_add_string annex "$annex" + json_add_string firmware "$firmware" + json_select .. + json_select .. +} + +ucidef_add_vdsl_modem() { + local annex="$1" + local tone="$2" + local xfer_mode="$3" + + json_select_object dsl + json_select_object modem + json_add_string type "vdsl" + json_add_string annex "$annex" + json_add_string tone "$tone" + json_add_string xfer_mode "$xfer_mode" + json_select .. + json_select .. +} + +ucidef_set_led_ataport() { + _ucidef_set_led_trigger "$1" "$2" "$3" ata"$4" +} + +_ucidef_set_led_common() { + local cfg="led_$1" + local name="$2" + local sysfs="$3" + + json_select_object led + + json_select_object "$1" + json_add_string name "$name" + json_add_string sysfs "$sysfs" +} + +ucidef_set_led_default() { + local default="$4" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string default "$default" + json_select .. + + json_select .. +} + +ucidef_set_led_gpio() { + local gpio="$4" + local inverted="$5" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string trigger "$trigger" + json_add_string type gpio + json_add_int gpio "$gpio" + json_add_boolean inverted "$inverted" + json_select .. + + json_select .. +} + +ucidef_set_led_ide() { + _ucidef_set_led_trigger "$1" "$2" "$3" disk-activity +} + +ucidef_set_led_netdev() { + local dev="$4" + local mode="${5:-link tx rx}" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string type netdev + json_add_string device "$dev" + json_add_string mode "$mode" + json_select .. + + json_select .. +} + +ucidef_set_led_oneshot() { + _ucidef_set_led_timer $1 $2 $3 "oneshot" $4 $5 +} + +ucidef_set_led_portstate() { + local port_state="$4" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string trigger port_state + json_add_string type portstate + json_add_string port_state "$port_state" + json_select .. + + json_select .. +} + +ucidef_set_led_rssi() { + local iface="$4" + local minq="$5" + local maxq="$6" + local offset="${7:-0}" + local factor="${8:-1}" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string type rssi + json_add_string name "$name" + json_add_string iface "$iface" + json_add_string minq "$minq" + json_add_string maxq "$maxq" + json_add_string offset "$offset" + json_add_string factor "$factor" + json_select .. + + json_select .. +} + +ucidef_set_led_switch() { + local trigger_name="$4" + local port_mask="$5" + local speed_mask="$6" + local mode="$7" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string trigger "$trigger_name" + json_add_string type switch + json_add_string mode "$mode" + json_add_string port_mask "$port_mask" + json_add_string speed_mask "$speed_mask" + json_select .. + + json_select .. +} + +_ucidef_set_led_timer() { + local trigger_name="$4" + local delayon="$5" + local delayoff="$6" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string type "$trigger_name" + json_add_string trigger "$trigger_name" + json_add_int delayon "$delayon" + json_add_int delayoff "$delayoff" + json_select .. + + json_select .. +} + +ucidef_set_led_timer() { + _ucidef_set_led_timer $1 $2 $3 "timer" $4 $5 +} + +_ucidef_set_led_trigger() { + local trigger_name="$4" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string trigger "$trigger_name" + json_select .. + + json_select .. +} + +ucidef_set_led_usbdev() { + local dev="$4" + + _ucidef_set_led_common "$1" "$2" "$3" + + json_add_string type usb + json_add_string device "$dev" + json_select .. + + json_select .. +} + +ucidef_set_led_usbhost() { + _ucidef_set_led_trigger "$1" "$2" "$3" usb-host +} + +ucidef_set_led_usbport() { + local obj="$1" + local name="$2" + local sysfs="$3" + shift + shift + shift + + _ucidef_set_led_common "$obj" "$name" "$sysfs" + + json_add_string type usbport + json_select_array ports + for port in "$@"; do + json_add_string port "$port" + done + json_select .. + json_select .. + + json_select .. +} + +ucidef_set_led_wlan() { + _ucidef_set_led_trigger "$1" "$2" "$3" "$4" +} + +ucidef_set_rssimon() { + local dev="$1" + local refresh="$2" + local threshold="$3" + + json_select_object rssimon + + json_select_object "$dev" + [ -n "$refresh" ] && json_add_int refresh "$refresh" + [ -n "$threshold" ] && json_add_int threshold "$threshold" + json_select .. + + json_select .. +} + +ucidef_add_gpio_switch() { + local cfg="$1" + local name="$2" + local pin="$3" + local default="${4:-0}" + + json_select_object gpioswitch + json_select_object "$cfg" + json_add_string name "$name" + json_add_int pin "$pin" + json_add_int default "$default" + json_select .. + json_select .. +} + +ucidef_set_hostname() { + local hostname="$1" + + json_select_object system + json_add_string hostname "$hostname" + json_select .. +} + +ucidef_set_ntpserver() { + local server + + json_select_object system + json_select_array ntpserver + for server in "$@"; do + json_add_string "" "$server" + done + json_select .. + json_select .. +} + +board_config_update() { + json_init + [ -f ${CFG} ] && json_load "$(cat ${CFG})" + + # auto-initialize model id and name if applicable + if ! json_is_a model object; then + json_select_object model + [ -f "/tmp/sysinfo/board_name" ] && \ + json_add_string id "$(cat /tmp/sysinfo/board_name)" + [ -f "/tmp/sysinfo/model" ] && \ + json_add_string name "$(cat /tmp/sysinfo/model)" + json_select .. + fi +} + +board_config_flush() { + json_dump -i -o ${CFG} +} diff --git a/root/target/linux/ipq40xx/base-files/lib/preinit/75_stm32_autoflash b/root/target/linux/ipq40xx/base-files/lib/preinit/75_stm32_autoflash new file mode 100644 index 00000000..5d96b340 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/preinit/75_stm32_autoflash @@ -0,0 +1,10 @@ +#!/bin/sh + +do_stm32_autoflash() { + # launch STM32 flash utility + [ -e "/sys/r2ec/reset" -a -f /usr/bin/autoflash ] && { + /usr/bin/autoflash + } +} + +[ "$INITRAMFS" = "1" ] || boot_hook_add preinit_main do_stm32_autoflash diff --git a/root/target/linux/ipq40xx/base-files/lib/preinit/82_modem_power b/root/target/linux/ipq40xx/base-files/lib/preinit/82_modem_power new file mode 100644 index 00000000..7db2ecf2 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/preinit/82_modem_power @@ -0,0 +1,20 @@ +set_state() { + local label="$1" + local disable + + [ -e "/sys/class/gpio/${label}_power/value" ] || return + + config_get disable ${label} disable + [ "${disable}" -eq 1 ] && \ + /sbin/mctl -s -m "${label}" || \ + /sbin/mctl -p -m "${label}" +} + +do_power_modem() { + [ -z "$(uci -q get system.modem.disable)" ] && /sbin/mctl -p || { + config_load system + config_foreach set_state mctl + } +} + +boot_hook_add preinit_main do_power_modem diff --git a/root/target/linux/ipq40xx/base-files/lib/preinit/82_version_migrate b/root/target/linux/ipq40xx/base-files/lib/preinit/82_version_migrate new file mode 100644 index 00000000..565ac215 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/preinit/82_version_migrate @@ -0,0 +1,21 @@ +#!/bin/sh +# Copyright (C) 2006 OpenWrt.org +# Copyright (C) 2010 Vertical Communications + +do_check_version() { + version="$(uci -q get system.@system[0].device_fw_version)" + [ -z "$version" ] && { + return 0 + } + + numeric_version="${version##*_}" + client_removed="${numeric_version#*.}" + major="${client_removed%%.*}" + + [ -f /sysupgrade.tgz ] && [ -n "$major" ] && [ "$major" -lt 2 ] && { + echo "- migration work -" + cp /rom/etc/inittab /etc/inittab + } +} + +[ "$INITRAMFS" = "1" ] || boot_hook_add preinit_main do_check_version diff --git a/root/target/linux/ipq40xx/base-files/lib/upgrade/ipq_failsafe.sh b/root/target/linux/ipq40xx/base-files/lib/upgrade/ipq_failsafe.sh new file mode 100644 index 00000000..c1c93d20 --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/lib/upgrade/ipq_failsafe.sh @@ -0,0 +1,383 @@ +find_mmc_part() { + local DEVNAME PARTNAME + + if grep -q "$1" /proc/mtd; then + echo "" && return 0 + fi + + for DEVNAME in /sys/block/mmcblk0/mmcblk*p*; do + PARTNAME=$(grep PARTNAME ${DEVNAME}/uevent | cut -f2 -d'=') + [ "$PARTNAME" = "$1" ] && echo "/dev/$(basename $DEVNAME)" && return 0 + done +} + +get_full_section_name() { + local img=$1 + local sec=$2 + + dumpimage -l ${img} | grep "^ Image.*(${sec})" | \ + sed 's,^ Image.*(\(.*\)),\1,' +} + +image_contains() { + local img=$1 + local sec=$2 + dumpimage -l ${img} | grep -q "^ Image.*(${sec}.*)" || return 1 +} + +print_sections() { + local img=$1 + + dumpimage -l ${img} | awk '/^ Image.*(.*)/ { print gensub(/Image .* \((.*)\)/,"\\1", $0) }' +} + +image_has_mandatory_section() { + local img=$1 + local mandatory_sections=$2 + + for sec in ${mandatory_sections}; do + image_contains $img ${sec} || {\ + return 1 + } + done +} + +image_demux() { + local img=$1 + + for sec in $(print_sections ${img}); do + local fullname=$(get_full_section_name ${img} ${sec}) + + local position=$(dumpimage -l ${img} | grep "(${fullname})" | awk '{print $2}') + dumpimage -i ${img} -o /tmp/${fullname}.bin -T "flat_dt" -p "${position}" ${fullname} > /dev/null || { \ + echo "Error while extracting \"${sec}\" from ${img}" + return 1 + } + done + return 0 +} + +image_is_FIT() { + if ! dumpimage -l $1 > /dev/null 2>&1; then + echo "$1 is not a valid FIT image" + return 1 + fi + return 0 +} + +switch_layout() { + local layout=$1 + local boot_layout=`find / -name boot_layout` + + # Layout switching is only required as the boot images (up to u-boot) + # use 512 user data bytes per code word, whereas Linux uses 516 bytes. + # It's only applicable for NAND flash. So let's return if we don't have + # one. + + [ -n "$boot_layout" ] || return + + case "${layout}" in + boot|1) echo 1 > $boot_layout;; + linux|0) echo 0 > $boot_layout;; + *) echo "Unknown layout \"${layout}\"";; + esac +} + +do_flash_mtd() { + local bin=$1 + local mtdname=$2 + local append="" + + local mtdpart=$(grep "\"${mtdname}\"" /proc/mtd | awk -F: '{print $1}') + local pgsz=$(cat /sys/class/mtd/${mtdpart}/writesize) + [ -f "$CONF_TAR" -a "$SAVE_CONFIG" -eq 1 -a "$2" == "rootfs" ] && append="-j $CONF_TAR" + + dd if=/tmp/${bin}.bin bs=${pgsz} conv=sync | mtd $append -e "/dev/${mtdpart}" write - "/dev/${mtdpart}" +} + +do_flash_emmc() { + local bin=$1 + local emmcblock=$2 + + dd if=/dev/zero of=${emmcblock} + dd if=/tmp/${bin}.bin of=${emmcblock} +} + +do_flash_partition() { + local bin=$1 + local mtdname=$2 + local emmcblock="$(find_mmc_part "$mtdname")" + + if [ -e "$emmcblock" ]; then + do_flash_emmc $bin $emmcblock + else + do_flash_mtd $bin $mtdname + fi +} + +do_flash_bootconfig() { + local bin=$1 + local mtdname=$2 + + # Fail safe upgrade + if [ -f /proc/boot_info/getbinary_${bin} ]; then + cat /proc/boot_info/getbinary_${bin} > /tmp/${bin}.bin + do_flash_partition $bin $mtdname + fi +} + +do_flash_failsafe_partition() { + local bin=$1 + local mtdname=$2 + local emmcblock + local primaryboot + + # Fail safe upgrade + [ -f /proc/boot_info/$mtdname/upgradepartition ] && { + default_mtd=$mtdname + mtdname=$(cat /proc/boot_info/$mtdname/upgradepartition) + primaryboot=$(cat /proc/boot_info/$default_mtd/primaryboot) + if [ $primaryboot -eq 0 ]; then + echo 1 > /proc/boot_info/$default_mtd/primaryboot + else + echo 0 > /proc/boot_info/$default_mtd/primaryboot + fi + } + + emmcblock="$(find_mmc_part "$mtdname")" + + if [ -e "$emmcblock" ]; then + do_flash_emmc $bin $emmcblock + else + do_flash_mtd $bin $mtdname + fi + +} + +do_flash_ubi() { + local bin=$1 + local mtdname=$2 + local mtdpart + local primaryboot + + mtdpart=$(grep "\"${mtdname}\"" /proc/mtd | awk -F: '{print $1}') + ubidetach -p /dev/${mtdpart} + + # Fail safe upgrade + [ -f /proc/boot_info/$mtdname/upgradepartition ] && { + primaryboot=$(cat /proc/boot_info/$mtdname/primaryboot) + if [ $primaryboot -eq 0 ]; then + echo 1 > /proc/boot_info/$mtdname/primaryboot + else + echo 0 > /proc/boot_info/$mtdname/primaryboot + fi + + mtdname=$(cat /proc/boot_info/$mtdname/upgradepartition) + } + + mtdpart=$(grep "\"${mtdname}\"" /proc/mtd | awk -F: '{print $1}') + ubiformat /dev/${mtdpart} -y -f /tmp/${bin}.bin +} + +do_flash_tz() { + local sec=$1 + local mtdpart=$(grep "\"0:QSEE\"" /proc/mtd | awk -F: '{print $1}') + local emmcblock="$(find_mmc_part "0:QSEE")" + + if [ -n "$mtdpart" -o -e "$emmcblock" ]; then + do_flash_failsafe_partition ${sec} "0:QSEE" + else + do_flash_failsafe_partition ${sec} "0:TZ" + fi +} + +do_flash_ddr() { + local sec=$1 + local mtdpart=$(grep "\"0:CDT\"" /proc/mtd | awk -F: '{print $1}') + local emmcblock="$(find_mmc_part "0:CDT")" + + if [ -n "$mtdpart" -o -e "$emmcblock" ]; then + do_flash_failsafe_partition ${sec} "0:CDT" + else + do_flash_failsafe_partition ${sec} "0:DDRPARAMS" + fi +} + +to_upper () { + echo $1 | awk '{print toupper($0)}' +} + +flash_section() { + local sec=$1 + + local board=$(board_name) + case "${sec}" in + hlos*) switch_layout linux; do_flash_failsafe_partition ${sec} "0:HLOS";; + rootfs*) switch_layout linux; do_flash_failsafe_partition ${sec} "rootfs";; + fs*) switch_layout linux; do_flash_failsafe_partition ${sec} "rootfs";; + ubi*) switch_layout linux; do_flash_ubi ${sec} "rootfs";; + #sbl1*) switch_layout boot; do_flash_partition ${sec} "0:SBL1";; + #sbl2*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:SBL2";; + #sbl3*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:SBL3";; + #mibib*) switch_layout boot; do_flash_partition ${sec} "0:MIBIB";; + #dtb-$(to_upper $board)*) switch_layout boot; do_flash_partition ${sec} "0:DTB";; + u-boot*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:APPSBL";; + #ddr-$(to_upper $board)*) switch_layout boot; do_flash_ddr ${sec};; + ddr-${board}-*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:CDT";; + #ssd*) switch_layout boot; do_flash_partition ${sec} "0:SSD";; + tz*) switch_layout boot; do_flash_tz ${sec};; + #rpm*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:RPM";; + *) echo "Section ${sec} ignored"; return 1;; + esac + + echo "Flashed ${sec}" +} + +erase_emmc_config() { + local emmcblock="$(find_mmc_part "rootfs_data")" + if [ -e "$emmcblock" ]; then + dd if=/dev/zero of=${emmcblock} + mkfs.ext4 "$emmcblock" + fi +} + +platform_pre_upgrade() { + cp /sbin/upgraded /tmp + ubus call system nandupgrade "{\"path\": \"$1\" }" +} + +platform_check_image_ipq() { + local board=$(board_name) + + local mandatory_nand="ubi" + local mandatory_nor_emmc="hlos fs" + local mandatory_nor="hlos" + local mandatory_section_found=0 + local optional="sbl2 u-boot ddr-${board} ssd tz rpm" + local ignored="mibib bootconfig sbl1" + + image_is_FIT $1 || return 1 + + image_has_mandatory_section $1 ${mandatory_nand} && {\ + mandatory_section_found=1 + } + + image_has_mandatory_section $1 ${mandatory_nor_emmc} && {\ + mandatory_section_found=1 + } + + image_has_mandatory_section $1 ${mandatory_nor} && {\ + mandatory_section_found=1 + } + + if [ $mandatory_section_found -eq 0 ]; then + echo "Error: mandatory section(s) missing from \"$1\". Abort..." + return 1 + fi + + for sec in ${optional}; do + image_contains $1 ${sec} || {\ + echo "Warning: optional section \"${sec}\" missing from \"$1\". Continue..." + } + done + + for sec in ${ignored}; do + image_contains $1 ${sec} && {\ + echo "Warning: section \"${sec}\" will be ignored from \"$1\". Continue..." + } + done + + image_demux $1 || {\ + echo "Error: \"$1\" couldn't be extracted. Abort..." + return 1 + } + + [ -f /tmp/hlos_version ] && rm -f /tmp/*_version + dumpimage -c $1 2>/dev/null + return $? +} + +platform_version_upgrade() { + local version_files="appsbl_version sbl_version tz_version hlos_version rpm_version" + local sys="/sys/devices/system/qfprom/qfprom0/" + local tmp="/tmp/" + + for file in $version_files; do + [ -f "${tmp}${file}" ] && { + echo "Updating "${sys}${file}" with `cat "${tmp}${file}"`" + echo `cat "${tmp}${file}"` > "${sys}${file}" + rm -f "${tmp}${file}" + } + done +} + + +# The U-Boot loader of the OpenMesh devices requires image sizes and +# checksums to be provided in the U-Boot environment. +# The OpenMesh devices come with 2 main partitions - while one is active +# sysupgrade will flash the other. The boot order is changed to boot the +# newly flashed partition. If the new partition can't be booted due to +# upgrade failures the previously used partition is loaded. + +platform_do_upgrade_ipq() { + local board=$(board_name) + + # verify some things exist before erasing + if [ ! -e $1 ]; then + echo "Error: Can't find $1 after switching to ramfs, aborting upgrade!" + reboot + fi + + for sec in $(print_sections $1); do + if [ ! -e /tmp/${sec}.bin ]; then + echo "Error: Cant' find ${sec} after switching to ramfs, aborting upgrade!" + reboot + fi + done + + case "$board" in + teltonika,rutx) + for sec in $(print_sections $1); do + flash_section ${sec} + done + + switch_layout linux + # update bootconfig to register that fw upgrade has been done + do_flash_bootconfig bootconfig "0:BOOTCONFIG" + do_flash_bootconfig bootconfig1 "0:BOOTCONFIG1" + platform_version_upgrade + + erase_emmc_config + return 0; + ;; + esac + + echo "Upgrade failed!" + return 1; +} + +platform_copy_config() { + local emmcblock="$(find_mmc_part "rootfs_data")" + mkdir -p /tmp/overlay + + if [ -e "$emmcblock" ]; then + mount -t ext4 "$emmcblock" /tmp/overlay + cp /tmp/sysupgrade.tgz /tmp/overlay/ + sync + umount /tmp/overlay + else + local mtdname=rootfs + local mtdpart + + [ -f /proc/boot_info/$mtdname/upgradepartition ] && { + mtdname=$(cat /proc/boot_info/$mtdname/upgradepartition) + } + + mtdpart=$(grep "\"${mtdname}\"" /proc/mtd | awk -F: '{print $1}') + ubiattach -p /dev/${mtdpart} + mount -t ubifs ubi0:rootfs_data /tmp/overlay + cp /tmp/sysupgrade.tgz /tmp/overlay/ + sync + umount /tmp/overlay + fi +} diff --git a/root/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh b/root/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh index dabdaa2d..a79b375c 100644 --- a/root/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh +++ b/root/target/linux/ipq40xx/base-files/lib/upgrade/platform.sh @@ -1,127 +1,45 @@ PART_NAME=firmware REQUIRE_IMAGE_METADATA=1 -RAMFS_COPY_BIN='fw_printenv fw_setenv' -RAMFS_COPY_DATA='/etc/fw_env.config /var/lock/fw_printenv.lock' - platform_check_image() { - case "$(board_name)" in - asus,rt-ac58u) - CI_UBIPART="UBI_DEV" - local ubidev=$(nand_find_ubi $CI_UBIPART) - local asus_root=$(nand_find_volume $ubidev jffs2) - - [ -n "$asus_root" ] || return 0 - - cat << EOF -jffs2 partition is still present. -There's probably no space left -to install the filesystem. - -You need to delete the jffs2 partition first: -# ubirmvol /dev/ubi0 --name=jffs2 - -Once this is done. Retry. -EOF - return 1 - ;; - esac - return 0; -} - -askey_do_upgrade() { - local tar_file="$1" - - local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') - board_dir=${board_dir%/} - - tar Oxf $tar_file ${board_dir}/root | mtd write - rootfs - - nand_do_upgrade "$1" -} - -zyxel_do_upgrade() { - local tar_file="$1" - - local board_dir=$(tar tf $tar_file | grep -m 1 '^sysupgrade-.*/$') - board_dir=${board_dir%/} - - tar Oxf $tar_file ${board_dir}/kernel | mtd write - kernel - - if [ -n "$UPGRADE_BACKUP" ]; then - tar Oxf $tar_file ${board_dir}/root | mtd -j "$UPGRADE_BACKUP" write - rootfs - else - tar Oxf $tar_file ${board_dir}/root | mtd write - rootfs - fi + platform_check_image_ipq "$1" } platform_do_upgrade() { - case "$(board_name)" in - 8dev,jalapeno |\ - aruba,ap-303 |\ - aruba,ap-303h |\ - aruba,ap-365 |\ - avm,fritzbox-7530 |\ - avm,fritzrepeater-1200 |\ - avm,fritzrepeater-3000 |\ - buffalo,wtr-m2133hp |\ - cilab,meshpoint-one |\ - engenius,eap2200 |\ - mobipromo,cm520-79f |\ - qxwlan,e2600ac-c2) - nand_do_upgrade "$1" - ;; - alfa-network,ap120c-ac) - part="$(awk -F 'ubi.mtd=' '{printf $2}' /proc/cmdline | sed -e 's/ .*$//')" - if [ "$part" = "rootfs1" ]; then - fw_setenv active 2 || exit 1 - CI_UBIPART="rootfs2" - else - fw_setenv active 1 || exit 1 - CI_UBIPART="rootfs1" - fi - nand_do_upgrade "$1" - ;; - asus,map-ac2200) - CI_KERNPART="linux" - nand_do_upgrade "$1" - ;; - asus,rt-ac58u) - CI_UBIPART="UBI_DEV" - CI_KERNPART="linux" - nand_do_upgrade "$1" - ;; - cellc,rtl30vw) - CI_UBIPART="ubifs" - askey_do_upgrade "$1" - ;; - compex,wpj419|\ - p2w,r619ac-128m|\ - p2w,r619ac) - nand_do_upgrade "$1" - ;; - linksys,ea6350v3 |\ - linksys,ea8300) - platform_do_upgrade_linksys "$1" - ;; - meraki,mr33) - CI_KERNPART="part.safe" - nand_do_upgrade "$1" - ;; - openmesh,a42 |\ - openmesh,a62) - PART_NAME="inactive" - platform_do_upgrade_openmesh "$1" - ;; - teltonika,rutx) - CI_UBIPART="rootfs" - nand_do_upgrade "$1" - ;; - zyxel,nbg6617) - zyxel_do_upgrade "$1" - ;; - *) - default_do_upgrade "$1" - ;; - esac + platform_do_upgrade_ipq "$1" +} + +# added with io_expander validation +platform_check_hw_support() { + local metadata="/tmp/sysupgrade.meta" + local io_expander_file="/proc/device-tree/io_expander" + local found=0 + + [ -e "$metadata" ] || ( fwtool -q -i $metadata $1 ) && { + json_load_file "$metadata" + # previous devices were always supported + [ ! -e "$io_expander_file" ] && return 0 + json_select hw_support + +# io_expander type validation + local io_expander="$(cat $io_expander_file)" + # if support type is absent in metadata, we assume it's supported + if ( json_select io_expander 2> /dev/null ); then + json_select io_expander + json_get_values io_exp_values + + for val in $io_exp_values; do + regex_value=$(echo "$io_expander" | grep -e "$val") + [ "$io_expander" = "$regex_value" ] && found=1 + done + + [ $found -eq 0 ] && return 1 + json_select .. + else + # fail if not default/initial type + [ "$io_expander" != "stm32" ] && return 1 + fi +# ... + } + return 0; } diff --git a/root/target/linux/ipq40xx/base-files/sbin/mctl b/root/target/linux/ipq40xx/base-files/sbin/mctl new file mode 100755 index 00000000..f501cc2e --- /dev/null +++ b/root/target/linux/ipq40xx/base-files/sbin/mctl @@ -0,0 +1,120 @@ +#!/bin/sh + +. /usr/share/libubox/jshn.sh + +PS_ON=1 +PS_OFF=2 + +MPS=0 +MLBL="modem" + +modem_reset() { + local label="$1" + + [ -e "/sys/class/gpio/${label}_reset/value" ] || return + + echo 1 > "/sys/class/gpio/${label}_reset/value" + sleep 1 + echo 0 > "/sys/class/gpio/${label}_reset/value" +} + +modem_off() { + local label="$1" + + [ -e "/sys/class/gpio/${label}_reset/value" ] || return + + echo 1 > "/sys/class/gpio/${label}_reset/value" +} + +modem_power() { + local label="$1" + + [ -e "/sys/class/gpio/${label}_power/value" ] || return + + # simulate power press + echo 1 > "/sys/class/gpio/${label}_power/value" + sleep 1 + echo 0 > "/sys/class/gpio/${label}_power/value" +} + +modem_list() { + local list="modem" + local label + + [ "$(modem_fetch_primary)" -eq 0 ] && { + echo "${list}" + return + } + + for m in /sys/class/gpio/modem*_power; do + label="$(basename $m | awk -F_ '{print $1}')" + [ "${label}" != "modem" ] && list="${list},${label}" + done + + echo "${list}" +} + +modem_fetch_primary() { + local modem modems primary + + json_init + json_load_file "/etc/board.json" + + json_get_keys modems modems + json_select modems + + for modem in $modems; do + json_select "$modem" + json_get_vars primary + + [ -n "$primary" ] && { + echo 1 + return + } + + json_select .. + done + echo 0 +} + +modem_is_available() { + local label="$1" + [ -e "/sys/class/gpio/${label}_power/value" ] +} + +usage() { + cat < +Control modem power state. + +Options: + -p, --power-on power on modem + -s, --shutdown shutdown modem + -r, --reboot reboot modem + -m, --modem