axf-os161 / submit / asst4 / asst4-answers.txt
asst4-answers.txt
Raw
Name: Danial Jaber, Will Chaba
Course: CPEN 331 2023W

----------

1. What are the ELF magic numbers?

    The ELF magic numbers are the first four bytes at the start of the ELF file header (specifically 16 bytes in OS161)
    that identify the file type as an ELF file. The second, third, and fourth byte in hex each translate to ‘E’,’L’,’F’ in ASCII.
    The ELF header can be found in kern/include/elf.h.

2. What is the difference between UIO_USERISPACE and UIO_USERSPACE?
    When should one use UIO_SYSSPACE instead?

    They are flags to specify a data region to copy to.
    UIO_USERISPACE, the former, defines user process code, while the latter defines user process data.
    Both are used to indicate that data should be copied to a user address. They can be found in kern/include/uio.h.

    In the same enum class, UIO_SYSSPACE belongs to the kernel space
    and should be used when data should be copied to a kernel address space.

3. Why can the struct uio that is used to read in a segment be allocated on the stack in load_segment()
    (i.e., where does the memory read actually go)?

    The uio struct contains iovec, the data block struct which points to a virtual address of &iov within load_segment().
    Because of this, allocating on the stack is unproblematic as the read also occurs in the user’s address space.
    The uio struct can be found in kern/include/uio.h and the load_segment() function can be found in kern/syscall/loadelf.c.

4. In runprogram(), why is it important to call vfs_close() before going to usermode?

    In runprogram(), vfs_open() is used to read the contents of a file containing the program code,
    when switching to usermode, the contents of the file have been transferred into memory
    and it is important to close the file with vfs_close() to release the resource so that it can be safely used in the future.

5. What function forces the processor to switch into usermode?
    Is this function machine dependent?

    The function that forces the processer to swtich into usermode is enter_new_process()
    defined on line 423 of kern/arch/mips/locore/trap.c.
    The function is responsible for setting up the user-level program context (program arguments,
    setting program counter and stack pointer etc) and transitioning execution into usermode.
    Low-level mechanisms for enabling user mode and setting program context can be processor architecture dependent.

6. In what file are copyin and copyout defined? memmove?
    Why can't copyin and copyout be implemented as simply as memmove?

    copyin/copyout are both defined in kern/vm/copyinout.c.
    memmove() is defined in src/common/libc/string/memmove.c.
    copyin/copyout cannot be implemented as memmove as they not only copy blocks of memory,
    but are responsible for transporting memory between kernel and user space, which is a privileged instruction.

7. What (briefly) is the purpose of userptr_t?

    userptr_t is a struct that is used to hold user-space addresses.
    The struct is used in functions such as copyin() and copyout().
    It ensures that the memory address it points to is within the correct user-space region.

8. at is the numerical value of the exception code for a MIPS system call?

    The numerical value for the MIPS system call exception code is 8,
    but is normally referenced as EX_SYS as defined on line 91 of kern/arch/mips/include/trapframe.h:
        #define EX_SYS    8    /* Syscall */

9. How many bytes is an instruction in MIPS? (Answer this by reading syscall() carefully, not by looking somewhere else.)

    An instruction in MIPS is 4 bytes, this can be seen on line 141 of kern/arch/mips/syscall/syscall.c
    where the program counter is advanced 4 bytes after execution of a syscall instruction:
        tf->tf_epc += 4;

10. Why do you "probably want to change" the implementation of kill_curthread()?

    The reason we will want to change the implementation of kill_curthread() is that it is not actually implemented at all.
    Executing kill_curthread() will always invoke a panic() with the message “I don’t know how to handle this”
    on line 117 of kern/arch/mips/locore/trap.c:
	    panic("I don't know how to handle this\n");

11. What would be required to implement a system call that took more than 4 arguments?

    In order to implement a system call that takes more than 4 arguments,
    the first 4 arguments would utilize registers a0, a1, a2 and a3, but additional arguments would need to be passed
    onto the stack before the system call. The system call handler would be responsible
    for accessing the arguments from the user stack, starting at sp + 16.

12. What is the purpose of the SYSCALL macro?

    The SYSCALL macro takes two parameters, which are the symbol and syscall number.
    The purpose of the macro is to determine and execute the appropriate system call based on the system call number,
    resulting in assigning the syscall code to the register $v0 as SYS_##sym. sym will be replaced
    by the chosen syscall (from the code) such as reboot, mmap, or any of the other known syscall symbols.
    This macro can be found at userland/lib/libc/arch/mips/syscalls-mips.S.

13. What is the MIPS instruction that actually triggers a system call?
    (Answer this by reading the source in this directory, not looking somewhere else.)

    The function syscall() is called in context of the mips_trap() function, which is initiated by the MIPS instruction jal mips_trap.
    This call can be found at kern/arch/mips/locore/exception-mips1.S.

14. After reading syscalls-mips.S and syscall.c, you should be prepared to answer the following question:
    OS/161 supports 64-bit values; lseek() takes and returns a 64-bit offset value.
    Thus, lseek() takes a 32-bit file handle (arg0), a 64-bit offset (arg1), a 32-bit whence (arg2),
    and needs to return a 64-bit offset value.
    In void syscall(struct trapframe *tf) where will you find each of the three arguments (in which registers)
    and how will you return the 64-bit offset?

    A 32-bit file handle (arg0) can be retrieved from the tf->tf_a0 register.
    
    For a 64-bit offset (arg1), it is split into two parts, with the higher 32 bits in tf->tf_a2 and the lower 32 bits in tf->tf_a3.
    This is due to 64-bit arguments being passed in aligned pairs of registers.

    A 32-bit whence (arg2) is situated at the stack position tf->tf_sp+16, and it can be obtained by invoking copyin().

    The 64-bit offset is returned in the v0 and v1 registers, following the typical function call convention.

    For reference, syscalls-mips.S can be found at userland/lib/libc/arch/mips/syscalls-mips.S
    and syscall.c can be found at kern/arch/mips/syscalls.c

15. As you were reading the code in runprogram.c and loadelf.c, you probably noticed how the kernel manipulates the files.
    Which kernel function is called to open a file? Which macro is called to read the file? What about to write a file?
    Which data structure is used in the kernel to represent an open file?

    In the process of opening, reading, and writing files, at the kernel level:
        To open a file, the function vfs_open() in kern/vfs/vfspath.c is invoked.
        For reading the file, VOP_READ(vn, uio) is utilized, found at kern/include/vnode.h.
        When writing to the file, VOP_WRITE(vn, uio) is utilized.
        Within the kernel, an open file is represented using a structure called struct vnode.

16. What is the purpose of VOP_INCREF and VOP_DECREF?

    VOP_INCREF and VOP_DECREF are responsible for increasing and decreasing the reference count of a vnode,
    specifically the vn_refcount. The reference count is a mechanism used to manage the number of references to a file.
    They can be found in kern/include/vnode.h.