本文共 3693 字,大约阅读时间需要 12 分钟。
驱动篇:内存与 I/O 访问(一)
1.CPU 与内存和I/O
Linux 系统中提供了复杂的内存管理功能,所以内存的概念在 Linux 系统中变得相对复杂,出现了常规内存、高端内存、虚拟地址、逻辑地址、总线地址、物理地址、I/O 内存、设备内存、预留内存等概念在 X86 处理器中存在着I/O 空间的概念,I/O 空间是相对于内存空间而言的,它通过特定的指令in、out 来访问。端口号标识了外设的寄存器地址。Intel 语法的in、out指令格式如下:
IN 累加器, { 端口号│DX}OUT { 端口号│DX},累加器
目前,大多数嵌入式微控制器如 ARM、PowerPC 等中并不提供 I/O 空间,而仅存在内存空间。内存空间可以直接通过地址、指针来访问,程序和程序运行中使用的变量和其他数据都存在于内存空间中。
内存地址可以直接由 C 语言指针操作,例如在 186 处理器中执行如下代码:unsigned char *p = (unsigned char *)0xF000FF00;*p=11;
而在 ARM、PowerPC 等未采用段地址的处理器中,p 指向的内存空间就是0xF000FF00,而*p = 11 就是在该地址写入 11。
即便是在 X86 处理器中,虽然提供了 I/O 空间,如果由我们自己设计电路板,外设仍然可以只挂接在内存空间。此时,CPU 可以像访问一个内存单元那样访问外设 I/O 端口,而不需要设立专门的 I/O 指令。因此,内存空间是必须的,而 I/O 空间是可选的。
2.内存管理单元 MMU 高性能处理器一般会提供一个内存管理单元(MMU),该单元辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和 Cache 缓存控制等硬件支持。操作系统内核借助 MMU,可以让用户感觉到好像程序可以使用非常大的内存空间,从而使得编程人员在写程序时不用考虑计算机中的物理内存的实际容量。为了理解基本的 MMU 操作原理,需先明晰几个概念。
l TLB:Translation Lookaside Buffer,即转换旁路缓存,TLB 是 MMU 的核
心部件,它缓存少量的虚拟地址与物理地址的转换关系,是转换表的 Cache,因此也经常被称为“快表”l TTW:Translation Table walk,即转换表漫游,当 TLB 中没有缓冲对应的地
址转换关系时,需要通过对内存中转换表(大多数处理器的转换表为多级页 表,如图所示)的访问来获得虚拟地址和物理地址的对应关系。TTW 成功后,结果应写入 TLB图 11.3 给出了一个典型的 ARM 处理器访问内存的过程,其他处理器也执行类似过程。当 ARM 要访问存储器时,MMU 先查找 TLB 中的虚拟地址表。如果 ARM 的结构支持分开的数据 TLB(DTLB)和指令 TLB(ITLB),则除取指令使用 ITLB 外,其他的都使用 DTLB。ARM 处理器的 MMU 如图 11.3 所示。
若 TLB 中没有虚拟地址的入口, 则转换表遍历硬件从存放于主存储器中的转换表中获取地址转换信息和访问权限(即执行 TTW),同时将这些信息放入 TLB,它或者被放在一个没有使用的入口或者替换一个已经存在的入口。之后,在 TLB 条目中控制信息的控制下,当访问权限允许时,对真实物理地址的访问将在 Cache 或者在内存中发生,如图 11.4 所示。
ARM 中的 TLB 条目中的控制信息用于控制对对应地址的访问权限以及 Cache 的操作。
l C(高速缓存)和 B(缓冲)位被用来控制对应地址的高速缓存和写缓冲, 并决定是否高速缓存。 l 访问权限和域位用来控制读写访问是否被允许。如果不允许,则 MMU 将向 ARM 处理器发送一个存储器异常,否则访问将被允许进行。 上述描述的 MMU 机制针对的虽然是 ARM 处理器, 但 PowerPC、 MIPS 等其他处理器也均有类似的操作。 MMU 具有虚拟地址和物理地址转换、内存访问权限保护等功能, 这将使得 Linux操作系统能单独为系统的每个用户进程分配独立的内存空间并保证用户空间不能访问内核空间的地址,为操作系统的虚拟内存管理模块提供硬件基础。 但是,MMU 并非对所有处理器都是必须的,例如常用的 SAMSUNG 基于ARM7TDMI 系列的 S3C44B0X 不附带 MMU,其上无法运行老版的 Linux,而只能运行改版的μcLinux,但是新版的 Linux 2.6 则支持不带 MMU 的处理器。在嵌入式系统中,仍存在大量无 MMU 的处理器,Linux 2.6 为了更广泛地应用于嵌入式系统,融合了μcLinux,以支持 MMU-less 系统,如 Dragonball、ColdFire、Hitachi H8/300 等。 在 S3C2410 的 vivi 这个 Bootloader 中,建立了一个 4GB 物理地址与虚拟地址一一映射的一级页表,我们来追踪一下其创建过程。vivi 的 main()函数首先会调用 mem_map_init()函数创建页表,其后调用 mmu_init()初始化并使能 MMU。vivi Bootloader 的 mem_map_init()函数void mem_map_init(void) { #ifdef CONFIG_S3C2410_NAND_BOOT /*CONFIG_S3C2410_NAND_BOOT = y ,在文件 include/autoconf.h 中定义 mem_map_nand_boot(); /* 最终调用 mem_mepping_linear, 建立页表 */*/#elsemem_map_nor(); #endif cache_clean_invalidate(); /* 清空 cache,使无效 cache */ tlb_invalidate(); /* 使无效快表 TLB */ }
mem_map_nand_boot()中会调用 mem_mapping_linear()进行页表创建工作
static inline void mem_mapping_linear(void){ unsigned long pageoffset, sectionNumber;putstr_hex("MMU table base address = 0x%", (unsignedlong)mmu_tlb_base);/* 4GB 虚拟地址映射到相同的物理地址,均不能缓存 */for (sectionNumber = 0; sectionNumber < 4096; sectionNumber++){ pageoffset = (sectionNumber << 20);*(mmu_tlb_base + (pageoffset >> 20)) = pageoffset | MMU_SECDESC; } /* 使 DRAM 的区域可缓存 */ /* SDRAM 物理地址 0x3000000-0x33ffffff, DRAM_BASE=0x30000000,DRAM_SIZE=64M*/ for (pageoffset = DRAM_BASE; pageoffset < (DRAM_BASE + DRAM_SIZE);pageoffset += SZ_1M) { //DPRINTK(3, "Make DRAM section cacheable: 0x%08lx\n",pageoffset);*(mmu_tlb_base + (pageoffset >> 20)) = pageoffset | MMU_SECDESC | MMU_CACHEABLE; } }
内核为 ARM920T 的 S3C2410 是一个 32 位的 CPU,它的地址空间为 4GB。共有4 种内存映射模式, 即 Fault ( 无映射)、 Coarse Page (粗页表)、 Section (段)、 Fine Page(细页表)。
上面代码使用了 ARM920T 内存映射的 Section 模式(实际可等同于页大小为 1MB 的情况),4GB 的虚拟空间被分成一个一个称为 Section 的单位。4GB 的虚拟内存总共可以被分成 4096 个段(1MB*4096 = 4GB),因此我们必须用 4096 个描述符来对这组段进行描述,每个描述符占用 4 个字节,故这组描述符的大小为 16KB,这4096 个描述符构成的表格就是转换表。对于 SDRAM 区域,在描述符中使能了 Cache。mmu_init()函数直接调用 arm920_setup()初始化 MMU, arm920_setup()函数包含一系列内嵌的汇编代码,用于控制 S3C2410 的 CP15 协处理器对 MMU 的处理。转载地址:https://blog.csdn.net/zytgg123456/article/details/109967927 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!