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