6.4 KiB
Handling the Shift key [20 points]
The keyboard driver in 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.
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 white-on-black 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).
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
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 running the program in user/bss.c by typing run bss at
the Yabloko prompt).
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.
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).
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.
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 if you attempt this and get stuck.
The badputs executable demonstrates
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
in the code. As a result, run badputs should no longer cause a kernel panic.
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].