2. Анализ процесса запуска ядра Linux

В последнем блоге объясняется структура кода и процесс компиляции ядра Linux . Этот блог в основном объясняет процесс запуска ядра, который полезен для портирования ядра. Чтобы понять процесс запуска ядра, вам необходимо знать назначение ядра. Основная цель некоторых сложных и громоздких операций перед запуском ядра состоит в том, чтобы позволить ядру работать. Какова цель работы ядра? Это должно быть для запуска приложений, поэтому основная цель ядра - монтировать корневую файловую систему, а затем запускать свои собственные программы . Поскольку ядро ​​Linux поддерживает различные платформы ЦП и множество отдельных плат, таких как u-boot , существуют функции инициализации, связанные с платами архитектуры и разработки . Процесс запуска ядра также можно разделить на две части: процесс загрузки, связанный с платой архитектуры / разработки, и последующий общий процесс запуска. На следующем рисунке показан процесс запуска ядра Linux vmlinux на процессоре архитектуры ARM. Здесь используется ядро ​​vmlinux, поскольку ядро ​​других форматов (Image, uImage и т. Д.) Получит vmlinux после некоторых специальных операций, а затем запустит vmlinux.

На первом этапе обычно используется программирование на ассемблере, поскольку ядро ​​поддерживает несколько архитектур ЦП и различных плат разработки, необходимо проверить, поддерживает ли ядро ​​такие архитектуры ЦП и платы разработки . В конце загрузки BootLoader машинный код будет передан ядру. После того, как тест пройден, поскольку виртуальный адрес используется при связывании ядра, вам нужно установить таблицу страниц и включить MMU- > подготовить к вызову среды C (start_kernel) следующего этапа, поэтому вам нужно скопировать сегмент данных, очистить сегмент BBS, установить стек Вызвать функцию start_kernel (то есть виртуальный адрес, используемый ядром) .

Поток кода первого этапа анализируется ниже (arch / arm / kernel / head.S)

Выше приведено содержимое в файле arch / $ (ARCH) /head.S, которое в основном используется для определения того, поддерживает ли ядро ​​архитектуру ЦП и одиночную плату (единственная плата - это первый параметр, передаваемый BootLoader при запуске ядра (сохранить в R1). )), Создайте таблицу страниц первого уровня, включите MMU. Ниже приводится подробное объяснение.

Проверьте, поддерживает ли ядро ​​ЦП этой платы разработки. Идентификатор ЦП можно получить из C0 в сопроцессоре CP15:

Номер производителя (8 бит) ARM использует 0x41 Под-номер продукта (4 бита) Номер версии системы ARM (4 бита) Основной номер продукта (8 бит) Номер версии процессора (4 бита)

Вышеуказанная функция определена в head_common.S. Для первой строки в функции __lookup_processor_type необходимо получить физический адрес метки 3: на R3 (функция MMU в это время не включена, поэтому процессор использует физический адрес) Примечание: адрес, полученный инструкцией ADR, основан на регистре ПК Рассчитано . Вторая строка кода предназначена для сохранения значений __proc_info_begin __proc_info_end и. (Это адреса, используемые в сценарии соединения, которые являются виртуальными адресами) в r5-r7. Это должно вычислить отклонение между физическим адресом и виртуальным адресом, а последние два - для вычисления физических адресов __proc_info_begin и __proc_info_end. Метка 1: Что делается внутри, это прочитать информацию proc_info_list из __proc_info_begin в __proc_info_end ( прототип структуры proc_info_list определен в include / asm-arm / procinfo.h, что означает CPU, поддерживаемый ядром. Для CPU архитектуры ARM эти структуры Тело определяется в каталоге arch / arm / mm , например, proc_arm920.S, который представляет файл определения структуры и другую информацию proc_info_list ЦП архитектуры arm920. Различные структуры proc_info_list используются для поддержки ЦП в разных учреждениях, все они определены В разделе .proc_info_init. Эти структуры организованы вместе (__proc_info_begin является начальным адресом ), а затем сравниваются со значением CPUID (r9), считанным из процессора, чтобы определить, принадлежит ли оно одному и тому же процессору. Если оно совпадает, оно передается метке 2: Вернитесь на место, если оно не совпадает, продолжите чтение, а затем сопоставьте. Ни одно совпадение не было успешным до конца, и r5 был назначен 0 для завершения.примечание: ЦП разных плат разработки может отличаться. ЦП архитектуры ARM определен в каталоге arch / arm / mm /, который определяет соответствующую информацию ЦП, такую ​​как __arm920_proc_info (структура proc_info_list), определенная в файле proc_arm920.S. Поэтому, когда вы используете этот процессор, вам нужно включить этот файл. При настройке ядра вам нужно его настроить (меню конфигурации - System Type-> Поддержка процессора ARM920T). По той же причине стоит использовать другие чипы .

WhetherПроверьте, поддерживает ли ядро ​​ID машины этой платы разработки. Идентификатор машины передается, когда BootLoader запускает ядро ​​(хранится в R1). Функция __look_machine_type также похожа на __look_processor_type и также определена в head_common.S .

Для каждой поддерживаемой платы разработки в ядре определена структура machine_desc, которая определяет некоторые свойства и функции, относящиеся к плате разработки, такие как идентификатор типа машины, физический адрес начального ввода-вывода и функция инициализации прерывания . Для файла платы определения ARM архитектуры , хранящийся в арке / руки / мах-хую / xxxx.c в , который содержит некоторую информацию , связанную с платой разработки, machine_desc прототип определяется в организме включает / ASM-руки / маш / В arch.h все структуры machine_desc находятся в разделе .arch.info.init. Начальный адрес этого раздела - __arch_info_begin, а конечный адрес - __arch_info_end. Чтобы использовать соответствующие платы разработки, вам также необходимо настроить ядро. Вам также необходимо включить соответствующие файлы в ядро ​​( например, arch / arm / math-s3c2410 / mach-smdk2410.c ). Меню конфигурации находится в системном типе -> XXX machine-> xx дюйм

        Функция __lookup_machine_type похожа на описанную выше функцию __lookup_processor_type. Виртуальный начальный адрес и конечный адрес раздела .arch_info_init получаются и преобразуются в физические адреса. Затем прочитайте идентификатор машины в machine_desc из начального адреса сегмента и сравните его с R1. Если он совпадает, перейдите к метке 2 и вернитесь. Если они не совпадают, продолжайте читать информацию внутри. Если они не совпадают, назначение R5 = 0 заканчивается.

③ Создайте таблицу страниц, чтобы включить MMU, инициализировать стек, очистить сегмент BSS, скопировать сегмент данных и т. Д. И вызвать start_kernel (в head-common.S), чтобы перейти на второй этап ядра.

После вызова __enable_mmu для включения функции MMU, функция, наконец, выполняет материал в R13 (то есть, __switch_data, сохраненный выше). __Switch_data определен в head_common.S, который определяет много функций.

В конце вышеупомянутой функции вызовите start_kernel в main.c, чтобы перейти ко второму этапу.

Второй этап запуска ядра:

Существует два типа параметров, которые u-boot передает ядру: один - это список тегов заранее существующего адреса, а другой - идентификатор компьютера, указанный в регистре R1 при вызове ядра. Идентификатор машины будет использоваться во время фазы загрузки (при определении идентификатора машины в приведенном выше), список тегов будет первоначально обрабатываться в функции setup_arch.

Функция функции set_arch: сделать некоторые настройки, относящиеся к процессору-> сделать некоторые настройки, относящиеся к плате разработки-> список тегов процесса-> параметры командной строки процесса-> повторно инициализировать таблицу страниц paging_init (& meminfo, mdesc).

В таблице страниц инициализации paging_init вторым параметром является структура machine_desc, возвращенная предыдущей функцией lookup_machine_type, которая определяется в функции arch / arm / mach-xxx / mach-smdkxxx.c. Определение платы разработки s3c2440 заключается в следующем. Эта структура часто важна, здесь определяется инициализация на уровне платы.

Map_io в paging_init-> devicemap_init-> mdesc-> map_io () является членом указателя функции .map_io в этой структуре . Необходимо изменить следующие часы, изменить 16934400 до 12 МГц.

Функция инициализации консоли:

Вызовите каждую функцию, определенную между __con_initcall_start и __con_initcall_end. Эти функции указываются с помощью макроса console_initcall (). Этот макрос используется для определения функции с атрибутом .con_initcall_init.

После серии инициализаций start_kernel вызовет rest_init (), а в функции rest_init () будет создан поток ядра kernel_init. Главным образом, чтобы смонтировать файловую систему и запустить приложение, смонтируйте корневую файловую систему в prepare_namespace () .

После монтирования файловой системы запустите init_post (), который в основном открывает файл устройства / dev / console, а затем вызывает run_init_process () для запуска программы init.

Ядро запустило первый процесс init, init запустит различные приложения. Затем вы можете начать запуск контента в пространстве пользователя.

Опубликовано 35 оригинальных статей · Нравится1 · Посещений 1870 года

рекомендация

отblog.csdn.net/lzj_linux188/article/details/102695522