上回讲到,4412官方提供了Ubuntu的12.04版本,但是呢,由于我要做的是Electron的框架,因此必须从12.04升级,这样就遇到了老大难问题了,怎么升级呢?
前排感谢大佬的付出,在移植过程中参考了你们很多的资料:
[2020.09.12 - xhr4412] 移植 u-boot-2020.07 & linux-5.8.5 & BusyBox-1.31.1 到 iTOP-4412 汇总
移植Ubuntu Base 20.04 LTS (Focal Fossa)到4412开发板
首先,我在网上搜索Ubuntu移植4412的时候,第一个看到的就是Jason416大佬的文章,估计是大佬技术太好了,中间省略了很多步骤,我来说说我踩的坑:
第一步要确定自己的uboot是不是自己的板子的型号,并且是要支持设备树的Uboot。
本来我是不想改uboot的,后来发现,到手开发板的出厂Uboot不支持设备树,而且支持uboot的设备树型号只有1G的,而我的开发板是2G的。
按照4412官方编译Uboot的教程,我照葫芦画瓢改了内存地址和数量,结果发现,不行。
究其原因,是因为SCP 2G的开发板在后期改了EMMC的型号,导致原本256*8的2G版变成了512*4的板子,Uboot还没适配,然后只能识别出1G内存。还好,还是能启动的。
后来我想了想,1g内存也不是不能用,当年我512Mb大战12.04还能依然坚挺。
第二件事是刷入Linux的内核。
由于之前对开发版的不熟悉,再加上第一次接触嵌入式Linux,导致刷了不下20次不知道哪里下的内核文件,还手贱的没去生成config,自己像x86上直接make,一行一行敲配置文件。没想到还有menuconfig这个好东西。
多扯一句,make menuconfig之前记得把终端拉大一点,否则会报错。
大佬的Github上写的比较简单,这里说一说用法:首先
git clone -b iTop-4412-dev https://github.com/jason416/ubuntu-base-20
然后在clone下来的目录里面的arch/arm/configs下找到对应的itop4412_deconfig文件,看看文件名(大佬手抖打错了),之后
make itop4412_deconfig
make menuconfig
在Boot options里面改一下你的系统init参数,Ubuntu 20是init=/bin/systemd。
注意,把参数上下两个选项的从bootloader里面加载参数的选项给去了,否则Linux启动时就会从uboot里面读取启动参数,设置的启动参数就无效了。
如果忘了去了,启动的时候肯定会卡在4.3左右,kernel panic -2,即,找不到文件,这里可以在system分区里面创建一个linuxrc文件,chmod为777以后
#/bin/sh
exec /bin/systemd
这种方法其实不好,属于是曲线救国,如果用的是讯为的老4412的uboot,即uboot 2010,在没有setenv命令的时候可以这么干。
其实最直接的方法是在uboot里
setenv bootargs=‘你的变量’
savenv
上面的内核参数保持原样就行了。
但是呢,我实测下来,官方发布的最新的4412uboot里面setenv以后boot能成功,savenv以后再重启就不行了。
大佬的配置不知道是不是和我的板子不一样,进入ubuntu以后总是崩,然后是在COM终端里面不能粘贴,很奇怪。
于是我复制了一份讯为的旧config到内核根目录下覆盖编译,竟然神奇的好了。
设备树在讯为网盘资料里面的不含光盘版——设备树移植资料——Image的
exynos4412-itop-elite.dtb
Uboot采用的讯为同一目录下的内核,但是,这个Uboot还是有点小毛病的:不能支持4G以上分区和写入4G以上的rootfs。
因此,要先把源码下载下来,然后修改几行代码,文件在u-boot-2020.10/common/itop4412/partition.c和u-boot-2020.10/include/itop4412/partition.h里面。
至于为什么会读取不到,请看这里:
完整代码修改后如下:
partition.c
#include <itop4412/partition.h>
static struct _part_info parts[PART_MAX] = {
DEF_PART(uboot, "bootloader", PART_FLAG_BOOTABLE),
DEF_PART(env, "env", PART_FLAG_NONE),
DEF_PART(kernel, "kernel", PART_FLAG_NONE),
DEF_PART(dtb1, "dtb", PART_FLAG_NONE),
DEF_PART(dtb2, "dtb2", PART_FLAG_NONE),
DEF_PART(mbr1, "mbr1", PART_FLAG_NONE),
DEF_PART(mbr2, "system", PART_FLAG_NONE),
DEF_PART(mbr3, "mbr3", PART_FLAG_NONE),
DEF_PART(mbr4, "mbr4", PART_FLAG_NONE),
};
static void show_parts_info(void)
{
struct _part_info *part;
int i;
printf("\n");
printf(" partion block start block count flag \n\n");
for (i = uboot; i < PART_MAX; i++)
{
part = &parts[i];
printf(" %-10s %7lld 0x%-8llX %8lld 0x%-8llX 0x%.2X \n",
part->name, part->blk_start, part->blk_start,
part->blk_count, part->blk_count, part->flag);
}
printf("\n");
}
static void calc_parts_start(void)
{
int i;
unsigned long long start;
start = parts[env].blk_start + parts[env].blk_count;
for (i = env + 1; i < mbr1; i++) {
parts[i].blk_start = start;
start += parts[i].blk_count;
}
}
static void read_mbr_parts_info(void)
{
int i, ret;
struct _part_info *part;
long long block_start, block_count;
unsigned char part_id;
char dev[2] = {"0"};
for(i = mbr1; i < mbr_end; i++)
{
ret = get_mmc_part_info(dev, i-mbr1+1, &block_start, &block_count, &part_id);
part = &parts[i];
part->blk_start = (unsigned long long)block_start;
part->blk_count = (unsigned long long)block_count;
if (part_id == PART_EXT4) {
part->flag |= PART_FLAG_EXT4;
part->part_id = part_id;
}
else if (part_id == PART_FAT) {
part->flag |= PART_FLAG_FAT;
part->part_id = part_id;
}
}
}
int itop4412_partition_init(void)
{
parts[uboot].blk_start = UBOOT_BLK_START;
parts[uboot].blk_count = UBOOT_BLK_COUNT;
parts[env].blk_start = ENV_BLK_START;
parts[env].blk_count =
(CONFIG_ENV_SIZE >> BLK_SHIFT) + !!(CONFIG_ENV_SIZE & (BLK_SIZE - 1));
parts[dtb1].blk_count = KB_BLK(CONFIG_PART_DTB_SIZE);
parts[dtb2].blk_count = KB_BLK(CONFIG_PART_DTB_SIZE);
parts[kernel].blk_count = MB_BLK(CONFIG_PART_KERNEL_SIZE);
read_mbr_parts_info();
calc_parts_start();
//show_parts_info();
return 0;
}
#define ext4_printf(args, ...)
static int check_compress_ext4(const void *img_base, const unsigned long long parti_size)
{
const ext4_file_header *file_header;
file_header = (const ext4_file_header *)img_base;
if (file_header->magic != EXT4_FILE_HEADER_MAGIC) {
printf("Invalid EXT4 Magic! 0x%2x\n", file_header->magic);
return CMD_RET_FAILURE;
}
if (file_header->major != EXT4_FILE_HEADER_MAJOR) {
printf("Invalid Version Info! 0x%2x\n", file_header->major);
return CMD_RET_FAILURE;
}
if (file_header->file_header_size != EXT4_FILE_HEADER_SIZE) {
printf("Invalid File Header Size! 0x%8x\n", file_header->file_header_size);
return CMD_RET_FAILURE;
}
if (file_header->chunk_header_size != EXT4_CHUNK_HEADER_SIZE) {
printf("Invalid Chunk Header Size! 0x%8x\n", file_header->chunk_header_size);
return CMD_RET_FAILURE;
}
if (file_header->block_size != EXT4_FILE_BLOCK_SIZE) {
printf("Invalid Block Size! 0x%8x\n", file_header->block_size);
return CMD_RET_FAILURE;
}
if ((parti_size/file_header->block_size) < file_header->total_blocks) {
printf("Invalid Volume Size! Image is bigger than partition size!\n");
printf("partion size %llu , image size %u \n",
(parti_size/file_header->block_size), file_header->total_blocks);
return CMD_RET_FAILURE;
}
/* image is compressed ext4 */
return 0;
}
int write_raw_chunk(const void* data, const unsigned long long sector,
const unsigned long long sector_size)
{
char run_cmd[64];
ext4_printf("write raw data in %d size %d \n", sector, sector_size);
sprintf(run_cmd, "mmc write 0x%lx 0x%llx 0x%llx",
(const unsigned long)data, sector, sector_size);
return run_command(run_cmd, 0);
}
int write_compressed_ext4(const void* img_base, unsigned long sector_base)
{
unsigned int sector_size;
int total_chunks;
const ext4_chunk_header *chunk_header;
const ext4_file_header *file_header;
int err, retry;
file_header = (const ext4_file_header*)img_base;
total_chunks = file_header->total_chunks;
ext4_printf("total chunk = %d\n", total_chunks);
img_base += EXT4_FILE_HEADER_SIZE;
while(total_chunks) {
chunk_header = (const ext4_chunk_header*)img_base;
sector_size = (chunk_header->chunk_size * file_header->block_size) >> BLK_SHIFT;
switch(chunk_header->type)
{
case EXT4_CHUNK_TYPE_RAW:
ext4_printf("raw_chunk\n");
retry = 5;
RETRY:
err = write_raw_chunk(img_base + EXT4_CHUNK_HEADER_SIZE,
sector_base, sector_size);
if (err) {
printf("[retry=%d] ext4 fs write fail !\n", retry--);
if (retry > 0) goto RETRY;
else return 1;
}
sector_base += sector_size;
break;
case EXT4_CHUNK_TYPE_FILL:
ext4_printf("fill_chunk\n");
sector_base += sector_size;
break;
case EXT4_CHUNK_TYPE_NONE:
ext4_printf("none chunk\n");
sector_base += sector_size;
break;
default:
ext4_printf("unknown chunk type\n");
sector_base += sector_size;
break;
}
total_chunks--;
ext4_printf("remain chunks = %d\n", total_chunks);
img_base += chunk_header->total_size;
};
ext4_printf("write done\n");
return 0;
}
//===========================
static int do_write_part_normal(const struct _part_info *part,
const unsigned long buf, const unsigned long long blk_size)
{
char run_cmd[64];
sprintf(run_cmd,"mmc write 0x%lx 0x%llx 0x%llx", buf,
part->blk_start, blk_size);
return run_command(run_cmd, 0);
}
static int do_write_part_boot(const struct _part_info *part,
const unsigned long long buf, const unsigned long long blk_size)
{
int ret;
if(run_command("mmc lock open", 0)) return 1;
ret = do_write_part_normal(part, buf, blk_size);
run_command("mmc lock close", 0);
return ret;
}
static int do_write_part(const struct _part_info *part,
const unsigned int buf, const unsigned long long blk_size)
{
unsigned char flag = part->flag;
if (!flag)
return do_write_part_normal(part, buf, blk_size);
else if (flag & PART_FLAG_BOOTABLE)
return do_write_part_boot(part, buf, blk_size);
else if ((flag & PART_FLAG_EXT4) && !(flag & PART_FLAG_FAT)) {
if (check_compress_ext4((void *)buf, part->blk_count << BLK_SHIFT))
return CMD_RET_FAILURE;
return write_compressed_ext4((void *)buf, part->blk_start);
}
return CMD_RET_FAILURE;
}
static int do_mmcpart_show(struct cmd_tbl *cmdtp,
int flag, int argc, char *const argv[])
{
read_mbr_parts_info();
show_parts_info();
return CMD_RET_SUCCESS;
}
static const struct _part_info *mmcpart_find(const char* name)
{
int i;
for(i = uboot; i < PART_MAX; i++)
if(!strcmp(parts[i].name, name)) {
return &parts[i];
}
return NULL;
}
static int mmcpart_check_input(struct _input_argv *out,
const int argc, char *const argv[])
{
unsigned long size;
if (argc < 2)
return CMD_RET_USAGE;
out->part = mmcpart_find(argv[1]);
if (!out->part) {
printf("can't find partition: %s\n"
"please run \"mmcpart show\" ..\n", argv[1]);
return CMD_RET_FAILURE;
}
if (argc > 2) {
out->addr = simple_strtoul(argv[2], NULL, 16);
if (!out->addr) {
printf("addr can't be 0 !\n");
return CMD_RET_FAILURE;
}
}
out->blk_size = out->part->blk_count;
if (argc > 3) {
size = simple_strtoul(argv[3], NULL, 16);
out->blk_size = (size >> BLK_SHIFT) + !!(size & (BLK_SIZE - 1));
if (out->blk_size > out->part->blk_count) {
printf("image size is larger than partition[%s]\n", out->part->name);
return CMD_RET_FAILURE;
}
out->blk_size += (out->part->blk_count - out->blk_size) >> 1;
}
if (run_command("mmc dev 0", 0)) {
printf("\ncannot switch to emmc dev !!\n");
return -CMD_RET_FAILURE;
}
return CMD_RET_SUCCESS;
}
static int do_mmcpart_read(struct cmd_tbl *cmdtp,
int flag, int argc, char *const argv[])
{
struct _input_argv input;
char cmd_read[64];
int ret;
if(argc < 3)
return CMD_RET_USAGE;
ret = mmcpart_check_input(&input, argc, argv);
if (ret)
return ret;
sprintf(cmd_read, "mmc read %lx %llx %llx", input.addr,
input.part->blk_start, input.blk_size);
return run_command(cmd_read, 0);
}
static int do_mmcpart_write(struct cmd_tbl *cmdtp,
int flag, int argc, char *const argv[])
{
struct _input_argv input;
int ret;
if(argc < 3)
return CMD_RET_USAGE;
ret = mmcpart_check_input(&input, argc, argv);
if (ret)
return ret;
return do_write_part(input.part, input.addr, input.blk_size);
}
static int do_mmcpart_erase(struct cmd_tbl *cmdtp,
int flag, int argc, char *const argv[])
{
struct _input_argv input;
char cmd_erase[64];
int ret;
ret = mmcpart_check_input(&input, argc, argv);
if (ret)
return ret;
sprintf(cmd_erase,"mmc erase %llx %llx",
input.part->blk_start, input.part->blk_count);
return run_command(cmd_erase, 0);
}
static struct cmd_tbl cmd_mmcpart[] = {
U_BOOT_CMD_MKENT(show, 1, 0, do_mmcpart_show, "", ""),
U_BOOT_CMD_MKENT(read, 4, 0, do_mmcpart_read, "", ""),
U_BOOT_CMD_MKENT(write, 4, 0, do_mmcpart_write, "", ""),
U_BOOT_CMD_MKENT(erase, 2, 0, do_mmcpart_erase, "", ""),
};
static int do_mmcpart(struct cmd_tbl *cmdtp,
int flag, int argc, char *const argv[])
{
struct cmd_tbl *cp;
cp = find_cmd_tbl(argv[1], cmd_mmcpart, ARRAY_SIZE(cmd_mmcpart));
/* Drop the mmcpart command */
argc--;
argv++;
if (cp == NULL || argc > cp->maxargs)
return CMD_RET_USAGE;
if (flag == CMD_FLAG_REPEAT && !cmd_is_repeatable(cp))
return CMD_RET_SUCCESS;
return cp->cmd(cmdtp, flag, argc, argv);
}
U_BOOT_CMD(
mmcpart, 5, 0, do_mmcpart,
"mmcpart, itop4412 emmc partitions sub system",
"mmcpart show - display all partitions of the itop4412 emmc\n"
"mmcpart read <part> <addr> [len] - read part's data to addr\n"
"mmcpart write <part> <addr> [len] - write addr(len) to part\n"
"mmcpart erase <part> - erase part's data"
);
partition.h
#ifndef _ITOP4412_PARTITION_H_
#define _ITOP4412_PARTITION_H_
#include <common.h>
#include <command.h>
#include <env.h>
#define UBOOT_BLK_START (0)
#define UBOOT_BLK_COUNT (0x40E)
#define ENV_BLK_START (0x460)
#define BLK_SHIFT (9)
#define BLK_SIZE (1 << BLK_SHIFT)
#define KB_BLK(x) ((x) * 1024 / BLK_SIZE)
#define MB_BLK(x) (KB_BLK(x) * 1024)
enum _part_order {
uboot = 0,
env,
kernel,
dtb1,
dtb2,
mbr1,
mbr2,
mbr3,
mbr4,
mbr_end,
PART_MAX = mbr_end,
};
#define PART_EXT4 (0x83)
#define PART_FAT (0x0C)
#define PART_FLAG_BOOTABLE (1 << 0)
#define PART_FLAG_EXT4 (1 << 1)
#define PART_FLAG_FAT (1 << 2)
#define PART_FLAG_NONE (0)
struct _part_info {
const char * name;
unsigned long long int blk_start;
unsigned long long blk_count;
unsigned char part_id;
unsigned char flag;
};
struct _input_argv {
unsigned long addr;
unsigned long long blk_size;
const struct _part_info *part;
};
#define DEF_PART(_idx, _name, _flag) \
[_idx] = {\
.name = _name,\
.flag = (_flag),\
.part_id = 0xFF,\
}
// defined in fdisk.c ==============================================
int get_mmc_part_info(char *device_name, int part_num,
long long int *block_start, long long int *block_count, unsigned char *part_Id);
// =================================================================
// -----------------------------------------------------------------
/* copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
typedef struct _ext4_file_header {
unsigned int magic;
unsigned short major;
unsigned short minor;
unsigned short file_header_size;
unsigned short chunk_header_size;
unsigned int block_size;
unsigned int total_blocks;
unsigned int total_chunks;
unsigned int crc32;
}ext4_file_header;
typedef struct _ext4_chunk_header {
unsigned short type;
unsigned short reserved;
unsigned int chunk_size;
unsigned int total_size;
}ext4_chunk_header;
#define EXT4_FILE_HEADER_MAGIC 0xED26FF3A
#define EXT4_FILE_HEADER_MAJOR 0x0001
#define EXT4_FILE_HEADER_MINOR 0x0000
#define EXT4_FILE_BLOCK_SIZE 0x1000
#define EXT4_FILE_HEADER_SIZE (sizeof(struct _ext4_file_header))
#define EXT4_CHUNK_HEADER_SIZE (sizeof(struct _ext4_chunk_header))
#define EXT4_CHUNK_TYPE_RAW 0xCAC1
#define EXT4_CHUNK_TYPE_FILL 0xCAC2
#define EXT4_CHUNK_TYPE_NONE 0xCAC3
//int write_compressed_ext4(char* img_base, unsigned int sector_base);
//int check_compress_ext4(char *img_base, unsigned int parti_size);
// -----------------------------------------------------------------
#endif // _ITOP4412_PARTITION_H_
基本上就是说,把和分区大小以及长度有关的地方的int改成long long int就行了,不过注意,指针类型的int别改,会出大问题。
下面一步是制作rootfs,这个问题大佬已经说的很详细了,官方的uboot格式化出来是ext3格式!!所以要在mkfs那一行命令下面改成mkfs.ext3,否则的话,开机就报checksum failed错误,总是崩。
进入Ubuntu,首先:
chmod 0600 /etc/sshd/*
mkdir /run/sshd
reboot
重启一下,然后就能连接ssh了。
也不知道为什么,插着OTG线的时候不能开机,把线拔了就没这个问题了。
apt源我用的官方的,清华源好像文件会少一些。
apt-get update
之后随便装几个软件好像就会报错,没关系,报错找不到哪个文件夹就新建那个文件夹就行了。
然后,大功告成!
但是,百思不得其解的是,同一块板子,昨天好的,今天怎么就坏了捏,气死我了。
换RK3359了,气死人了。