setup_machine_tags()

arch 번호로 태그테이블에서 machine을 검색하여 machine_desc 구조체 포인터를 찾고 ATAG를 디바이스 트리 구조로 변경한다.

setup_machine_tags

 

setup_machine_tags()

  • for_each_machine_desc()
    • __arch_info_begin ~ __arch_info_end 영역에 위치한 machine_desc 구조체 배열에서 머신 번호가 같은 경우를 찾는다.
    • machine_desc 구조체 배열은 .arch.info.init 섹션에 위치한다.
  • 만일 machine을 검색하여 찾지 못한 경우 machine table을 덤프하고 정지한다.
  • CONFIG_DEPRECATED_PARAM_STRUCT
    • ATAG 사용 하기 전에는 PARAM_STRUCT를 사용했다.
    •  convert_to_tag_list()
      • 태그의 처음이 ATAG_CORE가 아니면 PARAM_STRUCT 방식이라고 판단하여 ATAG 구조로 변환한다.
  • 처음 태그가 ATAG_CORE가 아닌 경우 “Warning: Neither atags nor dtb found” 경고 메시지를  출력하고 default 태그 구조체를 사용한다.
  • fixup 콜백함수가 null이 아닌 경우 fixup 콜백 함수를 수행한다.
    • 펌웨어에 문제가 있는 경우를 패치하기 위한 함수가 존재하는 경우 호출
    • 예) mach-msm/board-msm7x30.c – msm7x30_fixup() 참고
  • 태그가 ATAG_CORE 인 경우
    • 물리 메모리 사이즈가 이미 존재하는 경우 태그 정보를 무시하기 위해 squash_mem_tags()를 호출하여 ATAG_MEM을 ATAG_NONE으로 변경한다.
      • memblock_phys_mem_size = memblock.memory.total_size
    • save_atags()
      • 전역 변수 atags_copy 문자열 배열에 태그를 저장
    • parse_tags()
      • __tagtable_begin 부터 __tagtable_end 위치에 존재하는 태그 테이블에서 하나 씩 비교하여 동일한 태그인 경우 해당 태그의 parse 루틴을 호출한 후 리턴한다.
        • 태그 테이블은 .taglist.init 섹션에 위치한다.
      • parsing이 실패하면 “Ignoring unrecognised tag”라고 경고 출력한다.
      • 각 태그에 대한 파싱 함수 목록
        • ATAG_CORE: parse_tag_core()
        • ATAG_MEM: parse_tag_mem32()
        • ATAG_CMDLINE: parse_tag_cmdline
        • ATAG_INITRD: parse_tag_initrd()
        • ATAG_INITRD2, parse_tag_initrd2()
        • ATAG_VIDEOTEXT: parse_tag_videotext()
        • ATAG_RAMDIST: parse_tag_ramdisk()
        • ATAG_SERAIL: parse_tag_serialnr()
        • ATAG_REVISION: parse_tag_revision()
  • 마지막으로 전역 변수 boot_command_line에 default_cmd_line 값을 대입한다.
    • default_cmd_line은 컴파일 시 초기 설정된 값이 있고 커널 파라메터 설정에 따라 parse_tag_cmdline()을 수행하고 난 후 변경될 수 있다.
const struct machine_desc * __init
setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr)
{
        struct tag *tags = (struct tag *)&default_tags;
        const struct machine_desc *mdesc = NULL, *p;
        char *from = default_command_line;

        default_tags.mem.start = PHYS_OFFSET;

        /*
         * locate machine in the list of supported machines.
         */
        for_each_machine_desc(p)
                if (machine_nr == p->nr) {
                        pr_info("Machine: %s\n", p->name);
                        mdesc = p;
                        break;
                }

        if (!mdesc) {
                early_print("\nError: unrecognized/unsupported machine ID"
                            " (r1 = 0x%08x).\n\n", machine_nr);
                dump_machine_table(); /* does not return */
        }

        if (__atags_pointer)
                tags = phys_to_virt(__atags_pointer);
        else if (mdesc->atag_offset)
                tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);

#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
        /*
         * If we have the old style parameters, convert them to
         * a tag list.
         */
        if (tags->hdr.tag != ATAG_CORE)
                convert_to_tag_list(tags);
#endif
        if (tags->hdr.tag != ATAG_CORE) {
                early_print("Warning: Neither atags nor dtb found\n");
                tags = (struct tag *)&default_tags;
        }

        if (mdesc->fixup)
                mdesc->fixup(tags, &from);

        if (tags->hdr.tag == ATAG_CORE) {
                if (memblock_phys_mem_size())
                        squash_mem_tags(tags);
                save_atags(tags);
                parse_tags(tags);
        }

        /* parse_early_param needs a boot_command_line */
        strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);

        return mdesc;
}

 

ATAG용 Machine 정보

MACHINE_START()

  • ATAG용 machine_desc 구조체 선언 매크로
    • ATAG용에서는 nr로 검색하므로 nr 값이 중요하다.
      • arm용 머신 번호는 arch/arm/tools/mach-types 화일을 참고한다.
    • DTB용은 DT_MACHINE_START() 매크로를 사용하고 name으로 검색한다.
  • __used를 사용하여 이 객체가 참조되지 않아도 컴파일러가 제거하지 않도록 한다.
  • MACHINE_END()와 쌍으로 사용한다.

arch/arm/include/asm/mach/arch.h

/*
 * Set of macros to define architecture features.  This is built into
 * a table by the linker.
 */
#define MACHINE_START(_type,_name)                      \
static const struct machine_desc __mach_desc_##_type    \
 __used                                                 \
 __attribute__((__section__(".arch.info.init"))) = {    \
        .nr             = MACH_TYPE_##_type,            \
        .name           = _name,

#define MACHINE_END                             \
};

 

machine_desc 구조체

arch/arm/include/asm/mach/arch.h

struct machine_desc {
        unsigned int            nr;             /* architecture number  */
        const char              *name;          /* architecture name    */
        unsigned long           atag_offset;    /* tagged list (relative) */
        const char *const       *dt_compat;     /* array of device tree
                                                 * 'compatible' strings */

        unsigned int            nr_irqs;        /* number of IRQs */

#ifdef CONFIG_ZONE_DMA
        phys_addr_t             dma_zone_size;  /* size of DMA-able area */
#endif

        unsigned int            video_start;    /* start of video RAM   */
        unsigned int            video_end;      /* end of video RAM     */

        unsigned char           reserve_lp0 :1; /* never has lp0        */
        unsigned char           reserve_lp1 :1; /* never has lp1        */
        unsigned char           reserve_lp2 :1; /* never has lp2        */
        enum reboot_mode        reboot_mode;    /* default restart mode */
        unsigned                l2c_aux_val;    /* L2 cache aux value   */
        unsigned                l2c_aux_mask;   /* L2 cache aux mask    */
        void                    (*l2c_write_sec)(unsigned long, unsigned);
        struct smp_operations   *smp;           /* SMP operations       */
        bool                    (*smp_init)(void);
        void                    (*fixup)(struct tag *, char **);
        void                    (*dt_fixup)(void);
        void                    (*init_meminfo)(void);
        void                    (*reserve)(void);/* reserve mem blocks  */
        void                    (*map_io)(void);/* IO mapping function  */
        void                    (*init_early)(void);
        void                    (*init_irq)(void);
        void                    (*init_time)(void);
        void                    (*init_machine)(void);
        void                    (*init_late)(void);
#ifdef CONFIG_MULTI_IRQ_HANDLER
        void                    (*handle_irq)(struct pt_regs *);
#endif
        void                    (*restart)(enum reboot_mode, const char *);
};

 

라즈베리파이 1 & 2 MACHINE 구조체 선언

arch/arm/mach-bcm2709/bcm2709.c

static const char * const bcm2709_compat[] = {
        "brcm,bcm2709",
        "brcm,bcm2708", /* Could use bcm2708 in a pinch */
        NULL
};

MACHINE_START(BCM2709, "BCM2709")
    /* Maintainer: Broadcom Europe Ltd. */
#ifdef CONFIG_SMP
        .smp            = smp_ops(bcm2709_smp_ops),
#endif
        .map_io = bcm2709_map_io,
        .init_irq = bcm2709_init_irq,
        .init_time = bcm2709_timer_init,
        .init_machine = bcm2709_init,
        .init_early = bcm2709_init_early,
        .reserve = board_reserve,
        .restart        = bcm2709_restart,
        .dt_compat = bcm2709_compat,
MACHINE_END

MACHINE_START(BCM2708, "BCM2709")
    /* Maintainer: Broadcom Europe Ltd. */
#ifdef CONFIG_SMP
        .smp            = smp_ops(bcm2709_smp_ops),
#endif
        .map_io = bcm2709_map_io,
        .init_irq = bcm2709_init_irq,
        .init_time = bcm2709_timer_init,
        .init_machine = bcm2709_init,
        .init_early = bcm2709_init_early,
        .reserve = board_reserve,
        .restart        = bcm2709_restart,
        .dt_compat = bcm2709_compat,
MACHINE_END

 

ATAG Parsing

tag 및 tagtable 구조체

arch/arm/include/uapi/asm/setup.h

struct tag {
        struct tag_header hdr;
        union {
                struct tag_core         core;
                struct tag_mem32        mem;
                struct tag_videotext    videotext;
                struct tag_ramdisk      ramdisk;
                struct tag_initrd       initrd;
                struct tag_serialnr     serialnr;
                struct tag_revision     revision;
                struct tag_videolfb     videolfb;
                struct tag_cmdline      cmdline;

                /*
                 * Acorn specific
                 */
                struct tag_acorn        acorn;

                /*
                 * DC21285 specific
                 */
                struct tag_memclk       memclk;
        } u;
};

struct tagtable {
        __u32 tag;
        int (*parse)(const struct tag *);
};

 

 

parse_tag()

  •  태그 영역에 저장된 태그들 중 아키텍처 번호가 같은 태그들에 연결된 parse 콜백 함수를  호출한다.
/*
 * Scan the tag table for this tag, and call its parse function.
 * The tag table is built by the linker from all the __tagtable
 * declarations.
 */
static int __init parse_tag(const struct tag *tag)
{
        extern struct tagtable __tagtable_begin, __tagtable_end;
        struct tagtable *t;

        for (t = &__tagtable_begin; t < &__tagtable_end; t++)
                if (tag->hdr.tag == t->tag) {
                        t->parse(tag);
                        break;
                }

        return t < &__tagtable_end;
}

 

 

parse_tag_core()

  • 전역 변수 root_mountflags에 루트 마운트 플래그 속성에서 MS_RDONLY를 제거하고 저장
  • 전역 변수 ROOT_DEV에 디바이스 번호를 저장한다.

arch/arm/kernel/atags_parse.c

static int __init parse_tag_core(const struct tag *tag)
{
        if (tag->hdr.size > 2) {
                if ((tag->u.core.flags & 1) == 0)
                        root_mountflags &= ~MS_RDONLY;
                ROOT_DEV = old_decode_dev(tag->u.core.rootdev);
        }
        return 0;
}

__tagtable(ATAG_CORE, parse_tag_core);

 

parse_tag_mem32()

  • arm_add_memory() 함수를 사용하여 메모리 영역을 추가한다.
static int __init parse_tag_mem32(const struct tag *tag)
{
        return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
}

__tagtable(ATAG_MEM, parse_tag_mem32);

 

parse_tag_videotext()

  • screen_info 구조체에 파라메터 값들을 저장한다.
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
static int __init parse_tag_videotext(const struct tag *tag)
{
        screen_info.orig_x            = tag->u.videotext.x;
        screen_info.orig_y            = tag->u.videotext.y;
        screen_info.orig_video_page   = tag->u.videotext.video_page;
        screen_info.orig_video_mode   = tag->u.videotext.video_mode;
        screen_info.orig_video_cols   = tag->u.videotext.video_cols;
        screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx;
        screen_info.orig_video_lines  = tag->u.videotext.video_lines;
        screen_info.orig_video_isVGA  = tag->u.videotext.video_isvga;
        screen_info.orig_video_points = tag->u.videotext.video_points;
        return 0;
}

__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
#endif

 

parse_tag_ramdisk()

  • 전역 변수 rd_image_start에 램디스크 시작 주소를 저장한다.
  • 전역 변수 rd_doload와 rd_prompt에 플래그 상태를 저장한다.
  • 전역 변수 rd_size에 램디스크 사이즈를 저장한다.
#ifdef CONFIG_BLK_DEV_RAM
static int __init parse_tag_ramdisk(const struct tag *tag)
{
        extern int rd_size, rd_image_start, rd_prompt, rd_doload;

        rd_image_start = tag->u.ramdisk.start;
        rd_doload = (tag->u.ramdisk.flags & 1) == 0;
        rd_prompt = (tag->u.ramdisk.flags & 2) == 0;

        if (tag->u.ramdisk.size)
                rd_size = tag->u.ramdisk.size;

        return 0;
}

__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
#endif

 

parse_tag_serialnr()

  • 전역 변수 system_serial_low와 system_serial_high에 시리얼 low 값과 high 값을 저장한다.
static int __init parse_tag_serialnr(const struct tag *tag)
{
        system_serial_low = tag->u.serialnr.low;
        system_serial_high = tag->u.serialnr.high;
        return 0;
}

__tagtable(ATAG_SERIAL, parse_tag_serialnr);

 

parse_tag_revision()

  •  전역 변수 system_rev에 리비전 정보를 저장한다.
static int __init parse_tag_revision(const struct tag *tag)
{
        system_rev = tag->u.revision.rev;
        return 0;
}

__tagtable(ATAG_REVISION, parse_tag_revision);

 

parse_tag_cmdline()

  • 다음 3가지 case에 대해 수행한다.
    • CONFIG_CMDLINE_EXTEND
      • default_command_line에 ATAG가 전달한 cmdline을 추가한다.
    • CONFIG_CMDLINE_FORCE
      • ATAG가 전달한 cmdline을 무시하고 default_command_line을 사용한다.
    • cmdline 관련 옵션이 없는 경우
      • default_command_line에 ATAG가 전달한 cmdline을 겹쳐 쓴다.
static int __init parse_tag_cmdline(const struct tag *tag)
{
#if defined(CONFIG_CMDLINE_EXTEND)
        strlcat(default_command_line, " ", COMMAND_LINE_SIZE);
        strlcat(default_command_line, tag->u.cmdline.cmdline,
                COMMAND_LINE_SIZE);
#elif defined(CONFIG_CMDLINE_FORCE)
        pr_warn("Ignoring tag cmdline (using the default kernel command line)\n");
#else
        strlcpy(default_command_line, tag->u.cmdline.cmdline,
                COMMAND_LINE_SIZE);
#endif
        return 0;
}

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);

 

기타 함수

arm_add_memory()

arch/arm/kernel/setup.c

int __init arm_add_memory(u64 start, u64 size)
{
        u64 aligned_start;

        /*   
         * Ensure that start/size are aligned to a page boundary.
         * Size is rounded down, start is rounded up.
         */
        aligned_start = PAGE_ALIGN(start);
        if (aligned_start > start + size)
                size = 0; 
        else 
                size -= aligned_start - start;

#ifndef CONFIG_ARCH_PHYS_ADDR_T_64BIT
        if (aligned_start > ULONG_MAX) {
                pr_crit("Ignoring memory at 0x%08llx outside 32-bit physical address space\n",
                        (long long)start);
                return -EINVAL;
        }

        if (aligned_start + size > ULONG_MAX) {
                pr_crit("Truncating memory at 0x%08llx to fit in 32-bit physical address space\n",
                        (long long)start);
                /*   
                 * To ensure bank->start + bank->size is representable in
                 * 32 bits, we use ULONG_MAX as the upper limit rather than 4GB.
                 * This means we lose a page after masking.
                 */
                size = ULONG_MAX - aligned_start;
        }    
#endif

        if (aligned_start < PHYS_OFFSET) {
                if (aligned_start + size <= PHYS_OFFSET) {
                        pr_info("Ignoring memory below PHYS_OFFSET: 0x%08llx-0x%08llx\n",
                                aligned_start, aligned_start + size);
                        return -EINVAL;
                }

                pr_info("Ignoring memory below PHYS_OFFSET: 0x%08llx-0x%08llx\n",
                        aligned_start, (u64)PHYS_OFFSET);

                size -= PHYS_OFFSET - aligned_start;
                aligned_start = PHYS_OFFSET;
        }

        start = aligned_start;
        size = size & ~(phys_addr_t)(PAGE_SIZE - 1);

        /*   
         * Check whether this memory region has non-zero size or
         * invalid node number.
         */
        if (size == 0)
                return -EINVAL;

        memblock_add(start, size);
        return 0;
}
  • aligned_start = PAGE_ALIGN(start);
    • start 주소에 대해 4K round up 한다.
  • if (aligned_start > start + size)
    • 4K round up 한 aligned_start 주소가 start + size를 초과하는 경우 size를 0으로 변경하고 그렇지 않은 경우 4K round up으로 인해 발생한 그 차이만큼 size에서 뺀다.
    • 결국 4K align 되어 남는 하위 메모리는 버리게 된다.
    • 예) arm_add_memory(0x1234_5678, 0x1000_0000)
      • 물리 메모리 주소 0x1234_5678 부터 256M 크기의 메모리를 추가하라는 요청
      • 수행 후 물리 메모리 주소 0x1234_5000 부터 (256M – 0x678) 크기의 메모리를 추가
  • CONFIG_ARCH_PHYS_ADDR_T_64BIT
    • LPAE 설정 시 사용된다.
  • if (aligned_start + size > ULONG_MAX) {
    • 추가할 메모리 영역이 32비트 주소의 끝을 초과하는 경우 size를 32비트 이내에 들어갈 수 있도록 조정한다.
    • 예) arm_add_memory(0xf000_0000, 0x2000_0000)
      • 물리 메모리 주소 0xf000_0000 부터 512M 크기의 메모리를 추가하라는 요청
      • 수행 후 물리 메모리 주소 0xf000_0000 부터 0x0fff_ffff 크기의 메모리를 추가
  • if (aligned_start < PHYS_OFFSET) {
    • 요청한 시작 주소가 물리 메모리 시작 주소보다 작은 경우
  • if (aligned_start + size <= PHYS_OFFSET) {
    • size 까지 합친 요청 영역이 물리 메모리 시작 주소보다 작아 범위를 아예 벗어난 경우 에러를 경고하고 함수를 리턴한다.
    • 요청한 구간이 물리 메모리 이하에서 시작하였다는 것을 경고 출력하고 물리 메모리 시작 주소 이하의 요청 메모리를 제거한 범위를 시작 주소와 사이즈를 재 조정한다.
    • 예) arm_add_memory(0x1F00_0000, 0x1000_0000) 이 때 PHYS_OFFSET=0x2000_0000
      • aligned_start = 0x2000_0000
      • size = 0x0f00_000
  • size = size & ~(phys_addr_t)(PAGE_SIZE – 1);
    • 사이즈 또한 align 되어 있지 않으면 round down 하여 버린다.
    • 예) arm_add_memory(0x2000_0000, 0x1234_5678)
      • size = 0x1234_5000
  •  if (size == 0)
    • 추가 할 사이즈가 0이면 함수를 빠져나간다.
  • memblock_add(start, size);
    • memory memblock 에 메모리 영역을 추가한다.

 

참고

댓글 남기기