日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux kernel的virtual kernel memory layout介绍(aarch64)

發(fā)布時(shí)間:2025/3/21 linux 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux kernel的virtual kernel memory layout介绍(aarch64) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

相關(guān)文件:
memory.h
pgtable.h
fixmap.h
page.h

1、重要的配置
我們就以VA_BITS=48,PAGE_SIZE=4k來介紹
(1)、(VA_BITS)
(arch/arm64/Kconfig)

config ARM64_VA_BITS_36bool "36-bit" if EXPERTdepends on ARM64_16K_PAGESconfig ARM64_VA_BITS_39bool "39-bit"depends on ARM64_4K_PAGESconfig ARM64_VA_BITS_42bool "42-bit"depends on ARM64_64K_PAGESconfig ARM64_VA_BITS_47bool "47-bit"depends on ARM64_16K_PAGESconfig ARM64_VA_BITS_48bool "48-bit" CONFIG_ARM64_VA_BITS_48=y CONFIG_ARM64_VA_BITS=48

(2)、(PAGE_SIZE、PAGE_SHIFT)
如果選擇了ARM64_4K_PAGES,那么PAGE_SIZE = 4K,PAGE_SHIFT=12

#ifdef CONFIG_ARM64_64K_PAGES #define PAGE_SHIFT 16 #define CONT_SHIFT 5 #elif defined(CONFIG_ARM64_16K_PAGES) #define PAGE_SHIFT 14 #define CONT_SHIFT 7 #else #define PAGE_SHIFT 12 #define CONT_SHIFT 4 #endif #define PAGE_SIZE (_AC(1, UL) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1))

2、kernel 4.4代碼 : 計(jì)算各個(gè)區(qū)域的地址
(memory.h):

#define VA_BITS (CONFIG_ARM64_VA_BITS) #define VA_START (UL(0xffffffffffffffff) - \(UL(1) << VA_BITS) + 1) #define PAGE_OFFSET (UL(0xffffffffffffffff) - \(UL(1) << (VA_BITS - 1)) + 1) #define KIMAGE_VADDR (MODULES_END) #define MODULES_END (MODULES_VADDR + MODULES_VSIZE) #define MODULES_VADDR (VA_START + KASAN_SHADOW_SIZE) #define MODULES_VSIZE (SZ_128M) #define PCI_IO_END (PAGE_OFFSET - SZ_2M) #define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE) #define FIXADDR_TOP (PCI_IO_START - SZ_2M) #define TASK_SIZE_64 (UL(1) << VA_BITS) #ifdef CONFIG_KASAN #define KASAN_SHADOW_SIZE (UL(1) << (VA_BITS - 3)) #else #define KASAN_SHADOW_SIZE (0) #endif #define MODULES_VSIZE (SZ_128M) #define SZ_128M 0x08000000 #define PCI_IO_SIZE SZ_16M //(0x01000000) #define PCI_IO_END (PAGE_OFFSET - SZ_2M) #define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE)

(fixmap.h)

#define FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT) #define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)

(pgtable.h)

#define VMEMMAP_SIZE ALIGN((1UL << (VA_BITS - PAGE_SHIFT)) * sizeof(struct page), PUD_SIZE) #define VMALLOC_START (MODULES_END) #define VMALLOC_END (PAGE_OFFSET - PUD_SIZE - VMEMMAP_SIZE - SZ_64K) #define VMEMMAP_START (VMALLOC_END + SZ_64K)

KASAN_SHADOW_SIZE = 0x2000_0000_0000
MODULES_VSIZE = 0x0800_0000

VA_BITS = 48
VA_START = 0xffff_0000_0000_0000
PAGE_OFFSET = 0xffff_8000_0000_0000

MODULES_VADDR=(VA_START + KASAN_SHADOW_SIZE) = 0xffff_2000_0000_0000
KIMAGE_VADDR = (MODULES_END) = (MODULES_VADDR + MODULES_VSIZE) = 0xffff_2000_0800_0000
KIMAGE_VADDR = 0xffff_2000_0800_0000

PCI_IO_END = 0xffff_8000_0000_0000 - 0x0000_0800 = 0xffff_7fff_ffff_e800
PCI_IO_START = 0xffff_7fff_ffff_e800 - 0x0100_0000 = 0xffff_7fff_feff_e800
FIXADDR_TOP = (PCI_IO_START - SZ_2M) = 0xffff_7fff_feff_e800 - 0x0020_0000 = 0xffff_7fff_fedf_e800 //(end addr)
PCI_IO_START = 0xffff_7fff_feff_e800

可見VMALLOC_START和KIMAGE_VADDR是重疊的,也就是kernel遷移到VMALLOC區(qū)域

再看VMEMMAP_SIZE :
(例如VA_BITS=48, PAGE_SHIFT=12的情況下)
(1UL << (VA_BITS - PAGE_SHIFT)) 表示48位的有效虛擬地址,一共可以表示多數(shù)個(gè)page頁
再乘以sizeof(struct page), 表示需要多數(shù)內(nèi)存來存儲struct page
也就是說VMEMMAP是用來存儲所有頁面的struct page結(jié)構(gòu)體的

結(jié)合以上地址,我們畫了張圖,更直觀

3、kernel 4.14代碼 : 計(jì)算各個(gè)區(qū)域的地址
(memory.h):

#define VA_BITS (CONFIG_ARM64_VA_BITS) #define VA_START (UL(0xffffffffffffffff) - \(UL(1) << VA_BITS) + 1) #define PAGE_OFFSET (UL(0xffffffffffffffff) - \(UL(1) << (VA_BITS - 1)) + 1) #define KIMAGE_VADDR (MODULES_END) #define MODULES_END (MODULES_VADDR + MODULES_VSIZE) #define MODULES_VADDR (VA_START + KASAN_SHADOW_SIZE) #define MODULES_VSIZE (SZ_128M) #define VMEMMAP_START (PAGE_OFFSET - VMEMMAP_SIZE) #define PCI_IO_END (VMEMMAP_START - SZ_2M) #define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE) #define FIXADDR_TOP (PCI_IO_START - SZ_2M)

同樣,我們也畫了一張圖:

4、kernel image搬移到vmalloc區(qū)域后,virt_to_phys的變化
virt_to_phys的作用是將內(nèi)核虛擬地址轉(zhuǎn)換成物理地址(針對線性映射區(qū)域)。
在kernel image還在線性映射區(qū)域的時(shí)候,virt_to_phys宏可以將kernel代碼中的一個(gè)地址轉(zhuǎn)換成物理地址,因?yàn)榫€性映射區(qū)域,物理地址和虛擬地址只有一個(gè)偏移。因此兩者很容易轉(zhuǎn)換。
那么現(xiàn)在kernel image和線性映射區(qū)域分開了,virt_to_phys宏又該如何實(shí)現(xiàn)呢?

在kernel中PAGE_OFFSET = 0x8000_0000_0000

#define PAGE_OFFSET (UL(0xffffffffffffffff) - \(UL(1) << (VA_BITS - 1)) + 1)

當(dāng)virt_to_phys調(diào)用時(shí)候,先判斷bit47(最高有效位),如果為1,則表示是(memory)DRAM的地址。那么直接使用X[46:0]和PHYS_OFFSET相加即可

#define __virt_to_phys(x) ({ \phys_addr_t __x = (phys_addr_t)(x); \__x & BIT(VA_BITS - 1) ? (__x & ~PAGE_OFFSET) + PHYS_OFFSET : \(__x - kimage_voffset); })

PHYS_OFFSET是DRAM的真實(shí)物理地址

memstart_addr = round_down(memblock_start_of_DRAM(),ARM64_MEMSTART_ALIGN);

當(dāng)virt_to_phys調(diào)用時(shí)候,先判斷bit47(最高有效位),如果為0,則表示是kernel image的地址, 那么直接使用X[46:0]和kimage_voffset相減即可

kimage_voffset來自匯編中的__mmap_switched函數(shù)

str_l x21, __fdt_pointer, x5 // Save FDT pointerldr_l x4, kimage_vaddr // Save the offset between sub x4, x4, x24 // the kernel virtual and str_l x4, kimage_voffset, x5 // physical mappings

綜上所述,virt_to_phys()當(dāng)前能夠轉(zhuǎn)換的依然還是:線性區(qū)域、kimg區(qū)域(kernel image區(qū)域)

5、PCI/IO區(qū)域
如X86處理器為外設(shè)專門實(shí)現(xiàn)了一個(gè)單獨(dú)的地址空間,稱為"I/O地址空間"或者"I/O端口空間",CPU通過專門的I/O指令(如X86的IN和OUT指令)來訪問這一空間中的地址單元
在arm64中,其實(shí)也有類似的指令,只是我們沒有用到這個(gè)區(qū)域,也沒有使用這些指令
具體在kernel/include/asm-generic/io.h中:
其中PCI_IOBASE對應(yīng)的就是PCI/IO的及地址(PCI_IO_START)。

#ifndef insb #define insb insb static inline void insb(unsigned long addr, void *buffer, unsigned int count) {readsb(PCI_IOBASE + addr, buffer, count); } #endif#ifndef insw #define insw insw static inline void insw(unsigned long addr, void *buffer, unsigned int count) {readsw(PCI_IOBASE + addr, buffer, count); } #endif#ifndef insl #define insl insl static inline void insl(unsigned long addr, void *buffer, unsigned int count) {readsl(PCI_IOBASE + addr, buffer, count); } #endif

6、ioremap
那么ioremap映射到了哪個(gè)區(qū)域呢?

(arch/arm64/include/asm/io.h)

#define ioremap(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) #define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) #define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) #define ioremap_wt(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) #define iounmap __iounmap

(ioremap.c)

void __iomem *__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot) {return __ioremap_caller(phys_addr, size, prot,__builtin_return_address(0)); } EXPORT_SYMBOL(__ioremap); static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size,pgprot_t prot, void *caller) {unsigned long last_addr;unsigned long offset = phys_addr & ~PAGE_MASK;int err;unsigned long addr;struct vm_struct *area;/** Page align the mapping address and size, taking account of any* offset.*/phys_addr &= PAGE_MASK;size = PAGE_ALIGN(size + offset);/** Don't allow wraparound, zero size or outside PHYS_MASK.*/last_addr = phys_addr + size - 1;if (!size || last_addr < phys_addr || (last_addr & ~PHYS_MASK))return NULL;/** Don't allow RAM to be mapped.*/if (WARN_ON(pfn_valid(__phys_to_pfn(phys_addr))))return NULL;area = get_vm_area_caller(size, VM_IOREMAP, caller);if (!area)return NULL;addr = (unsigned long)area->addr;area->phys_addr = phys_addr;err = ioremap_page_range(addr, addr + size, phys_addr, prot);if (err) {vunmap((void *)addr);return NULL;}return (void __iomem *)(offset + addr); }

調(diào)用了get_vm_area_caller(size, VM_IOREMAP, caller)
(vmalloc.c)

struct vm_struct *__get_vm_area_caller(unsigned long size, unsigned long flags,unsigned long start, unsigned long end,const void *caller) {return __get_vm_area_node(size, 1, flags, start, end, NUMA_NO_NODE,GFP_KERNEL, caller); } static struct vm_struct *__get_vm_area_node(unsigned long size,unsigned long align, unsigned long flags, unsigned long start,unsigned long end, int node, gfp_t gfp_mask, const void *caller) {struct vmap_area *va;struct vm_struct *area;BUG_ON(in_interrupt());if (flags & VM_IOREMAP)align = 1ul << clamp_t(int, fls_long(size),PAGE_SHIFT, IOREMAP_MAX_ORDER);size = PAGE_ALIGN(size);if (unlikely(!size))return NULL;area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);if (unlikely(!area))return NULL;if (!(flags & VM_NO_GUARD))size += PAGE_SIZE;va = alloc_vmap_area(size, align, start, end, node, gfp_mask);if (IS_ERR(va)) {kfree(area);return NULL;}setup_vmalloc_vm(area, va, flags, caller);return area; }

具體代碼不再深究,但可以知道iorempa是在vmalloc區(qū)域分配的

總結(jié)

以上是生活随笔為你收集整理的linux kernel的virtual kernel memory layout介绍(aarch64)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。