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