需求:有一台没有PMON源码的龙芯3A5000 7A1000的机器,在不动任何软件的情况下获取到固件传递的VBIOS内容。
从串口打印中找到关键信息
vsp = 0x900000000f00f4c8 ssp = 0x900000000f00f568
一、源码分析
1 initstack
2 {
3 ...
4 1047 /* build environment vector on stack */
5 1048 if (ec) {
6 1049 printf("vsp = 08x%llx, ssp @ 08x%llx\n", (unsigned long long )vsp, (unsigned long l ong) ssp);
7 1050 if (ssp != (nsp + vectorlen + stringlen))
8 1051 {
9 1052 printf("!!! Error @@@: stack not meet \n");
10 1053 }
11 1054 envbuild (vsp, ssp);//这里配置的VBIOS
12 1055 }
13 1056 else {
14 1057 *vsp++ = (char *)0;
15 1058 }
16
17 ...
18 }
439 void envbuild(char **vsp, char *ssp)
440 {//vsp没有用到
441 struct boot_params * bp;
442 bp = (struct boot_params *) ssp;//0x900000000f00f568
443
444 init_boot_param(bp);
445 }
45 struct boot_params{
46 u64 sign; //{'B', 'P', 'I', '0', '1', '0', '0', '0'}
47 void *efitab;
48 struct ext_list_hdr *elh;
49 u64 flags;
50 } __attribute__((__packed__));
40 int init_boot_param(struct boot_params *bp)
41 {
42 u64 tab_offset = sizeof(struct boot_params);
43 char sign_bp[8] = {'B', 'P', 'I', '0', '1', '0', '0', '0'};
44
45 init_systab();
46 memcpy(bp, sign_bp, 8);
47 bp->efitab = &systab;
48 bp->elh = NULL;
49 bp->flags = 0;
50
51 #ifdef MEM_TAB
52 tab_offset += init_mem(bp, tab_offset);
53 #endif
54 #ifdef VBIOS_TAB
55 tab_offset += init_vbios(bp, tab_offset);
56 #endif
57 #ifdef SMBIOS_SUPPORT
58 loongson_smbios_init();
59 #endif
60 #ifdef ACPI_SUPPORT
61 loongson_acpi_init();
62 #endif
63 return bp;
64 }
44 u64 init_mem(struct boot_params *bp, u64 offset)
45 {
46 struct mem_info *mem_data = (struct mem_info *)((u64)bp + offset);
47 char data[8] = {'M', 'E', 'M'};
48 u64 j = 0;
49
50 memcpy(&mem_data->header.sign, data, sizeof(data));
51 mem_data->header.rev = 0;
52 mem_data->header.len = sizeof (*mem_data);
53 mem_data->map_num = 0;
54 /*
55 * 1. The lowest 2M region cannot record in MemMap, cause Linux ram region should begin with usable ram.
56 * map_entry_init(mem_data, MEM_RESERVED, 0x0, 0x200000); // 0x0-0x200000
57 */
58
59 /* 2. Available SYSTEM_RAM area. */
60 map_entry_init(mem_data, SYSTEM_RAM, 0x200000, 0xf000000 - 0x200000); // 0x200000~0xf000000
61
62 /* 3. Reserved low memory highest 16M. */
63 map_entry_init(mem_data, MEM_RESERVED, 0xf000000, 0x1000000); // 0xf000000~0x10000000
64
65 /* 4. Available SYSTEM_RAM area */
66 map_entry_init(mem_data, SYSTEM_RAM, HIGH_MEM_WIN_BASE_ADDR + 0x10000000, memorysize_high_n[0] - 0x10000000); // (HIGH _MEM_WIN_BASE_ADDR + 0x10000000) ~ MAX
67
68 for (j = 1; j < TOT_NODE_NUM; j++) {
69 if (memorysize_high_n[j])
70 map_entry_init(mem_data, SYSTEM_RAM, HIGH_MEM_WIN_BASE_ADDR | (j << 44), memorysize_high_n[j]);
71 }
72 addlist(bp, &mem_data->header);
73 #if 0
74 for (j = 0; j < mem_data->map_num; j++) {
75 printf("%d: type: %x, start: %llx, size %llx\n", j, mem_data->map[j].type, mem_data->map[j].start, mem_data->map[j ].size);
76 }
77
78 #endif
79 return sizeof(struct mem_info);
80 }
15 u64 init_vbios(struct boot_params *bp, u64 offset)
16 {
17 struct vbios_info *vbios_data = (struct vbios_info *)((u64)bp + offset);
18 char data[8] = {'V', 'B', 'I', 'O', 'S'};
19
20 memcpy(&vbios_data->header.sign, data, sizeof(data));
21 vbios_data->header.rev = 0;
22 vbios_data->header.len = sizeof (*vbios_data);
23
24 ls7a_spi_read_vgabios(readspi_result, VBIOS_SIZE);
25 if (!ls7a_vgabios_crc_check(readspi_result, VBIOS_SIZE)) {
26 vbios_data->VbiosAddr = readspi_result;
27 } else {
28 vbios_data->VbiosAddr = Vbios;
29 }
30
31 addlist(bp, &vbios_data->header);
32 return sizeof(struct vbios_info);
33 }
从以上代码可以看出数据结构关系如下:
//------------------ <-----0x900000000f00f568
struct boot_params
//------------------
struct mem_info
//------------------
70 struct vbios_info {
71 struct ext_list_hdr header; 22字节 // {VBIOS}
72 u64 VbiosAddr;
73 } __attribute__((__packed__));
//..................
确定vbios便宜的方式有三种:
1.通过gdb可以直接获得VbiosAddr的偏移,但是目前LA编译PMON后GDB不好用,
2.另外找一块开发版增加打印信息,输出Vbios便宜
3.直接从ssp开始读足够大的空间然后根据vbios_info->header的 sign确定Vbios的位置
采用第二种方法:
得到vbios_info offse为0xa37
&vbios_data->VbiosAddr = 0x900000000f00ffb5
相对于ssp的偏移为0xa4d
二、总结
ssp开始的数据结构如下:
//------------------ <-----ssp地址
struct boot_params
//------------------
struct mem_info
//------------------ offset:0xa37
70 struct vbios_info {
71 struct ext_list_hdr header; 22字节 // {VBIOS}
72 u64 VbiosAddr; offset:0xa4d
73 } __attribute__((__packed__));
//..................
1.首先查看串口启动信息找到ssp的地址
2.ssp地址加上偏移0xa4d即为VbiosAddr的地址,里面存储vbios的首地址
3.在PMON命令行下首先执行pcs -1
,然虎再执行 d1 VbiosAddr地址 1
获取到Vbios的首地址
4.d1 Vbios的首地址 0x45c6即为整个VBIOS内容。