Add some docs

This commit is contained in:
Mark
2020-04-07 17:39:37 +03:00
parent 3621a567a8
commit 05b8ad8aaf
10 changed files with 277 additions and 41 deletions
+1
View File
@@ -6,3 +6,4 @@ bochsrc.txt
config
test.img
eth_*
doc/_build
+3
View File
@@ -18,6 +18,9 @@ include etc/make/$(ARCH)/conf.mk
all: mkdirs config $(TARGETS)
docs: $(HDRS)
@make -C doc
clean:
@rm -rf $(O)
+2
View File
@@ -0,0 +1,2 @@
all:
sphinx-build . _build
+54
View File
@@ -0,0 +1,54 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'yggdrasil'
copyright = '2020, alnyan'
author = 'alnyan'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
highlight_language = "c"
+42
View File
@@ -0,0 +1,42 @@
PCI
===
Creating a PCI driver
---------------------
PCI driver source code file reside in ``drivers/pci`` directory. Writing a
driver for a device is as simple as just registering a function which will
be triggered once matching device/class is found when enumerating a bus.
Here's a simple "stub" driver for some superficial device and class::
#include "drivers/pci/pci.h"
#include "sys/attr.h"
static void my_class_driver(pci_device_t *dev) { ... }
static void my_device_driver(pci_device_t *dev) { ... }
static __init void register_drivers(void) {
pci_add_class_driver(0x123456, my_class_driver, "my-class-driver");
pci_add_device_driver(PCI_ID(0x1234, 0x5678), my_device_driver, "my-device-driver");
}
Weird number ``0x123456`` here repesents ``(class, subclass, prog. if)`` tuple. For
example, for ``(0x0C, 0x03, 0x00)`` (USB UHCI) this value will is ``0x0C0300``. If
prog. if byte is set to ``0xFF``, then all prog. if values of devices are matched.
In driver code, configuration space words can be read and written::
uint32_t pci_config_read_dword(struct pci_device *dev, uint16_t off);
void pci_config_write_dword(struct pci_device *dev, uint16_t off, uint32_t val);
And the following ``off`` values are defined:
* ``PCI_CONFIG_ID`` --- device identification
* ``PCI_CONFIG_CMD`` --- device control and status
* ``PCI_CONFIG_CLASS`` --- class, subclass etc.
* ``PCI_CONFIG_BAR(n)`` --- Base Address Registers
* ``PCI_CONFIG_IRQ`` --- IRQ information
For the actual meaning of these configuration space words, see `corresponding osdev wiki
page <https://wiki.osdev.org/PCI>`_
+18
View File
@@ -0,0 +1,18 @@
.. yggdrasil documentation master file, created by
sphinx-quickstart on Tue Apr 7 16:09:56 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
yggdrasil kernel documentation
==============================
.. toctree::
:maxdepth: 2
:caption: Contents:
sys/mem.rst
dev/pci.rst
.. * :ref:`genindex`
.. * :ref:`modindex`
.. * :ref:`search`
+35
View File
@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
+122
View File
@@ -0,0 +1,122 @@
Memory management functions
===========================
The document describes the facilities implemented for managing virtual and physical
memory and related resources.
All virtual memory is split into two spaces: kernel and user. ``0xFFFFFF0000000000``
is the boundary, any addresses beyond which are considered kernel-space and any address
lower which is user-space.
Physical memory management
--------------------------
Kernel functions can allocate physical memory using the following two functions::
uintptr_t mm_phys_alloc_page(void);
uintptr_t mm_phys_alloc_contiguous(size_t count);
These two functions return physical pointers (or ``MM_NADDR`` in case of failure).
The first one allocates and returns a single page, whereas the latter one attempts
an allocation of contiguous physical range of ``count`` 4KiB pages.
After usage, the pages can be freed using ``mm_phys_free_page()``::
void mm_phys_free_page(uintptr_t addr)
This function only deallocates a single page, so for contiguous ranges, a loop is
required::
for (size_t i = 0; i < count; ++i) {
mm_phys_free_page(addr + i * MM_PAGE_SIZE);
}
Kernel heap
-----------
For small and frequent allocations, heap is available::
void *kmalloc(size_t size);
void kfree(void *ptr);
These two functions should work exactly as ``malloc(3)``/``free(3)`` everyone's
familiar with, so no further description is needed.
Kernel virtual memory management
--------------------------------
Function useful in developing kernel features are described here. For userspace virtual
memory facilities see `Userspace memory management`_.
The kernel has lower 1GiB of physical memory mapped at ``0xFFFFFF0000000000``, which
allows for easier access to physical memory without needing to map it first.
``MM_VIRTUALIZE(addr)`` macro is used to convert a physical memory address into a
kernel-space pointer.
Likewise, ``MM_PHYS(addr)`` macro converts the virtual address to physical one.
.. warning ::
These functions don't provide any boundary check and may cause undefined behavior
if provided invalid input.
Userspace memory management
---------------------------
Userspace memory consists of two kinds of virtual regions:
1. Unique ``virt`` -> ``phys`` mappings. These always correspond to unqiue physical
pages.
2. Shared memory regions (``mmap()`` ed or any other kind). These may refer either
to shared physical memory regions, or can be file/device-mapped.
Primary function for manipulating non-shared mappings is ``mm_map_single()`` function::
int mm_map_single(mm_space_t space, uintptr_t virt, uintptr_t phys, uint64_t flags);
The function is used to map a single virtual memory page to a physical address, creating
``virt .. virt + MM_PAGE_SIZE`` -> ``phys .. phys + MM_PAGE_SIZE`` association. Currently,
only 4KiB pages can be mapped this way. The ``flags`` parameter controls permission bits
for the page and can be one of the following:
* ``MM_PAGE_USER`` --- the page is accessible from userspace code
* ``MM_PAGE_WRITE`` --- the page is writable
* ``MM_PAGE_EXEC`` --- code execution is allowed for this page
A mapped address can be queried for its corresponding physical address using
``mm_map_get()``::
uintptr_t mm_map_get(mm_space_t space, uintptr_t virt, uint64_t *flags)
Corresponding physical address is returned on success, optionally setting ``*flags``
to mapping permission bits. In case of failure (the address is not mapped), ``MM_NADDR``
is returned.
Finally, once the virtual mapping is no longer needed, it can be removed from
process' virtual address tables using ``mm_umap_single``::
uintptr_t mm_umap_single(mm_space_t space, uintptr_t virt, uint32_t size);
``size`` parameter here limits which kinds of pages are unmapped, returning ``MM_NADDR``
if trying, for example, to unmap a 4KiB page, but the virtual address actually represens
2MiB page. ``size`` takes the following values:
* 0 --- Any mapping is removed
* 1 --- 4KiB mapping is removed
On success, this function will return physical memory page address which was referred
to by ``virt`` in the memory space. Otherwise, ``MM_NADDR`` is reported.
Contiguous regions in memory spaces can be bound to physical memory using ``vmfind()``
and ``vmalloc()`` functions::
uintptr_t vmfind(const mm_space_t pd, uintptr_t from, uintptr_t to, size_t npages);
uintptr_t vmalloc(mm_space_t pd, uintptr_t from, uintptr_t to, size_t npages, uint64_t flags);
``vmfind()`` searches for a contiguous free space of ``npages`` * 4KiB pages in
``from .. to`` range and returns it. ``vmalloc()`` does the same, additionally mapping
the region to a set of physical pages with given permission ``flags``. Both functions
return ``MM_NADDR`` in case of failure.
Then, ``vmfree()`` function can be used to release the region, unmapping and freeing
physical pages.
-8
View File
@@ -48,19 +48,11 @@ struct shm_region_ref {
} map;
};
// Setup an empty shared memory region of N pages
struct shm_region *shm_region_create(size_t count);
// Destroy an empty region
void shm_region_free(struct shm_region *region);
// Map a shared physical page region into
// thread's virtial memory space
uintptr_t shm_region_map(struct shm_region *region, struct thread *thr);
// Release all shared memory regions of the specified thread
// If noumap != 0, then thread's page structures are assumed
// to be non-existent and no virtual unmaps are performed
void shm_region_release_all(struct thread *thr, int noumap);
// Find a shared memory region by its virtual address
struct shm_region_ref *shm_region_find(struct thread *thr, uintptr_t ptr);
void shm_space_fork(struct thread *dst, struct thread *src);
-33
View File
@@ -8,39 +8,6 @@
#define VM_ALLOC_WRITE (MM_PAGE_WRITE)
#define VM_ALLOC_USER (MM_PAGE_USER)
/**
* @brief Find a free contiguous memory range inside a given one
* in a virtual memory space.
* @param pd Virtual memory space
* @param from Start of the range to allocate from
* @param to End of the range to allocate from
* @param npages Size of the needed range in 4K pages
* @return Address of the resulting range on success,
* MM_NADDR if such range cannot be allocated
*/
uintptr_t vmfind(const mm_space_t pd, uintptr_t from, uintptr_t to, size_t npages);
/**
* @brief Allocate a contiguous virtual memory range in a space
* within `from'..`to' limits and map it to a set of
* physical pages (which may not be contiguous).
* @param pd Virtual memory space
* @param from Start of the range to allocate from
* @param to End of the range to allocate from
* @param npages Count of 4K pages to allocate
* @param flags:
* * VM_ALLOC_USER - The range is available for userspace
* * VM_ALLOC_WRITE - The range is writable
* @return Virtual address of the resulting range on success,
* MM_NADDR otherwise
*/
uintptr_t vmalloc(mm_space_t pd, uintptr_t from, uintptr_t to, size_t npages, uint64_t flags);
/**
* @brief Deallocate a virtual memory range and physical pages
* it's mapped to.
* @param pd Virtual memory space
* @param addr Address of the beginning of the range
* @param npages Size of the range in 4K pages
*/
void vmfree(mm_space_t pd, uintptr_t addr, size_t npages);