github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/ring0/pagetables/walker_amd64.go (about)

     1  // Copyright 2018 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 amd64
    16  // +build amd64
    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  	for pgdIndex := uint16((start & pgdMask) >> pgdShift); start < end && pgdIndex < entriesPerPage; pgdIndex++ {
    25  		var (
    26  			pgdEntry   = &w.pageTables.root[pgdIndex]
    27  			pudEntries *PTEs
    28  		)
    29  		if !pgdEntry.Valid() {
    30  			if !w.visitor.requiresAlloc() {
    31  				// Skip over this entry.
    32  				start = next(start, pgdSize)
    33  				continue
    34  			}
    35  
    36  			// Allocate a new pgd.
    37  			pudEntries = w.pageTables.Allocator.NewPTEs() // escapes: depends on allocator.
    38  			pgdEntry.setPageTable(w.pageTables, pudEntries)
    39  		} else {
    40  			pudEntries = w.pageTables.Allocator.LookupPTEs(pgdEntry.Address()) // escapes: see above.
    41  		}
    42  
    43  		// Map the next level.
    44  		clearPUDEntries := uint16(0)
    45  
    46  		for pudIndex := uint16((start & pudMask) >> pudShift); start < end && pudIndex < entriesPerPage; pudIndex++ {
    47  			var (
    48  				pudEntry   = &pudEntries[pudIndex]
    49  				pmdEntries *PTEs
    50  			)
    51  			if !pudEntry.Valid() {
    52  				if !w.visitor.requiresAlloc() {
    53  					// Skip over this entry.
    54  					clearPUDEntries++
    55  					start = next(start, pudSize)
    56  					continue
    57  				}
    58  
    59  				// This level has 1-GB super pages. Is this
    60  				// entire region at least as large as a single
    61  				// PUD entry?  If so, we can skip allocating a
    62  				// new page for the pmd.
    63  				if start&(pudSize-1) == 0 && end-start >= pudSize {
    64  					pudEntry.SetSuper()
    65  					if !w.visitor.visit(uintptr(start&^(pudSize-1)), pudEntry, pudSize-1) {
    66  						return false
    67  					}
    68  					if pudEntry.Valid() {
    69  						start = next(start, pudSize)
    70  						continue
    71  					}
    72  				}
    73  
    74  				// Allocate a new pud.
    75  				pmdEntries = w.pageTables.Allocator.NewPTEs() // escapes: see above.
    76  				pudEntry.setPageTable(w.pageTables, pmdEntries)
    77  
    78  			} else if pudEntry.IsSuper() {
    79  				// Does this page need to be split?
    80  				if w.visitor.requiresSplit() && (start&(pudSize-1) != 0 || end < next(start, pudSize)) {
    81  					// Install the relevant entries.
    82  					pmdEntries = w.pageTables.Allocator.NewPTEs() // escapes: see above.
    83  					for index := uint16(0); index < entriesPerPage; index++ {
    84  						pmdEntries[index].SetSuper()
    85  						pmdEntries[index].Set(
    86  							pudEntry.Address()+(pmdSize*uintptr(index)),
    87  							pudEntry.Opts())
    88  					}
    89  					pudEntry.setPageTable(w.pageTables, pmdEntries)
    90  				} else {
    91  					// A super page to be checked directly.
    92  					if !w.visitor.visit(uintptr(start&^(pudSize-1)), pudEntry, pudSize-1) {
    93  						return false
    94  					}
    95  
    96  					// Might have been cleared.
    97  					if !pudEntry.Valid() {
    98  						clearPUDEntries++
    99  					}
   100  
   101  					// Note that the super page was changed.
   102  					start = next(start, pudSize)
   103  					continue
   104  				}
   105  			} else {
   106  				pmdEntries = w.pageTables.Allocator.LookupPTEs(pudEntry.Address()) // escapes: see above.
   107  			}
   108  
   109  			// Map the next level, since this is valid.
   110  			clearPMDEntries := uint16(0)
   111  
   112  			for pmdIndex := uint16((start & pmdMask) >> pmdShift); start < end && pmdIndex < entriesPerPage; pmdIndex++ {
   113  				var (
   114  					pmdEntry   = &pmdEntries[pmdIndex]
   115  					pteEntries *PTEs
   116  				)
   117  				if !pmdEntry.Valid() {
   118  					if !w.visitor.requiresAlloc() {
   119  						// Skip over this entry.
   120  						clearPMDEntries++
   121  						start = next(start, pmdSize)
   122  						continue
   123  					}
   124  
   125  					// This level has 2-MB huge pages. If this
   126  					// region is continued in a single PMD entry?
   127  					// As above, we can skip allocating a new page.
   128  					if start&(pmdSize-1) == 0 && end-start >= pmdSize {
   129  						pmdEntry.SetSuper()
   130  						if !w.visitor.visit(uintptr(start&^(pmdSize-1)), pmdEntry, pmdSize-1) {
   131  							return false
   132  						}
   133  						if pmdEntry.Valid() {
   134  							start = next(start, pmdSize)
   135  							continue
   136  						}
   137  					}
   138  
   139  					// Allocate a new pmd.
   140  					pteEntries = w.pageTables.Allocator.NewPTEs() // escapes: see above.
   141  					pmdEntry.setPageTable(w.pageTables, pteEntries)
   142  
   143  				} else if pmdEntry.IsSuper() {
   144  					// Does this page need to be split?
   145  					if w.visitor.requiresSplit() && (start&(pmdSize-1) != 0 || end < next(start, pmdSize)) {
   146  						// Install the relevant entries.
   147  						pteEntries = w.pageTables.Allocator.NewPTEs()
   148  						for index := uint16(0); index < entriesPerPage; index++ {
   149  							pteEntries[index].Set(
   150  								pmdEntry.Address()+(pteSize*uintptr(index)),
   151  								pmdEntry.Opts())
   152  						}
   153  						pmdEntry.setPageTable(w.pageTables, pteEntries)
   154  					} else {
   155  						// A huge page to be checked directly.
   156  						if !w.visitor.visit(uintptr(start&^(pmdSize-1)), pmdEntry, pmdSize-1) {
   157  							return false
   158  						}
   159  
   160  						// Might have been cleared.
   161  						if !pmdEntry.Valid() {
   162  							clearPMDEntries++
   163  						}
   164  
   165  						// Note that the huge page was changed.
   166  						start = next(start, pmdSize)
   167  						continue
   168  					}
   169  				} else {
   170  					pteEntries = w.pageTables.Allocator.LookupPTEs(pmdEntry.Address()) // escapes: see above.
   171  				}
   172  
   173  				// Map the next level, since this is valid.
   174  				clearPTEEntries := uint16(0)
   175  
   176  				for pteIndex := uint16((start & pteMask) >> pteShift); start < end && pteIndex < entriesPerPage; pteIndex++ {
   177  					var (
   178  						pteEntry = &pteEntries[pteIndex]
   179  					)
   180  					if !pteEntry.Valid() && !w.visitor.requiresAlloc() {
   181  						clearPTEEntries++
   182  						start += pteSize
   183  						continue
   184  					}
   185  
   186  					// At this point, we are guaranteed that start%pteSize == 0.
   187  					if !w.visitor.visit(uintptr(start&^(pteSize-1)), pteEntry, pteSize-1) {
   188  						return false
   189  					}
   190  					if !pteEntry.Valid() && !w.visitor.requiresAlloc() {
   191  						clearPTEEntries++
   192  					}
   193  
   194  					// Note that the pte was changed.
   195  					start += pteSize
   196  					continue
   197  				}
   198  
   199  				// Check if we no longer need this page.
   200  				if clearPTEEntries == entriesPerPage {
   201  					pmdEntry.Clear()
   202  					w.pageTables.Allocator.FreePTEs(pteEntries) // escapes: see above.
   203  					clearPMDEntries++
   204  				}
   205  			}
   206  
   207  			// Check if we no longer need this page.
   208  			if clearPMDEntries == entriesPerPage {
   209  				pudEntry.Clear()
   210  				w.pageTables.Allocator.FreePTEs(pmdEntries) // escapes: see above.
   211  				clearPUDEntries++
   212  			}
   213  		}
   214  
   215  		// Check if we no longer need this page.
   216  		if clearPUDEntries == entriesPerPage {
   217  			pgdEntry.Clear()
   218  			w.pageTables.Allocator.FreePTEs(pudEntries) // escapes: see above.
   219  		}
   220  	}
   221  	return true
   222  }