目录
通过boot cmd参数 禁用console 控制台,关闭串口打印
SD卡升级
使用平台 君正,其他平台可能会有一些差异
uboot下利用命令行升级的方法
查看sd卡文件
uboot 中,执行 fatls mmc 0 可以查看 SD 卡中的文件
加载文件
fatload mmc 0 mem_addr file_name 可以将文件 file_name 传送到 Uboot 的内存
举例):fatload mmc 0 0x80600000 file.bin
擦写(nor flash)
sf probe;sf erase 0x0 0x1000000;sf write 0x80600000 0x0 0x1000000
在 main_loop 中添加调用
#define JY_SD_UBOOT "uboot-SN"
#define JY_SD_KERNEL "kernel-SN"
#define JY_SD_APP "systrem-SN"
typedef struct SD_bin_head_t
{
unsigned char jy_flag;
unsigned char type[32];
unsigned int crc;
unsigned int len;
unsigned char tmp[84];//预留84个字节
}SD_bin_head_t;
static int jy_update_crc(const char * name, int crc)
{
char crc_buf[32];
sprintf(crc_buf, "%02x", crc);
char* cur_crc = getenv(name);
if (NULL == cur_crc)
{
printf("%s is null update UbootVer value %s\n", name, crc_buf);
setenv(name, crc_buf);
saveenv();
}
else
{
if(0 != strcmp(cur_crc, crc_buf))
{
printf("update env value [%s]\n", crc_buf);
setenv(name, crc_buf);
saveenv();
}
else
{
return 1;
}
}
return 0;
}
static int CalCrc(int crc, const char *buf, int len)
{
unsigned int byte;
unsigned char k;
unsigned short ACC,TOPBIT;
// unsigned short remainder = 0x0000;
unsigned short remainder = crc;
TOPBIT = 0x8000;
for (byte = 0; byte < len; ++byte)
{
ACC = buf[byte];
remainder ^= (ACC <<8);
for (k = 8; k > 0; --k)
{
if (remainder & TOPBIT)
{
remainder = (remainder << 1) ^0x8005;
}
else
{
remainder = (remainder << 1);
}
}
}
remainder=remainder^0x0000;
return remainder;
}
static int jy_mmc_check()
{
block_dev_desc_t *stor_dev;
//LOG_INFO("device name %s!\n", "mmc");
stor_dev = get_dev("mmc", 0);
if (NULL == stor_dev) {
printf("error: [jy_mmc_check] Unknow device type!!!\n");
return -1;
}
if (fat_register_device(stor_dev, 1) != 0) {
printf("error: [jy_mmc_check] Unable to use for fatls!!!\n");
return -1;
}
if (file_fat_detectfs() != 0) {
printf("error: [jy_mmc_check] file_fat_detectfs failed!!!\n");
return -1;
}
printf("info: [jy_mmc_check] SD find success..\n");
return 0;
}
static int jy_do_upgrade(const char *upg_name, char * const argv[])
{
u32 offset = 0;
size_t len = 0;
if (NULL == upg_name)
return -1;
long sz = file_fat_read(upg_name, 0x80600000, 0x1c00000);
if (sz < 128)
{
printf("info: [jy_do_upgrade] Not find SD bin[%s], sz = %ld.\n", upg_name, sz);
return -1;
}
//将升级镜像读入内存
cmd_tbl_t *bcmd;
/* Load the rescue image */
bcmd = find_cmd("fatload");
if (!bcmd)
{
printf("error: [jy_do_upgrade] find_cmd faild!!!\n");
return -1;
}
int ret = do_fat_fsload (bcmd, 0, 5, argv);
if (0 != ret)
{
printf("error: [jy_do_upgrade] do_fat_fsload faild!!!\n");
return -1;
}
if (0 == strcmp(upg_name, JY_SD_UBOOT))
{
printf("info: [jy_do_upgrade] u-boot partition upgrade start...\n");
len = 256 * 1024;
offset = 0;
}
else if (0 == strcmp(upg_name, JY_SD_KERNEL))
{
printf("info: [jy_do_upgrade] kernel partition upgrade start...\n");
len = 3 * 1024 * 1024;
offset = 256 * 1024;
}
else if (0 == strcmp(upg_name, JY_SD_APP))
{
printf("info: [jy_do_upgrade] APP partition upgrade start...\n");
len = 11010048;
offset = (3 * 1024 * 1024) + (256 * 1024);
}
else
{
printf("error: [jy_do_upgrade] Unknow type!!!\n");
return -1;
}
//校验升级镜像
int crc = CalCrc(0, 0x80600000 + sizeof(SD_bin_head_t), len);
SD_bin_head_t *p_head = 0x80600000;
printf("info: [jy_do_upgrade] data[crc = %02x], head[crc = %02x, len = %d] \n", crc, p_head->crc, p_head->len);
if ((crc != p_head->crc) || (p_head->len != len))
{
printf("error: [jy_do_upgrade] Verification failed!!!");
return -1;
}
if (jy_update_crc(upg_name, crc))
{
printf("info: [jy_do_upgrade] Same version, skip.\n");
return -1;
}
struct spi_flash *spi;
spi = spi_flash_probe(0, 0, 1000000, 0x3);
if (!spi) {
printf("%s: spi_flash probe failed.\n", __func__);
return -1;
}
ret = spi_flash_erase(spi, offset, len);
if (ret) {
printf("%s: spi_flash erase failed.\n", __func__);
return -1;
}
ret = spi_flash_write(spi, offset, len, 0x80600000 + sizeof(SD_bin_head_t));
if (ret) {
printf("%s: spi_flash write failed.\n", __func__);
spi_flash_free(spi);
return -1;
}
spi_flash_free(spi);
printf("info: [jy_do_upgrade] SD upg success..\n");
return 0;
}
static void jy_mmc_upg()
{
int ret = 0;
int old_ctrlc;
printf("info: [jy_mmc_upg] check..\n");
//检查是否插入了fat32格式的SD卡
if (0 != jy_mmc_check())
return;
old_ctrlc = disable_ctrlc(0);
char * const boot_argv[5] = { "fatload", "mmc", "0", "0x80600000", JY_SD_UBOOT};
ret = jy_do_upgrade(JY_SD_UBOOT, boot_argv);
if (0 == ret)
saveenv();
char * const kernel_argv[5] = { "fatload", "mmc", "0", "0x80600000", JY_SD_KERNEL};
ret = jy_do_upgrade(JY_SD_KERNEL, kernel_argv);
char * const app_argv[5] = { "fatload", "mmc", "0", "0x80600000", JY_SD_APP};
ret = jy_do_upgrade(JY_SD_APP, app_argv);
disable_ctrlc(old_ctrlc);
}
注:SD卡升级接口最好放在 读秒之前,main.c中的main_loop中。
将Uboot版本信息 加入环境变量
需要包含头文件#include <version.h>
char szUbootVer[68];
snprintf(szUbootVer, 64, "12XP %s", version_string);
char* UbootVer = getenv("UbootVer");
if (NULL == UbootVer)
{
printf("UbootVer is null update UbootVer value 0000\n");
setenv("UbootVer", (char*)szUbootVer);
saveenv();
}
else
{
if(strcmp(UbootVer, (char*)szUbootVer) !=0)
{
printf("update UbootVer value \n");
setenv("UbootVer", (char*)szUbootVer);
saveenv();
}
}
上层解析版本并转成16进制
static int parse_uboot_version(char * p_uboot_ver, char *p_ver)
{
if (NULL == p_uboot_ver || NULL == p_ver)
{
printf("p_uboot_ver=%p p_jy_ver=%p param error!\n", p_uboot_ver, p_ver);
return -1;
}
char ver_tmp[64] = { 0 };
char chip_tmp[64] = { 0 };
sscanf(p_uboot_ver, "%*[^(](%[^)]64s", ver_tmp);
sscanf(p_uboot_ver, "%*[^=]=%[^ ]64s", chip_tmp);
char mon[3] = {0};
sys_version_info_t ver_info;
memset(&ver_info, 0, sizeof(ver_info));
sscanf(ver_tmp, "%3s %2d %4d - %2d:%2d:%2d", mon, &ver_info.date, &ver_info.year,
&ver_info.hour, &ver_info.min, &ver_info.sec);
convert_2_inte_month(mon, &ver_info.mon);
sprintf(p_ver, "%s%02X%02X%02X%02X%02X%02X\n", chip_tmp, ver_info.year%100, ver_info.mon, ver_info.date,
ver_info.hour, ver_info.min, ver_info.sec);
return 0;
}
重新编译后进入uboot查看
fw_printenv
linux下查看环境变量可以使用fw_printenv
在uboot代码目录下make HOSTCC=mips-linux-uclibc-gnu-gcc env,编译后有报错不影响,将tools/env/下生成的fw_printenv和fw_env.config拷贝到目标机即可
这里fw_env.config的参数
Device offset 对应 CONFIG_ENV_OFFSET
Env. size 对应 CONFIG_ENV_SIZE
Flash sector size 对应 CONFIG_ENV_SECT_SIZE
(以上情况对应的是ENV分区和boot在一个分区的情况,所以这里的Device offset 就是要偏移到ENV信息的位置)
如果是ENV时单独的一个分区,比如:
这里Device offset就是0x0 从开头开始。
Env大小是256KB,所以Env. size就是0x40000
Flash sector是一个block的大小,这款flash是128KB所以是0x20000,总大小256KB,所以有两个block, Flash sector size 为2
boot的默认CMD参数
boot下printenv可以看到环境变量,环境变量可以通过setenv写入其对应的分区位置。
如果环境变量中没有写入内容 则会使用boot中的默认参数。
找到平台对应的头文件修改或者增加即可boot\include\configs\infinity6b0.h(mstar),注意不同平台版本对应宏定义名称可能不同。
自定义组合键进入uboot
正常情况下 我们进入uboot的方式是在读秒的时候按回车, 但是如果可以修改进入的方式,这样别人就算拿到我们的设备也没办法轻易进入uboot。
修改内容:
修改文件:uboot\common\main.c
abortboot_normal修改的方法:
加密原理:在uboot倒计时阶段 自定义组合键方式 进入uboot,我这里用的是0x1f 即【<ctrl> + <shift> + <->】的组合键才能进入uboot
如果是在abortboot_keyed中修改:
可以直接赋值
也可以找到对应的宏定义在头文件中修改
获取组合键的ascii方法:
#include <stdio.h>
int main(void)
{
int in;
in = getchar();
printf("%#x\n", in);
return 0;
}
uboot增加密码保护
mstar平台
默认uboot 按回车即可进入控制台,安全一点的做法可以 自定义组合键进入 boot ,更安全的做法,进入boot 之后可以加一层密码保护效果如下
输入正确的密码之后才能 成功登陆。
void main_loop(void)增加 我们的login_autch接口
这里用到了aes对输入的密码进行加密后比较也可以直接明文比较
修改lib/Makefile 增加对aes.c的编译(这里不同的平台打开使用aes的方式不同,有些平台可以通过menuconfig 打开,我这里没有menuconfig配置开关,因此自己修改)
找到Makefile修改的过程:
1、在boot代码里面搜索发现有aes的实现aes.c,于是直接引用,发现编译报错未定义,于是找到aes.c所在路径boot/lib/发现没有生成.o。
2、在aes.c所在路径下随便找一个 有生成.o(这里找的是errno.o)的文件,进到Makefile中找到对应的位置仿照添加 aes.o 如下图:
login_autch实现
#include <aes.h>
#define CONFIG_JY_LOGIN_PWD "f7423c53cf548719b5c610d"
static int do_aes_cbc_encrypt(unsigned char *data, int len)
{
u8 key[AES_KEY_LENGTH] = { 0x0c, 0x46, 0x31, 0x3a, 0xee, 0xf6, 0xff, 0x73, 0xd8, 0xdd, 0x8e, 0xee, 0x36, 0x1c, 0x6a, 0x7b };
u8 iv[AES_KEY_LENGTH] = { 0 };
u8 key_exp[AES_EXPAND_KEY_LENGTH];
u32 num_aes_blocks = 0;
num_aes_blocks = (len + AES_KEY_LENGTH - 1) / AES_KEY_LENGTH;
aes_expand_key(key, key_exp);
aes_cbc_encrypt_blocks(key_exp, (u8*)data, (u8*)data, num_aes_blocks);
return 0;
}
static void login_auth(void)
{
#define MAX_PASSWD_LEN 16
#define PASSWD_STR "password: "
int auth = 0;
char in_pass[MAX_PASSWD_LEN * 2];
char enc_pass[MAX_PASSWD_LEN * 2] = {0xd9, 0x0, 0xca, 0xff, 0x37, 0xaf, 0xee, 0xd, 0xbf, 0x73, 0xc4, 0x91, 0xb9, 0x7, 0x50, 0xf};
int len = 0, enc_len;
int c, i;
while (!auth) {
puts(PASSWD_STR);
memset(in_pass, 0, MAX_PASSWD_LEN * 2);
len = 0;
while (1) {
if (tstc()) {
/* Check for input string overflow */
if (len >= MAX_PASSWD_LEN)
break;
c = getc();
if ('\r' == c || '\n' == c)
break;
else if ('\0' == c || 0x1f == c)
continue;
in_pass[len++] = c;
}
}
if (len > 0) {
do_aes_cbc_encrypt((u8*)in_pass, len);
printf("do_aes_cbc_encrypt in_pass \n");
for (i = 0; i < MAX_PASSWD_LEN; ++i)
printf("%#x ", in_pass[i]);
printf("\n\n");
printf("do_aes_cbc_encrypt enc_pass \n");
for (i = 0; i < MAX_PASSWD_LEN; ++i)
printf("%#x ", enc_pass[i]);
printf("\n\n");
//printf("shj debug boot passwd --- ##len = %d [%s][%s] \n", len, in_pass, enc_pass);
auth = 1;
for (i = 0; i < MAX_PASSWD_LEN; i++) {
if (in_pass[i] ^ enc_pass[i]) {
auth = 0;
printf("AES faild\n");
break;
}
}
if (!strcmp(in_pass, enc_pass))
{
printf("success\n");
auth = 1;
}
else
{
//auth = 1;/
printf("faild\n");
}
}
puts("\r\n");
}
}
通过uboot cmd进入单用户模式
在ARM中,bootargs参数用于传递引导参数给内核。在某些Linux发行版中,bootargs参数可以设置为"single"。"single"参数的作用是将系统引导到单用户模式,这通常用于系统维护和修复。
在单用户模式下,只有root用户可以访问系统,其他用户无法登录。这使得系统管理员可以更安全地进行系统维护和故障排除,而不会受到其他用户的干扰。另外,单用户模式还提供了root权限,允许系统管理员执行一些敏感操作,例如修改系统文件、调整系统设置等。
需要注意的是,不同的Linux发行版可能对bootargs参数的设置有所不同。因此,具体的操作步骤和效果可能因发行版而异。
屏蔽串口打印信息
通过boot cmd参数 禁用console 控制台,关闭串口打印
出于安全考虑,需要关闭串口打印内容,防止信息被窃取。可以通过在bootargs后面增添console=null来实现
修改后将不会输出内核已经程序打印,但是后台依然是正常可用的
在arm linux系统中传递kernel参数:禁用console(控制台)/取消命令行界面/取消串口打印_linux 禁用 console-CSDN博客
进一步屏蔽boot的输出信息
修改代码(Mstar)
效果:
通过tftp在uboot 下将RAM上载到tftp服务端
以nand为例子
补齐ip mask getway等项
拿到分区地址信息 比如:0x000000400000-0x000000800000 : "rootfs"
nand read、tftpput
拆解镜像 unsquashfs
我这里是squashfs文件系统
./unsquashfs -d out_rootfs rootfs.bin
重新压缩镜像 mksquashfs
mksquashfs_4.2 out_rootfs/ rootfs-new1.bin