// Copyright 2018 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package usermem import ( "fmt" ) // Addr represents a generic virtual address. // // +stateify savable type Addr uintptr // AddLength adds the given length to start and returns the result. ok is true // iff adding the length did not overflow the range of Addr. // // Note: This function is usually used to get the end of an address range // defined by its start address and length. Since the resulting end is // exclusive, end == 0 is technically valid, and corresponds to a range that // extends to the end of the address space, but ok will be false. This isn't // expected to ever come up in practice. func (v Addr) AddLength(length uint64) (end Addr, ok bool) { end = v + Addr(length) // The second half of the following check is needed in case uintptr is // smaller than 64 bits. ok = end >= v && length <= uint64(^Addr(0)) return } // RoundDown returns the address rounded down to the nearest page boundary. func (v Addr) RoundDown() Addr { return v & ^Addr(PageSize-1) } // RoundUp returns the address rounded up to the nearest page boundary. ok is // true iff rounding up did not wrap around. func (v Addr) RoundUp() (addr Addr, ok bool) { addr = Addr(v + PageSize - 1).RoundDown() ok = addr >= v return } // MustRoundUp is equivalent to RoundUp, but panics if rounding up wraps // around. func (v Addr) MustRoundUp() Addr { addr, ok := v.RoundUp() if !ok { panic(fmt.Sprintf("usermem.Addr(%d).RoundUp() wraps", v)) } return addr } // HugeRoundDown returns the address rounded down to the nearest huge page // boundary. func (v Addr) HugeRoundDown() Addr { return v & ^Addr(HugePageSize-1) } // HugeRoundUp returns the address rounded up to the nearest huge page boundary. // ok is true iff rounding up did not wrap around. func (v Addr) HugeRoundUp() (addr Addr, ok bool) { addr = Addr(v + HugePageSize - 1).HugeRoundDown() ok = addr >= v return } // PageOffset returns the offset of v into the current page. func (v Addr) PageOffset() uint64 { return uint64(v & Addr(PageSize-1)) } // IsPageAligned returns true if v.PageOffset() == 0. func (v Addr) IsPageAligned() bool { return v.PageOffset() == 0 } // AddrRange is a range of Addrs. // // type AddrRange <generated by go_generics> // ToRange returns [v, v+length). func (v Addr) ToRange(length uint64) (AddrRange, bool) { end, ok := v.AddLength(length) return AddrRange{v, end}, ok } // IsPageAligned returns true if ar.Start.IsPageAligned() and // ar.End.IsPageAligned(). func (ar AddrRange) IsPageAligned() bool { return ar.Start.IsPageAligned() && ar.End.IsPageAligned() } // String implements fmt.Stringer.String. func (ar AddrRange) String() string { return fmt.Sprintf("[%#x, %#x)", ar.Start, ar.End) } // PageRoundDown/Up are equivalent to Addr.RoundDown/Up, but without the // potentially truncating conversion from uint64 to Addr. This is necessary // because there is no way to define generic "PageRoundDown/Up" functions in Go. // PageRoundDown returns x rounded down to the nearest page boundary. func PageRoundDown(x uint64) uint64 { return x &^ (PageSize - 1) } // PageRoundUp returns x rounded up to the nearest page boundary. // ok is true iff rounding up did not wrap around. func PageRoundUp(x uint64) (addr uint64, ok bool) { addr = PageRoundDown(x + PageSize - 1) ok = addr >= x return }