github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/ring0/pagetables/walker_arm64.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build arm64
    16  // +build arm64
    17  
    18  package pagetables
    19  
    20  // iterateRangeCanonical walks a canonical range.
    21  //
    22  //go:nosplit
    23  func (w *Walker) iterateRangeCanonical(start, end uintptr) bool {
    24  	pgdEntryIndex := w.pageTables.root
    25  	if start >= upperBottom {
    26  		pgdEntryIndex = w.pageTables.archPageTables.root
    27  	}
    28  
    29  	for pgdIndex := (uint16((start & pgdMask) >> pgdShift)); start < end && pgdIndex < entriesPerPage; pgdIndex++ {
    30  		var (
    31  			pgdEntry   = &pgdEntryIndex[pgdIndex]
    32  			pudEntries *PTEs
    33  		)
    34  		if !pgdEntry.Valid() {
    35  			if !w.visitor.requiresAlloc() {
    36  				// Skip over this entry.
    37  				start = next(start, pgdSize)
    38  				continue
    39  			}
    40  
    41  			// Allocate a new pgd.
    42  			pudEntries = w.pageTables.Allocator.NewPTEs()
    43  			pgdEntry.setPageTable(w.pageTables, pudEntries)
    44  		} else {
    45  			pudEntries = w.pageTables.Allocator.LookupPTEs(pgdEntry.Address())
    46  		}
    47  
    48  		// Map the next level.
    49  		clearPUDEntries := uint16(0)
    50  
    51  		for pudIndex := uint16((start & pudMask) >> pudShift); start < end && pudIndex < entriesPerPage; pudIndex++ {
    52  			var (
    53  				pudEntry   = &pudEntries[pudIndex]
    54  				pmdEntries *PTEs
    55  			)
    56  			if !pudEntry.Valid() {
    57  				if !w.visitor.requiresAlloc() {
    58  					// Skip over this entry.
    59  					clearPUDEntries++
    60  					start = next(start, pudSize)
    61  					continue
    62  				}
    63  
    64  				// This level has 1-GB sect pages. Is this
    65  				// entire region at least as large as a single
    66  				// PUD entry?  If so, we can skip allocating a
    67  				// new page for the pmd.
    68  				if start&(pudSize-1) == 0 && end-start >= pudSize {
    69  					pudEntry.SetSect()
    70  					if !w.visitor.visit(uintptr(start), pudEntry, pudSize-1) {
    71  						return false
    72  					}
    73  					if pudEntry.Valid() {
    74  						start = next(start, pudSize)
    75  						continue
    76  					}
    77  				}
    78  
    79  				// Allocate a new pud.
    80  				pmdEntries = w.pageTables.Allocator.NewPTEs()
    81  				pudEntry.setPageTable(w.pageTables, pmdEntries)
    82  
    83  			} else if pudEntry.IsSect() {
    84  				// Does this page need to be split?
    85  				if w.visitor.requiresSplit() && (start&(pudSize-1) != 0 || end < next(start, pudSize)) {
    86  					// Install the relevant entries.
    87  					pmdEntries = w.pageTables.Allocator.NewPTEs()
    88  					for index := uint16(0); index < entriesPerPage; index++ {
    89  						pmdEntries[index].SetSect()
    90  						pmdEntries[index].Set(
    91  							pudEntry.Address()+(pmdSize*uintptr(index)),
    92  							pudEntry.Opts())
    93  					}
    94  					pudEntry.setPageTable(w.pageTables, pmdEntries)
    95  				} else {
    96  					// A sect page to be checked directly.
    97  					if !w.visitor.visit(uintptr(start), pudEntry, pudSize-1) {
    98  						return false
    99  					}
   100  
   101  					// Might have been cleared.
   102  					if !pudEntry.Valid() {
   103  						clearPUDEntries++
   104  					}
   105  
   106  					// Note that the sect page was changed.
   107  					start = next(start, pudSize)
   108  					continue
   109  				}
   110  
   111  			} else {
   112  				pmdEntries = w.pageTables.Allocator.LookupPTEs(pudEntry.Address())
   113  			}
   114  
   115  			// Map the next level, since this is valid.
   116  			clearPMDEntries := uint16(0)
   117  
   118  			for pmdIndex := uint16((start & pmdMask) >> pmdShift); start < end && pmdIndex < entriesPerPage; pmdIndex++ {
   119  				var (
   120  					pmdEntry   = &pmdEntries[pmdIndex]
   121  					pteEntries *PTEs
   122  				)
   123  				if !pmdEntry.Valid() {
   124  					if !w.visitor.requiresAlloc() {
   125  						// Skip over this entry.
   126  						clearPMDEntries++
   127  						start = next(start, pmdSize)
   128  						continue
   129  					}
   130  
   131  					// This level has 2-MB huge pages. If this
   132  					// region is contined in a single PMD entry?
   133  					// As above, we can skip allocating a new page.
   134  					if start&(pmdSize-1) == 0 && end-start >= pmdSize {
   135  						pmdEntry.SetSect()
   136  						if !w.visitor.visit(uintptr(start), pmdEntry, pmdSize-1) {
   137  							return false
   138  						}
   139  						if pmdEntry.Valid() {
   140  							start = next(start, pmdSize)
   141  							continue
   142  						}
   143  					}
   144  
   145  					// Allocate a new pmd.
   146  					pteEntries = w.pageTables.Allocator.NewPTEs()
   147  					pmdEntry.setPageTable(w.pageTables, pteEntries)
   148  
   149  				} else if pmdEntry.IsSect() {
   150  					// Does this page need to be split?
   151  					if w.visitor.requiresSplit() && (start&(pmdSize-1) != 0 || end < next(start, pmdSize)) {
   152  						// Install the relevant entries.
   153  						pteEntries = w.pageTables.Allocator.NewPTEs()
   154  						for index := uint16(0); index < entriesPerPage; index++ {
   155  							pteEntries[index].Set(
   156  								pmdEntry.Address()+(pteSize*uintptr(index)),
   157  								pmdEntry.Opts())
   158  						}
   159  						pmdEntry.setPageTable(w.pageTables, pteEntries)
   160  					} else {
   161  						// A huge page to be checked directly.
   162  						if !w.visitor.visit(uintptr(start), pmdEntry, pmdSize-1) {
   163  							return false
   164  						}
   165  
   166  						// Might have been cleared.
   167  						if !pmdEntry.Valid() {
   168  							clearPMDEntries++
   169  						}
   170  
   171  						// Note that the huge page was changed.
   172  						start = next(start, pmdSize)
   173  						continue
   174  					}
   175  
   176  				} else {
   177  					pteEntries = w.pageTables.Allocator.LookupPTEs(pmdEntry.Address())
   178  				}
   179  
   180  				// Map the next level, since this is valid.
   181  				clearPTEEntries := uint16(0)
   182  
   183  				for pteIndex := uint16((start & pteMask) >> pteShift); start < end && pteIndex < entriesPerPage; pteIndex++ {
   184  					var (
   185  						pteEntry = &pteEntries[pteIndex]
   186  					)
   187  					if !pteEntry.Valid() && !w.visitor.requiresAlloc() {
   188  						clearPTEEntries++
   189  						start += pteSize
   190  						continue
   191  					}
   192  
   193  					// At this point, we are guaranteed that start%pteSize == 0.
   194  					if !w.visitor.visit(uintptr(start), pteEntry, pteSize-1) {
   195  						return false
   196  					}
   197  					if !pteEntry.Valid() {
   198  						if w.visitor.requiresAlloc() {
   199  							panic("PTE not set after iteration with requiresAlloc!")
   200  						}
   201  						clearPTEEntries++
   202  					}
   203  
   204  					// Note that the pte was changed.
   205  					start += pteSize
   206  					continue
   207  				}
   208  
   209  				// Check if we no longer need this page.
   210  				if clearPTEEntries == entriesPerPage {
   211  					pmdEntry.Clear()
   212  					w.pageTables.Allocator.FreePTEs(pteEntries)
   213  					clearPMDEntries++
   214  				}
   215  			}
   216  
   217  			// Check if we no longer need this page.
   218  			if clearPMDEntries == entriesPerPage {
   219  				pudEntry.Clear()
   220  				w.pageTables.Allocator.FreePTEs(pmdEntries)
   221  				clearPUDEntries++
   222  			}
   223  		}
   224  
   225  		// Check if we no longer need this page.
   226  		if clearPUDEntries == entriesPerPage {
   227  			pgdEntry.Clear()
   228  			w.pageTables.Allocator.FreePTEs(pudEntries)
   229  		}
   230  	}
   231  	return true
   232  }