Matrix653 加载器

更新时间:
2023-12-13
下载文档

Matrix653 加载器

本节将介绍 Matrix653 加载器的原理。用户可通过重载相关函数实现自定义分区镜像的加载。 本节详细定义在 mx_elf_loader.h 头文件中。

加载器相关 API

下表展示了加载器相关的 API:

API描述
_mx_partition_load_elf_file分区启动时镜像加载
_mx_partition_reload_elf_file分区重启时镜像重加载

_mx_partition_load_elf_file()

  • 描述 分区启动时镜像加载

  • 函数原型

extern RETURN_CODE_TYPE _mx_partition_load_elf_file (
        /*in */ MX_PARTITION_TYPE       *current_partition);
  • 参数
输入/输出参数描述
[in]current_partition要加载镜像的分区控制块指针
  • 返回值 Matrix653 内核错误码

  • 注意事项 如果重载此函数,需要完成分区各个段的合理映射,函数入口、段信息记录等功能

  • 示例

/*********************************************************************************************************
** Header files
*********************************************************************************************************/
#include <MATRIX653.h>
#include "mx_internal.h"
#include "mx_elf.h"
/*********************************************************************************************************
** Macro definitions
*********************************************************************************************************/
#if MX_ARCH_CPU_BITS == 32
#define Elf_Ehdr    Elf32_Ehdr
#define Elf_Phdr    Elf32_Phdr
#define Elf_Addr    Elf32_Addr
#define Elf_Shdr    Elf32_Shdr
#else
#define Elf_Ehdr    Elf64_Ehdr
#define Elf_Phdr    Elf64_Phdr
#define Elf_Addr    Elf64_Addr
#define Elf_Shdr    Elf64_Shdr
#endif

#define __MX_PERSISTENT_DATA_SECTION_NAME   ".persistent.data"
#define __MX_PERSISTENT_BSS_SECTION_NAME    ".persistent.bss"
/*********************************************************************************************************
** Function name:           _mx_partition_load_elf_file
**
** Descriptions:            Partition load elf file
**
** Input parameters:        partition       Pointer to partition
**
** Output parameters:       NONE
**
** Return value:            ERROR CODE
*********************************************************************************************************/
RETURN_CODE_TYPE _mx_partition_load_elf_file (
        /*in */ MX_PARTITION_TYPE       *current_partition)
{
    const MX_PARTITION_CONFIG_TYPE  *config = current_partition->config;
    MX_BOOL                         debug_mode = config->debug_mode ? MX_TRUE : MX_FALSE;
    const char                      *file_name = config->file_name;
    Elf_Ehdr                        ehdr;
    Elf_Phdr                        phdr;
    RETURN_CODE_TYPE                ret = NO_ERROR;
    MX_UINT32                       i;
    MX_UINT32                       nb_elf_segments;
    MX_ELF_SEGMENT_TYPE             *elf_segment;
    MX_VIRT_ADDR_TYPE               persistent_data_addr = 0;
    MX_VIRT_SIZE_TYPE               persistent_data_size = 0;
    MX_VIRT_ADDR_TYPE               persistent_bss_addr  = 0;
    MX_VIRT_SIZE_TYPE               persistent_bss_size  = 0;

    /*
     * Read & check ELF file header
     */
    ret = _mx_romfs_read_file(file_name, 0, &ehdr, sizeof(ehdr));

    if (ret == NO_ERROR) {
        if ((ehdr.e_ident[EI_MAG0] != ELFMAG0) ||
            (ehdr.e_ident[EI_MAG1] != ELFMAG1) ||
            (ehdr.e_ident[EI_MAG2] != ELFMAG2) ||
            (ehdr.e_ident[EI_MAG3] != ELFMAG3) ||
            (ehdr.e_type != ET_EXEC) ||
            (ehdr.e_phentsize != sizeof(Elf_Phdr)) ||
            (ehdr.e_phnum < 1) ||
            (ehdr.e_machine != MX_ARCH_ELF_MACHINE)) {
            ret = INVALID_CONFIG;
        }
    }

    if (ret == NO_ERROR) {
        Elf_Shdr    shdr;
        Elf_Shdr    strtab_shdr;
        char        shname[sizeof(__MX_PERSISTENT_DATA_SECTION_NAME)];
        MX_UINT32   found = 0;

        ret = _mx_romfs_read_file(file_name, ehdr.e_shoff + ehdr.e_shstrndx * sizeof(strtab_shdr), &strtab_shdr, sizeof(strtab_shdr));

        if (ret == NO_ERROR) {
            for (i = 0; i < ehdr.e_shnum; i++) {
                ret = _mx_romfs_read_file(file_name, ehdr.e_shoff + i * sizeof(shdr), &shdr, sizeof(shdr));
                if (ret != NO_ERROR) {
                    break;
                }

                if ((shdr.sh_type == SHT_PROGBITS) || (shdr.sh_type == SHT_NOBITS)) {
                    ret = _mx_romfs_read_file(file_name, strtab_shdr.sh_offset + shdr.sh_name, shname, sizeof(shname));
                    if (ret != NO_ERROR) {
                        break;
                    }

                    if (mx_strcmp(shname, __MX_PERSISTENT_DATA_SECTION_NAME) == 0) {
                        persistent_data_addr = shdr.sh_addr;
                        persistent_data_size = shdr.sh_size;
                        found |= MX_BIT(0);

                    } else if (mx_strcmp(shname, __MX_PERSISTENT_BSS_SECTION_NAME) == 0) {
                        persistent_bss_addr = shdr.sh_addr;
                        persistent_bss_size = shdr.sh_size;
                        found |= MX_BIT(1);
                    }

                    if (found == (MX_BIT(0) | MX_BIT(1))) {
                        break;
                    }
                }
            }
        }
    }

    if (ret == NO_ERROR) {
        nb_elf_segments = 0;

        /*
         * Read program head table, and calculate how many segments we need to load
         */
        for (i = 0; i < ehdr.e_phnum; i++) {
            ret = _mx_romfs_read_file(file_name, ehdr.e_phoff + i * sizeof(phdr), &phdr, sizeof(phdr));
            if (ret != NO_ERROR) {
                break;
            }

            if ((phdr.p_type != PT_LOAD) || (phdr.p_memsz == 0)) {
                continue;
            }

            nb_elf_segments++;
        }

        if (ret == NO_ERROR) {
            if (nb_elf_segments == 0) {
                ret = INVALID_CONFIG;
            } else {
                /*
                 * Alloc segments information structure memory
                 */
                elf_segment = (MX_ELF_SEGMENT_TYPE *)_mx_partition_kmalloc(current_partition, sizeof(MX_ELF_SEGMENT_TYPE) * nb_elf_segments);
                if (elf_segment != MX_NULL) {
                    /*
                     * We store the segments information in the partition control block,
                     * because when this partition restarting, we will use them again.
                     */
                    current_partition->elf_segments    = elf_segment;
                    current_partition->nb_elf_segments = nb_elf_segments;
                } else {
                    current_partition->elf_segments    = MX_NULL;
                    current_partition->nb_elf_segments = 0;
                    ret = INVALID_CONFIG;
                }
            }
        }
    }

    if (ret == NO_ERROR) {
        MX_VMM_CONTEXT_TYPE     *current_vmm_context = &(current_partition->vmm_context);
        MX_CPU_BITMAP_TYPE      assigned_cpus_bitmap = config->assigned_cpus_bitmap;
        MX_PHYS_ADDR_TYPE       phys_addr;
        MX_VMM_ATTR_TYPE        map_attr;
        MX_VIRT_SIZE_TYPE       memory_size;
        MX_VIRT_ADDR_TYPE       elf_segment_mend;

        for (i = 0; i < ehdr.e_phnum; i++) {
            /*
             * Read program head table again
             */
            ret = _mx_romfs_read_file(file_name, ehdr.e_phoff + i * sizeof(phdr), &phdr, sizeof(phdr));
            if (ret != NO_ERROR) {
                break;
            }

            if ((phdr.p_type != PT_LOAD) || (phdr.p_memsz == 0)) {
                continue;
            }

            if (!MX_IS_ALIGNED(phdr.p_vaddr, MX_ARCH_MMU_PAGE_SIZE)) {
                ret = INVALID_CONFIG;
                break;
            }

            memory_size = MX_ROUND_UP_PAGE_SIZE(phdr.p_memsz);

            if ((phdr.p_vaddr < mx_partition_virt_space_base) ||
                ((phdr.p_vaddr + memory_size) > mx_partition_virt_space_end)) {
                ret = INVALID_CONFIG;
                break;
            }

            /*
             * Reserved this segment used virtual address space
             */
            ret = __mx_space_reserved(current_partition->virt_space_allocator, phdr.p_vaddr, memory_size);
            if (ret != NO_ERROR) {
                break;
            }

            /*
             * Alloc physical memory in partition private memory space to load this segment data
             */
            phys_addr = _mx_partition_phys_mem_alloc(current_partition, memory_size);
            if (phys_addr == 0) {
                ret = INVALID_CONFIG;
                break;
            }

            map_attr = MX_VMM_ATTR_USER;
            if (phdr.p_flags & PF_R) {
                map_attr |= MX_VMM_ATTR_R;
                map_attr |= MX_VMM_ATTR_CACHE_WRITE_BACK;
            }
            if (phdr.p_flags & PF_W) {
                map_attr |= MX_VMM_ATTR_W;
                map_attr |= MX_VMM_ATTR_CACHE_WRITE_BACK;
            }
            if (phdr.p_flags & PF_X) {
                map_attr |= MX_VMM_ATTR_X;
                if (debug_mode) {
                    map_attr |= MX_VMM_ATTR_W;  /* If partition has debug mode, the text segment need changed by break point */
                }
            }

            /*
             * The first mapping, physical address to virtual address
             */
            ret = _mx_vmm_context_map(current_vmm_context,
                                      phdr.p_vaddr,
                                      phys_addr,
                                      memory_size,
                                      MX_VMM_ATTR_W | map_attr, /* Here need write permission, because we need to copy data */
                                      assigned_cpus_bitmap);
            if (ret != NO_ERROR) {
                break;
            }

            if (phdr.p_filesz > 0) {
                /*
                 * Loading segment data
                 */
                ret = _mx_romfs_read_file(file_name,
                                          phdr.p_offset,
                                          (void *)phdr.p_vaddr, phdr.p_filesz);
                if (ret != NO_ERROR) {
                    break;
                }
            }

            if (phdr.p_filesz < memory_size) {
                mx_bzero((void *)(phdr.p_vaddr + phdr.p_filesz),
                         memory_size - phdr.p_filesz);
            }

            if (map_attr & MX_VMM_ATTR_X) {
                /*
                 * Update text segment icache
                 */
                mx_cache_text_update(assigned_cpus_bitmap,
                                      phdr.p_vaddr, memory_size);
            }

            /*
             * The second mapping, If you don't need write permission, remove it
             */
            if ((map_attr & MX_VMM_ATTR_W) == 0) {
                ret = _mx_vmm_context_map(current_vmm_context,
                                          phdr.p_vaddr,
                                          phys_addr,
                                          memory_size,
                                          map_attr,
                                          assigned_cpus_bitmap);
                if (ret != NO_ERROR) {
                    break;
                }
            }

            /*
             * Record this segment information
             */
            elf_segment->file_offset  = phdr.p_offset;
            elf_segment->file_size    = phdr.p_filesz;
            elf_segment->virt_addr    = phdr.p_vaddr; /* When creating a process, we check whether the entry point is in the text segment */
            elf_segment->memory_size  = memory_size;
            elf_segment->phys_addr    = phys_addr;
            elf_segment->map_attr     = map_attr;

            elf_segment_mend = elf_segment->virt_addr + memory_size;

            mx_space_init(&elf_segment->load_space, elf_segment->virt_addr, memory_size);

            if (persistent_data_size > 0) {
                if ((persistent_data_addr >= elf_segment->virt_addr) && (persistent_data_addr < elf_segment_mend)) {
                    ret = __mx_space_reserved(&elf_segment->load_space, persistent_data_addr, persistent_data_size);
                    if (ret != NO_ERROR) {
                        break;
                    }
                }
            }

            if (persistent_bss_size > 0) {
                if ((persistent_bss_addr >= elf_segment->virt_addr) && (persistent_bss_addr < elf_segment_mend)) {
                    ret = __mx_space_reserved(&elf_segment->load_space, persistent_bss_addr, persistent_bss_size);
                    if (ret != NO_ERROR) {
                        break;
                    }
                }
            }

            elf_segment++;
        }
    }

    if (ret == NO_ERROR) {
#if MX_ARCH_ELF_MACHINE == EM_PPC64
        if (ehdr->e_flags & EF_PPC64_ELFV1_ABI) {
            /*
             * For the 64-bit PowerPC ELF V1 ABI, e_entry is a function
             * descriptor pointer with the first double word being the
             * address of the entry point of the function.
             */
            partition->entry_addr = *(MX_VIRT_ADDR_TYPE *)ehdr.e_entry;
        } else
#endif
        {
            /*
             * Record this partition entry point
             */
            current_partition->entry_addr = (MX_VIRT_ADDR_TYPE)ehdr.e_entry;
        }
    }

    return ret;
}

_mx_partition_reload_elf_file()

  • 描述 分区重启时镜像重加载

  • 函数原型

extern RETURN_CODE_TYPE _mx_partition_reload_elf_file (
        /*in */ MX_PARTITION_TYPE       *current_partition);
  • 参数
输入/输出参数描述
[in]current_partition要加载镜像的分区控制块指针
  • 返回值 Matrix653 内核错误码

  • 注意事项

  • 示例

/*********************************************************************************************************
** Function name:           _mx_partition_reload_elf_file
**
** Descriptions:            Partition reload elf file
**
** Input parameters:        partition       Pointer to partition
**
** Output parameters:       NONE
**
** Return value:            ERROR CODE
*********************************************************************************************************/
RETURN_CODE_TYPE _mx_partition_reload_elf_file (
        /*in */ MX_PARTITION_TYPE       *current_partition)
{
    const MX_PARTITION_CONFIG_TYPE  *config = current_partition->config;
    const char                      *file_name = config->file_name;
    MX_CPU_BITMAP_TYPE              assigned_cpus_bitmap = config->assigned_cpus_bitmap;
    MX_ELF_SEGMENT_TYPE             *elf_segment = current_partition->elf_segments;
    MX_UINT32                       nb_elf_segments = current_partition->nb_elf_segments;
    MX_VMM_CONTEXT_TYPE             *current_vmm_context = &current_partition->vmm_context;
    MX_VIRT_ADDR_TYPE               elf_segment_fend;
    MX_UINT32                       i;
    RETURN_CODE_TYPE                ret = NO_ERROR;

    for (i = 0; i < nb_elf_segments; i++) {
        if (elf_segment->map_attr & MX_VMM_ATTR_X) {
            if (current_partition->mode == WARM_START) {
                // In this mode, the partition's initialization phase is in progress.
                // Preemption is locked with LOCK_LEVEL > 0 (main process implicitly
                // has preemption locked and implicitly owns the preemption lock mutex)
                // and the associated application is executing its respective initialization
                // code. This mode is similar to the COLD_START, but the initial
                // environment (the hardware context in which the partition starts) may
                // be different (e.g., no need for copying code from Non Volatile Memory
                // to RAM).
                elf_segment++;
                continue;
            }
        }

        /*
         * The first mapping, Need write permission, because we need to copy data
         */
        if ((elf_segment->map_attr & MX_VMM_ATTR_W) == 0) {
            ret = _mx_vmm_context_map(current_vmm_context,
                                      elf_segment->virt_addr,
                                      elf_segment->phys_addr,
                                      elf_segment->memory_size,
                                      MX_VMM_ATTR_W | elf_segment->map_attr,
                                      assigned_cpus_bitmap);
            if (ret != NO_ERROR) {
                break;
            }
        }

        elf_segment_fend = elf_segment->virt_addr + elf_segment->file_size;

        if (current_partition->mode == WARM_START) {
            MX_LIST_HEAD_TYPE   *itervar;
            MX_SPACE_NODE_TYPE  *node;
            MX_VIRT_ADDR_TYPE   node_end;
            MX_VIRT_SIZE_TYPE   read_size;
            MX_VIRT_SIZE_TYPE   zero_size;

            mx_list_for_each(itervar, &elf_segment->load_space.sub_space_list) {
                node = MX_CONTAINER_OF(itervar, MX_SPACE_NODE_TYPE, node);
                if (node->size > 0) {
                    node_end = node->address + node->size;

                    if (node->address < elf_segment_fend) {
                        if (node_end >= elf_segment_fend) {
                            zero_size = node_end - elf_segment_fend;
                            read_size = node->size - zero_size;
                        } else {
                            read_size = node->size;
                            zero_size = 0;
                        }
                        /*
                         * Loading segment data
                         */
                        ret = _mx_romfs_read_file(file_name,
                                                  elf_segment->file_offset + (node->address - elf_segment->virt_addr),
                                                  (void *)(MX_VIRT_ADDR_TYPE)node->address, read_size);
                        if (ret != NO_ERROR) {
                            break;
                        }
                    } else {
                        zero_size = node->size;
                        read_size = 0;
                    }

                    if (zero_size > 0) {
                        mx_bzero((void *)(MX_VIRT_ADDR_TYPE)(node->address + read_size), zero_size);
                    }
                }
            }

            if (ret != NO_ERROR) {
                break;
            }

        } else {
            if (elf_segment->file_size > 0) {
                ret = _mx_romfs_read_file(file_name,
                                          elf_segment->file_offset,
                                          (void *)elf_segment->virt_addr, elf_segment->file_size);
                if (ret != NO_ERROR) {
                    break;
                }
            }

            if (elf_segment->file_size < elf_segment->memory_size) {
                mx_bzero((void *)elf_segment_fend,
                         elf_segment->memory_size - elf_segment->file_size);
            }
        }

        if (elf_segment->map_attr & MX_VMM_ATTR_X) {
            /*
             * Update text segment icache
             */
            mx_cache_text_update(assigned_cpus_bitmap,
                                 elf_segment->virt_addr, elf_segment->memory_size);
        }

        /*
         * The second mapping, If you don't need write permission, remove it
         */
        if ((elf_segment->map_attr & MX_VMM_ATTR_W) == 0) {
            ret = _mx_vmm_context_map(current_vmm_context,
                                      elf_segment->virt_addr,
                                      elf_segment->phys_addr,
                                      elf_segment->memory_size,
                                      elf_segment->map_attr,
                                      assigned_cpus_bitmap);
            if (ret != NO_ERROR) {
                break;
            }
        }

        elf_segment++;
    }

    return ret;
}
文档内容是否对您有所帮助?
有帮助
没帮助