Porting Linux to a new processor architecture, part 2: The early code

发布于:2025-05-13 ⋅ 阅读:(17) ⋅ 点赞:(0)

In part 1 of this series, we laid the groundwork for porting Linux to a new processor architecture by explaining the (non-code-related) preliminary steps. This article continues from there to delve into the boot code. This includes what code needs to be written in order to get from the early assembly boot code to the creation of the first kernel thread.
在本系列的第一部分中,我们为将 Linux 移植到新处理器架构打下了基础,解释了与代码无关的初始步骤。本文将从此继续,深入探讨启动代码的实现过程,包括从最初的汇编启动代码过渡到创建第一个内核线程所需编写的代码。

The header files
头文件

As briefly mentioned in the previous article, the arch header files (in my case, located under linux/arch/tsar/include/) constitute the two interfaces between the architecture-specific and architecture-independent code required by Linux.
如前文简要提到的,架构相关头文件(在我的案例中位于 linux/arch/tsar/include/ 下)构成了 Linux 中架构相关代码与架构无关代码之间的两个接口。

The first portion of these headers (subdirectory asm/) is part of the kernel interface and is used internally by the kernel source code. The second portion (uapi/asm/) is part of the user interface and is meant to be exported to user space—even though the various standard C libraries tend to reimplement the headers instead of including the exported ones. These interfaces are not completely airtight, as many of the asm headers are used by user space.
这些头文件的第一部分(位于 asm/ 子目录中)属于内核接口,由内核源码内部使用。第二部分(位于 uapi/asm/ 中)则属于用户接口,旨在导出给用户空间使用——尽管各种标准 C 库往往会自行实现这些头文件,而不是直接包含导出的版本。这些接口并不完全隔离,因为许多 asm 头文件也会被用户空间使用。

Both interfaces are typically more than a hundred header files altogether, which is why headers represent one of the biggest tasks in porting Linux to a new processor architecture. Fortunately, over the past few years, developers noticed that many processor architectures were sharing similar code (because they often exhibited the same behaviors), so the majority of this code has been aggregated into a generic layer of header files (in linux/include/asm-generic/ and linux/include/uapi/asm-generic/).
这两个接口总共往往涉及上百个头文件,这也是将 Linux 移植到新处理器架构时头文件工作量如此巨大的原因。幸运的是,过去几年中,开发者们注意到许多处理器架构共享类似的代码(因为它们通常具有相似的行为),因此大多数此类代码已被汇总到通用的头文件层中(位于 linux/include/asm-generic/linux/include/uapi/asm-generic/)。

The real benefit is that it is possible to refer to these generic header files, instead of providing custom versions, by simply writing appropriate Kbuild files. For example, the few first lines of a typical include/asm/Kbuild looks like:
真正的好处是,只需通过编写合适的 Kbuild 文件,就可以引用这些通用头文件,而无需提供定制版本。例如,典型的 include/asm/Kbuild 文件开头几行可能如下所示:

    generic-y += atomic.h
    generic-y += barrier.h
    generic-y += bitops.h
    ...

When porting Linux, I'm afraid there is no other choice than to make a list of all of the possible headers and examine them one by one in order to decide whether the generic version can be used or if it requires customization. Such a list can be created from the generic headers already provided by Linux as well as the customized ones implemented by other architectures.
在移植 Linux 时,恐怕别无选择,只能列出所有可能涉及的头文件,并逐个检查,以决定能否使用通用版本,或是否需要进行定制。这样的列表可以从 Linux 已提供的通用头文件以及其他架构实现的定制版本中整理出来。

Basically, a specific version must be developed for all of the headers that are related to the details of an architecture, as defined by the hardware or by the software through the ABI: cache (asm/cache.h) and TLB management (asm/tlbflush.h), the ELF format (asm/elf.h), interrupt enabling/disabling (asm/irqflags.h), page table management (asm/page.h, asm/pgalloc.h, asm/pgtable.h), context switching (asm/mmu_context.h, asm/ptrace.h), byte ordering (uapi/asm/byteorder.h), and so on.
基本上,所有涉及架构细节(由硬件定义或由 ABI 所规范的软件接口)的头文件都需要开发专门版本,例如:缓存管理(asm/cache.h)和 TLB 管理(asm/tlbflush.h)、ELF 格式(asm/elf.h)、中断开关控制(asm/irqflags.h)、页表管理(asm/page.hasm/pgalloc.hasm/pgtable.h)、上下文切换(asm/mmu_context.hasm/ptrace.h)、字节序(uapi/asm/byteorder.h)等等。

Boot sequence
启动流程

As explained in part 1, figuring out the boot sequence helps to understand the minimal set of architecture-specific functions that must be implemented—and in which order.
如第一部分所述,弄清启动流程有助于确定必须实现的最小架构相关函数集合——以及它们的调用顺序。

The boot sequence always starts with a function that must be written manually, usually in assembly code (in my case, this function is called kernel_entry() and is located in arch/tsar/kernel/head.S). It is defined as the main entry point of the kernel image, which indicates to the bootloader where to jump after loading the image in memory.
启动流程总是从一个必须手动编写的函数开始,通常是用汇编实现的(在我的案例中,这个函数名为 kernel_entry(),位于 arch/tsar/kernel/head.S)。它被定义为内核镜像的主入口点&#x


网站公告

今日签到

点亮在社区的每一天
去签到