The Master said, “If virtue is not solitary, there must be neighbours.” The Analects of Confucius: Li Ren
A hundred blog series. This paper is: the v15. Xx HongMeng kernel source code analysis (memory mapping) | virtual memory deficiency in where
Memory management:
- V11. Xx HongMeng kernel source code analysis (memory allocation) | what memory allocation
- V12. Xx HongMeng kernel source code analysis (memory management) | what is virtual memory panorama
- V14. Xx HongMeng kernel source code analysis (memory assembly) | who is the foundation of virtual memory implementation
- V15. Xx HongMeng kernel source code analysis (memory mapping) | virtual memory deficiency in where
- V16. Xx HongMeng kernel source code analysis rules (memory) | what memory management in the tube
- V17. Xx HongMeng kernel source code analysis (physical memory) | how to manage physical memory
The essence of MMU
Virtual address (VA): The linear address, which is all VA, is allocated by the compiler and linker when locating the program. Each application uses the same virtual memory address space, and these virtual memory address Spaces are actually mapped to different physical memory space. The CPU only knows the virtual address and asks for data from the virtual address, but in its protected mode, the address signal is intercepted by the MMU on the way, and the MMU replaces the virtual address with the physical address to get the real data.
Physical address (PA) : The location of the actual physical memory allocated by the program’s instructions and constant data, global variable data, and dynamic memory allocation at runtime.
MMU uses page table to translate virtual to real addresses. Page table entries not only describe the direct conversion of virtual pages to physical pages, but also provide access rights (read, write, and executable) and storage properties of pages. The essence of the MMU is to play with the high (20 bits) of the virtual address, and the low 12 bits is the in-page offset address. That is, the lower 12 bits of the virtual address are the same as the lower 12 bits of the physical address.
MMU is through the two-level page table structure: L1 and L2 to achieve the mapping function, hongmeng kernel of course also implemented the implementation of the two-level page table conversion. This is a series of articles on the memory part of the most satisfactory one, but also the most difficult to understand one, strongly suggest a combination of source code. HongMeng kernel source code comments < G | G | | C C >
Level 1 page table L1
The L1 page table divides the entire 4G address space into 4096 1M sections. Each item in the page table is 32 bits, and its content is the BASE address of the L2 page table or the base address of some 1M physical memory. The high 12 bits of the virtual address is used to locate the page table item, that is, the index of 4096 page items, the base address of L1 page table, also known as the base address of conversion table, stored in THE C2 (TTB) register of CP15, the source code analysis of the Kernel (memory assembly) has a detailed description, to see.
L1 page entries have three description formats, hongmeng source code as follows.
/* L1 descriptor type */
#define MMU_DESCRIPTOR_L1_TYPE_INVALID (0x0 << 0)
#define MMU_DESCRIPTOR_L1_TYPE_PAGE_TABLE (0x1 << 0)
#define MMU_DESCRIPTOR_L1_TYPE_SECTION (0x2 << 0)
#define MMU_DESCRIPTOR_L1_TYPE_MASK (0x3 << 0)
Copy the code
The first type: Fault (INVALID) page entry, indicating that the corresponding virtual address is not mapped, the access will generate a data abort exception.
PAGE_TABLE = PAGE_TABLE = PAGE_TABLE = PAGE_TABLE = PAGE_TABLE = PAGE_TABLE
The third type: SECTION page entry, pointing to the 1M SECTION page entry
The lowest two bits [1:0] of a page entry are used to define the type of a page entry. A section entry corresponds to a 1M section. The physical address can be obtained by replacing the highest 12 bits of the virtual address with the highest 12 bits of the page entry. Or look directly at the hongmeng source code to clear, each line has added detailed comments.
LOS_ArchMmuQuery
// Query a physical address using a virtual address
STATUS_T LOS_ArchMmuQuery(const LosArchMmu *archMmu, VADDR_T vaddr, PADDR_T *paddr, UINT32 *flags)
{ArchMmu ->virtTtb: convert table base address
PTE_T l1Entry = OsGetPte1(archMmu - > virtTtb vaddr);// get PTE vaddr right 20 bits to get L1 descriptor subaddress
PTE_T l2Entry;
PTE_T* l2Base = NULL;
if (OsIsPte1Invalid(l1Entry)) {// Check whether the L1 descriptor is valid
return LOS_ERRNO_VM_NOT_FOUND;// Invalid returned virtual address not found
} else if (OsIsPte1Section(l1Entry)) {// section page entry: l1Entry whether the lower bits are 10
if(paddr ! =NULL) {// Physical address = section base address (12 bits higher than section page entry) + virtual address 20 bits lower
*paddr = MMU_DESCRIPTOR_L1_SECTION_ADDR(l1Entry) + (vaddr & (MMU_DESCRIPTOR_L1_SMALL_SIZE - 1));
}
if(flags ! =NULL) {
OsCvtSecAttsToFlags(l1Entry, flags);// Obtain the virtual memory flag information}}else if (OsIsPte1PageTable(l1Entry)) {//PAGE_TABLE Page entry: l1Entry whether the lower two bits are 01
l2Base = OsGetPte2BasePtr(l1Entry);// get the base address of the L2 transform table
if (l2Base == NULL) {
return LOS_ERRNO_VM_NOT_FOUND;
}
l2Entry = OsGetPte2(l2Base vaddr);// Get the address of L2 descriptor
if (OsIsPte2SmallPage(l2Entry) || OsIsPte2SmallPageXN(l2Entry)) {
if(paddr ! =NULL) {// Physical address = small page base address (upper 20 bits of L2 page entry) + virtual address 12 bits lower
*paddr = MMU_DESCRIPTOR_L2_SMALL_PAGE_ADDR(l2Entry) + (vaddr & (MMU_DESCRIPTOR_L2_SMALL_SIZE - 1));
}
if(flags ! =NULL) {
OsCvtPte2AttsToFlagsL1Entry, l2Entry, Flags);// Obtain the virtual memory flag information}}else if (OsIsPte2LargePage(l2Entry)) {// Hongmeng currently does not support 64K large pages, it should be supported in the future mobile version.
LOS_Panic("%s %d, large page unimplemented\n", __FUNCTION__ __LINE__); }else {
returnLOS_ERRNO_VM_NOT_FOUND; }}return LOS_OK;
}
Copy the code
This is the most frequently used address function of the Hongmon kernel, through the virtual address to get physical address and flag information, see where it will be called.
Secondary page table L2
L1 page entry represents the address range of 1M, L2 divides 1M into more small pages, and a page of the core is calculated as 4K, so it is divided into 256 small pages.
The L2 page table contains 256 page entries, each 32 bits (4 bytes). The L2 page table requires 256*4 = 1K space and must be aligned by 1K. Each L2 page entry converts 4K virtual memory addresses to physical addresses.
L2 page entries have three formats:
/* L2 descriptor type */
#define MMU_DESCRIPTOR_L2_TYPE_INVALID (0x0 << 0)
#define MMU_DESCRIPTOR_L2_TYPE_LARGE_PAGE (0x1 << 0)
#define MMU_DESCRIPTOR_L2_TYPE_SMALL_PAGE (0x2 << 0)
#define MMU_DESCRIPTOR_L2_TYPE_SMALL_PAGE_XN (0x3 << 0)
#define MMU_DESCRIPTOR_L2_TYPE_MASK (0x3 << 0)
Copy the code
The first type: Fault (INVALID) page entry, indicating that the corresponding virtual address is not mapped, the access will generate a data abort exception.
The second type: large page table entry, containing a pointer to 64K pages, but the Hongmun kernel does not implement the support of large page table, gives a hint that is not implemented
if (OsIsPte2LargePage(l2Entry)) {
LOS_Panic("%s %d, large page unimplemented\n", __FUNCTION__ __LINE__); }Copy the code
Third: small page entry, containing a pointer to 4K pages.
Mapping initialization process
Let’s look at the relationship between the call and the called
// Start mapping initialization
VOID OsInitMappingStartUp(VOID)
{
OsArmInvalidateTlbBarrier(a);// Disable TLB
OsSwitchTmpTTB(a);// Switch to temporary TTB
OsSetKSectionAttr(a);// Set the kernel segment (text, rodata, BSS) mapping
OsArchMmuInitPerCPU(a);// Initialize CPU and MMU information
}
Copy the code
Crisp, called four functions, three of them in the cloud kernel source code analysis (memory assembly) involved, not expanded, here said OsSetKSectionAttr
It implements the kernel space mapping of each area, the kernel itself is also a program, the kernel space HongMeng was separated in physical memory, that is to say, one region in the physical memory is only for kernel space to enjoy, from the root is the kernel and the APP space isolation, inside is the kernel of important data (including code, constants and global variable), If you look at the code, it’s a long code, and the whole function is posted and commented.
OsSetKSectionAttr Sets and maps kernel space
typedef struct ArchMmuInitMapping {
PADDR_T phys;// Physical address
VADDR_T virt;// Virtual address
size_t size;/ / size
unsigned int flags;// Mark read/write /.. VM_MAP_REGION_FLAG_PERM_*
const char *name;/ / name
} LosArchMmuInitMapping;
VADDR_T *OsGFirstTableGet(a)
{
return (VADDR_T *)g_firstPageTable;//UINT8 g_firstPageTable[MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS]
}
// Set the kernel space segment property, it can be seen that the kernel space is fixed to the physical address
STATIC VOID OsSetKSectionAttr(VOID)
{
/* every section should be page aligned */
UINTPTR textStart = (UINTPTR)&__text_start;// Start position of code segment
UINTPTR textEnd = (UINTPTR)&__text_end;// The end of the code segment
UINTPTR rodataStart = (UINTPTR)&__rodata_start;// Start of constant read-only segment
UINTPTR rodataEnd = (UINTPTR)&__rodata_end;// Constant end of read-only segment
UINTPTR ramDataStart = (UINTPTR)&__ram_data_start;// Start position of global variable segment
UINTPTR bssEnd = (UINTPTR)&__bss_end;// BSS end position
UINT32 bssEndBoundary = ROUNDUP(bssEnd, MB); LosArchMmuInitMapping mmuKernelMappings[] = {{. Phys = SYS_MEM_BASE + textStart - KERNEL_VMM_BASE,// Map physical memory locationsVirt = textStart,// The kernel code area
.size = ROUNDUP(textEnd - textStart, MMU_DESCRIPTOR_L2_SMALL_SIZE),// Code area sizeThe flags = VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_EXECUTE,// The code snippet is readable and executable
.name = "kernel_text"}, {. Phys = SYS_MEM_BASE + rodatastart-kernel_vmm_base,// Map physical memory locationsVirt = rodataStart,// the kernel constant area
.size = ROUNDUP(rodataend-rodatastart, MMU_DESCRIPTOR_L2_SMALL_SIZE),/ / 4 k alignmentThe flags = VM_MAP_REGION_FLAG_PERM_READ,// The constant segment is read-only
.name = "kernel_rodata"}, {. Phys = SYS_MEM_BASE + ramDataStart - KERNEL_VMM_BASE,// Map physical memory locations.virt = ramDataStart,.size =ROUNDUP(bssendboundary-ramdatastart, MMU_DESCRIPTOR_L2_SMALL_SIZE), The flags = VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE,// The global variable area is readable and writable
.name = "kernel_data_bss"}}; LosVmSpace *kSpace =LOS_GetKVmSpace(a);// Get the kernel space
status_t status;
UINT32 length;
paddr_t oldTtPhyBase;
int i;
LosArchMmuInitMapping *kernelMap = NULL;// Kernel mapping
UINT32 kmallocLength;
/* use second-level mapping of default READ and WRITE */
kSpace->archMmu.virtTtb = (PTE_T *)g_firstPageTable;//__attribute__((section(".bss.prebss.translation_table"))) UINT8 g_firstPageTable[MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS];
kSpace->archMmu.physTtb = LOS_PaddrQuery(kSpace->archMmu.virtTtb);// Query the TTB physical address using the TTB virtual address
status = LOS_ArchMmuUnmap(&kspace ->archMmu, KERNEL_VMM_BASE, (bssEndBoundary - KERNEL_VMM_BASE) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT);// Unbind the bssendboundary-kernel_vmm_base mapping
if(status ! = ((bssEndBoundary - KERNEL_VMM_BASE) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {// Unbind failed
VM_ERR("unmap failed, status: %d", status);return;
}
// Map textStart - KERNEL_VMM_BASE
status = LOS_ArchMmuMap(&kspace ->archMmu, KERNEL_VMM_BASE, SYS_MEM_BASE, (textStart - KERNEL_VMM_BASE) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE | VM_MAP_REGION_FLAG_PERM_EXECUTE);if(status ! = ((textStart - KERNEL_VMM_BASE) >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {VM_ERR("mmap failed, status: %d", status);return;
}
length = sizeof(mmuKernelMappings) / sizeof(LosArchMmuInitMapping);
for (i = 0; i < length; i++) {// Set mmuKernelMappings to mmuKernelMappings
kernelMap = &mmuKernelMappings[i];
status = LOS_ArchMmuMap(&kspace ->archMmu, kernelMap->virt, kernelMap->phys, kernelMap->size >> MMU_DESCRIPTOR_L2_SMALL_SHIFT, kernelMap->flags);if(status ! = (kernelMap->size >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {VM_ERR("mmap failed, status: %d", status);return;
}
LOS_VmSpaceReserve(kSpace, kernelMap->size, kernelMap->virt)/ / the reservations
}
// Map the remaining space well
kmallocLength = KERNEL_VMM_BASE + SYS_MEM_SIZE_DEFAULT - bssEndBoundary;
status = LOS_ArchMmuMap(&kspace ->archMmu, bssEndBoundary, SYS_MEM_BASE + bssEndBoundary - KERNEL_VMM_BASE, KmallocLength > > MMU_DESCRIPTOR_L2_SMALL_SHIFT, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE);if(status ! = (kmallocLength >> MMU_DESCRIPTOR_L2_SMALL_SHIFT)) {VM_ERR("unmap failed, status: %d", status);return;
}
LOS_VmSpaceReserveKSpace, kmallocLength, bssEndBoundary);/* we need free tmp ttbase */
oldTtPhyBase = OsArmReadTtbr0(a);// Read the TTB value
oldTtPhyBase = oldTtPhyBase & MMU_DESCRIPTOR_L2_SMALL_FRAME;
OsArmWriteTtbr0(kSpace->archMmu.physTtb | MMU_TTBRx_FLAGS);// kernel page table base address write cp15c2 (TTB register)
ISB;
/* we changed page table entry, so we need to clean TLB here */
OsCleanTLB(a);// Clear the TLB buffer
(VOID)LOS_MemFree(VOID *)(UINTPTR)(oldTTphyBase-sys_mem_base + KERNEL_VMM_BASE));// Release the memory pool
}
Copy the code
LOS_ArchMmuMap
Mmu map is to generate L1, L2 page table entry process, for the conversion of virtual real address, or directly look at the code, the code explains everything!
// Map is the process of generating entries on L1 and L2 pages
status_t LOS_ArchMmuMapLosArchMmu *archMmu, VADDR_T vaddr, PADDR_T paddr,size_tThe count, UINT32 flags)
{
PTE_T l1Entry;
UINT32 saveCounts = 0;
INT32 mapped = 0;
INT32 checkRst;
checkRst = OsMapParamCheck(flags, vaddr, paddr);// Check the parameters
if (checkRst < 0) {
return checkRst;
}
/* see what kind of mapping we can use */
while (count > 0) {
if (MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(vaddr) && // The virtual address is used when the physical address is aligned with 0x100000 (1M)
MMU_DESCRIPTOR_IS_L1_SIZE_ALIGNED(paddr) && //section Page entry format
count >= MMU_DESCRIPTOR_L2_NUMBERS_PER_L1) { //MMU_DESCRIPTOR_L2_NUMBERS_PER_L1 = 0x100
/* compute the arch flags for L1 sections cache, r ,w ,x, domain and type */
saveCounts = OsMapSection(archMmu, flags, &vaddr, &paddr, &count);// Generate L1 Section type page entries and save them
} else {
/* have to use a L2 mapping, we only allocate 4KB for L1, support 0 ~ 1GB */
l1Entry = OsGetPte1(archMmu - > virtTtb vaddr);// Get the L1 page item
if (OsIsPte1Invalid(l1Entry)) {//L1 fault page item type
OsMapL1PTE(archMmu, &l1Entry, vaddr, flags);// Generate the L1 page table page entry and save it
saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count);// Generate L2 page table items and save
} else if (OsIsPte1PageTable(l1Entry)) {//L1 page Table Page item type
saveCounts = OsMapL2PageContinous(l1Entry, flags, &vaddr, &paddr, &count);// Generate L2 page table items and save
} else {
LOS_Panic("%s %d, unimplemented tt_entry %x\n", __FUNCTION__, __LINE__, l1Entry); } } mapped += saveCounts; }return mapped;
}
STATIC UINT32 OsMapL2PageContinousPTE_T PTE1, UINT32 FLAGS, VADDR_T * VADDR, PADDR_T * PADDr, UINT32 *count)
{
PTE_T *pte2BasePtr = NULL;
UINT32 archFlags;
UINT32 saveCounts;
pte2BasePtr = OsGetPte2BasePtr(pte1);
if (pte2BasePtr == NULL) {
LOS_Panic("%s %d, pte1 %#x error\n", __FUNCTION__, __LINE__, pte1); }/* compute the arch flags for L2 4K pages */
archFlags = OsCvtPte2FlagsToAttrs(flags);
saveCounts = OsSavePte2Continuous(pte2BasePtr,OsGetPte2Index(* vaddr), * paddr | archFlags, * count); *paddr += (saveCounts << MMU_DESCRIPTOR_L2_SMALL_SHIFT); *vaddr += (saveCounts << MMU_DESCRIPTOR_L2_SMALL_SHIFT); *count -= saveCounts;return saveCounts;
}
Copy the code
OsMapL2PageContinous does not add a comment, hope you are not too lazy, hurry up, by here should be able to understand! It is best to combine the hongmeng kernel source code analysis (memory assembly) together to see the understanding will be deeper.
Intensive reading of the kernel source code
Four code stores synchronous annotation kernel source code, >> view the Gitee repository
Analysis of 100 blogs. Dig deep into the core
Add comments to hongmeng kernel source code process, sort out the following article. Content based on the source code, often in life scene analogy as much as possible into the kernel knowledge of a scene, with a pictorial sense, easy to understand memory. It’s important to speak in a way that others can understand! The 100 blogs are by no means a bunch of ridiculously difficult concepts being put forward by Baidu. That’s not interesting. More hope to make the kernel become lifelike, feel more intimate. It’s hard, it’s hard, but there’s no turning back. 😛 and code bugs need to be constantly debug, there will be many mistakes and omissions in the article and annotation content, please forgive, but will be repeatedly amended, continuous update. Xx represents the number of modifications, refined, concise and comprehensive, and strive to create high-quality content.
Compile build | The fundamental tools | Loading operation | Process management |
---|---|---|---|
Compile environment The build process Environment script Build tools Designed.the gn application Ninja ninja |
Two-way linked list Bitmap management In the stack way The timer Atomic operation Time management |
The ELF format The ELF parsing Static link relocation Process image |
Process management Process concept Fork Special process Process recycling Signal production Signal consumption Shell editor Shell parsing |
Process of communication | Memory management | Ins and outs | Task management |
spinlocks The mutex Process of communication A semaphore Incident control The message queue |
Memory allocation Memory management Memory assembly The memory mapping Rules of memory Physical memory |
Total directory Scheduling the story Main memory slave The source code comments Source structure Static site |
The clock task Task scheduling Task management The scheduling queue Scheduling mechanism Thread concept Concurrent parallel The system calls Task switching |
The file system | Hardware architecture | ||
File concept The file system The index node Mount the directory Root file system Character device VFS File handle Pipeline file |
Compilation basis Assembly and the cords Working mode register Anomaly over Assembly summary Interrupt switch Interrupt concept Interrupt management |
HongMeng station | into a little bit every day, the original is not easy, welcome to reprint, please indicate the source.