Name: Danial Valentino Jaber Course: CPEN 331 2023W Lab Section: L1B Student Number: 15766819 ---------- Step 4. 1. First of all, let's make sure you can run System/161. In a shell, invoke sys161 with your newly built kernel (you should know how to do that if you have complete step 1) and copy the output of that command into your submit file. danial27@ssh-linux4:~/os161/root$ sys161 kernel sys161: System/161 release 2.0.3, compiled Sep 17 2022 09:27:01 OS/161 base system version 1.99.08 Copyright (c) 2000, 2001-2005, 2008-2011, 2013, 2014 President and Fellows of Harvard College. All rights reserved. Put-your-group-name-here's system version 0 (DUMBVM #1) 292k physical memory available Device probe... lamebus0 (system main bus) emu0 at lamebus0 ltrace0 at lamebus0 ltimer0 at lamebus0 beep0 at ltimer0 rtclock0 at ltimer0 lrandom0 at lamebus0 random0 at lrandom0 lhd0 at lamebus0 lhd1 at lamebus0 lser0 at lamebus0 con0 at lser0 cpu0: MIPS/161 (System/161 2.x) features 0x0 OS/161 kernel [? for menu]: 2. Now, let's make sure that you got your git repository working. From any directory in your git-controlled source tree, type: git log. danial27@ssh-linux4:~/os161/src$ git log commit 51b8e019effaebaf081a134745a3092e3462a19c (HEAD -> main, tag: asst1-start, origin/main, origin/HEAD) Author: Valentino Jaber <valentino.jaber@gmail.com> Date: Fri Sep 8 02:59:23 2023 -0700 Initial commit of os161 for CPEN331 commit 9a39b2f7e4a1665767ec472877676cce207094c5 Author: Valentino Jaber <valentino.jaber@gmail.com> Date: Fri Sep 8 02:54:42 2023 -0700 first commit 3. Next execute the git tag command and copy/paste its output into your submit file. danial27@ssh-linux4:~/os161/src$ git tag asst1-start ---------- Step 5. Question 1: In the book chapters and in class you were introduced to the mechanisms used to transfer control between user processes and the operating system. Tell us where we can find the first line of OS/161 code that is executed when a trap occurs. Then tell us where control gets transferred to from that point. What about an interrupt? How does that differ? When a trap occurs, the first line of code that is executed can typically be found in the exception and interrupt handling area of the code. The first line of OS/161 code can be found at src/kern/arch/mips/locore/exception-mips1.S -UTLB exception:69 .text .globl mips_utlb_handler .type mips_utlb_handler,@function .ent mips_utlb_handler mips_utlb_handler: j common_exception /* Don't need to do anything special */ // line 69 -Common exception:87 .text .globl mips_general_handler .type mips_general_handler,@function .ent mips_general_handler mips_general_handler: j common_exception /* Don't need to do anything special */ // line 87 From there, control gets transferred to mips_trap() in src/kern/arch/mips/locore/trap.c. In the case of an interrupt, a type of trap, the function mainbus_interrupt(trapframe) may be called from the src/kern/arch/sys161/dev/lamebus_machdep.c source file. Question 2: Making a system call, such as write, ultimately leads to a trap. Find the code in OS/161 that invokes system calls from user programs and causes traps. In which file and on which lines did you find this code? The place where syscall is executed can be found at line 84 within src/userland/lib/libc/arch/mips/syscalls-mips.S. Where system calls are invoked: .set noreorder .text .type __syscall,@function .ent __syscall __syscall: syscall /* make system call */ Question 3: Why do you suppose there are libc functions in the "common" part of the source tree (common/libc) as well as in userland/lib/libc? Two different sets of libc functions exist for protection between both the kernel and the user. Any changes made to the user's version of the libc functions will not impact those belonging to the kernel, since it has its own set of libc functions. Additionally, the kernel should depend on user-level code as little as possible. Thus, by having different sets of libc functions, the kernel stays small, modular (can reuse and repurpose), and independent. Question 4: Name two things that configure configures. What might invalidate that configuration and make you need/want to rerun it? 1. Configure can be used when building os161 to change things such as target platform and other build configurations. This is what was displayed inside the os161.config.mk # The target machine type is set when you configure the source tree. # If you change it, be sure to make distclean and recompile everything. 2. When looking through some files and makefiles, configure will provide necessary things such as where the os161 goes. This is what was displayed at the top of the defs.mk # This file was generated by configure. Edits will disappear if you rerun # configure. If you find that you need to edit this file to make things # work, let the course staff know and we'll try to fix the configure script. Different computers may require a reconfigure since things such as architecture and OS may change. As mentioned above, changing the target platform and build options may also require 'configure' to be rerun. Question 5: What are some of the details which would make a function "machine dependent"? Why might it be important to maintain this separation, instead of just putting all of the code in one function? A function is machine dependent if it relies on certain hardware or system configurations for a specific environment. Assembly and machine code are two of some low-level machine dependent languages. This dependence can include instructions that directly interact with the hardware (such as devices) and dependencies on any system libraries (ex. directX for Windows only). Its important to maintain separation between machine-dependent code and the rest of the program because it improves portability, allowing the code to run on different machines without modification, such as Java using JVM. It also promotes modularity and makes the code more understandable and maintainable by clearly defining the boundaries between machine-specific and general functionality, aiding in debugging and collaboration. Question 6: How large is a trapframe? Why? I determined this by looking at the file src/kern/arch/mips/include/trapframe.h. The trapframe struct contained 37 elements of type uint32_t, which are 37*4bytes = 148 bytes total for trapframe. Question 7: Under what circumstances should you re-run the kern/conf/config script? Looking through the long code in the file, I find frequent references to finding, checking, and adding devices/settings. An example of some code comments regarding a part of its implementation. # No bus specified to attach the device to (really, to find the # device attached on.) This is only legal if no attachments # at all were defined for the device, which is the case if the # device is in fact not attached to anything else (like the main # system bus, or a device like /dev/null that has no hardware.) and # 4. Process device attachments into $CONFTMP.attach. # Also add device/attachment files to $CONFTMP.files. Looking through the rest of it, I would assume it is re-run when things such as devices, settings, or files change. Question 8: Under what circumstances should you run bmake depend in kern/compile/DUMBVM? Since I recalled using this in the os161 setup instructions on the course website, I quoted this: Rerun bmake depend if/when you change header file inclusions, or after re-running config. You should run bmake depend if any header files were added/removed/modified or if config (discussed above) was run. Question 9: Under what circumstances should you run bmake or bmake install in kern/compile/DUMBVM? Again, I looked through the course os161 instructions and quoted this: You can compile a single program by typing bmake in its source directory; typing bmake install will install just that program into the right place in ~/os161/root. From this, it can be deduced that bmake or bmake install should be run if any kernel programs are modified. Question 10: When you booted your kernel, you found that there were several commands that you could issue to experiment with it. Explain exactly where and what you would have to do to add a command that printed out, "Hello world!" Looking through the system, I located src/kern/main/main.c which seems to contain the command dispatcher: /* * In-kernel menu and command dispatcher. */ I would create a function within here like this (used notation from related functions): #include <stdio.h> static int cmd_helloworld(int nargs, char **args) { if (nargs > 1) { printf("Too many arguments!\n"); return 1; } printf("Hello, %s!\n", nargs > 0 ? args[0] : "world"); return 0; } In addition, I would have to make sure to add an entry to the command table starting on line 527 and to the opsmenu on line 434. Question 11: Why do we need to include these in your OS/161 distribution? Why can't you just use the standard utilities that are present on the machine on which you're working? I utilize the Linux servers provided by UBC to do my classwork and use os161. Without the above commands, MIPs and its associated interface are not naturally supported on such architecture. Without them, the capabilities become very limited, moreso if the standard utilities are machine dependent. Question 12: When a user program exits, what is done with the program's return value? Looking at src/userland/lib/crt0/mips/crt0.S, we can see some answers to this question: * Basically, this is the startup code that gets invoked before main(), * and regains control when main returns. and here: /* * Now, we have the return value of main in v0. * * Move it to s0 (which is callee-save) so we still have * it in case exit() returns. * * Also move it to a0 so it's the argument to exit. */ move s0, v0 /* save return value */ From this, we see that when main returns, the return value is stored in v0, followed by being stored in s0 and a0. Question 13: Imagine that you wanted to add a new system call. List all the places that you would need to modify/add code. Then review your answers to questions 7-9 and note which of those actions you need to take in order to test the new system call. Let's assume my system call will be called flyaway(). System call implementations seem to be added here in src/kern/include/syscall.h /* * Prototypes for IN-KERNEL entry points for system call implementations. */ int sys_reboot(int code); int sys___time(userptr_t user_seconds, userptr_t user_nanoseconds); ints sys___flyaway(int code); int sys___flyaway(...); The place where system calls are stored is src/kern/include/kern/syscall.h. Add an entry here. #define SYS_flyaway 121 In src/kern/arch/mips/syscall/syscall.c, it seems there is a switch block for system calls which should be updated. case SYS___time: err = sys___time((userptr_t)tf->tf_a0, (userptr_t)tf->tf_a1); break; case SYS___flyaway: err = sys___flyaway(...); break; Lastly, following the location of the above example (sys_time), the code for the system call should go in sys/kern/syscall. The code for sys_time is present in that directory as well, under the same time_syscalls.c The kern/conf/config script makes mention of checks related to new source files and thus I think should be ran regardless. Noting that a new file was created as well as a header file was modified, bmake depend, bmake, and bmake install should all be ran to compile the kernel. ---------- Step 7. Question 14: What is the name of the very first function that executes when OS161 starts up? When OS161 starts up, the first function that executes is __start. (gdb) target remote unix:.sockets/gdb Remote debugging using unix:.sockets/gdb __start () at ../../arch/sys161/main/start.S:54 54 addiu sp, sp, -24 Question 15: What is the very first assembly instruction that executes? The first assembly instruction that executes is addiu sp,sp,-24. (gdb) disassemble Dump of assembler code for function __start: => 0x8002d6f0 <+0>: addiu sp,sp,-24 // here 0x8002d6f4 <+4>: sw ra,20(sp) 0x8002d6f8 <+8>: lui s0,0x8003 Question 16: Set the breakpoints in the kernel function that shows the menu and in the kernel main function. Now tell GDB to display all the breakpoints that were set and copy the output to your submit file. (gdb) b kmain Breakpoint 1 at 0x800139e4: file ../../main/main.c, line 211. (gdb) b menu Breakpoint 2 at 0x80014a0c: file ../../main/menu.c, line 697. (gdb) info breakpoints Num Type Disp Enb Address What 1 breakpoint keep y 0x800139e4 in kmain at ../../main/main.c:211 2 breakpoint keep y 0x80014a0c in menu at ../../main/menu.c:697 Question 17: Briefly describe what happens between the beginning of the execution and the invocation of the kernel main function. From the start of execution, here are some key activities: - We stash _end in a saved register la s0, _end - Make boostring second arg and _end first arg move a1, a0 move a0, s0 - Copy and store boostring and its length jal strcpy jal strlen - Round up to the next page boundary addi t0, t0, 4095 - Add page to hold the stack addi t0, t0, 4096 - start the kernel stack move sp, t0 - loading a variety of mips addresses la a1, mips_general_handler la a2, mips_general_end - get a mask value and set the status register li t0, CST_IRQMASK mtc0 t0, c0_status Question 18: What is the assembly language instruction that calls the kernel main function? The assembly language instruction that calls the kernel main function is jal kmain. (gdb) n 215 jal kmain Question 19: Step through the boot() code to find out what functions are called during early initialization. Paste the gdb output that shows you what these functions are. The gdb ouput that shows the functions called during early initialization. (gdb) n 109 ram_bootstrap(); (gdb) n 110 proc_bootstrap(); (gdb) n 111 thread_bootstrap(); (gdb) n 112 hardclock_bootstrap(); (gdb) n 113 vfs_bootstrap(); (gdb) n 114 kheap_nextgeneration(); It can be verified that these 6 functions are indeed the correct ones since src/kern/main/main.c outlines them: /* Early initialization. */ ram_bootstrap(); proc_bootstrap(); thread_bootstrap(); hardclock_bootstrap(); vfs_bootstrap(); kheap_nextgeneration(); Question 20: Set a breakpoint in thread_bootstrap(). Once you hit that breakpoint, at the very first line of that function, attempt to print the contents of the *bootcpu variable. Copy the output into the submit file. The results of this showed no access to the memory: (gdb) p *bootcpu Cannot access memory at address 0x80000 Question 21: Now, step through that function until after the line that says 'bootcpu = cpu_create(0)'. Now print the content of *bootcpu and paste the output. The results this time around: (gdb) p bootcpu {c_self = 0x8003af00, c_number = 0, c_hardware_number = 0, c_curthread = 0x8003bf80, c_zombies = {tl_head = {tln_prev = 0x0, tln_next = 0x8003af1c, tln_self = 0x0}, tl_tail = { tln_prev = 0x8003af10, tln_next = 0x0, tln_self = 0x0}, tl_count = 0}, c_hardclocks = 0, c_spinlocks = 0, c_isidle = false, c_runqueue = { tl_head = {tln_prev = 0x0, tln_next = 0x8003af44, tln_self = 0x0}, tl_tail = {tln_prev = 0x8003af38, tln_next = 0x0, tln_self = 0x0}, tl_count = 0}, c_runqueue_lock = {splk_lock = 0, splk_holder = 0x0}, c_ipi_pending = 0, c_shootdown = {{ts_placeholder = 0} <repeats 16 times>}, c_numshootdown = 0, c_ipi_lock = {splk_lock = 0, splk_holder = 0x0}} Question 22: Print the allcpus array before the boot() function is executed. Paste the output. The output shows that there are no cpus. (gdb) cpuarray allcpus 0 cpus Question 23: Print again the same array after the boot() function is executed. Paste the output. The results this time around: (gdb) cpuarray allcpus _isidle = false, c_runqueue = {tl_head = {tln_prev = 0x0, tln_next = 0x8003a f44, tln_self = 0x0}, tl_tail = {tln_prev = 0x8003af38, tln_next = 0x0, tln_self = 0x0}, tl_count = 0}, c_runqueue_lock = {splk_lock = 0, splk_ho lder = 0x0}, c_ipi_pending = 0, c_shootdown = {{ ts_placeholder = 0} <repeats 16 times>}, c_numshootdown = 0, c_ipi_lock = {splk_lock = 0, splk_holder = 0x0}} ----------