github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/ring0/pagetables/walker_unmap_arm64.go (about)

     1  //go:build arm64
     2  // +build arm64
     3  
     4  package pagetables
     5  
     6  // iterateRangeCanonical walks a canonical range.
     7  //
     8  //go:nosplit
     9  func (w *unmapWalker) iterateRangeCanonical(start, end uintptr) bool {
    10  	pgdEntryIndex := w.pageTables.root
    11  	if start >= upperBottom {
    12  		pgdEntryIndex = w.pageTables.archPageTables.root
    13  	}
    14  
    15  	for pgdIndex := (uint16((start & pgdMask) >> pgdShift)); start < end && pgdIndex < entriesPerPage; pgdIndex++ {
    16  		var (
    17  			pgdEntry   = &pgdEntryIndex[pgdIndex]
    18  			pudEntries *PTEs
    19  		)
    20  		if !pgdEntry.Valid() {
    21  			if !w.visitor.requiresAlloc() {
    22  
    23  				start = unmapnext(start, pgdSize)
    24  				continue
    25  			}
    26  
    27  			pudEntries = w.pageTables.Allocator.NewPTEs()
    28  			pgdEntry.setPageTable(w.pageTables, pudEntries)
    29  		} else {
    30  			pudEntries = w.pageTables.Allocator.LookupPTEs(pgdEntry.Address())
    31  		}
    32  
    33  		clearPUDEntries := uint16(0)
    34  
    35  		for pudIndex := uint16((start & pudMask) >> pudShift); start < end && pudIndex < entriesPerPage; pudIndex++ {
    36  			var (
    37  				pudEntry   = &pudEntries[pudIndex]
    38  				pmdEntries *PTEs
    39  			)
    40  			if !pudEntry.Valid() {
    41  				if !w.visitor.requiresAlloc() {
    42  
    43  					clearPUDEntries++
    44  					start = unmapnext(start, pudSize)
    45  					continue
    46  				}
    47  
    48  				if start&(pudSize-1) == 0 && end-start >= pudSize {
    49  					pudEntry.SetSect()
    50  					if !w.visitor.visit(uintptr(start), pudEntry, pudSize-1) {
    51  						return false
    52  					}
    53  					if pudEntry.Valid() {
    54  						start = unmapnext(start, pudSize)
    55  						continue
    56  					}
    57  				}
    58  
    59  				pmdEntries = w.pageTables.Allocator.NewPTEs()
    60  				pudEntry.setPageTable(w.pageTables, pmdEntries)
    61  
    62  			} else if pudEntry.IsSect() {
    63  
    64  				if w.visitor.requiresSplit() && (start&(pudSize-1) != 0 || end < unmapnext(start, pudSize)) {
    65  
    66  					pmdEntries = w.pageTables.Allocator.NewPTEs()
    67  					for index := uint16(0); index < entriesPerPage; index++ {
    68  						pmdEntries[index].SetSect()
    69  						pmdEntries[index].Set(
    70  							pudEntry.Address()+(pmdSize*uintptr(index)),
    71  							pudEntry.Opts())
    72  					}
    73  					pudEntry.setPageTable(w.pageTables, pmdEntries)
    74  				} else {
    75  
    76  					if !w.visitor.visit(uintptr(start), pudEntry, pudSize-1) {
    77  						return false
    78  					}
    79  
    80  					if !pudEntry.Valid() {
    81  						clearPUDEntries++
    82  					}
    83  
    84  					start = unmapnext(start, pudSize)
    85  					continue
    86  				}
    87  
    88  			} else {
    89  				pmdEntries = w.pageTables.Allocator.LookupPTEs(pudEntry.Address())
    90  			}
    91  
    92  			clearPMDEntries := uint16(0)
    93  
    94  			for pmdIndex := uint16((start & pmdMask) >> pmdShift); start < end && pmdIndex < entriesPerPage; pmdIndex++ {
    95  				var (
    96  					pmdEntry   = &pmdEntries[pmdIndex]
    97  					pteEntries *PTEs
    98  				)
    99  				if !pmdEntry.Valid() {
   100  					if !w.visitor.requiresAlloc() {
   101  
   102  						clearPMDEntries++
   103  						start = unmapnext(start, pmdSize)
   104  						continue
   105  					}
   106  
   107  					if start&(pmdSize-1) == 0 && end-start >= pmdSize {
   108  						pmdEntry.SetSect()
   109  						if !w.visitor.visit(uintptr(start), pmdEntry, pmdSize-1) {
   110  							return false
   111  						}
   112  						if pmdEntry.Valid() {
   113  							start = unmapnext(start, pmdSize)
   114  							continue
   115  						}
   116  					}
   117  
   118  					pteEntries = w.pageTables.Allocator.NewPTEs()
   119  					pmdEntry.setPageTable(w.pageTables, pteEntries)
   120  
   121  				} else if pmdEntry.IsSect() {
   122  
   123  					if w.visitor.requiresSplit() && (start&(pmdSize-1) != 0 || end < unmapnext(start, pmdSize)) {
   124  
   125  						pteEntries = w.pageTables.Allocator.NewPTEs()
   126  						for index := uint16(0); index < entriesPerPage; index++ {
   127  							pteEntries[index].Set(
   128  								pmdEntry.Address()+(pteSize*uintptr(index)),
   129  								pmdEntry.Opts())
   130  						}
   131  						pmdEntry.setPageTable(w.pageTables, pteEntries)
   132  					} else {
   133  
   134  						if !w.visitor.visit(uintptr(start), pmdEntry, pmdSize-1) {
   135  							return false
   136  						}
   137  
   138  						if !pmdEntry.Valid() {
   139  							clearPMDEntries++
   140  						}
   141  
   142  						start = unmapnext(start, pmdSize)
   143  						continue
   144  					}
   145  
   146  				} else {
   147  					pteEntries = w.pageTables.Allocator.LookupPTEs(pmdEntry.Address())
   148  				}
   149  
   150  				clearPTEEntries := uint16(0)
   151  
   152  				for pteIndex := uint16((start & pteMask) >> pteShift); start < end && pteIndex < entriesPerPage; pteIndex++ {
   153  					var (
   154  						pteEntry = &pteEntries[pteIndex]
   155  					)
   156  					if !pteEntry.Valid() && !w.visitor.requiresAlloc() {
   157  						clearPTEEntries++
   158  						start += pteSize
   159  						continue
   160  					}
   161  
   162  					if !w.visitor.visit(uintptr(start), pteEntry, pteSize-1) {
   163  						return false
   164  					}
   165  					if !pteEntry.Valid() {
   166  						if w.visitor.requiresAlloc() {
   167  							panic("PTE not set after iteration with requiresAlloc!")
   168  						}
   169  						clearPTEEntries++
   170  					}
   171  
   172  					start += pteSize
   173  					continue
   174  				}
   175  
   176  				if clearPTEEntries == entriesPerPage {
   177  					pmdEntry.Clear()
   178  					w.pageTables.Allocator.FreePTEs(pteEntries)
   179  					clearPMDEntries++
   180  				}
   181  			}
   182  
   183  			if clearPMDEntries == entriesPerPage {
   184  				pudEntry.Clear()
   185  				w.pageTables.Allocator.FreePTEs(pmdEntries)
   186  				clearPUDEntries++
   187  			}
   188  		}
   189  
   190  		if clearPUDEntries == entriesPerPage {
   191  			pgdEntry.Clear()
   192  			w.pageTables.Allocator.FreePTEs(pudEntries)
   193  		}
   194  	}
   195  	return true
   196  }
   197  
   198  // Walker walks page tables.
   199  type unmapWalker struct {
   200  	// pageTables are the tables to walk.
   201  	pageTables *PageTables
   202  
   203  	// Visitor is the set of arguments.
   204  	visitor unmapVisitor
   205  }
   206  
   207  // iterateRange iterates over all appropriate levels of page tables for the given range.
   208  //
   209  // If requiresAlloc is true, then Set _must_ be called on all given PTEs. The
   210  // exception is super pages. If a valid super page (huge or jumbo) cannot be
   211  // installed, then the walk will continue to individual entries.
   212  //
   213  // This algorithm will attempt to maximize the use of super/sect pages whenever
   214  // possible. Whether a super page is provided will be clear through the range
   215  // provided in the callback.
   216  //
   217  // Note that if requiresAlloc is true, then no gaps will be present. However,
   218  // if alloc is not set, then the iteration will likely be full of gaps.
   219  //
   220  // Note that this function should generally be avoided in favor of Map, Unmap,
   221  // etc. when not necessary.
   222  //
   223  // Precondition: start must be page-aligned.
   224  // Precondition: start must be less than end.
   225  // Precondition: If requiresAlloc is true, then start and end should not span
   226  // non-canonical ranges. If they do, a panic will result.
   227  //
   228  //go:nosplit
   229  func (w *unmapWalker) iterateRange(start, end uintptr) {
   230  	if start%pteSize != 0 {
   231  		panic("unaligned start")
   232  	}
   233  	if end < start {
   234  		panic("start > end")
   235  	}
   236  	if start < lowerTop {
   237  		if end <= lowerTop {
   238  			w.iterateRangeCanonical(start, end)
   239  		} else if end > lowerTop && end <= upperBottom {
   240  			if w.visitor.requiresAlloc() {
   241  				panic("alloc spans non-canonical range")
   242  			}
   243  			w.iterateRangeCanonical(start, lowerTop)
   244  		} else {
   245  			if w.visitor.requiresAlloc() {
   246  				panic("alloc spans non-canonical range")
   247  			}
   248  			if !w.iterateRangeCanonical(start, lowerTop) {
   249  				return
   250  			}
   251  			w.iterateRangeCanonical(upperBottom, end)
   252  		}
   253  	} else if start < upperBottom {
   254  		if end <= upperBottom {
   255  			if w.visitor.requiresAlloc() {
   256  				panic("alloc spans non-canonical range")
   257  			}
   258  		} else {
   259  			if w.visitor.requiresAlloc() {
   260  				panic("alloc spans non-canonical range")
   261  			}
   262  			w.iterateRangeCanonical(upperBottom, end)
   263  		}
   264  	} else {
   265  		w.iterateRangeCanonical(start, end)
   266  	}
   267  }
   268  
   269  // next returns the next address quantized by the given size.
   270  //
   271  //go:nosplit
   272  func unmapnext(start uintptr, size uintptr) uintptr {
   273  	start &= ^(size - 1)
   274  	start += size
   275  	return start
   276  }