github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/hostarch/addr.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  package hostarch
    16  
    17  import (
    18  	"fmt"
    19  )
    20  
    21  // Addr represents a generic virtual address.
    22  //
    23  // +stateify savable
    24  type Addr uintptr
    25  
    26  // AddLength adds the given length to start and returns the result. ok is true
    27  // iff adding the length did not overflow the range of Addr.
    28  //
    29  // Note: This function is usually used to get the end of an address range
    30  // defined by its start address and length. Since the resulting end is
    31  // exclusive, end == 0 is technically valid, and corresponds to a range that
    32  // extends to the end of the address space, but ok will be false. This isn't
    33  // expected to ever come up in practice.
    34  func (v Addr) AddLength(length uint64) (end Addr, ok bool) {
    35  	end = v + Addr(length)
    36  	// The second half of the following check is needed in case uintptr is
    37  	// smaller than 64 bits.
    38  	ok = end >= v && length <= uint64(^Addr(0))
    39  	return
    40  }
    41  
    42  // RoundDown returns the address rounded down to the nearest page boundary.
    43  func (v Addr) RoundDown() Addr {
    44  	return v & ^Addr(PageSize-1)
    45  }
    46  
    47  // RoundUp returns the address rounded up to the nearest page boundary. ok is
    48  // true iff rounding up did not wrap around.
    49  func (v Addr) RoundUp() (addr Addr, ok bool) {
    50  	addr = Addr(v + PageSize - 1).RoundDown()
    51  	ok = addr >= v
    52  	return
    53  }
    54  
    55  // MustRoundUp is equivalent to RoundUp, but panics if rounding up wraps
    56  // around.
    57  func (v Addr) MustRoundUp() Addr {
    58  	addr, ok := v.RoundUp()
    59  	if !ok {
    60  		panic(fmt.Sprintf("hostarch.Addr(%d).RoundUp() wraps", v))
    61  	}
    62  	return addr
    63  }
    64  
    65  // HugeRoundDown returns the address rounded down to the nearest huge page
    66  // boundary.
    67  func (v Addr) HugeRoundDown() Addr {
    68  	return v & ^Addr(HugePageSize-1)
    69  }
    70  
    71  // HugeRoundUp returns the address rounded up to the nearest huge page boundary.
    72  // ok is true iff rounding up did not wrap around.
    73  func (v Addr) HugeRoundUp() (addr Addr, ok bool) {
    74  	addr = Addr(v + HugePageSize - 1).HugeRoundDown()
    75  	ok = addr >= v
    76  	return
    77  }
    78  
    79  // PageOffset returns the offset of v into the current page.
    80  func (v Addr) PageOffset() uint64 {
    81  	return uint64(v & Addr(PageSize-1))
    82  }
    83  
    84  // IsPageAligned returns true if v.PageOffset() == 0.
    85  func (v Addr) IsPageAligned() bool {
    86  	return v.PageOffset() == 0
    87  }
    88  
    89  // AddrRange is a range of Addrs.
    90  //
    91  // type AddrRange <generated by go_generics>
    92  
    93  // ToRange returns [v, v+length).
    94  func (v Addr) ToRange(length uint64) (AddrRange, bool) {
    95  	end, ok := v.AddLength(length)
    96  	return AddrRange{v, end}, ok
    97  }
    98  
    99  // IsPageAligned returns true if ar.Start.IsPageAligned() and
   100  // ar.End.IsPageAligned().
   101  func (ar AddrRange) IsPageAligned() bool {
   102  	return ar.Start.IsPageAligned() && ar.End.IsPageAligned()
   103  }
   104  
   105  // String implements fmt.Stringer.String.
   106  func (ar AddrRange) String() string {
   107  	return fmt.Sprintf("[%#x, %#x)", ar.Start, ar.End)
   108  }
   109  
   110  // PageRoundDown/Up are equivalent to Addr.RoundDown/Up, but without the
   111  // potentially truncating conversion from uint64 to Addr. This is necessary
   112  // because there is no way to define generic "PageRoundDown/Up" functions in Go.
   113  
   114  // PageRoundDown returns x rounded down to the nearest page boundary.
   115  func PageRoundDown(x uint64) uint64 {
   116  	return x &^ (PageSize - 1)
   117  }
   118  
   119  // PageRoundUp returns x rounded up to the nearest page boundary.
   120  // ok is true iff rounding up did not wrap around.
   121  func PageRoundUp(x uint64) (addr uint64, ok bool) {
   122  	addr = PageRoundDown(x + PageSize - 1)
   123  	ok = addr >= x
   124  	return
   125  }