Files
zerowrt-bot b62bb2fefb network: add MediaTek DHCPD support
- Add header: include/net/mtk_dhcpd.h
- Add implementation: net/mtk_dhcpd.c
- Add Kconfig option: CONFIG_MTK_DHCPD (depends on CONFIG_HTTPD)
- Include mtk_dhcpd.o in net/Makefile
- failsafe/failsafe.c:
    - call mtk_dhcpd_start() before Web failsafe starts
    - call mtk_dhcpd_stop() after Web failsafe exits
- net/net.c: call mtk_dhcpd_start() after net_loop(TCP) initialization
  to provide a fallback hook
2026-03-10 11:59:05 +08:00

457 lines
10 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 MediaTek Inc. All Rights Reserved.
*
* Author: Weijie Gao <weijie.gao@mediatek.com>
*
* Failsafe Web UI
*/
#include <command.h>
#include <errno.h>
#include <env.h>
#include <malloc.h>
#include <net.h>
#include <net/mtk_tcp.h>
#include <net/mtk_httpd.h>
#include <net/mtk_dhcpd.h>
#include <u-boot/md5.h>
#include <linux/stringify.h>
#include <dm/ofnode.h>
#include <vsprintf.h>
#include <version_string.h>
#include <failsafe/fw_type.h>
#include "../board/mediatek/common/boot_helper.h"
#include "fs.h"
static u32 upload_data_id;
static const void *upload_data;
static size_t upload_size;
static int upgrade_success;
static failsafe_fw_t fw_type;
#ifdef CONFIG_MEDIATEK_MULTI_MTD_LAYOUT
static const char *mtd_layout_label;
const char *get_mtd_layout_label(void);
#define MTD_LAYOUTS_MAXLEN 128
#endif
int __weak failsafe_validate_image(const void *data, size_t size, failsafe_fw_t fw)
{
return 0;
}
int __weak failsafe_write_image(const void *data, size_t size, failsafe_fw_t fw)
{
return -ENOSYS;
}
static int output_plain_file(struct httpd_response *response,
const char *filename)
{
const struct fs_desc *file;
int ret = 0;
file = fs_find_file(filename);
response->status = HTTP_RESP_STD;
if (file) {
response->data = file->data;
response->size = file->size;
} else {
response->data = "Error: file not found";
response->size = strlen(response->data);
ret = 1;
}
response->info.code = 200;
response->info.connection_close = 1;
response->info.content_type = "text/html";
return ret;
}
static void version_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
if (status != HTTP_CB_NEW)
return;
response->status = HTTP_RESP_STD;
response->data = version_string;
response->size = strlen(response->data);
response->info.code = 200;
response->info.connection_close = 1;
response->info.content_type = "text/plain";
}
static void index_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
if (status == HTTP_CB_NEW)
output_plain_file(response, "index.html");
}
static void upload_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
static char md5_str[33] = "";
static char resp[128];
struct httpd_form_value *fw;
#ifdef CONFIG_MEDIATEK_MULTI_MTD_LAYOUT
struct httpd_form_value *mtd = NULL;
#endif
u8 md5_sum[16];
int i;
static char hexchars[] = "0123456789abcdef";
if (status != HTTP_CB_NEW)
return;
response->status = HTTP_RESP_STD;
response->info.code = 200;
response->info.connection_close = 1;
response->info.content_type = "text/plain";
#ifdef CONFIG_MTK_BOOTMENU_MMC
fw = httpd_request_find_value(request, "gpt");
if (fw) {
fw_type = FW_TYPE_GPT;
goto done;
}
#endif
fw = httpd_request_find_value(request, "fip");
if (fw) {
fw_type = FW_TYPE_FIP;
if (failsafe_validate_image(fw->data, fw->size, fw_type))
goto fail;
goto done;
}
fw = httpd_request_find_value(request, "bl2");
if (fw) {
fw_type = FW_TYPE_BL2;
if (failsafe_validate_image(fw->data, fw->size, fw_type))
goto fail;
goto done;
}
fw = httpd_request_find_value(request, "firmware");
if (fw) {
fw_type = FW_TYPE_FW;
if (failsafe_validate_image(fw->data, fw->size, fw_type))
goto fail;
#ifdef CONFIG_MEDIATEK_MULTI_MTD_LAYOUT
mtd = httpd_request_find_value(request, "mtd_layout");
#endif
goto done;
}
fw = httpd_request_find_value(request, "initramfs");
if (fw) {
fw_type = FW_TYPE_INITRD;
if (fdt_check_header(fw->data))
goto fail;
goto done;
}
fail:
response->data = "fail";
response->size = strlen(response->data);
return;
done:
upload_data_id = upload_id;
upload_data = fw->data;
upload_size = fw->size;
md5((u8 *)fw->data, fw->size, md5_sum);
for (i = 0; i < 16; i++) {
u8 hex = (md5_sum[i] >> 4) & 0xf;
md5_str[i * 2] = hexchars[hex];
hex = md5_sum[i] & 0xf;
md5_str[i * 2 + 1] = hexchars[hex];
}
#ifdef CONFIG_MEDIATEK_MULTI_MTD_LAYOUT
if (mtd) {
mtd_layout_label = mtd->data;
sprintf(resp, "%ld %s %s", fw->size, md5_str, mtd->data);
} else {
sprintf(resp, "%ld %s", fw->size, md5_str);
}
#else
sprintf(resp, "%ld %s", fw->size, md5_str);
#endif
response->data = resp;
response->size = strlen(response->data);
return;
}
struct flashing_status {
char buf[4096];
int ret;
int body_sent;
};
static void result_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
struct flashing_status *st;
u32 size;
if (status == HTTP_CB_NEW) {
st = calloc(1, sizeof(*st));
if (!st) {
response->info.code = 500;
return;
}
st->ret = -1;
response->session_data = st;
response->status = HTTP_RESP_CUSTOM;
response->info.http_1_0 = 1;
response->info.content_length = -1;
response->info.connection_close = 1;
response->info.content_type = "text/html";
response->info.code = 200;
size = http_make_response_header(&response->info,
st->buf, sizeof(st->buf));
response->data = st->buf;
response->size = size;
return;
}
if (status == HTTP_CB_RESPONDING) {
st = response->session_data;
if (st->body_sent) {
response->status = HTTP_RESP_NONE;
return;
}
if (upload_data_id == upload_id) {
#ifdef CONFIG_MEDIATEK_MULTI_MTD_LAYOUT
if (mtd_layout_label &&
strcmp(get_mtd_layout_label(), mtd_layout_label) != 0) {
printf("httpd: saving mtd_layout_label: %s\n", mtd_layout_label);
env_set("mtd_layout_label", mtd_layout_label);
env_save();
}
#endif
if (fw_type == FW_TYPE_INITRD)
st->ret = 0;
else
st->ret = failsafe_write_image(upload_data,
upload_size, fw_type);
}
/* invalidate upload identifier */
upload_data_id = rand();
if (!st->ret)
response->data = "success";
else
response->data = "failed";
response->size = strlen(response->data);
st->body_sent = 1;
return;
}
if (status == HTTP_CB_CLOSED) {
st = response->session_data;
upgrade_success = !st->ret;
free(response->session_data);
if (upgrade_success)
mtk_tcp_close_all_conn();
}
}
static void style_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
if (status == HTTP_CB_NEW) {
output_plain_file(response, "style.css");
response->info.content_type = "text/css";
}
}
static void js_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
if (status == HTTP_CB_NEW) {
output_plain_file(response, "main.js");
response->info.content_type = "text/javascript";
}
}
static void not_found_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
if (status == HTTP_CB_NEW) {
output_plain_file(response, "404.html");
response->info.code = 404;
}
}
static void html_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
if (status != HTTP_CB_NEW)
return;
if (output_plain_file(response, request->urih->uri + 1))
not_found_handler(status, request, response);
}
#ifdef CONFIG_MEDIATEK_MULTI_MTD_LAYOUT
static const char *get_mtdlayout_str(void)
{
static char mtd_layout_str[MTD_LAYOUTS_MAXLEN];
ofnode node, layout;
sprintf(mtd_layout_str, "%s;", get_mtd_layout_label());
node = ofnode_path("/mtd-layout");
if (ofnode_valid(node) && ofnode_get_child_count(node)) {
ofnode_for_each_subnode(layout, node) {
strcat(mtd_layout_str, ofnode_read_string(layout, "label"));
strcat(mtd_layout_str, ";");
}
}
return mtd_layout_str;
}
#endif
static void mtd_layout_handler(enum httpd_uri_handler_status status,
struct httpd_request *request,
struct httpd_response *response)
{
if (status != HTTP_CB_NEW)
return;
response->status = HTTP_RESP_STD;
#ifdef CONFIG_MEDIATEK_MULTI_MTD_LAYOUT
response->data = get_mtdlayout_str();
#else
response->data = "error";
#endif
response->size = strlen(response->data);
response->info.code = 200;
response->info.connection_close = 1;
response->info.content_type = "text/plain";
}
int start_web_failsafe(void)
{
struct httpd_instance *inst;
inst = httpd_find_instance(80);
if (inst)
httpd_free_instance(inst);
inst = httpd_create_instance(80);
if (!inst) {
printf("Error: failed to create HTTP instance on port 80\n");
return -1;
}
httpd_register_uri_handler(inst, "/", &index_handler, NULL);
httpd_register_uri_handler(inst, "/bl2.html", &html_handler, NULL);
httpd_register_uri_handler(inst, "/booting.html", &html_handler, NULL);
httpd_register_uri_handler(inst, "/cgi-bin/luci", &index_handler, NULL);
httpd_register_uri_handler(inst, "/cgi-bin/luci/", &index_handler, NULL);
httpd_register_uri_handler(inst, "/fail.html", &html_handler, NULL);
httpd_register_uri_handler(inst, "/flashing.html", &html_handler, NULL);
httpd_register_uri_handler(inst, "/getmtdlayout", &mtd_layout_handler, NULL);
#ifdef CONFIG_MTK_BOOTMENU_MMC
httpd_register_uri_handler(inst, "/gpt.html", &html_handler, NULL);
#endif
httpd_register_uri_handler(inst, "/initramfs.html", &html_handler, NULL);
httpd_register_uri_handler(inst, "/main.js", &js_handler, NULL);
httpd_register_uri_handler(inst, "/result", &result_handler, NULL);
httpd_register_uri_handler(inst, "/style.css", &style_handler, NULL);
httpd_register_uri_handler(inst, "/uboot.html", &html_handler, NULL);
httpd_register_uri_handler(inst, "/upload", &upload_handler, NULL);
httpd_register_uri_handler(inst, "/version", &version_handler, NULL);
httpd_register_uri_handler(inst, "", &not_found_handler, NULL);
if (IS_ENABLED(CONFIG_MTK_DHCPD))
mtk_dhcpd_start();
net_loop(MTK_TCP);
if (IS_ENABLED(CONFIG_MTK_DHCPD))
mtk_dhcpd_stop();
return 0;
}
static int do_httpd(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
u32 local_ip;
int ret;
#ifdef CONFIG_NET_FORCE_IPADDR
net_ip = string_to_ip(CONFIG_IPADDR);
net_netmask = string_to_ip(CONFIG_NETMASK);
#endif
local_ip = ntohl(net_ip.s_addr);
printf("\nWeb failsafe UI started\n");
printf("URL: http://%u.%u.%u.%u/\n",
(local_ip >> 24) & 0xff, (local_ip >> 16) & 0xff,
(local_ip >> 8) & 0xff, local_ip & 0xff);
printf("\nPress Ctrl+C to exit\n");
ret = start_web_failsafe();
if (upgrade_success) {
if (fw_type == FW_TYPE_INITRD)
boot_from_mem((ulong)upload_data);
else
do_reset(NULL, 0, 0, NULL);
}
return ret;
}
U_BOOT_CMD(httpd, 1, 0, do_httpd,
"Start failsafe HTTP server", ""
);