1import{_ as n,c as a,e,o as l}from"./app-CjUkjRdW.js";const i={};function t(p,s){return l(),a("div",null,s[0]||(s[0]=[e(`<h1 id="system-calls" tabindex="-1"><a class="header-anchor" href="#system-calls"><span>System Calls</span></a></h1><p>User programs run in a restricted environment. They can't access hardware directly, allocate memory, or do any privileged operations. Instead, they must ask the kernel to do these things for them. The kernel provides these services through system calls. System calls are the interface between user programs and the kernel.</p><p>Transferring control to the kernel requires special support from the CPU. Traditionally, this has been done using software interrupts, e.g. <code>int 0x80</code> in Linux. However, modern CPUs provide a more efficient way to do this: the <code>syscall</code>/<code>sysret</code> instruction pair.</p><h2 id="system-call-interface" tabindex="-1"><a class="header-anchor" href="#system-call-interface"><span>System Call Interface</span></a></h2><p>The <code>syscall</code>/<code>sysret</code> instructions simply transfer control to the kernel and back to the user program. They don't define the interface between user programs and the kernel. The kernel defines this interface, i.e. the system call numbers and the arguments for each system call. The kernel also defines the calling convention for system calls, e.g. which registers to use for arguments and return values. This is called Application Binary Interface (ABI).</p><p>We're not building a kernel adhering to any particular ABI; we'll define our own. Let's start with the system call number and arguments. We'll use the following registers for these:</p><ul><li><code>rdi</code>: system call number</li><li><code>rsi</code>: first argument</li><li><code>rdx</code>: second argument</li><li><code>r8</code>: third argument</li><li><code>r9</code>: fourth argument</li><li><code>r10</code>: fifth argument</li></ul><p>We'll use <code>rax</code> for the return value. Notice that we're not using <code>rcx</code> or <code>r11</code> in the system call interface. This is because when executing <code>syscall</code>, the CPU stores the user <code>rip</code> and <code>rflags</code> in <code>rcx</code> and <code>r11</code>, respectively. Upon returning to user mode, the CPU restores <code>rip</code> and <code>rflags</code> from <code>rcx</code> and <code>r11</code>. So we have to make sure that <code>rcx</code> and <code>r11</code> are preserved across system calls.</p><p>Also, the CPU doesn't switch stacks for us when executing <code>syscall</code>. We have to do that ourselves. This is in contrast with interrupts, where the CPU switches to the kernel stack before executing the interrupt handler. So it's a bit more inconvenient to handle system calls than interrupts, but it's a faster mechanism.</p><h2 id="initialization" tabindex="-1"><a class="header-anchor" href="#initialization"><span>Initialization</span></a></h2><p>There are a few things we need to do to initialize system calls. They're all done through Model Specific Registers (MSRs).</p><ul><li>Set the <code>SCE</code> (SYSCALL Enable) flag in the <code>IA32_EFER</code> MSR.</li><li>Set the kernel and user mode segment selectors in the <code>IA32_STAR</code> MSR.</li><li>Set the syscall entry point in the <code>IA32_LSTAR</code> MSR.</li><li>Set the kernel mode CPU flags mask in the <code>IA32_FMASK</code> MSR.</li></ul><p>Since we're going to be reading/writing CPU registers, let's create a module for that. Let's add <code>src/kernel/cpu.nim</code> and define some constants for the MSRs, and two procs to read/write them.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/cpu.nim</span></span> 2<span class="line"></span> 3<span class="line"><span class="token keyword">const</span></span> 4<span class="line"> IA32_EFER<span class="token operator">*</span> <span class="token operator">=</span> <span class="token number">0xC0000080'u32</span></span> 5<span class="line"></span> 6<span class="line"> IA32_STAR<span class="token operator">*</span> <span class="token operator">=</span> <span class="token number">0xC0000081'u32</span></span> 7<span class="line"> IA32_LSTAR<span class="token operator">*</span> <span class="token operator">=</span> <span class="token number">0xC0000082'u32</span></span> 8<span class="line"> IA32_FMASK<span class="token operator">*</span> <span class="token operator">=</span> <span class="token number">0xC0000084'u32</span></span> 9<span class="line"></span> 10<span class="line"><span class="token keyword">proc</span> <span class="token function">readMSR<span class="token operator">*</span></span><span class="token punctuation">(</span>ecx<span class="token operator">:</span> uint32<span class="token punctuation">)</span><span class="token operator">:</span>
10 uint64 <span class="token operator">=</span></span> 11<span class="line"> <span class="token keyword">var</span> eax<span class="token punctuation">,</span> edx<span class="token operator">:</span> uint32</span> 12<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 13<span class="line"> rdmsr</span> 14<span class="line"> : "=a"(\`eax\`), "=d"(\`edx\`)</span> 15<span class="line"> : "c"(\`ecx\`)</span> 16<span class="line"> """</span></span> 17<span class="line"> result <span class="token operator">=</span> <span class="token punctuation">(</span>edx<span class="token operator">.</span>uint64 <span class="token operator">shl</span> <span class="token number">32</span><span class="token punctuation">)</span> <span class="token operator">or</span> eax</span> 18<span class="line"></span> 19<span class="line"><span class="token keyword">proc</span> <span class="token function">writeMSR<span class="token operator">*</span></span><span class="token punctuation">(</span>ecx<span class="token operator">:</span> uint32<span class="token punctuation">,</span> value<span class="token operator">:</span> uint64<span class="token punctuation">)</span> <span class="token operator">=</span></span> 20<span class="line"> <span class="token keyword">var</span> eax<span class="token punctuation">,</span> edx<span class="token operator">:</span> uint32</span> 21<span class="line"> eax <span class="token operator">=</span> value<span class="token operator">.</span>uint32</span> 22<span class="line"> edx <span class="token operator">=</span> <span class="token punctuation">(</span>value <span class="token operator">shr</span> <span class="token number">32</span><span class="token punctuation">)</span><span class="token operator">.</span>uint32</span> 23<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 24<span class="line"> wrmsr</span> 25<span class="line"> :</span> 26<span class="line"> : "c"(\`ecx\`), "a"(\`eax\`), "d"(\`edx\`)</span> 27<span class="line"> """</span></span> 28<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Now, let's create another module <code>src/kernel/syscalls.nim</code> and add a proc to initialize system calls, and a dummy syscall entry point.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 29<span class="line"></span> 30<span class="line"><span class="token keyword">import</span> cpu</span> 31<span class="line"><span class="token keyword">import</span> gdt</span> 32<span class="line"></span> 33<span class="line"><span class="token keyword">proc</span> <span class="token function">syscallEntry</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{.</span>asmNoStackFrame<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 34<span class="line"> <span class="token comment"># just halt for now</span></span> 35<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 36<span class="line"> cli</span> 37<span class="line"> hlt</span> 38<span class="line"> """</span></span> 39<span class="line"></span> 40<span class="line"><span class="token keyword">proc</span> <span class="token function">syscallInit<span class="token operator">*</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span></span> 41<span class="line"> <span class="token comment"># enable syscall feature</span></span> 42<span class="line"> <span class="token function">writeMSR</span><span class="token punctuation">(</span>IA32_EFER<span class="token punctuation">,</span> <span class="token function">readMSR</span><span class="token punctuation">(</span>IA32_EFER<span class="token punctuation">)</span> <span class="token operator">or</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment"># Bit 0: SYSCALL Enable</span></span> 43<span class="line"></span> 44<span class="line"> <span class="token comment"># set up segment selectors in IA32_STAR (Syscall Target Address Register)</span></span> 45<span class="line"> <span class="token comment">#</span></span> 46<span class="line"> <span class="token comment"># we use KernelCodeSegmentSelector for both parts of the register (47:32 and 63:48)</span></span> 47<span class="line"> <span class="token comment"># so for SYSCALL, the kernel segment selectors are:</span></span> 48<span class="line"> <span class="token comment"># CS: IA32_STAR[47:32] <-- KernelCodeSegmentSelector</span></span> 49<span class="line"> <span class="token comment"># SS: IA32_STAR[47:32] + 8 <-- DataSegmentSelector (shared)</span></span> 50<span class="line"> <span class="token comment">#</span></span> 51<span class="line"> <span class="token comment"># and for SYSRET, the user segment selectors are:</span></span> 52<span class="line"> <span class="token comment"># CS: IA32_STAR[63:48] + 16 <-- UserCodeSegmentSelector</span></span> 53<span class="line"> <span class="token comment"># SS: IA32_STAR[63:48] + 8 <-- DataSegmentSelector (shared)</span></span> 54<span class="line"> <span class="token comment">#</span></span> 55<span class="line"> <span class="token comment"># thus, setting both parts of the register to KernelCodeSegmentSelector</span></span> 56<span class="line"> <span class="token comment"># satisfies both requirements (+0 is kernel CS, +8 is shared data segment, +16 is user CS)</span></span> 57<span class="line"> <span class="token keyword">let</span> star <span class="token operator">=</span> <span class="token punctuation">(</span></span> 58<span class="line"> <span class="token punctuation">(</span>KernelCodeSegmentSelector<span class="token operator">.</span>
58uint64 <span class="token operator">shl</span> <span class="token number">32</span><span class="token punctuation">)</span> <span class="token function">or</span></span> 59<span class="line"> <span class="token punctuation">(</span>KernelCodeSegmentSelector<span class="token operator">.</span>uint64 <span class="token operator">shl</span> <span class="token number">48</span><span class="token punctuation">)</span></span> 60<span class="line"> <span class="token punctuation">)</span></span> 61<span class="line"> <span class="token function">writeMSR</span><span class="token punctuation">(</span>IA32_STAR<span class="token punctuation">,</span> star<span class="token punctuation">)</span></span> 62<span class="line"></span> 63<span class="line"> <span class="token comment"># set up syscall entry point</span></span> 64<span class="line"> <span class="token function">writeMSR</span><span class="token punctuation">(</span>IA32_LSTAR<span class="token punctuation">,</span> <span class="token function">cast[uint64]</span><span class="token punctuation">(</span>syscallEntry<span class="token punctuation">)</span><span class="token punctuation">)</span></span> 65<span class="line"></span> 66<span class="line"> <span class="token comment"># set up flags mask (should mask interrupt flag to disable interrupts)</span></span> 67<span class="line"> <span class="token function">writeMSR</span><span class="token punctuation">(</span>IA32_FMASK<span class="token punctuation">,</span> <span class="token number">0x200</span><span class="token punctuation">)</span> <span class="token comment"># rflags will be ANDed with the *complement* of this value</span></span> 68<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The <code>syscallEntry</code> proc is a low-level entry point for system calls, hence the pure assembly. We can't rely on conventional prologue/epilogue code here, since the CPU doesn't switch stacks for us. We'll have to do that ourselves as early as possible in the entry point. Right now we just want to make sure that the syscall transition to kernel mode works.</p><p>The <code>syscallInit</code> proc does the actual initialization. It enables the syscall feature, sets up the segment selectors, sets the syscall entry point, and sets the flags mask. The flags mask is used to <em>clear</em> the flags corresponding to the bits set in the mask when entering kernel mode.</p><p>Finally, let's call <code>syscallInit</code> from <code>src/kernel/main.nim</code>.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/main.nim</span></span> 69<span class="line"></span> 70<span class="line highlighted"><span class="token keyword">
70import</span> syscalls</span> 71<span class="line"><span class="token operator">..</span></span> 72<span class="line"></span> 73<span class="line"><span class="token keyword">proc</span> <span class="token function">KernelMainInner</span><span class="token punctuation">(</span>bootInfo<span class="token operator">:</span> <span class="token keyword">ptr</span> BootInfo<span class="token punctuation">)</span> <span class="token operator">=</span></span> 74<span class="line"> debugln <span class="token string">""</span></span> 75<span class="line"> debugln <span class="token string">"kernel: Fusion Kernel"</span></span> 76<span class="line"></span> 77<span class="line"> <span class="token operator">...</span></span> 78<span class="line"></span> 79<span class="line highlighted"> debug <span class="token string">"kernel: Initializing Syscalls "</span></span> 80<span class="line highlighted"> <span class="token function">syscallInit</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span> 81<span class="line highlighted"> debugln <span class="token string">"[success]"</span></span> 82<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="invoking-system-calls" tabindex="-1"><a class="header-anchor" href="#invoking-system-calls"><span>Invoking System Calls</span></a></h2><p>We should now be able to invoke system calls from user mode. Let's modify our user program to do that. We're going to pass the system call number in <code>rdi</code>, but we won't pass any arguments for now.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/user/utask.nim</span></span> 83<span class="line"><span class="token operator">...</span></span> 84<span class="line"></span> 85<span class="line"><span class="token keyword">proc</span> <span class="token function">UserMain<span class="token operator">*</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{.</span>exportc<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 86<span class="line"> <span class="token function">NimMain</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span> 87<span class="line"></span> 88<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 89<span class="line highlighted"> mov rdi, 1</span> 90<span class="line"> syscall</span> 91<span class="line"></span> 92<span class="line"> .loop:</span> 93<span class="line"> pause</span> 94<span class="line"> jmp .loop</span> 95<span class="line"> """</span></span> 96<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let's try this out and use the QEMU monitor to check where execution stops.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">(qemu) x /2i $eip-2</span> 97<span class="line">
970xffff800000120490: fa cli</span> 98<span class="line">0xffff800000120491: f4 hlt</span> 99<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The command <code>x /2i $eip-2</code> disassembles the two instructions just before the current instruction pointer, which shows that we're executing the <code>cli</code> and <code>hlt</code> instructions in <code>syscallEntry</code>. Just to double-check, we can confirm this by comparing the value of <code>rip</code> with the address of <code>syscallEntry</code> from the kernel linker map.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">ffff800000120490 ffff800000120490 4e 16 .../fusion/build/@msyscalls.nim.c.o:(.ltext.syscallEntry__syscalls_u23)</span> 100<span class="line">ffff800000120490 ffff800000120490 4e 1 syscallEntry__syscalls_u23</span> 101<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div></div></div><p>Indeed, the value of <code>rip - 2</code> is the same as the address of <code>syscallEntry</code>.</p><p>Now, let's check the CPU registers.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">(qemu) info registers</span> 102<span class="line">CPU#0</span> 103<span class="line highlighted">RAX=ffff800000327540 RBX=ffff800000327548 RCX=0000000040000067 RDX=000000004000add8</span> 104<span class="line highlighted">RSI=0000000000000001 RDI=0000000000000001 RBP=0000000050000ff8 RSP=0000000050000fc8</span> 105<span class="line highlighted">R8 =ffff800100003c00 R9 =0000000000000000 R10=0000000000000000 R11=0000000000000202</span> 106<span class="line">R12=0000000000000000 R13=0000000006bb1588 R14=0000000000000000 R15=0000000007ebf1e0</span> 107<span class="line">RIP=ffff8000001204a9 RFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=1</span> 108<span class="line">ES =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 109<span class="line highlighted">CS =0008 0000000000000000 ffffffff 00a09b00 DPL=0 CS64 [-RA]</span> 110<span class="line highlighted">SS =0010 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA]</span> 111<span class="line">DS =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 112<span class="line">FS =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 113<span class="line">GS =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 114<span class="line">LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT</span> 115<span class="line">TR =0020 ffff800000326430 00000067 00008900 DPL=0 TSS64-avl</span> 116<span class="line">GDT= ffff8000003264d0 0000002f</span> 117<span class="line">IDT= ffff800000326500 00000fff</span> 118<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The three registers important to us here are <code>rcx</code>, <code>r11</code>, and <code>rdi</code>:</p><ul><li><code>rcx</code> contains the user <code>rip</code> to return to after the system call (<code>0x40000067</code>)</li><li><code>r11</code> contains the user <code>rflags</code> to restore after the system call (<code>0x202</code>)</li><li><code>rdi</code> contains the system call number (<code>1</code>)</li></ul><p>We can also see that <code>CS</code> and <code>SS</code> are set to the kernel code and data segments, respectively, and their DPL=0. <code>rflags</code> also has the <code>IF</code> (interrupt flag) cleared. So everything looks good so far. Notice that <code>rsp</code> is set to <code>0x50000fc8</code>, which is within the user stack. As I mentioned earlier, we'll need to switch to the kernel stack ourselves.</p><p>Let's test <code>sysret</code> to make sure we can return to user mode. We'll modify <code>syscallEntry</code> to put a dummy value in <code>rax</code> as a return code, and then call <code>sysretq</code> (the <code>q</code> suffix is for returning to 64-bit mode; otherwise, <code>sysret</code> would return to 32-bit compatibility mode).</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 119<span class="line"><span class="token operator">...</span></span> 120<span class="line"></span> 121<span class="line"><span class="token keyword">proc</span> <span class="token function">syscallEntry</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{.</span>asmNoStackFrame<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 122<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 123<span class="line highlighted"> mov rax, 0x5050</span> 124<span class="line"> sysretq</span> 125<span class="line"> """</span></span> 126<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let's run it and see where we stop.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">(qemu) x /2i $eip-2</span> 127<span class="line">0x40000067: f3 90 pause</span> 128<span class="line">0x40000069: e9 f9 ff ff ff jmp 0x40000067</span> 129<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We're now executing the <code>pause</code> loop in <code>UserMain</code>, so we're back in user mode. Let's check the registers.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">(qemu) info registers</span> 130<span class="line">CPU#0</span> 131<span class="line highlighted">RAX=0000000000005050 RBX=0000000000000000 RCX=0000000040000067 RDX=000000004000add8</span> 132<span class="line">RSI=0000000000000001 RDI=0000000000000001 RBP=0000000050000ff8 RSP=0000000050000fc8</span> 133<span class="line">R8 =ffff800100003c00 R9 =0000000000000000 R10=000000000636d001 R11=0000000000000202
133</span> 134<span class="line">R12=0000000000000000 R13=0000000006bb1588 R14=0000000000000000 R15=0000000007ebf1e0</span> 135<span class="line highlighted">RIP=0000000040000069 RFL=00000202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0</span> 136<span class="line">ES =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 137<span class="line highlighted">CS =001b 0000000000000000 ffffffff 00a0fb00 DPL=3 CS64 [-RA]</span> 138<span class="line highlighted">SS =0013 0000000000000000 ffffffff 00c0f300 DPL=3 DS [-WA]</span> 139<span class="line">DS =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 140<span class="line">FS =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 141<span class="line">GS =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 142<span class="line">LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT</span> 143<span class="line">TR =0020 ffff8000003263f0 00000067 00008900 DPL=0 TSS64-avl</span> 144<span class="line">GDT= ffff800000326490 0000002f</span> 145<span class="line">IDT= ffff8000003264c0 00000fff</span> 146<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We can see that <code>rip</code> is back in user space, and <code>CS</code> and <code>SS</code> are set to user code and data segments, respectively, and their DPL=3. The <code>rflags</code> are also restored to the user value with interrupts enabled. Everything looks good.</p><h2 id="switching-stacks" tabindex="-1"><a class="header-anchor" href="#switching-stacks"><span>Switching Stacks</span></a></h2><p>As I mentioned earlier, the CPU doesn't switch stacks for us when executing <code>syscall</code>. We need to switch to a kernel stack ourselves. We'll use the same stack we use for interrupts, the one we stored its address in <code>tss.rsp0</code>. We'll also need to save the user <code>rsp</code> somewhere so we can restore it later. We'll define two global variables for this in the <code>syscalls</code> module.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 147<span class="line"></span> 148<span class="line"><span class="token keyword">var</span></span> 149<span class="line"> kernelStackAddr<span class="token operator">:</span> uint64</span> 150<span class="line"> userRsp<span class="token operator">:</span> uint64</span> 151<span class="line"><span class="token operator">...</span></span> 152<span class="line"></span> 153<span class="line"><span class="token keyword">proc</span> <span class="token function">syscallInit<span class="token operator">*</span></span><span class="token punctuation">(</span>kernelStack<span class="token operator">:</span> uint64<span class="token punctuation">)</span> <span class="token operator">=</span></span> 154<span class="line"> kernelStackAddr <span class="token operator">=</span> kernelStack</span> 155<span class="line"> <span class="token operator">...</span></span> 156<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let's pass the kernel stack address to <code>syscallInit</code> from <code>main.nim</code>.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/main.nim</span></span> 157<span class="line"></span> 158<span class="line"><span class="token keyword">
158import</span> syscalls</span> 159<span class="line"></span> 160<span class="line"><span class="token keyword">proc</span> <span class="token function">KernelMainInner</span><span class="token punctuation">(</span>bootInfo<span class="token operator">:</span> <span class="token keyword">ptr</span> BootInfo<span class="token punctuation">)</span> <span class="token operator">=</span></span> 161<span class="line"> debugln <span class="token string">""</span></span> 162<span class="line"> debugln <span class="token string">"kernel: Fusion Kernel"</span></span> 163<span class="line"></span> 164<span class="line"> <span class="token operator">...</span></span> 165<span class="line"></span> 166<span class="line"> <span class="token comment"># create a kernel switch stack and set tss.rsp0</span></span> 167<span class="line"> debugln <span class="token string">"kernel: Creating kernel switch stack"</span></span> 168<span class="line"> <span class="token operator">...</span></span> 169<span class="line"></span> 170<span class="line highlighted"> debug <span class="token string">"kernel: Initializing Syscalls "</span></span> 171<span class="line highlighted"> <span class="token function">syscallInit</span><span class="token punctuation">(</span>tss<span class="token operator">.</span>rsp0<span class="token punctuation">)</span></span> 172<span class="line highlighted"> debugln <span class="token string">"[success]"</span></span> 173<span class="line"></span> 174<span class="line"> <span class="token operator">...</span></span> 175<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Now, let's modify <code>syscallEntry</code> to switch to the kernel stack and save the user <code>rsp</code>. We'll also push <code>rcx</code> and <code>r11</code> (user <code>rip</code> and <code>rflags</code>, respectively) on the kernel stack and restore them before calling <code>sysretq</code> to return to user mode.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 176<span class="line"></span> 177<span class="line"><span class="token keyword">proc</span> <span class="token function">syscallEntry</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{.</span>asmNoStackFrame<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 178<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 179<span class="line"> # save user stack pointer</span> 180<span class="line"> mov %0, rsp</span> 181<span class="line"></span> 182<span class="line"> # switch to kernel stack</span> 183<span class="line"> mov rsp, %1</span> 184<span class="line"></span> 185<span class="line"> push r11 # user rflags</span> 186<span class="line"> push rcx # user rip</span> 187<span class="line"></span> 188<span class="line"> # TODO: dispatch system call</span> 189<span class="line"></span> 190<span class="line"> # restore user rip and rflags</span> 191<span class="line"> pop r11</span> 192<span class="line"> pop rcx</span> 193<span class="line"></span> 194<span class="line"> # switch to user stack</span> 195<span class="line"> mov rsp, %0</span> 196<span class="line"></span> 197<span class="line"> sysretq</span> 198<span class="line"> : "
198+r"(\`userRsp\`)</span> 199<span class="line"> : "m"(\`kernelStackAddr\`)</span> 200<span class="line"> : "rcx", "r11"</span> 201<span class="line"> """</span></span> 202<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Right now, we're not doing much to handle the system call itself. We're just switching stacks, and saving and restoring the user <code>rip</code> and <code>rflags</code>. In order to do something useful, we need to define a system call handler and a way to pass arguments to it.</p><h2 id="system-call-handler" tabindex="-1"><a class="header-anchor" href="#system-call-handler"><span>System Call Handler</span></a></h2><p>Let's now define the actual system call handler. We'll define a <code>SyscallArgs</code> type to hold the system call number and arguments, and implement a <code>syscall</code> proc that takes a pointer to <code>SyscallArgs</code> and returns a <code>uint64</code> as the return value.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 203<span class="line"></span> 204<span class="line"><span class="token keyword">type</span></span> 205<span class="line"> SyscallArgs <span class="token operator">=</span> <span class="token keyword">object</span></span> 206<span class="line"> num<span class="token operator">:</span> uint64 <span class="token comment"># rdi</span></span> 207<span class="line"> arg1<span class="token operator">:</span> uint64 <span class="token comment"># rsi</span></span> 208<span class="line"> arg2<span class="token operator">:</span> uint64 <span class="token comment"># rdx</span></span> 209<span class="line"> arg3<span class="token operator">:</span> uint64 <span class="token comment"># r8</span></span> 210<span class="line"> arg4<span class="token operator">:</span> uint64 <span class="token comment"># r9</span></span> 211<span class="line"> arg5<span class="token operator">:</span> uint64 <span class="token comment"># r10</span></span> 212<span class="line"></span> 213<span class="line"><span class="token operator">...</span></span> 214<span class="line"></span> 215<span class="line"><span class="token keyword">proc</span> <span class="token function">syscall<span class="token operator">*</span></span><span class="token punctuation">(</span>args<span class="token operator">:</span> <span class="token keyword">ptr</span> SyscallArgs<span class="token punctuation">)</span><span class="token operator">:</span> uint64 <span class="token punctuation">{.</span>exportc<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 216<span class="line"> debugln <span class="token operator">&</span><span class="token string">"syscall: num={args.num}"</span></span> 217<span class="line"> result <span class="token operator">=</span> <span class="token number">0x5050</span> <span class="token comment"># dummy return value</span></span> 218<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Notice that we're using the <code>exportc</code> pragma to export the <code>syscall</code> proc, since we'll be calling it from assembly code.</p><p>Now, let's modify <code>syscallEntry</code> to call <code>syscall</code> with the system call number and arguments. We'll create the <code>SyscallArgs</code> object on the kernel stack by pushing the appropriate registers, and pass its address to <code>syscall</code>.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 219<span class="line"><span class="token operator">...</span></span> 220<span class="line"></span> 221<span class="line"><span class="token keyword">proc</span> <span class="token function">syscallEntry</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{.</span>asmNoStackFrame<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 222<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 223<span class="line"> # save user stack pointer</span> 224<span class="line"> mov %0, rsp</span> 225<span class="line"></span> 226<span class="line"> # switch to kernel stack</span> 227<span class="line"> mov rsp, %1</span> 228<span class="line"></span> 229<span class="line"> push r11 # user rflags</span> 230<span class="line"> push rcx # user rip</span> 231<span class="line"></span> 232<span class="line highlighted"> # create SyscallArgs on the stack</span> 233<span class="line highlighted"> push r10</span> 234<span class="line highlighted"> push r9</span> 235<span class="line highlighted"> push r8</span> 236<span class="line highlighted"> push rdx</span> 237<span class="line highlighted"> push rsi</span> 238<span class="line highlighted"> push rdi</span> 239<span class="line highlighted"></span> 240<span class="line highlighted"> # rsp is now pointing to SyscallArgs, pass it to syscall</span> 241<span class="line highlighted"> mov rdi, rsp</span> 242<span class="line highlighted"> call syscall</span> 243<span class="line highlighted"></span> 244<span class="line highlighted"> # pop SyscallArgs</span> 245<span class="line highlighted"> pop rdi</span> 246<span class="line highlighted"> pop rsi</span> 247<span class="line highlighted"> pop rdx</span> 248<span class="line highlighted"> pop r8</span> 249<span class="line highlighted"> pop r9</span> 250<span class="line highlighted"> pop r10</span> 251<span class="line"></span> 252<span class="line"> # prepare for sysretq</span> 253<span class="line"> pop rcx # user rip</span> 254<span class="line"> pop r11 # user rflags</span> 255<span class="line"></span> 256<span class="line"> # switch to user stack</span> 257<span class="line"> mov rsp, %0</span> 258<span class="line"></span> 259<span class="line"> sysretq</span> 260<span class="line"> : "
260+r"(\`userRsp\`)</span> 261<span class="line"> : "m"(\`kernelStackAddr\`)</span> 262<span class="line highlighted"> : "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "rax"</span> 263<span class="line"> """</span></span> 264<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Notice that on the last line we're telling the compiler that <code>syscallEntry</code> clobbers the indicated registers. Otherwise, the compiler might try to use them for other purposes.</p><p>Let's try this out. We still have the user program passing <code>1</code>, so we should see that printed by <code>syscall</code>, and the dummy return value <code>0x5050</code> should be in <code>rax</code> when we return to user mode.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">kernel: Initializing Syscalls [success]</span> 265<span class="line">kernel: Switching to user mode</span> 266<span class="line">syscall: num=1</span> 267<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Great! The <code>syscall</code> proc was called and received the correct syscall number. Let's look at the <code>rax</code> register to see if it contains the dummy return value.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">(qemu) info registers</span> 268<span class="line">CPU#0</span> 269<span class="line highlighted">RAX=0000000000005050 RBX=ffff800000327220 RCX=0000000040000074 RDX=000000004000ade8</span> 270<span class="line">RSI=0000000000000001 RDI=0000000000000001 RBP=0000000050000ff8 RSP=0000000050000fc8</span> 271<span class="line">R8 =ffff800100003c00 R9 =0000000000000000 R10=0000000050000fc8 R11=0000000000000202</span> 272<span class="line">R12=0000000000000000 R13=0000000006bb1588 R14=0000000000000000 R15=0000000007ebf1e0</span> 273<span class="line">RIP=0000000040000076 RFL=00000202 [-------] CPL=3 II=0 A20=1 SMM=0 HLT=0</span> 274<span class="line">ES =0013 0000000000000000 000fffff 000ff300 DPL=3 DS [-WA]</span> 275<span class="line">CS =001b 0000000000000000 ffffffff 00a0fb00 DPL=3 CS64 [-RA]</span> 276<span class="line">SS =0013 0000000000000000 ffffffff 00c0f300 DPL=3 DS [-WA]</span> 277<span class="line">...</span> 278<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Indeed, <code>rax</code> contains <code>0x5050</code>, and from the <code>rip</code>, <code>cs</code>, and <code>ss</code> register values we can see that we're back in user mode. So everything is working as expected.</p><h2 id="system-call-table" tabindex="-1"><a class="header-anchor" href="#system-call-table"><span>System Call Table</span></a></h2><p>Over time, we'll have more system calls, so we'll need a way to dispatch them. One way to do this is store the system call handlers in a table indexed by the system call number. Let's create that table.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 279<span class="line"></span> 280<span class="line"><span class="token keyword">type</span></span> 281<span class="line highlighted"> SyscallHandler<span class="token operator">*</span> <span class="token operator">=</span> <span class="token function">proc</span> <span class="token punctuation">(</span>args<span class="token operator">:</span> <span class="token keyword">ptr</span> SyscallArgs<span class="token punctuation">)</span><span class="token operator">:</span>
281 uint64 <span class="token punctuation">{.</span>cdecl<span class="token punctuation">.}</span></span> 282<span class="line"> SyscallArgs <span class="token operator">=</span> <span class="token keyword">object</span></span> 283<span class="line"> num<span class="token operator">:</span> uint64</span> 284<span class="line"> arg1<span class="token punctuation">,</span> arg2<span class="token punctuation">,</span> arg3<span class="token punctuation">,</span> arg4<span class="token punctuation">,</span> arg5<span class="token operator">:</span> uint64</span> 285<span class="line highlighted"> SyscallError<span class="token operator">*</span> <span class="token operator">=</span> <span class="token keyword">enum</span></span> 286<span class="line highlighted"> None</span> 287<span class="line highlighted"> InvalidSyscall</span> 288<span class="line"></span> 289<span class="line highlighted"><span class="token keyword">var</span></span> 290<span class="line highlighted"> syscallTable<span class="token operator">:</span> array<span class="token punctuation">[</span><span class="token number">256</span><span class="token punctuation">,</span> SyscallHandler<span class="token punctuation">]</span></span> 291<span class="line"></span> 292<span class="line"><span class="token operator">...</span></span> 293<span class="line"></span> 294<span class="line"><span class="token keyword">proc</span> <span class="token function">syscall<span class="token operator">*</span></span><span class="token punctuation">(</span>args<span class="token operator">:</span> <span class="token keyword">ptr</span> SyscallArgs<span class="token punctuation">)</span><span class="token operator">:</span> uint64 <span class="token punctuation">{.</span>exportc<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 295<span class="line"> debugln <span class="token operator">&</span><span class="token string">"syscall: num={args.num}"</span></span> 296<span class="line highlighted"> <span class="token keyword">if</span> args<span class="token operator">.</span>num <span class="token operator">></span> syscallTable<span class="token operator">.</span>high<span class="token operator">.</span>uint64 <span class="token operator">or</span> syscallTable<span class="token punctuation">[</span>args<span class="token operator">.</span>num<span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token keyword">nil</span><span class="token operator">:</span></span> 297<span class="line highlighted"> <span class="token keyword">return</span> InvalidSyscall<span class="token operator">.</span>uint64</span> 298<span class="line highlighted"> result <span class="token operator">=</span> <span class="token function">syscallTable[args.num]</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span></span> 299<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Now, let's define a system call to output a string to the debug console. The system call will take one argument: a pointer to a <code>string</code> object containing the string to output. We'll register the system call handler in <code>syscallInit</code>.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 300<span class="line"><span class="token operator">...</span></span> 301<span class="line"></span> 302<span class="line"><span class="token keyword">proc</span> <span class="token function">print<span class="token operator">*</span></span><span class="token punctuation">(</span>args<span class="token operator">:</span> <span class="token keyword">ptr</span> SyscallArgs<span class="token punctuation">)</span><span class="token operator">:</span>
302 uint64 <span class="token punctuation">{.</span>cdecl<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 303<span class="line"> debugln <span class="token string">"syscall: print"</span></span> 304<span class="line"> <span class="token keyword">let</span> s <span class="token operator">=</span> <span class="token function">cast[ptr string]</span><span class="token punctuation">(</span>args<span class="token operator">.</span>arg1<span class="token punctuation">)</span></span> 305<span class="line"> debugln s<span class="token punctuation">[</span><span class="token punctuation">]</span></span> 306<span class="line"> result <span class="token operator">=</span> <span class="token number">0</span></span> 307<span class="line"></span> 308<span class="line"><span class="token keyword">proc</span> <span class="token function">syscallInit<span class="token operator">*</span></span><span class="token punctuation">(</span>kernelStack<span class="token operator">:</span> uint64<span class="token punctuation">)</span> <span class="token operator">=</span></span> 309<span class="line"> <span class="token operator">...</span></span> 310<span class="line"> syscallTable<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> print</span> 311<span class="line"> <span class="token operator">...</span></span> 312<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let's try to invoke this system call from our user program.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/user/utask.nim</span></span> 313<span class="line"><span class="token operator">...</span></span> 314<span class="line"></span> 315<span class="line highlighted"><span class="token keyword">let</span></span> 316<span class="line highlighted"> msg <span class="token operator">=</span> <span class="token string">"user: Hello from user mode!"</span></span> 317<span class="line highlighted"> pmsg <span class="token operator">=</span> msg<span class="token operator">.</span><span class="token keyword">addr</span></span> 318<span class="line"></span> 319<span class="line"><span class="token keyword">proc</span> <span class="token function">UserMain<span class="token operator">*</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{.</span>exportc<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 320<span class="line"> <span class="token function">NimMain</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span> 321<span class="line"></span> 322<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 323<span class="line highlighted"> mov rdi, 1</span> 324<span class="line"> mov rsi, %0</span> 325<span class="line"> syscall</span> 326<span class="line"></span> 327<span class="line"> .loop:</span> 328<span class="line"> pause</span> 329<span class="line"> jmp .loop</span> 330<span class="line"> :</span> 331<span class="line highlighted"> : "r"(\`pmsg\`)</span> 332<span class="line highlighted"> : "rdi", "rsi", "rcx", "r11"</span> 333<span class="line"> """</span></span> 334<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We're passing the system call number <code>1</code> in <code>rdi</code>, and the address of the string in <code>rsi</code>. Notice that we tell the compiler that the <code>rcx</code> and <code>r11</code> registers are clobbered (they will be modified by the CPU during the syscall). Let's run it and see what happens.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">kernel: Initializing Syscalls [success]</span> 335<span class="line">kernel: Switching to user mode</span> 336<span class="line">syscall: num=1</span> 337<span class="line">syscall: print</span> 338<span class="line">user: Hello from user mode!</span> 339<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Great! We can now ask the kernel to print a string for us. This is our first kernel service provided through a system call!</p><h2 id="argument-validation" tabindex="-1"><a class="header-anchor" href="#argument-validation"><span>Argument Validation</span></a></h2><p>There's one important piece missing though. Arguments to system calls have to be validated thoroughly. We can't just blindly trust the user program to pass valid arguments. We already did this for the system call number. But what about the string pointer? The user can pass any pointer value, so it's imperative that we validate it before dereferencing it. In this case, we'll keep it simple and make sure that the pointer is within the user address space. We can check if it's mapped, but that's going to be expensive. Instead, we'll just check if it's within the user address space range, and if it isn't mapped, we'll let the page fault handler deal with it.</p><p>Here's the modified <code>print</code> system call.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 340<span class="line"></span> 341<span class="line"><span class="token keyword">type</span></span> 342<span class="line"> SyscallError<span class="token operator">*</span> <span class="token operator">=</span> <span class="token keyword">enum</span></span> 343<span class="line"> None</span> 344<span class="line"> InvalidSyscall</span> 345<span class="line"> InvalidArg</span> 346<span class="line"></span> 347<span class="line highlighted"><span class="token keyword">const</span></span> 348<span class="line highlighted"> UserAddrSpaceEnd<span class="token operator">*</span> <span class="token operator">=</span> <span class="token number">0x00007FFFFFFFFFFF</span></span> 349<span class="line"></span> 350<span class="line"><span class="token operator">...</span></span> 351<span class="line"></span> 352<span class="line"><span class="token keyword">proc</span> <span class="token function">print<span class="token operator">*</span></span><span class="token punctuation">(</span>args<span class="token operator">:</span> <span class="token keyword">ptr</span> SyscallArgs<span class="token punctuation">)</span><span class="token operator">:</span>
352 uint64 <span class="token punctuation">{.</span>cdecl<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 353<span class="line"> debugln <span class="token string">"syscall: print"</span></span> 354<span class="line"></span> 355<span class="line highlighted"> <span class="token keyword">if</span> args<span class="token operator">.</span>arg1 <span class="token operator">></span> UserAddrSpaceEnd<span class="token operator">:</span></span> 356<span class="line highlighted"> debugln <span class="token string">"syscall: print: Invalid pointer"</span></span> 357<span class="line highlighted"> <span class="token keyword">return</span> InvalidArg<span class="token operator">.</span>uint64</span> 358<span class="line"></span> 359<span class="line"> <span class="token keyword">let</span> s <span class="token operator">=</span> <span class="token function">cast[ptr string]</span><span class="token punctuation">(</span>args<span class="token operator">.</span>arg1<span class="token punctuation">)</span></span> 360<span class="line"> debugln s<span class="token punctuation">[</span><span class="token punctuation">]</span></span> 361<span class="line"></span> 362<span class="line"> result <span class="token operator">=</span> <span class="token number">0</span></span> 363<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let's try it out by passing an address in kernel space to the system call.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/user/utask.nim</span></span> 364<span class="line"></span> 365<span class="line"><span class="token keyword">let</span></span> 366<span class="line"> msg <span class="token operator">=</span> <span class="token string">"user: Hello from user mode!"</span></span> 367<span class="line highlighted"> pmsg <span class="token operator">=</span> <span class="token number">0xffff800000100000</span> <span class="token comment"># kernel space address</span></span> 368<span class="line"></span> 369<span class="line"><span class="token operator">...</span></span> 370<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>If we run this, we should see the error message printed by the kernel.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">kernel: Initializing Syscalls [success]</span> 371<span class="line">kernel: Switching to user mode</span> 372<span class="line">syscall: num=1</span> 373<span class="line">syscall: print</span> 374<span class="line">syscall: print: Invalid pointer</span> 375<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Awesome! Our argument validation works as expected.</p><h2 id="the-exit-system-call" tabindex="-1"><a class="header-anchor" href="#the-exit-system-call"><span>The <code>exit</code> System Call</span></a></h2><p>Before we leave this section, let's add one more system call: <code>exit</code>. This system call will take one argument: the exit code. Keep in mind that we don't have a scheduler yet; our kernel transferred control to the user program, the user program called a system call to print a message, and will exit user mode in one thread of execution. So, without other tasks to switch to at the moment, we'll just halt the CPU when the user program exits.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 376<span class="line"></span> 377<span class="line"><span class="token keyword">proc</span> <span class="token function">exit<span class="token operator">*</span></span><span class="token punctuation">(</span>args<span class="token operator">:</span> <span class="token keyword">ptr</span> SyscallArgs<span class="token punctuation">)</span><span class="token operator">:</span>
377 uint64 <span class="token punctuation">{.</span>cdecl<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 378<span class="line"> debugln <span class="token operator">&</span><span class="token string">"syscall: exit: code={args.arg1}"</span></span> 379<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 380<span class="line"> cli</span> 381<span class="line"> hlt</span> 382<span class="line"> """</span></span> 383<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We'll give the <code>exit</code> system call the number 1 instead of <code>print</code>, and we'll make print system call number 2.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/syscalls.nim</span></span> 384<span class="line"><span class="token operator">...</span></span> 385<span class="line"></span> 386<span class="line"><span class="token keyword">proc</span> <span class="token function">syscallInit<span class="token operator">*</span></span><span class="token punctuation">(</span>kernelStack<span class="token operator">:</span> uint64<span class="token punctuation">)</span> <span class="token operator">=</span></span> 387<span class="line"> <span class="token operator">...</span></span> 388<span class="line"> syscallTable<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">=</span> exit</span> 389<span class="line"> syscallTable<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token operator">=</span> print</span> 390<span class="line"> <span class="token operator">...</span></span> 391<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Now, let's modify the user program to call <code>exit</code> after printing the message.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/user/utask.nim</span></span> 392<span class="line"><span class="token operator">...</span></span> 393<span class="line"></span> 394<span class="line"><span class="token keyword">proc</span> <span class="token function">UserMain<span class="token operator">*</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{.</span>exportc<span class="token punctuation">.}</span> <span class="token operator">=</span></span> 395<span class="line"> <span class="token function">NimMain</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span> 396<span class="line"></span> 397<span class="line"> <span class="token keyword">asm</span> <span class="token string">"""</span> 398<span class="line highlighted"> # call print</span> 399<span class="line highlighted"> mov rdi, 2</span> 400<span class="line"> mov rsi, %0</span> 401<span class="line"> syscall</span> 402<span class="line"></span> 403<span class="line highlighted"> # call exit</span> 404<span class="line highlighted"> mov rdi, 1</span> 405<span class="line highlighted"> mov rsi, 0</span> 406<span class="line highlighted"> syscall</span> 407<span class="line"> :</span> 408<span class="line"> : "r"(\`pmsg\`)</span> 409<span class="line"> : "rdi", "rsi", "rcx", "r11"</span> 410<span class="line"> """</span></span> 411<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Notice that I removed the infinite loop, as the <code>exit</code> syscall does not return. Let's run it and see what happens.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">kernel: Initializing Syscalls [success]</span> 412<span class="line">kernel: Switching to user mode</span> 413<span class="line">syscall: num=2</span> 414<span class="line">syscall: print</span> 415<span class="line">user: Hello from user mode!</span> 416<span class="line">syscall: num=1</span> 417<span class="line">syscall: exit: code=0</span> 418<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Looks good! The <code>exit</code> system call was called and received the correct exit code, and the kernel halted the CPU.</p><p>This is another big milestone. We now have a working system call interface, and we can invoke kernel services from user mode. In the next section, we'll look into encapsulating user task related context in a <code>Task</code> object.</p>`,90)]))}const o=n(i,[["render",t],["__file","18-system-calls.html.vue"]]),r=JSON.parse(`{"path":"/osdev/18-system-calls.html","title":"System Calls","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"System Call Interface","slug":"system-call-interface","link":"#system-call-interface","children":[]},{"level":2,"title":"Initialization","slug":"initialization","link":"#initialization","children":[]},{"level":2,"title":"Invoking System Calls","slug":"invoking-system-calls","link":"#invoking-system-calls","children":[]},{"level":2,"title":"Switching Stacks","slug":"switching-stacks","link":"#switching-stacks","children":[]},{"level":2,"title":"System Call Handler","slug":"system-call-handler","link":"#system-call-handler","children":[]},{"level":2,"title":"System Call Table","slug":"system-call-table","link":"#system-call-table","children":[]},{"level":2,"title":"Argument Validation","slug":"argument-validation","link":"#argument-validation","children":[]},{"level":2,"title":"The exit System Call","slug":"the-exit-system-call","link":"#the-exit-system-call","children":[]}
418],"git":{"updatedTime":1745166318000},"filePathRelative":"osdev/18-system-calls.md","excerpt":"\\n<p>User programs run in a restricted environment. They can't access hardware directly,\\nallocate memory, or do any privileged operations. Instead, they must ask the kernel to do\\nthese things for them. The kernel provides these services through system calls. System\\ncalls are the interface between user programs and the kernel.</p>"}`);export{o as comp,r as data};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.