.. index::
   pair: virtual mapping; design

.. _design-vm:


Virtual mapping
===============

.. mps:prefix:: design.mps.vm


Introduction
------------

:mps:tag:`intro` This the design of the VM interface. The VM interface
provides a simple, low-level, operating-system independent interface
to address-space. Each call to :c:func:`VMInit()` reserves (from the
operating-system) a single contiguous range of addresses, and updates
a :c:type:`VMStruct` thereafter used to manage this address-space. The VM
interface has separate implementations for each platform that supports
it (at least conceptually, in practice some of them may be the same).
The VM module provides a mechanism to reserve large (relative to the
amount of RAM) amounts of address space, and functions to map (back
with RAM) and unmap portions of this address space.

:mps:tag:`motivation` The VM is used by the VM Arena Class. It provides the
basic substrate to provide sparse address maps. Sparse address maps
have at least two uses: to encode information into the address of an
object which is used in tracing (the Zone Test) to speed things up; to
avoid fragmentation at the segment level and above (since the amount
of address space reserved is large compared to the RAM, the hope is
that there will also be enough address space somewhere to fit any
particular segment in).


Definitions
-----------

:mps:tag:`def.reserve` The *reserve* operation: exclusively reserve a
portion of the virtual address space without arranging RAM or backing
store for the virtual addresses. The intention is that no other
component in the process will make use of the reserved virtual
addresses, but in practice this may entail assuming a certain amount
of cooperation. When reserving address space, the requester simply
asks for a particular size, not a particular range of virtual
addresses. Accessing (read/write/execute) reserved addresses is
illegal unless those addresses have been mapped.

:mps:tag:`def.map` The *map* operation: arrange that a specified portion of
the virtual address space is mapped from the swap, effectively
allocating RAM and/or swap space for a particular range of addresses.
If successful, accessing the addresses is now legal. Only reserved
addresses should be mapped.

:mps:tag:`def.unmap` The *unmap* operation: the inverse of the map
operation. Arrange that a specified portion of the virtual address
space is no longer mapped, effectively freeing up the RAM and swap
space that was in use. Accessing the addresses is now illegal. The
addresses return to the reserved state.

:mps:tag:`def.vm` "VM" stands for Virtual Memory. Various meanings: a
processor architecture's virtual space and structure; the generic
idea, interface, or implementation of the MPS VM module; the C
structure (``struct VMStruct``) used to encapsulate the functionality
of the MPS VM module; an instance of such a structure.

:mps:tag:`def.vm.mps` In the MPS, a "VM" is a :c:type:`VMStruct`, providing access
to the single contiguous range of address-space that was reserved
(from the operating-system) when :c:func:`VMInit()` was called.


Interface
---------

.. c:function:: Size PageSize(void)

:mps:tag:`if.page.size` Return the operating system's "page size", that is,
the granularity with which the operating system can map and unmap
virtual memory. For speed (on systems like Windows where determining
the page size involves a system call), this is cached in each VM
descriptor and accessible via the :c:func:`VMPageSize()` function.

.. c:function:: Res VMParamFromArgs(void *params, size_t paramSize, ArgList args)

:mps:tag:`if.param.from.args` This function processes the keyword arguments
(the ones that are relevant to the VM implementation) in the ``args``
parameter and stores a description of them in the buffer pointed to by
``params`` (which is ``paramSize`` bytes long). It is an error if the
buffer is not big enough store the parameters for this VM
implementation.

.. c:function:: Res VMInit(VM vm, Size size, Size grainSize, void *params)

:mps:tag:`if.init` :c:func:`VMInit()` is responsible for reserving an amount
of virtual address space. The ``params`` argument points to a
parameter block initialized by a call to :c:func:`VMParamFromArgs()`. If
successful, the VM descriptor given by the parameter ``vm`` is
updated to describe the address space, and ``ResOK`` is returned. The
created VM has at least ``size`` bytes of virtual memory reserved
starting at an address which is a multiple of ``grainSize``.

If there's not enough address space to reserve a block of the given
size, ``ResRESOURCE`` is returned. The reserved virtual memory can be
mapped and unmapped by calling :c:func:`VMMap()` and :c:func:`VMUnmap()`.

.. c:function:: void VMFinish(VM vm)

:mps:tag:`if.finish` A VM is destroyed by calling :c:func:`VMFinish()`. Any
address space that was mapped through this VM is unmapped.

.. c:function:: Res VMMap(VM vm, Addr base, Addr limit)

:mps:tag:`if.map` Map the range of addresses from ``base`` (inclusive) to
``limit`` (exclusive) into memory. It is an error if the range does
not lie between ``VMBase(vm)`` and ``VMLimit(vm)``, or if ``base`` and
``limit`` are not multiples of ``VMPageSize(vm)``. Return ``ResOK``
if successful, ``ResMEMORY`` if not.

.. c:function:: void VMUnmap(VM vm, Addr base, Addr limit)

:mps:tag:`if.unmap` Unmap the range of addresses from ``base`` (inclusive)
to ``limit`` (exclusive). The conditions are the same as for
:c:func:`VMMap()`.

.. c:function:: Addr VMBase(VM vm)

:mps:tag:`if.base` Return the base address of the VM (the lowest address in
the VM that is a multiple of the grain size).

.. c:function:: Addr VMLimit(VM vm)

:mps:tag:`if.limit` Return the limit address of the VM (the limit of the
last grain that is wholly inside the VM).

.. c:function:: Size VMReserved(VM vm)

:mps:tag:`if.reserved` Return the total number of bytes of address space
reserved by the VM. (This may include addresses that are not available
for mapping because of the requirement for ``VMBase(vm)`` and
``VMLimit(vm)`` to be multiples of the grain size.)

.. c:function:: Size VMMapped(VM vm)

:mps:tag:`if.mapped` Return the total number of bytes of address space
currently mapped into memory by the VM.


Notes
-----

:mps:tag:`testing` It is important to test that a VM implementation will
work in extreme cases.

:mps:tag:`testing.large` It must be able to reserve a large address space.
Clients will want multi-GB spaces, more than that OSs will allow. If
they ask for too much, :c:func:`mps_arena_create()` (and hence
:c:func:`VMInit()`) must fail in a predictable way.

:mps:tag:`testing.larger` It must be possible to allocate in a large space;
sometimes commiting will fail, because there's not enough space to
replace the "reserve" mapping. See request.epcore.160201_ for details.

.. _request.epcore.160201: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/epcore/160201

:mps:tag:`testing.lots` It must be possible to have lots of mappings. The OS
must either combine adjacent mappings or have lots of space in the
kernel tables. See request.epcore.160117_ for ideas on how to test
this.

.. _request.epcore.160117: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/epcore/160117