diff --git a/.gitignore b/.gitignore index d11aba0..b250e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ bochsrc.txt config test.img eth_* +doc/_build diff --git a/Makefile b/Makefile index d44d3ae..b8800ce 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,9 @@ include etc/make/$(ARCH)/conf.mk all: mkdirs config $(TARGETS) +docs: $(HDRS) + @make -C doc + clean: @rm -rf $(O) diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..32720a9 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,2 @@ +all: + sphinx-build . _build diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..ae5a614 --- /dev/null +++ b/doc/conf.py @@ -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" diff --git a/doc/dev/pci.rst b/doc/dev/pci.rst new file mode 100644 index 0000000..560401d --- /dev/null +++ b/doc/dev/pci.rst @@ -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 `_ diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..1b00629 --- /dev/null +++ b/doc/index.rst @@ -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` diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 0000000..27f573b --- /dev/null +++ b/doc/make.bat @@ -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 diff --git a/doc/sys/mem.rst b/doc/sys/mem.rst new file mode 100644 index 0000000..4a30b0c --- /dev/null +++ b/doc/sys/mem.rst @@ -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. diff --git a/include/sys/mem/shmem.h b/include/sys/mem/shmem.h index 8418d22..7f07a5b 100644 --- a/include/sys/mem/shmem.h +++ b/include/sys/mem/shmem.h @@ -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); diff --git a/include/sys/mem/vmalloc.h b/include/sys/mem/vmalloc.h index de75763..731bc5a 100644 --- a/include/sys/mem/vmalloc.h +++ b/include/sys/mem/vmalloc.h @@ -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);