The NetBSD UVM library provides real virtual memory support including separation of address spaces, demand paging, memory mapped file, memory protection, and so on. This library is based on NetBSD’s new virtual memory system UVM (see http://www.netbsd.org/Documentation/kernel/uvm.html). Since this library is derived from a virtual memory system for UNIX, the services provided are almost the same as for a traditional UNIX virtual memory system. With the UVM library, applications are able to manage multiple user address spaces and map them onto CPU’s virtual address space. Also, applications can allocate more memory than there is physical memory available on machines where a disk swap partition is available.
Although the UVM library provides a method to separate kernel and user address space, it does not offer a way to switch between user and kernel mode. Such functions are separately provided by the Simple Process Library (See Section 30).
The UVM library depends on the OSKit’s POSIX Threads library (See Section 29). The UVM library is thread safe, multiple threads can manipulate the same user address space.
Currently only the x86 implementation is available but it will be not difficult to port to another architecture where NetBSD has already ported.
The x86 LDT (Local Descriptor Table) is not supported.
No single threaded implementation is provided.
The address space layout is shown in figure 28.1. Since UVM was originally designed for UNIX, the address space layout is similar to traditional UNIXes; virtual address spaces are separated into two regions, the kernel and user regions. The same kernel image appears in every virtual address space and only one user address space can be mapped in the user region. Unlike NetBSD, the kernel does not reside at higher addresses but remains at its loaded address. The user region is above the kernel region. The base address of a user address space is fixed at 0x40000000. A thread running in kernel mode (CPL (Current Privilege Level < 3) can access both the kernel and the user region and a thread running in user mode (CPL=3) can access only the user region.
At the initialization of the UVM library, all physical memory (0 to phys_max_lmm) are mapped as V = R (virtual address = real (physical) address). The kernel pages are changed to read-only but all other pages in the V = R range are left read-write since the OSKit LMM library needs write access. A stack redzone is also created, although stack overflows are currently fatal since there is not enough support to allow recovery.
The kernel heap area used for memory allocation (See Section 28.4) starts at 0x30000000. The area 0x3f800000 to 0x3fffffff is used by the UVM internally for page table manipulation.
The UVM library also provides an implementation of the oskit_mem COM interface (See Section 13.4). This implementation allocates memory from the kernel heap area described above. The kernel heap area can grow automatically and might be paged out. This interface will be the default memory allocator if the UVM library is used and thus, the default malloc implementation uses this interface.
There are some restrictions with this implementation of the oskit_mem interface:
Also, the UVM library provides the following osenv_mem_* APIs for the OSKit device driver framework (See Section 8).
With the UVM library, all threads are associated with a particular virtual address space. Initially all threads are associated with the kernel-only virtual address space, which maps only the kernel area. oskit_uvm_vmspace_set associates a calling thread with a specified virtual address space. Once such an association is made, that virtual address space is used for all memory access from the thread.
All page faults are processed by the page fault handler (oskit_uvm_pfault_handler). The page fault handler runs using the context of the thread that caused the page fault. If a page fault is processed successfully, the context of the thread that caused the page fault is automatically restored. In case of an error such as a page protection violation or an invalid memory access, the application’s handler is called if one was installed with oskit_uvm_handler_set. Otherwise, the SIGSEGV or SIGKILL signal is raised by oskit_sendsig.
The signature of an application fault handler is as follows:
typedef void (*oskit_uvm_handler)(struct oskit_vmspace *vm, int signo, struct trap_state *frame); |
A page fault handler should either map a page of memory at the indicated virtual address or terminate the thread which caused the page fault.
The UVM library provides implementations of the mmap, munmap, mprotect, and madvise system calls.
Initialize the UVM library. It does:
This function must be called after pthread_init but before any other threads have been created. This is because the OSKit UVM library associates information with every thread and currently the implementation relies on that information being set before the thread starts.
Initialize the swap subsystem. This function must be called before calling oskit_uvm_swap_on.
oskit_uvm_swap_on, oskit_uvm_swap_off
Add a swap area. Multiple such areas are possible.
oskit_uvm_swap_init, oskit_uvm_swap_off, swapon
Remove a swap area. Currently this function is broken due to a bug in the NetBSD code on which this library is based.
oskit_uvm_swap_init, oskit_uvm_swap_on, swapoff
Start the page daemon thread. Must be called after calling oskit_uvm_swap_init.
oskit_uvm_swap_init
#include <oskit/uvm.h>
oskit_error_t oskit_uvm_create(oskit_size_t size, [out] oskit_vmspace_t *out_vm);
Create a virtual address space. The virtual address space information is stored in the opaque oskit_vmspace_t structure which is defined in <oskit/uvm.h>. This function does not map the created address space as the current address space. Use oskit_uvm_vmspace_set to access the created virtual address space.
oskit_uvm_destroy
Destroy a virtual address space. No thread must be associated with the specified virtual address space.
oskit_uvm_create
#include <oskit/uvm.h>
oskit_error_t oskit_uvm_mmap(oskit_vmspace_t vm, [in/out] oskit_addr_t *addr, oskit_size_t size, int prot, int flags, oskit_iunknown_t *iunknown, oskit_off_t foff);
Map an object into a virtual address space. This is similar to mmap but allows the virtual address space affected to be explicitly specified.
Returns 0 on success, or an error code specified in #include <oskit/errno.h>, on error.
oskit_uvm_munmap
#include <oskit/uvm.h>
oskit_error_t oskit_uvm_munmap(oskit_vmspace_t vm, oskit_addr_t addr, oskit_size_t len);
Deletes the mapping of the specified address range from the given address space. Causes further references to addresses within the range to generate invalid memory references.
Returns 0 on success, or an error code specified in #include <oskit/errno.h>, on error.
oskit_uvm_mmap
#include <oskit/uvm.h>
oskit_error_t oskit_uvm_mprotect(oskit_vmspace_t vm, oskit_addr_t addr, oskit_size_t len, int prot);
Changes the specified pages to have protection prot.
Returns 0 on success, or an error code specified in #include <oskit/errno.h>, on error.
oskit_uvm_mmap
#include <oskit/uvm.h>
oskit_error_t oskit_uvm_madvise(struct oskit_vmspace *vm, oskit_addr_t addr, oskit_size_t len, int behav);
Gives the UVM a hint of the memory access behavior of the application. Known behaviors are given in #include <oskit/c/sys/mman.h>:
#define MADV_NORMAL 0 /* no further special treatment */ #define MADV_RANDOM 1 /* expect random page references */ #define MADV_SEQUENTIAL 2 /* expect sequential page references */ #define MADV_WILLNEED 3 /* will need these pages */ #define MADV_DONTNEED 4 /* dont need these pages */ #define MADV_SPACEAVAIL 5 /* insure that resources are reserved */ #define MADV_FREE 6 /* pages are empty, free them */ |
Returns 0 on success, or an error code specified in #include <oskit/errno.h>, on error.
#include <oskit/uvm.h>
oskit_vmspace_t oskit_uvm_vmspace_set(oskit_vmspace_t vm); oskit_vmspace_t oskit_uvm_kvmspace;
Associate the calling thread with specified virtual memory space. Once a thread is associated, all memory accesses from the thread refer to the associated virtual address space. Initially all threads are associated with the virtual address space oskit_uvm_kvmspace which maps the kernel region only.
Returns the previous virtual address space associated with the calling thread.
oskit_uvm_vmspace_get
Obtain the virtual address space associated with the calling thread.
Returns the virtual address space associated with the calling thread.
oskit_uvm_vmspace_set
Copy data from a user address space into the kernel address space if allowed by the current memory protection bits. The entire source memory region specified by from and len must be accessible for read by the current thread’s address space.
Returns 0 on success, OSKIT_E_POINTER if the user memory region is not accessible.
#include <oskit/uvm.h>
oskit_error_t copyinstr(const void *from, void *to, oskit_size_t len, [out] oskit_size_t *lencopied);
Copy a zero-terminated string of at most len bytes from a user address space into the kernel address space if allowed by the current memory protection bits. The source string specified by from must be accessible for read by the current thread’s address space.
Returns 0 on success, OSKIT_E_POINTER if the user memory region is not accessible.
Copy data from the kernel address space into a user address space if allowed by the current memory protection bits. The entire destination memory region specified by to and len must be accessible for write by the current thread’s address space.
Returns 0 on success, OSKIT_E_POINTER if the user memory region is not accessible.
#include <oskit/uvm.h>
oskit_error_t copyoutstr(const void *from, void *to, oskit_size_t len, [out] oskit_size_t *lencopied);
Copy a zero-terminated string of at most len bytes from the kernel address space into a user address space if allowed by the current memory protection bits. The destination string memory specified by to must be accessible for write by the current thread’s address space.
Returns 0 on success, OSKIT_E_POINTER if the user memory region is not accessible.
#include <oskit/uvm.h>
int oskit_uvm_mem_map_phys(oskit_addr_t pa, oskit_size_t size, void **addr, int flags);
Map the specified physical memory range into a virtual address space. This call is exactly the same as osenv_mem_map_phys.
Returns 0 on success, non-zero on error.
osenv_mem_map_phys
Install a handler that is called when the default page fault handler fails. See Section 28.6.
oskit_uvm_handler_get
Obtain the handler installed for the specified virtual address space.
oskit_uvm_handler_set
Install a hook that is called on every context switch. This API is intended to be used from the Simple Process Library.