<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.friendos.dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Veloya</id>
	<title>FriendOS Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.friendos.dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Veloya"/>
	<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php/Special:Contributions/Veloya"/>
	<updated>2026-05-12T01:14:11Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.38.4</generator>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=Limine-zig&amp;diff=32</id>
		<title>Limine-zig</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=Limine-zig&amp;diff=32"/>
		<updated>2023-09-01T00:48:16Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Added basic info on code for getting x86 limine with zig working&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Limine is a modern boot protocol developed with the Limine bootloader, this page covers how to take advantage of that protocol in a zig-based kernel. This does not cover making an ISO, just the code required, in the future this will change, but this is it for now.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
This code should work as long as you have zig 0.11.0 installed. As of writing this it is untested, but if you do test it out, and it works, remove this sentence, if it doesn't work please message me (veloya) on the discord server with the error message.&lt;br /&gt;
&lt;br /&gt;
=== src/main.zig ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;zig&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
const limine = @import(&amp;quot;./limine.zig&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
pub export var FRAMEBUF: limine.FramebufferRequest = .{};&lt;br /&gt;
&lt;br /&gt;
export fn _start() void {&lt;br /&gt;
    // Get an array of framebuffer pointers&lt;br /&gt;
    const framebuffers = FRAMEBUF.response.?.framebuffers();&lt;br /&gt;
&lt;br /&gt;
    // Get the first framebuffer provided&lt;br /&gt;
    const framebuffer = framebuffers[0];&lt;br /&gt;
&lt;br /&gt;
    // Height is in pixels, convert it to bytes&lt;br /&gt;
    const byte_height = framebuffer.height * 4;&lt;br /&gt;
&lt;br /&gt;
    // Get total byte size by multiplying the height by the stride (or pitch)&lt;br /&gt;
    const size = byte_height * framebuffer.pitch;&lt;br /&gt;
&lt;br /&gt;
    // Fill the framebuffer with white&lt;br /&gt;
    for (0..size) |index| {&lt;br /&gt;
        framebuffer.address[index] = 0xff;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    while (true) {}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== src/limine.zig ===&lt;br /&gt;
The limine.zig file can be found [https://github.com/limine-bootloader/limine-zig/tree/trunk here]&lt;br /&gt;
&lt;br /&gt;
=== config/linker.ld ===&lt;br /&gt;
TODO: Write a better linker script&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
ENTRY(_start)&lt;br /&gt;
&lt;br /&gt;
SECTIONS&lt;br /&gt;
{&lt;br /&gt;
    . = 0xffffffff80000000;&lt;br /&gt;
&lt;br /&gt;
    .text : ALIGN(4096) { *(.text .text.*) }&lt;br /&gt;
    .rodata : ALIGN(4096) { *(.rodata .rodata.*) }&lt;br /&gt;
    .data : ALIGN(4096) { *(.data .data.*) }&lt;br /&gt;
    .dynamic : ALIGN(4096) { *(.dynamic) }&lt;br /&gt;
    .bss : ALIGN(4096) {&lt;br /&gt;
        *(.bss .bss.*)&lt;br /&gt;
        *(COMMON)&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== config/limine.cfg ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;cfg&amp;quot;&amp;gt;&lt;br /&gt;
boot &amp;quot;test-kern&amp;quot; {&lt;br /&gt;
    protocol = &amp;quot;limine&amp;quot;;&lt;br /&gt;
    kernel-path = &amp;quot;boot:///boot/test-kern&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== build.zig ===&lt;br /&gt;
A build.zig file is required to properly customize the target.&amp;lt;syntaxhighlight lang=&amp;quot;zig&amp;quot;&amp;gt;&lt;br /&gt;
const std = @import(&amp;quot;std&amp;quot;);&lt;br /&gt;
const Target = @import(&amp;quot;std&amp;quot;).Target;&lt;br /&gt;
const CrossTarget = @import(&amp;quot;std&amp;quot;).zig.CrossTarget;&lt;br /&gt;
const Feature = @import(&amp;quot;std&amp;quot;).Target.Cpu.Feature;&lt;br /&gt;
&lt;br /&gt;
pub fn build(b: *std.Build) !void {&lt;br /&gt;
    var stdout = std.io.getStdOut();&lt;br /&gt;
&lt;br /&gt;
    const target = CrossTarget{&lt;br /&gt;
        .cpu_arch = Target.Cpu.Arch.x86_64,&lt;br /&gt;
        .os_tag = Target.Os.Tag.freestanding,&lt;br /&gt;
        .abi = Target.Abi.none,&lt;br /&gt;
    };&lt;br /&gt;
    const optimize = b.standardOptimizeOption(.{});&lt;br /&gt;
&lt;br /&gt;
    const kernel = b.addExecutable(.{&lt;br /&gt;
        .name = &amp;quot;test-kern&amp;quot;,&lt;br /&gt;
        .root_source_file = .{ .path = &amp;quot;src/main.zig&amp;quot; },&lt;br /&gt;
        .target = target,&lt;br /&gt;
        .optimize = optimize,&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    kernel.setLinkerScriptPath(.{ .path = &amp;quot;config/linker.ld&amp;quot; });&lt;br /&gt;
    kernel.code_model = .medium;&lt;br /&gt;
    kernel.rdynamic = true;&lt;br /&gt;
    kernel.pie = true;&lt;br /&gt;
&lt;br /&gt;
    _ = try stdout.write(&amp;quot;Building kernel\n&amp;quot;);&lt;br /&gt;
    b.installArtifact(kernel);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=Category:X86&amp;diff=31</id>
		<title>Category:X86</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=Category:X86&amp;diff=31"/>
		<updated>2023-08-31T23:45:39Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Created the page, time to work on it&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;TODO&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=30</id>
		<title>RISC-V/Paging</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=30"/>
		<updated>2023-08-31T23:34:34Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Added information on Sv32, also added a section for RSW bits.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Paging in RISC-V can be enabled by first setting up a page table, then pointing the CPU to that page table via the [[RISC-V/CSR#satp | &amp;lt;code&amp;gt;satp&amp;lt;/code&amp;gt; CSR]]. This article will describe these steps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Paging type can be determined at runtime using the device tree, its typically located in the CPU specific nodes, such as `cpu@x` where x denotes which number of CPU it is, it will be located under the `mmu-type` property in the form of a string such as `riscv,svY` is either &amp;quot;32&amp;quot;, &amp;quot;39&amp;quot;, &amp;quot;48&amp;quot;, or &amp;quot;57&amp;quot; depending on paging type.&lt;br /&gt;
&lt;br /&gt;
== Terminology ==&lt;br /&gt;
&lt;br /&gt;
There are many acronyms and other terms that are used in the RISC-V specification which, for the purpose of brevity and consistency, will be used in this article as well. I will assume a baseline knowledge of the purpose and basics of paging and virtual addressing.&lt;br /&gt;
&lt;br /&gt;
=== PPNs and VPNs ===&lt;br /&gt;
&lt;br /&gt;
A physical page number (or PPN) is a unique, page-aligned identifier for a page in physical memory. The number of PPN segments for an address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
To construct a PPN from a physical address, you can simply (logically, not arithmetically) shift the value right by 12 bits (as 2**12 = 4096). Note that this operation will truncate the offset into the page, if the address was not page-aligned.&lt;br /&gt;
&lt;br /&gt;
A virtual page number (or VPN) is very similar to a PPN, but in the virtual address space. Again, the number of VPN segments for a virtual address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
=== RSW ===&lt;br /&gt;
Bits marked as RSW indicate that they can be written, read, and handled in any way the OS wants without causing issues. These can be used as OS-specific flags.&lt;br /&gt;
&lt;br /&gt;
== RISC-V Page Table Layout ==&lt;br /&gt;
&lt;br /&gt;
There are various paging modes available in the RISC-V ISA, differing by the number of virtual address bits:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Memory Size !! SXLEN&lt;br /&gt;
|-&lt;br /&gt;
| Sv32 || 4GiB || 32-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv39 || 512GiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv48 || 256TiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv57 || 128PiB || 64-bit only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Feel free to choose between these modes as you see fit; this page will describe all of them. Note that in 64-bit systems, Sv39 is more than enough for most hobby operating systems (though it is not much more difficult to go further).&lt;br /&gt;
&lt;br /&gt;
=== Sv32 ===&lt;br /&gt;
&lt;br /&gt;
Sv32 is the only available paging mode for a 32 bit system, it provides 32 bit virtual addresses, and 34 bit physical addresses.&lt;br /&gt;
&lt;br /&gt;
'''Address and PTE Layouts'''&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Sv32 virtual address&lt;br /&gt;
!Bit Range (inclusive)&lt;br /&gt;
!Length (bits)&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|0-11&lt;br /&gt;
|12&lt;br /&gt;
|Page offset&lt;br /&gt;
|-&lt;br /&gt;
|12-21&lt;br /&gt;
|10&lt;br /&gt;
|Page VPN (VPN[0])&lt;br /&gt;
|-&lt;br /&gt;
|22-31&lt;br /&gt;
|10&lt;br /&gt;
|Megapage VPN (VPN[0])&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Sv32 physical address&lt;br /&gt;
!Bit Range (inclusive)&lt;br /&gt;
!Length (bits)&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|0-11&lt;br /&gt;
|12&lt;br /&gt;
|Page offset&lt;br /&gt;
|-&lt;br /&gt;
|12-21&lt;br /&gt;
|10&lt;br /&gt;
|PPN[0]&lt;br /&gt;
|-&lt;br /&gt;
|22-33&lt;br /&gt;
|12&lt;br /&gt;
|PPN[1]&lt;br /&gt;
|}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Sv32 page table entry layout&lt;br /&gt;
!Bit Range (inclusive)&lt;br /&gt;
!Length (bits)&lt;br /&gt;
!Description&lt;br /&gt;
|-&lt;br /&gt;
|0&lt;br /&gt;
|1&lt;br /&gt;
|Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1&lt;br /&gt;
|Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|1&lt;br /&gt;
|Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|1&lt;br /&gt;
|U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|1&lt;br /&gt;
|Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|1&lt;br /&gt;
|Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|1&lt;br /&gt;
|Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
|8-9&lt;br /&gt;
|2&lt;br /&gt;
|RSW&lt;br /&gt;
|-&lt;br /&gt;
|10-19&lt;br /&gt;
|10&lt;br /&gt;
|&amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|20-31&lt;br /&gt;
|12&lt;br /&gt;
|&amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv39 ===&lt;br /&gt;
&lt;br /&gt;
Sv39 is the simplest paging mode for 64-bit systems (though larger modes are not too much more difficult to implement). It supports the standard 4KiB pages, as well as 2MiB megapages and 1GiB gigapages. &lt;br /&gt;
&lt;br /&gt;
TODO: actually describe what a page table looks like, and also PTE's RWX encoding&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 30-55 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-53 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv48 ===&lt;br /&gt;
&lt;br /&gt;
TODO: Explain&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 39-47 || 9 || (&amp;lt;nowiki&amp;gt;VPN[3]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || (&amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || (&amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || (&amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 39-47 || 9 || (&amp;lt;nowiki&amp;gt;PPN[3]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-36 || 9 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 37-53 || 17 || &amp;lt;nowiki&amp;gt;PPN[3]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv57 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
== Enabling Paging ==&lt;br /&gt;
&lt;br /&gt;
TODO: Explain better&lt;br /&gt;
&lt;br /&gt;
Paging on RISC-V is split into 4 different modes, 32 bit, 39 bit, 48 bit, and 57 bit. 32 bit is only available on 32 bit systems, and not available on 64 bit systems.&lt;br /&gt;
Paging is handled by the `SATP` CSR which contains 3 separate sections, the PPN(Physical Page Number) which is similar to the physical address, the ASID(Address Space Identifier), and finally, the Mode. To enable paging you need to write a paging mode into the Mode section of the `SATP` register. On 64 bits you can write 0 for no paging, 8 for Sv39, 9 for Sv48, 10 for Sv57, and in the future 11 will be available for 64 bits. On 32 bits you can write 0 for no paging, or 1 for Sv32.&lt;br /&gt;
&lt;br /&gt;
Rust example code:&lt;br /&gt;
&lt;br /&gt;
32 bits&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
pub unsafe fn enable_paging(root_page_table: usize) {&lt;br /&gt;
    let ppn = root_page_table &amp;gt;&amp;gt; 12;&lt;br /&gt;
    let mode = 1 &amp;lt;&amp;lt; 31;&lt;br /&gt;
&lt;br /&gt;
    let write = mode | ppn;&lt;br /&gt;
&lt;br /&gt;
    core::arch::asm(&lt;br /&gt;
        &amp;quot;csrw satp, {}&amp;quot;,&lt;br /&gt;
        in(reg) write&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
64 bits&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
pub unsafe fn enable_paging(root_page_table: usize, mode: u8) {&lt;br /&gt;
    let ppn = root_page_table &amp;gt;&amp;gt; 12;&lt;br /&gt;
    let new_mode = (mode &amp;amp; 0xf) &amp;lt;&amp;lt; 60;&lt;br /&gt;
&lt;br /&gt;
    let write = mode | ppn;&lt;br /&gt;
&lt;br /&gt;
    core::arch::asm(&lt;br /&gt;
        &amp;quot;csrw satp, {}&amp;quot;,&lt;br /&gt;
        in(reg) write&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=CLINT&amp;diff=29</id>
		<title>CLINT</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=CLINT&amp;diff=29"/>
		<updated>2023-02-16T03:15:06Z</updated>

		<summary type="html">&lt;p&gt;Veloya: VERY basic description of it, as well as a memory map as specified by the following link, update HIGHLY recommended https://sifive.cdn.prismic.io/sifive/ad5577a0-9a00-45c9-a5d0-424a3d586060_u74_core_complex_manual_21G3.pdf&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The CLINT(Core-Local Interruptor) is a device made for handling interrupts occurring within a core, such as software interrupts. The CLINT operates on individual cores, meaning there is 1 for each core.&lt;br /&gt;
&lt;br /&gt;
== SiFive U74 CLINT ==&lt;br /&gt;
Memory layout:&lt;br /&gt;
&lt;br /&gt;
base + 0x0000: MSIP, used for generating machine level interrupts&lt;br /&gt;
&lt;br /&gt;
base + 0x0004: Reserved&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
base + 0x3FFF: Reserved&lt;br /&gt;
&lt;br /&gt;
base + 0x4000: MTIMECMP, not sure&lt;br /&gt;
&lt;br /&gt;
base + 0x4008: Reserved&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
base + 0xBFF7: Reserved&lt;br /&gt;
&lt;br /&gt;
base + 0xBFF8: mtime, Timer register&lt;br /&gt;
&lt;br /&gt;
base + 0xC000: Reserved&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=28</id>
		<title>RISC-V/Paging</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=28"/>
		<updated>2023-02-10T23:18:03Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Added paging enabling code, and a short explanation on it&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Paging in RISC-V can be enabled by first setting up a page table, then pointing the CPU to that page table via the [[RISC-V/CSR#satp | &amp;lt;code&amp;gt;satp&amp;lt;/code&amp;gt; CSR]]. This article will describe these steps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Paging type can be determined at runtime using the device tree, its typically located in the CPU specific nodes, such as `cpu@x` where x denotes which number of CPU it is, it will be located under the `mmu-type` property in the form of a string such as `riscv,svY` is either &amp;quot;32&amp;quot;, &amp;quot;39&amp;quot;, &amp;quot;48&amp;quot;, or &amp;quot;57&amp;quot; depending on paging type.&lt;br /&gt;
&lt;br /&gt;
== Terminology ==&lt;br /&gt;
&lt;br /&gt;
There are many acronyms and other terms that are used in the RISC-V specification which, for the purpose of brevity and consistency, will be used in this article as well. I will assume a baseline knowledge of the purpose and basics of paging and virtual addressing.&lt;br /&gt;
&lt;br /&gt;
=== PPNs and VPNs ===&lt;br /&gt;
&lt;br /&gt;
A physical page number (or PPN) is a unique, page-aligned identifier for a page in physical memory. The number of PPN segments for an address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
To construct a PPN from a physical address, you can simply (logically, not arithmetically) shift the value right by 12 bits (as 2**12 = 4096). Note that this operation will truncate the offset into the page, if the address was not page-aligned.&lt;br /&gt;
&lt;br /&gt;
A virtual page number (or VPN) is very similar to a PPN, but in the virtual address space. Again, the number of VPN segments for a virtual address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
== RISC-V Page Table Layout ==&lt;br /&gt;
&lt;br /&gt;
There are various paging modes available in the RISC-V ISA, differing by the number of virtual address bits:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Memory Size !! SXLEN&lt;br /&gt;
|-&lt;br /&gt;
| Sv32 || 4GiB || 32-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv39 || 512GiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv48 || 256TiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv57 || 128PiB || 64-bit only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Feel free to choose between these modes as you see fit; this page will describe all of them. Note that in 64-bit systems, Sv39 is more than enough for most hobby operating systems (though it is not much more difficult to go further).&lt;br /&gt;
&lt;br /&gt;
=== Sv32 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Sv39 ===&lt;br /&gt;
&lt;br /&gt;
Sv39 is the simplest paging mode for 64-bit systems (though larger modes are not too much more difficult to implement). It supports the standard 4KiB pages, as well as 2MiB megapages and 1GiB gigapages. &lt;br /&gt;
&lt;br /&gt;
TODO: actually describe what a page table looks like, and also PTE's RWX encoding&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 30-55 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-53 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv48 ===&lt;br /&gt;
&lt;br /&gt;
TODO: Explain&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 39-47 || 9 || (&amp;lt;nowiki&amp;gt;VPN[3]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || (&amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || (&amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || (&amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 39-47 || 9 || (&amp;lt;nowiki&amp;gt;PPN[3]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-36 || 9 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 37-53 || 17 || &amp;lt;nowiki&amp;gt;PPN[3]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv57 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
== Enabling Paging ==&lt;br /&gt;
&lt;br /&gt;
TODO: Explain better&lt;br /&gt;
&lt;br /&gt;
Paging on RISC-V is split into 4 different modes, 32 bit, 39 bit, 48 bit, and 57 bit. 32 bit is only available on 32 bit systems, and not available on 64 bit systems.&lt;br /&gt;
Paging is handled by the `SATP` CSR which contains 3 separate sections, the PPN(Physical Page Number) which is similar to the physical address, the ASID(Address Space Identifier), and finally, the Mode. To enable paging you need to write a paging mode into the Mode section of the `SATP` register. On 64 bits you can write 0 for no paging, 8 for Sv39, 9 for Sv48, 10 for Sv57, and in the future 11 will be available for 64 bits. On 32 bits you can write 0 for no paging, or 1 for Sv32.&lt;br /&gt;
&lt;br /&gt;
Rust example code:&lt;br /&gt;
&lt;br /&gt;
32 bits&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
pub unsafe fn enable_paging(root_page_table: usize) {&lt;br /&gt;
    let ppn = root_page_table &amp;gt;&amp;gt; 12;&lt;br /&gt;
    let mode = 1 &amp;lt;&amp;lt; 31;&lt;br /&gt;
&lt;br /&gt;
    let write = mode | ppn;&lt;br /&gt;
&lt;br /&gt;
    core::arch::asm(&lt;br /&gt;
        &amp;quot;csrw satp, {}&amp;quot;,&lt;br /&gt;
        in(reg) write&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
64 bits&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
pub unsafe fn enable_paging(root_page_table: usize, mode: u8) {&lt;br /&gt;
    let ppn = root_page_table &amp;gt;&amp;gt; 12;&lt;br /&gt;
    let new_mode = (mode &amp;amp; 0xf) &amp;lt;&amp;lt; 60;&lt;br /&gt;
&lt;br /&gt;
    let write = mode | ppn;&lt;br /&gt;
&lt;br /&gt;
    core::arch::asm(&lt;br /&gt;
        &amp;quot;csrw satp, {}&amp;quot;,&lt;br /&gt;
        in(reg) write&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=27</id>
		<title>RISC-V/Paging</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=27"/>
		<updated>2023-02-10T18:11:13Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Messed up on the bit sizes for PPN3 in Sv48&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Paging in RISC-V can be enabled by first setting up a page table, then pointing the CPU to that page table via the [[RISC-V/CSR#satp | &amp;lt;code&amp;gt;satp&amp;lt;/code&amp;gt; CSR]]. This article will describe these steps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Paging type can be determined at runtime using the device tree, its typically located in the CPU specific nodes, such as `cpu@x` where x denotes which number of CPU it is, it will be located under the `mmu-type` property in the form of a string such as `riscv,svY` is either &amp;quot;32&amp;quot;, &amp;quot;39&amp;quot;, &amp;quot;48&amp;quot;, or &amp;quot;57&amp;quot; depending on paging type.&lt;br /&gt;
&lt;br /&gt;
== Terminology ==&lt;br /&gt;
&lt;br /&gt;
There are many acronyms and other terms that are used in the RISC-V specification which, for the purpose of brevity and consistency, will be used in this article as well. I will assume a baseline knowledge of the purpose and basics of paging and virtual addressing.&lt;br /&gt;
&lt;br /&gt;
=== PPNs and VPNs ===&lt;br /&gt;
&lt;br /&gt;
A physical page number (or PPN) is a unique, page-aligned identifier for a page in physical memory. The number of PPN segments for an address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
To construct a PPN from a physical address, you can simply (logically, not arithmetically) shift the value right by 12 bits (as 2**12 = 4096). Note that this operation will truncate the offset into the page, if the address was not page-aligned.&lt;br /&gt;
&lt;br /&gt;
A virtual page number (or VPN) is very similar to a PPN, but in the virtual address space. Again, the number of VPN segments for a virtual address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
== RISC-V Page Table Layout ==&lt;br /&gt;
&lt;br /&gt;
There are various paging modes available in the RISC-V ISA, differing by the number of virtual address bits:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Memory Size !! SXLEN&lt;br /&gt;
|-&lt;br /&gt;
| Sv32 || 4GiB || 32-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv39 || 512GiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv48 || 256TiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv57 || 128PiB || 64-bit only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Feel free to choose between these modes as you see fit; this page will describe all of them. Note that in 64-bit systems, Sv39 is more than enough for most hobby operating systems (though it is not much more difficult to go further).&lt;br /&gt;
&lt;br /&gt;
=== Sv32 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Sv39 ===&lt;br /&gt;
&lt;br /&gt;
Sv39 is the simplest paging mode for 64-bit systems (though larger modes are not too much more difficult to implement). It supports the standard 4KiB pages, as well as 2MiB megapages and 1GiB gigapages. &lt;br /&gt;
&lt;br /&gt;
TODO: actually describe what a page table looks like, and also PTE's RWX encoding&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 30-55 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-53 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv48 ===&lt;br /&gt;
&lt;br /&gt;
TODO: Explain&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 39-47 || 9 || (&amp;lt;nowiki&amp;gt;VPN[3]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || (&amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || (&amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || (&amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 39-47 || 9 || (&amp;lt;nowiki&amp;gt;PPN[3]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-36 || 9 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 37-53 || 17 || &amp;lt;nowiki&amp;gt;PPN[3]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv57 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
== Enabling Paging ==&lt;br /&gt;
&lt;br /&gt;
TODO&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=26</id>
		<title>RISC-V/Paging</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=26"/>
		<updated>2023-02-10T16:38:14Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Added some info about Sv48 paging&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Paging in RISC-V can be enabled by first setting up a page table, then pointing the CPU to that page table via the [[RISC-V/CSR#satp | &amp;lt;code&amp;gt;satp&amp;lt;/code&amp;gt; CSR]]. This article will describe these steps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Paging type can be determined at runtime using the device tree, its typically located in the CPU specific nodes, such as `cpu@x` where x denotes which number of CPU it is, it will be located under the `mmu-type` property in the form of a string such as `riscv,svY` is either &amp;quot;32&amp;quot;, &amp;quot;39&amp;quot;, &amp;quot;48&amp;quot;, or &amp;quot;57&amp;quot; depending on paging type.&lt;br /&gt;
&lt;br /&gt;
== Terminology ==&lt;br /&gt;
&lt;br /&gt;
There are many acronyms and other terms that are used in the RISC-V specification which, for the purpose of brevity and consistency, will be used in this article as well. I will assume a baseline knowledge of the purpose and basics of paging and virtual addressing.&lt;br /&gt;
&lt;br /&gt;
=== PPNs and VPNs ===&lt;br /&gt;
&lt;br /&gt;
A physical page number (or PPN) is a unique, page-aligned identifier for a page in physical memory. The number of PPN segments for an address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
To construct a PPN from a physical address, you can simply (logically, not arithmetically) shift the value right by 12 bits (as 2**12 = 4096). Note that this operation will truncate the offset into the page, if the address was not page-aligned.&lt;br /&gt;
&lt;br /&gt;
A virtual page number (or VPN) is very similar to a PPN, but in the virtual address space. Again, the number of VPN segments for a virtual address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
== RISC-V Page Table Layout ==&lt;br /&gt;
&lt;br /&gt;
There are various paging modes available in the RISC-V ISA, differing by the number of virtual address bits:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Memory Size !! SXLEN&lt;br /&gt;
|-&lt;br /&gt;
| Sv32 || 4GiB || 32-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv39 || 512GiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv48 || 256TiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv57 || 128PiB || 64-bit only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Feel free to choose between these modes as you see fit; this page will describe all of them. Note that in 64-bit systems, Sv39 is more than enough for most hobby operating systems (though it is not much more difficult to go further).&lt;br /&gt;
&lt;br /&gt;
=== Sv32 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Sv39 ===&lt;br /&gt;
&lt;br /&gt;
Sv39 is the simplest paging mode for 64-bit systems (though larger modes are not too much more difficult to implement). It supports the standard 4KiB pages, as well as 2MiB megapages and 1GiB gigapages. &lt;br /&gt;
&lt;br /&gt;
TODO: actually describe what a page table looks like, and also PTE's RWX encoding&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 30-55 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-53 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv48 ===&lt;br /&gt;
&lt;br /&gt;
TODO: Explain&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 39-47 || 9 || (&amp;lt;nowiki&amp;gt;VPN[3]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || (&amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || (&amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || (&amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 39-47 || 9 || (&amp;lt;nowiki&amp;gt;PPN[3]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv48 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-36 || 9 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 37-53 || 9 || &amp;lt;nowiki&amp;gt;PPN[3]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv57 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
== Enabling Paging ==&lt;br /&gt;
&lt;br /&gt;
TODO&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=25</id>
		<title>RISC-V/Paging</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=RISC-V/Paging&amp;diff=25"/>
		<updated>2023-02-10T16:21:24Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Added a short explanation for getting the MMU type with the DT&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Paging in RISC-V can be enabled by first setting up a page table, then pointing the CPU to that page table via the [[RISC-V/CSR#satp | &amp;lt;code&amp;gt;satp&amp;lt;/code&amp;gt; CSR]]. This article will describe these steps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Paging type can be determined at runtime using the device tree, its typically located in the CPU specific nodes, such as `cpu@x` where x denotes which number of CPU it is, it will be located under the `mmu-type` property in the form of a string such as `riscv,svY` is either &amp;quot;32&amp;quot;, &amp;quot;39&amp;quot;, &amp;quot;48&amp;quot;, or &amp;quot;57&amp;quot; depending on paging type.&lt;br /&gt;
&lt;br /&gt;
== Terminology ==&lt;br /&gt;
&lt;br /&gt;
There are many acronyms and other terms that are used in the RISC-V specification which, for the purpose of brevity and consistency, will be used in this article as well. I will assume a baseline knowledge of the purpose and basics of paging and virtual addressing.&lt;br /&gt;
&lt;br /&gt;
=== PPNs and VPNs ===&lt;br /&gt;
&lt;br /&gt;
A physical page number (or PPN) is a unique, page-aligned identifier for a page in physical memory. The number of PPN segments for an address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
To construct a PPN from a physical address, you can simply (logically, not arithmetically) shift the value right by 12 bits (as 2**12 = 4096). Note that this operation will truncate the offset into the page, if the address was not page-aligned.&lt;br /&gt;
&lt;br /&gt;
A virtual page number (or VPN) is very similar to a PPN, but in the virtual address space. Again, the number of VPN segments for a virtual address will depend on the paging mode.&lt;br /&gt;
&lt;br /&gt;
== RISC-V Page Table Layout ==&lt;br /&gt;
&lt;br /&gt;
There are various paging modes available in the RISC-V ISA, differing by the number of virtual address bits:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Memory Size !! SXLEN&lt;br /&gt;
|-&lt;br /&gt;
| Sv32 || 4GiB || 32-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv39 || 512GiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv48 || 256TiB || 64-bit only&lt;br /&gt;
|-&lt;br /&gt;
| Sv57 || 128PiB || 64-bit only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Feel free to choose between these modes as you see fit; this page will describe all of them. Note that in 64-bit systems, Sv39 is more than enough for most hobby operating systems (though it is not much more difficult to go further).&lt;br /&gt;
&lt;br /&gt;
=== Sv32 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Sv39 ===&lt;br /&gt;
&lt;br /&gt;
Sv39 is the simplest paging mode for 64-bit systems (though larger modes are not too much more difficult to implement). It supports the standard 4KiB pages, as well as 2MiB megapages and 1GiB gigapages. &lt;br /&gt;
&lt;br /&gt;
TODO: actually describe what a page table looks like, and also PTE's RWX encoding&lt;br /&gt;
&lt;br /&gt;
==== Address and PTE Layouts ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 virtual address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || Page VPN (&amp;lt;nowiki&amp;gt;VPN[0]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || Megapage VPN (&amp;lt;nowiki&amp;gt;VPN[1]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 30-38 || 9 || Gigapage VPN (&amp;lt;nowiki&amp;gt;VPN[2]&amp;lt;/nowiki&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 physical address&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-11 || 12 || Page offset&lt;br /&gt;
|-&lt;br /&gt;
| 12-20 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 21-29 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 30-55 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable mw-collapsible&amp;quot;&lt;br /&gt;
|+ Sv39 page table entry layout&lt;br /&gt;
|-&lt;br /&gt;
! Bit Range (inclusive) !! Length (bits) !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Valid flag (V)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Readable flag (R)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 1 || Writable flag (W)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || 1 || Executable flag (X)&lt;br /&gt;
|-&lt;br /&gt;
| 4 || 1 || U-mode accessible flag (U)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || 1 || Global flag (G)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || 1 || Accessed flag (A)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || 1 || Dirty flag (D)&lt;br /&gt;
|-&lt;br /&gt;
| 8-9 || 2 || RSW (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 10-18 || 9 || &amp;lt;nowiki&amp;gt;PPN[0]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 19-27 || 9 || &amp;lt;nowiki&amp;gt;PPN[1]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 28-53 || 26 || &amp;lt;nowiki&amp;gt;PPN[2]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 54-60 || 7 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 61-62 || 2 || PBMT (TODO)&lt;br /&gt;
|-&lt;br /&gt;
| 63 || 1 || N bit (TODO)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Sv48 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Sv57 ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
== Enabling Paging ==&lt;br /&gt;
&lt;br /&gt;
TODO&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=Interrupts&amp;diff=24</id>
		<title>Interrupts</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=Interrupts&amp;diff=24"/>
		<updated>2023-02-08T14:23:22Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Not a very good explanation, but I thought it'd be good enough, for now.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Interrupts are used in operating system design in order to allow things such as input, or timing, and often used with system calls to allow for an easy interface with the operating system. Interrupts can typically be split into several categories. External interrupts, Exceptions, and Software interrupts are some.&lt;br /&gt;
&lt;br /&gt;
== External Interrupts ==&lt;br /&gt;
External interrupts are used by external devices, such as keyboards, to tell the CPU that there has been an update in their state. The way of handling these external interrupts differs from platform to platform, but on RISC-V its handled with the [[PLIC]].&lt;br /&gt;
&lt;br /&gt;
== Exceptions ==&lt;br /&gt;
Exceptions are errors that occur on a hardware level, sometimes from issues with the software, such as illegally storing to memory. Exceptions typically have specific error codes, and specific ways of getting details on why they occurred, often stored in specific registers.&lt;br /&gt;
&lt;br /&gt;
== Software Interrupts ==&lt;br /&gt;
Software interrupts are generated, as the name implies, by software, these are used to time things, interact with the operating system, through syscalls, or to handle something important within the operating system.&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=PLIC&amp;diff=23</id>
		<title>PLIC</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=PLIC&amp;diff=23"/>
		<updated>2023-02-08T02:53:11Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Updated priorities and threshold section, I suggest someone double check just in case&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Platform Interrupt Controller (PLIC) is the standardized device for receiving, routing, and completing [[Interrupts#External|external interrupts]] with configuration per-[[hart]] and per-[[Privilege Mode|privilege mode]]. Originally, the PLIC design is based on early SiFive designs which were then slightly refined and standardized and only supports basic interrupt enabling/disabling, prioritization and routing. As part of the [[RISC-V Advanced Interrupt Architecture]] (AIA), the original PLIC design is extended to support more types of interrupts, namely [[Message Signaled Interrupt]]&amp;lt;nowiki/&amp;gt;s (MSIs), which are important for technologies like [[PCI]]. The PLIC specification can be found at https://github.com/riscv/riscv-plic-spec while the APLIC specification is available at https://github.com/riscv/riscv-aia.&lt;br /&gt;
&lt;br /&gt;
== Original PLIC Design ==&lt;br /&gt;
The original PLIC design consist of a set of: interrupt source priorities, interrupt pending bits, per-context interrupt enable bits, and then per-context priority threshold &amp;amp; claim/complete regions. A context is the combination of a hart ID and a privilege mode, though the specific ordering of contexts is platform-specific and cannot be generalized between them. A small table below has been added for quick reference, but likely will not contain a formula for calculating the context for every platform. The memory layout for the PLIC is as follows (taken from the PLIC spec):&lt;br /&gt;
 base + 0x000000: Reserved (interrupt source 0 does not exist)&lt;br /&gt;
 base + 0x000004: Interrupt source 1 priority&lt;br /&gt;
 base + 0x000008: Interrupt source 2 priority&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x000FFC: Interrupt source 1023 priority&lt;br /&gt;
 base + 0x001000: Interrupt Pending bit 0-31&lt;br /&gt;
 base + 0x00107C: Interrupt Pending bit 992-1023&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x002000: Enable bits for sources 0-31 on context 0&lt;br /&gt;
 base + 0x002004: Enable bits for sources 32-63 on context 0&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x00207C: Enable bits for sources 992-1023 on context 0&lt;br /&gt;
 base + 0x002080: Enable bits for sources 0-31 on context 1&lt;br /&gt;
 base + 0x002084: Enable bits for sources 32-63 on context 1&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x0020FC: Enable bits for sources 992-1023 on context 1&lt;br /&gt;
 base + 0x002100: Enable bits for sources 0-31 on context 2&lt;br /&gt;
 base + 0x002104: Enable bits for sources 32-63 on context 2&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x00217C: Enable bits for sources 992-1023 on context 2&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x1F1F80: Enable bits for sources 0-31 on context 15871&lt;br /&gt;
 base + 0x1F1F84: Enable bits for sources 32-63 on context 15871&lt;br /&gt;
 base + 0x1F1FFC: Enable bits for sources 992-1023 on context 15871&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x1FFFFC: Reserved&lt;br /&gt;
 base + 0x200000: Priority threshold for context 0&lt;br /&gt;
 base + 0x200004: Claim/complete for context 0&lt;br /&gt;
 base + 0x200008: Reserved&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x200FFC: Reserved&lt;br /&gt;
 base + 0x201000: Priority threshold for context 1&lt;br /&gt;
 base + 0x201004: Claim/complete for context 1&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x3FFF000: Priority threshold for context 15871&lt;br /&gt;
 base + 0x3FFF004: Claim/complete for context 15871&lt;br /&gt;
 base + 0x3FFF008: Reserved&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x3FFFFFC: Reserved&lt;br /&gt;
&lt;br /&gt;
Or, more usefully:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
#[repr(C)]&lt;br /&gt;
struct Plic {&lt;br /&gt;
    source_priorities: [u32; 1024],&lt;br /&gt;
    interrupt_pending: [u32; 32],&lt;br /&gt;
    _padding1: [u8; 3968],&lt;br /&gt;
    interrupt_enable: [[u32; 32]; 15872],&lt;br /&gt;
    _padding2: [u8; 57344],&lt;br /&gt;
    threshold_and_claim: [[u32; 1024]; 15872],&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt; inside of the &amp;lt;code&amp;gt;source_priorities&amp;lt;/code&amp;gt; is a priority level from &amp;lt;code&amp;gt;0..=7&amp;lt;/code&amp;gt; (on most platforms, see your platform specification for more complete information) of increasing priority level where priority &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; is considered &amp;quot;never interrupt&amp;quot; and will cause the interrupt to effectively be disabled, with the exception of interrupt #0, which does not exist and is reserved. &amp;lt;code&amp;gt;interrupt_pending&amp;lt;/code&amp;gt; is a read-only bitmap of the pending status for priorities &amp;lt;code&amp;gt;1..=1024&amp;lt;/code&amp;gt; (with the interrupt #0 bit hardwired to zero). Both &amp;lt;code&amp;gt;interrupt_enable&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;threshold_and_claim&amp;lt;/code&amp;gt; work on the aforementioned contexts, and need to be indexed at a platform-specific index for a given hart and privilege mode combination. The &amp;lt;code&amp;gt;interrupt_enable&amp;lt;/code&amp;gt; consists of another bitmap of interrupt sources (again with interrupt #0 for each context being hardwired to zero) which allow enabling and disabling specific interrupt sources at the context level, but also means that if you wish for a given interrupt to trigger across any hart, it will need to be enabled for each context for the privilege mode you are executing in. &amp;lt;code&amp;gt;threshold_and_claim&amp;lt;/code&amp;gt; is made up of a threshold &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt; value and a claim &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt; value, with the rest of the &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;s being reserved and should not be written to.&lt;br /&gt;
&lt;br /&gt;
=== Hart &amp;amp; Privilege Context Mappings ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Platform&lt;br /&gt;
!M-mode Context&lt;br /&gt;
!S-mode Context&lt;br /&gt;
|-&lt;br /&gt;
|QEMU virt&lt;br /&gt;
|&amp;lt;code&amp;gt;hart_id * 2&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
 hart_id * 2 + 1&lt;br /&gt;
|-&lt;br /&gt;
|QEMU sifive_u&amp;lt;sup&amp;gt;†&amp;lt;/sup&amp;gt;&lt;br /&gt;
|Monitor hart: &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other harts: &amp;lt;code&amp;gt;hart_id * 2 + 1&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;hart_id * 2&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;sup&amp;gt;†&amp;lt;/sup&amp;gt;This platform contains a [[Monitor Hart]] which does not support S-mode and does not have the same extension availability as the general-purpose harts&lt;br /&gt;
&lt;br /&gt;
=== Setting Interrupt Source Priorities &amp;amp; Thresholds ===&lt;br /&gt;
TODO: Explain better&lt;br /&gt;
&lt;br /&gt;
Every interrupt source has a priority, these priorities effect which interrupts get dealt with first, and which don't get dealt with at all. An interrupt won't be dealt with at all if it is below the threshold of the context handling it.&lt;br /&gt;
To set the interrupt source priority, index into the `source_priorities` field of the PLIC, and then write the desired priority(typically from 0-7).&lt;br /&gt;
To set the threshold of a context, index into the `threshold_and_claim` field of the PLIC based off the context, and then index to 0 in that, then write the desired threshold(typically 0-7 like priority).&lt;br /&gt;
&lt;br /&gt;
Example in rust:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
unsafe fn set_priority(plic: *mut Plic, interrupt_source: usize, priority: u32) {&lt;br /&gt;
    // Sanity check&lt;br /&gt;
    if interrupt_source &amp;gt;= 1024 {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    let src_priority = unsafe {&amp;amp;mut (*plic).source_priorities[interrupt_source]};&lt;br /&gt;
&lt;br /&gt;
    *src_priority = priority;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsafe fn set_threshold(plic: *mut Plic, context: usize, thresh: u32) {&lt;br /&gt;
    // Sanity check&lt;br /&gt;
    if context &amp;gt;= 15872 {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    let threshold = unsafe {&amp;amp;mut (*plic).threshold_and_claim[context][0]};&lt;br /&gt;
&lt;br /&gt;
    *threshold = thresh;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Enabling and Disabling Interrupts ===&lt;br /&gt;
To enable or disable any given interrupt source, simply toggle the specific bit that corresponds to the interrupt source in the &amp;lt;code&amp;gt;interrupt_enable&amp;lt;/code&amp;gt; for the context for which you wish to enable or disable the interrupt. Example Rust code:&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
unsafe fn enable_interrupt(plic: *mut Plic, context: usize, interrupt_source: usize) {&lt;br /&gt;
    // Sanity checks, neither values would be valid&lt;br /&gt;
    if context &amp;gt;= 15872 || interrupt_source &amp;gt;= 1024 {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Calculate the bit-group index and the bit index for that bit-group&lt;br /&gt;
    let (u32_index, bit) = (interrupt_source / 32, interrupt_source % 32);&lt;br /&gt;
    &lt;br /&gt;
    // Calculate the address of the bit-group&lt;br /&gt;
    let bit_group_ptr = unsafe { core::ptr::addr_of_mut!((*plic).interrupt_enable[context][u32_index]) };&lt;br /&gt;
    &lt;br /&gt;
    // Read the current value&lt;br /&gt;
    let previous = unsafe { bit_group_ptr.read_volatile() };&lt;br /&gt;
    &lt;br /&gt;
    // Set the bit for the interrupt source&lt;br /&gt;
    let new = previous | (1 &amp;lt;&amp;lt; bit);&lt;br /&gt;
    &lt;br /&gt;
    // Write the new value&lt;br /&gt;
    unsafe { bit_group_ptr.write_volatile(new) };&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsafe fn disable_interrupt(plic: *mut Plic, context: usize, interrupt_source: usize) {&lt;br /&gt;
    // Sanity checks, neither values would be valid&lt;br /&gt;
    if context &amp;gt;= 15872 || interrupt_source &amp;gt;= 1024 {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Calculate the bit-group index and the bit index for that bit-group&lt;br /&gt;
    let (u32_index, bit) = (interrupt_source / 32, interrupt_source % 32);&lt;br /&gt;
    &lt;br /&gt;
    // Calculate the address of the bit-group&lt;br /&gt;
    let bit_group_ptr = unsafe { core::ptr::addr_of_mut!((*plic).interrupt_enable[context][u32_index]) };&lt;br /&gt;
    &lt;br /&gt;
    // Read the current value&lt;br /&gt;
    let previous = unsafe { bit_group_ptr.read_volatile() };&lt;br /&gt;
    &lt;br /&gt;
    // Clear the bit for the interrupt source&lt;br /&gt;
    let new = previous &amp;amp; !(1 &amp;lt;&amp;lt; bit);&lt;br /&gt;
    &lt;br /&gt;
    // Write the new value&lt;br /&gt;
    unsafe { bit_group_ptr.write_volatile(new) };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Acknowledging Interrupts — Claim/Complete Mechanism ===&lt;br /&gt;
TODO: Explain it better&lt;br /&gt;
&lt;br /&gt;
When receiving an interrupt before you can return from it you need to acknowledge that you finished the interrupt, this is the Claim/Complete mechanism.  As the name implies, first you must &amp;quot;claim&amp;quot; an interrupt, which is tells the PLIC &amp;quot;I am working on handling this interrupt, do not try to tell other CPU's about it while I'm working on it&amp;quot;.  Then once it is handled you must &amp;quot;complete&amp;quot; it, which tells the PLIC &amp;quot;this interrupt is finished and the hardware device can signal again if it wants&amp;quot;.  It is perfectly normal for claiming an interrupt to sometimes fail if you have multiple CPU's, essentially another CPU has beaten you in the race.  The race is mediated by the hardware so it all works out in the end.&lt;br /&gt;
&lt;br /&gt;
In order to claim it you must take the interrupt number, and write that to the `threshold_and_claim` register, first index it based off the context, and then index to 1, and write the interrupt number.&lt;br /&gt;
Example rust code:&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
unsafe fn claim_interrupt(plic: *mut Plic, context: usize, interrupt_source: usize) {&lt;br /&gt;
    // Sanity checks, neither values would be valid&lt;br /&gt;
    if context &amp;gt;= 15872 || interrupt_source &amp;gt;= 1024 {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    let threshold_and_claim = unsafe {&amp;amp;mut (*plic).threshold_and_claim};&lt;br /&gt;
    &lt;br /&gt;
    // Write the interrupt&lt;br /&gt;
    threshold_and_claim[context][1] = interrupt_source;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
__FORCETOC__&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
	<entry>
		<id>https://wiki.friendos.dev/index.php?title=PLIC&amp;diff=21</id>
		<title>PLIC</title>
		<link rel="alternate" type="text/html" href="https://wiki.friendos.dev/index.php?title=PLIC&amp;diff=21"/>
		<updated>2023-02-06T13:55:42Z</updated>

		<summary type="html">&lt;p&gt;Veloya: Did my best to explain and demonstrate Claim/Complete Mechanism, I suggest someone who is more qualified review this later&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Platform Interrupt Controller (PLIC) is the standardized device for receiving, routing, and completing [[Interrupts#External|external interrupts]] with configuration per-[[hart]] and per-[[Privilege Mode|privilege mode]]. Originally, the PLIC design is based on early SiFive designs which were then slightly refined and standardized and only supports basic interrupt enabling/disabling, prioritization and routing. As part of the [[RISC-V Advanced Interrupt Architecture]] (AIA), the original PLIC design is extended to support more types of interrupts, namely [[Message Signaled Interrupt]]&amp;lt;nowiki/&amp;gt;s (MSIs), which are important for technologies like [[PCI]]. The PLIC specification can be found at https://github.com/riscv/riscv-plic-spec while the APLIC specification is available at https://github.com/riscv/riscv-aia.&lt;br /&gt;
&lt;br /&gt;
== Original PLIC Design ==&lt;br /&gt;
The original PLIC design consist of a set of: interrupt source priorities, interrupt pending bits, per-context interrupt enable bits, and then per-context priority threshold &amp;amp; claim/complete regions. A context is the combination of a hart ID and a privilege mode, though the specific ordering of contexts is platform-specific and cannot be generalized between them. A small table below has been added for quick reference, but likely will not contain a formula for calculating the context for every platform. The memory layout for the PLIC is as follows (taken from the PLIC spec):&lt;br /&gt;
 base + 0x000000: Reserved (interrupt source 0 does not exist)&lt;br /&gt;
 base + 0x000004: Interrupt source 1 priority&lt;br /&gt;
 base + 0x000008: Interrupt source 2 priority&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x000FFC: Interrupt source 1023 priority&lt;br /&gt;
 base + 0x001000: Interrupt Pending bit 0-31&lt;br /&gt;
 base + 0x00107C: Interrupt Pending bit 992-1023&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x002000: Enable bits for sources 0-31 on context 0&lt;br /&gt;
 base + 0x002004: Enable bits for sources 32-63 on context 0&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x00207C: Enable bits for sources 992-1023 on context 0&lt;br /&gt;
 base + 0x002080: Enable bits for sources 0-31 on context 1&lt;br /&gt;
 base + 0x002084: Enable bits for sources 32-63 on context 1&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x0020FC: Enable bits for sources 992-1023 on context 1&lt;br /&gt;
 base + 0x002100: Enable bits for sources 0-31 on context 2&lt;br /&gt;
 base + 0x002104: Enable bits for sources 32-63 on context 2&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x00217C: Enable bits for sources 992-1023 on context 2&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x1F1F80: Enable bits for sources 0-31 on context 15871&lt;br /&gt;
 base + 0x1F1F84: Enable bits for sources 32-63 on context 15871&lt;br /&gt;
 base + 0x1F1FFC: Enable bits for sources 992-1023 on context 15871&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x1FFFFC: Reserved&lt;br /&gt;
 base + 0x200000: Priority threshold for context 0&lt;br /&gt;
 base + 0x200004: Claim/complete for context 0&lt;br /&gt;
 base + 0x200008: Reserved&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x200FFC: Reserved&lt;br /&gt;
 base + 0x201000: Priority threshold for context 1&lt;br /&gt;
 base + 0x201004: Claim/complete for context 1&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x3FFF000: Priority threshold for context 15871&lt;br /&gt;
 base + 0x3FFF004: Claim/complete for context 15871&lt;br /&gt;
 base + 0x3FFF008: Reserved&lt;br /&gt;
 ...&lt;br /&gt;
 base + 0x3FFFFFC: Reserved&lt;br /&gt;
&lt;br /&gt;
Or, more usefully:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line&amp;gt;&lt;br /&gt;
#[repr(C)]&lt;br /&gt;
struct Plic {&lt;br /&gt;
    source_priorities: [u32; 1024],&lt;br /&gt;
    interrupt_pending: [u32; 32],&lt;br /&gt;
    _padding1: [u8; 3968],&lt;br /&gt;
    interrupt_enable: [[u32; 32]; 15872],&lt;br /&gt;
    _padding2: [u8; 57344],&lt;br /&gt;
    threshold_and_claim: [[u32; 1024]; 15872],&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt; inside of the &amp;lt;code&amp;gt;source_priorities&amp;lt;/code&amp;gt; is a priority level from &amp;lt;code&amp;gt;0..=7&amp;lt;/code&amp;gt; (on most platforms, see your platform specification for more complete information) of increasing priority level where priority &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; is considered &amp;quot;never interrupt&amp;quot; and will cause the interrupt to effectively be disabled, with the exception of interrupt #0, which does not exist and is reserved. &amp;lt;code&amp;gt;interrupt_pending&amp;lt;/code&amp;gt; is a read-only bitmap of the pending status for priorities &amp;lt;code&amp;gt;1..=1024&amp;lt;/code&amp;gt; (with the interrupt #0 bit hardwired to zero). Both &amp;lt;code&amp;gt;interrupt_enable&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;threshold_and_claim&amp;lt;/code&amp;gt; work on the aforementioned contexts, and need to be indexed at a platform-specific index for a given hart and privilege mode combination. The &amp;lt;code&amp;gt;interrupt_enable&amp;lt;/code&amp;gt; consists of another bitmap of interrupt sources (again with interrupt #0 for each context being hardwired to zero) which allow enabling and disabling specific interrupt sources at the context level, but also means that if you wish for a given interrupt to trigger across any hart, it will need to be enabled for each context for the privilege mode you are executing in. &amp;lt;code&amp;gt;threshold_and_claim&amp;lt;/code&amp;gt; is made up of a threshold &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt; value and a claim &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt; value, with the rest of the &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;s being reserved and should not be written to.&lt;br /&gt;
&lt;br /&gt;
=== Hart &amp;amp; Privilege Context Mappings ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Platform&lt;br /&gt;
!M-mode Context&lt;br /&gt;
!S-mode Context&lt;br /&gt;
|-&lt;br /&gt;
|QEMU virt&lt;br /&gt;
|&amp;lt;code&amp;gt;hart_id * 2&amp;lt;/code&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
 hart_id * 2 + 1&lt;br /&gt;
|-&lt;br /&gt;
|QEMU sifive_u&amp;lt;sup&amp;gt;†&amp;lt;/sup&amp;gt;&lt;br /&gt;
|Monitor hart: &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other harts: &amp;lt;code&amp;gt;hart_id * 2 + 1&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;hart_id * 2&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;sup&amp;gt;†&amp;lt;/sup&amp;gt;This platform contains a [[Monitor Hart]] which does not support S-mode and does not have the same extension availability as the general-purpose harts&lt;br /&gt;
&lt;br /&gt;
=== Setting Interrupt Source Priorities &amp;amp; Thresholds ===&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== Enabling and Disabling Interrupts ===&lt;br /&gt;
To enable or disable any given interrupt source, simply toggle the specific bit that corresponds to the interrupt source in the &amp;lt;code&amp;gt;interrupt_enable&amp;lt;/code&amp;gt; for the context for which you wish to enable or disable the interrupt. Example Rust code:&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
unsafe fn enable_interrupt(plic: *mut Plic, context: usize, interrupt_source: usize) {&lt;br /&gt;
    // Sanity checks, neither values would be valid&lt;br /&gt;
    if context &amp;gt;= 15872 || interrupt_source &amp;gt;= 1024 {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Calculate the bit-group index and the bit index for that bit-group&lt;br /&gt;
    let (u32_index, bit) = (interrupt_source / 32, interrupt_source % 32);&lt;br /&gt;
    &lt;br /&gt;
    // Calculate the address of the bit-group&lt;br /&gt;
    let bit_group_ptr = unsafe { core::ptr::addr_of_mut!((*plic).interrupt_enable[context][u32_index]) };&lt;br /&gt;
    &lt;br /&gt;
    // Read the current value&lt;br /&gt;
    let previous = unsafe { bit_group_ptr.read_volatile() };&lt;br /&gt;
    &lt;br /&gt;
    // Set the bit for the interrupt source&lt;br /&gt;
    let new = previous | (1 &amp;lt;&amp;lt; bit);&lt;br /&gt;
    &lt;br /&gt;
    // Write the new value&lt;br /&gt;
    unsafe { bit_group_ptr.write_volatile(new) };&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
unsafe fn disable_interrupt(plic: *mut Plic, context: usize, interrupt_source: usize) {&lt;br /&gt;
    // Sanity checks, neither values would be valid&lt;br /&gt;
    if context &amp;gt;= 15872 || interrupt_source &amp;gt;= 1024 {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Calculate the bit-group index and the bit index for that bit-group&lt;br /&gt;
    let (u32_index, bit) = (interrupt_source / 32, interrupt_source % 32);&lt;br /&gt;
    &lt;br /&gt;
    // Calculate the address of the bit-group&lt;br /&gt;
    let bit_group_ptr = unsafe { core::ptr::addr_of_mut!((*plic).interrupt_enable[context][u32_index]) };&lt;br /&gt;
    &lt;br /&gt;
    // Read the current value&lt;br /&gt;
    let previous = unsafe { bit_group_ptr.read_volatile() };&lt;br /&gt;
    &lt;br /&gt;
    // Clear the bit for the interrupt source&lt;br /&gt;
    let new = previous &amp;amp; !(1 &amp;lt;&amp;lt; bit);&lt;br /&gt;
    &lt;br /&gt;
    // Write the new value&lt;br /&gt;
    unsafe { bit_group_ptr.write_volatile(new) };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Acknowledging Interrupts — Claim/Complete Mechanism ===&lt;br /&gt;
TODO: Explain it better&lt;br /&gt;
&lt;br /&gt;
When receiving an interrupt before you can return from it you need to acknowledge that you finished the interrupt, this is the Claim/Complete mechanism.&lt;br /&gt;
In order to claim it you must take the interrupt number, and write that to the `threshold_and_claim` register, first index it based off the context, and then index to 1, and write the interrupt number.&lt;br /&gt;
Example rust code:&amp;lt;syntaxhighlight lang=&amp;quot;rust&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
unsafe fn claim_interrupt(plic: *mut Plic, context: usize, interrupt_source: usize) {&lt;br /&gt;
    // Sanity checks, neither values would be valid&lt;br /&gt;
    if context &amp;gt;= 15872 || interrupt_source &amp;gt;= 1024 {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    let threshold_and_claim = unsafe {&amp;amp;mut (*plic).threshold_and_claim};&lt;br /&gt;
    &lt;br /&gt;
    // Write the interrupt&lt;br /&gt;
    unsafe { threshold_and_claim[context][1] = interrupt_source };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
__FORCETOC__&lt;/div&gt;</summary>
		<author><name>Veloya</name></author>
	</entry>
</feed>