From 2ef286066b2ae1fbdfd4ed4ce2a5b06cc0ff3035 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sun, 19 Jan 2025 02:56:57 +0400 Subject: [PATCH] Add tasks.md. --- tasks.md | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 tasks.md diff --git a/tasks.md b/tasks.md new file mode 100644 index 0000000..4d5a960 --- /dev/null +++ b/tasks.md @@ -0,0 +1,131 @@ +## Handling the Shift key [20 points] + +The keyboard driver in +[keyboard.c](https://gitlab.myltsev.ru/caos/hw/yabloko/-/blob/main/drivers/keyboard.c) +does not support the Shift key correctly. When the Shift key is pressed, a +question mark ('?') appears on the screen. + +To resolve this, you need to determine the scancodes sent by the keyboard when +the Shift keys are pressed and released. Start a debug session with `make +debug`, set a breakpoint at the `interrupt_handler` function, and press the +Shift keys in the Qemu window. Make sure to test both the left and right Shift +keys. + +Next, implement support for the Shift keys in the keyboard driver. When either +Shift key is pressed, letter keys should produce uppercase letters (e.g., `A` +when typing Shift+A), and digit keys should produce special symbols (e.g., `%` +when typing Shift+5). + +You will need to add static variables to `keyboard.c` to keep track of the state +of the Shift keys. + +## Handling the Backspace key [20 points] + +Yabloko does not handle Backspace correctly. If you press it, a question mark +appears on the screen. Your task is to fix this. + +Start by implementing a `vga_backspace` function in +[vga.c](https://gitlab.myltsev.ru/caos/hw/yabloko/-/blob/main/drivers/vga.c). +The function should move the cursor one position back with `vga_set_cursor` +and output a space in this position, so +``` +> some texts_ +``` +(with the cursor as `_`) becomes +``` +> some text_ +``` + +If the cursor is already at the beginning of the screen (`vga_get_cursor` +returns 0), `vga_backspace` should do nothing. + +As your next step, figure out the scancode of Backspace (see previous task) and +process it in `interrupt_handler` appropriately (decreasing `kbd_buf_size` and +calling `vga_backspace` if `kbd_buf_size` was nonzero). + +## Drawing a spinner [20 points] +The programmable interrupt timer (PIT) of the simulated computer +is configured to generate an interrupt approximately 100 times per second. +You can arrange for your handler to be called every time this happens +with `add_timer_callback(my_handler)` somewhere inside `kmain`. + +Your task is to draw a blue spinner in the upper right corner of the screen, +rotating once per second. The spinner can be drawn by displaying the next symbol +in the sequence `↑/→\↓/←\`. The arrows have codes 0x18-0x1b in the encoding +used by the simulated terminal ([CP437](https://en.wikipedia.org/wiki/Code_page_437)). + +You will need to make the `vga_set_char` function +in `vga.c` more configurable, so that it can output symbols of different colors. + +## Support `.bss` [60 points] +Yabloko does not honor the size of `.bss` that your program requests. +Instead, it [reads your program file](https://gitlab.myltsev.ru/caos/hw/yabloko/-/blob/main/proc.c#L53) +into memory, reserving just enough pages that it fits. If you create a large +`.bss` with (for example) `static char zeroes[8000]`, then accessing `zeroes[7000]` will +cause a page fault (try this out). + +Fix this by parsing the header of the executable file and mapping the required amount +of pages. We already have the header structure `hdr` in [proc.c](https://gitlab.myltsev.ru/caos/hw/yabloko/-/blob/main/proc.c#L58). + +There are `hdr->e_phnum` program sections in the executable. The first program header +starts `hdr->e_phoff` bytes after `hdr`, and each header has size `hdr->e_phentsize`. + +Treat each program header as a `Elf32_Phdr` structure (see [`man elf`](https://man7.org/linux/man-pages/man5/elf.5.html)). +The corresponding program section starts at `phdr->p_vaddr` and has size +`phdr->p_memsz` in memory. Figure out the highest page that any of the sections +reaches into, and map the required pages with a call to `allocuvm`, +similar to how they were [mapped for the executable](https://gitlab.myltsev.ru/caos/hw/yabloko/-/blob/main/proc.c#L49). +The pages mapped by `allocuvm` are automatically zeroed out. + +## Checking user supplied pointers [100 points] + +This task requires understanding of virtual memory mappings and the memory +layout of Yabloko. Contact [@myltsev](https://t.me/myltsev) if you attempt +this and get stuck. + +The `shout` executable [demonstrates](https://gitlab.myltsev.ru/caos/hw/yabloko/-/blob/main/user/shout.c#L14) +the use of the `SYS_puts` system call. It supplies the string pointer as an argument +of the system call, and the string is printed to the screen. + +However, the kernel does not actually check if the string pointer is correct! +It is easy to cause a panic by supplying a random value: +``` + syscall(SYS_puts, 0x12345678); + // Kernel panic: Page Fault +``` + +Your task is to check if the pointer actually points to a valid zero-terminated string, +completing a [FIXME](https://gitlab.myltsev.ru/caos/hw/yabloko/-/blob/main/cpu/idt.c#L124) +in the code. + +### Figuring out the size of mapped memory after the pointer +During the handling of this system call, the page table of the calling process +is installed. Our first task is to figure out if the virtual address in the pointer +is mapped to a page readable by userspace, and how many bytes after this address +are also readable by userspace. + +Let us consider an example. Let's say that in the curent page table, two pages +are mapped at 0x1234000 and 0x1235000, both readable by userspace, and no +mapping exists at 0x1236000. If the user supplies the pointer 0x1234ff0, then +0x100f bytes are readable after it. + +The current page directory is accessible in `proc.c` as `vm.user_task->pgdir`. +This is a virtual address of the page directory, so you can read the first entry +of the page directory as `pde_t entry = vm.user_task->pgdir[0]`. + +The page directory holds *physical* addresses of second level page tables. +If an entry of the page directory is present (`entry & PTE_P`), then +you can retrieve a virtual address of the page table as +`pte_t *table = P2V(PTE_ADDR(entry))`. + +In order to be readable by userspace, a page table entry has to be present +and user accessible (both `PTE_P` and `PTE_U` have to be set). + +Write a function `uintptr_t user_readable_after(uintptr_t ptr)` which +calculates the required value (e.g., 0 for a pointer that does not belong +to a mapped page). + +### Checking that a zero-terminated string exists at the pointer +Now we just have to check that there is a null terminator +somewhere inside the user-readable memory after the pointer. +You can directly read bytes with `((const char*)ptr)[index]`.