Commit Graph

44 Commits

Author SHA1 Message Date
Andreas Kling
d5d45d1088 Rage hacking to get bash to run. It finally runs. So cool! :^) 2018-11-11 15:38:07 +01:00
Andreas Kling
c97a5862ce Remove MM::allocate_physical_pages() since it wasn't used.
Everyone was allocating one page at a time with allocate_physical_page().
2018-11-10 22:14:41 +01:00
Andreas Kling
e71cb1c56b Fix some paging related bugs exposed by the spawn stress test.
- Process::exec() needs to restore the original paging scope when called
  on a non-current process.
- Add missing InterruptDisabler guards around g_processes access.
- Only flush the TLB when modifying the active page tables.
2018-11-09 01:25:31 +01:00
Andreas Kling
7b96218389 Fix VMO leak in Process::exec().
Gotta make sure things get cleaned up before we yield-teleport in exec().
Also VMOs and regions are now viewable through /proc/mm and /proc/regions.
2018-11-08 22:25:29 +01:00
Andreas Kling
cd1e7419f0 Teach Process::exec() about the magic of file-backed VMO's.
This is really sweet! :^) The four instances of /bin/sh spawned at
startup now share their read-only text pages.

There are problems and limitations here, and plenty of room for
improvement. But it kinda works.
2018-11-08 21:20:09 +01:00
Andreas Kling
3b2dcd5929 Add a VMO pointer to VNode.
This way, if anyone tries to map an already mapped file, we share the VMO.
2018-11-08 15:39:26 +01:00
Andreas Kling
862f108cb5 Refactor the virtual memory object model a bit:
Process now has a number of Regions.
Each Region is backed by a VMObject.
A VMObject can be file-backed or anonymous. These can be shared.
2018-11-08 14:42:16 +01:00
Andreas Kling
3c8064a787 Support basic mmap'ing of a file!
All right, we can now mmap() a file and it gets magically paged in from fs
in response to an NP page fault. This is really cool :^)

I need to refactor this to support sharing of read-only file-backed pages,
but it's cool to just have something working.
2018-11-08 12:59:16 +01:00
Andreas Kling
153ea704af Add some basic signal support.
It only works for sending a signal to a process that's in userspace code.

We implement reception by synthesizing a PUSHA+PUSHF in the receiving process
(operating on values in the TSS.)
The TSS CS:EIP is then rerouted to the signal handler and a tiny return
trampoline is constructed in a dedicated region in the receiving process.

Also hacked up /bin/kill to be able to send arbitrary signals (kill -N PID)
2018-11-06 10:56:41 +01:00
Andreas Kling
9e62eb4856 Only COW on fault if the physical page has retain_count > 1.
This makes COW pages lazily-but-transparently revert back to read/write.
2018-11-05 14:11:21 +01:00
Andreas Kling
2d045d2a64 Implement COW pages! :^)
sys$fork() now clones all writable regions with per-page COW bits.
The pages are then mapped read-only and we handle a PF by COWing the pages.

This is quite delightful. Obviously there's lots of work to do still,
and it needs better data structures, but the general concept works.
2018-11-05 13:48:07 +01:00
Andreas Kling
e85c22fe58 Tidy up the page fault code a bit in preparation. 2018-11-05 10:29:19 +01:00
Andreas Kling
72cdc62155 Replace zones with individually tracked physical pages.
It's just a simple struct { ref_count, paddr }.
This will allow me to implement lazy zeroing and COW pages.
2018-11-05 10:23:00 +01:00
Andreas Kling
7fe4063323 Region::clone() should share the zone if it's read-only.
This avoids copying the .text and .rodata segments in fork().
The next step is copy-on-write support for the writable regions.
2018-11-03 23:20:49 +01:00
Andreas Kling
da13c9a264 Map pages in read-only ELF sections as non-writable.
This is so cool! :^) Now you'll crash if you try to write into your
.text or .rodata segments.
2018-11-03 11:36:45 +01:00
Andreas Kling
aa6d06b47e Use ELF program headers to load executables smarter.
This turned out way better than the old code. ELF loading is now quite
straightforward, and we don't need the weird concept of subregions anymore.

Next step is to respect the is_writable flag.
2018-11-03 11:29:30 +01:00
Andreas Kling
20fb1fc377 Fix some bugs in execve() and make sh use it for process launching.
Interrupting children of sh now always works with ^C :^)
2018-11-03 02:08:06 +01:00
Andreas Kling
202bdb553c Implemented sys$execve().
It's really crufty, but it basically works!
2018-11-03 01:51:42 +01:00
Andreas Kling
b59ce22fc5 Fix dumb-but-hard-to-find bug in paging.
This was the fix:

-process.m_page_directory[0] = m_kernel_page_directory[0];
-process.m_page_directory[1] = m_kernel_page_directory[1];
+process.m_page_directory->entries[0] = m_kernel_page_directory->entries[0];
+process.m_page_directory->entries[1] = m_kernel_page_directory->entries[1];

I spent a good two hours scratching my head, not being able to figure out why
user process page directories felt they had ownership of page tables in the
kernel page directory.

It was because I was copying the entire damn kernel page directory into
the process instead of only sharing the two first PDE's. Dang!
2018-11-03 00:35:57 +01:00
Andreas Kling
8accc92c3c Implement fork()!
This is quite cool! The syscall entry point plumbs the register dump
down to sys$fork(), which uses it to set up the child process's TSS
in order to resume execution right after the int 0x80 fork() call. :^)

This works pretty well, although there is some problem with the kernel
alias mappings used to clone the parent process's regions. If I disable
the MM::release_page_directory() code, there's no problem. Probably there's
a premature freeing of a physical page somehow.
2018-11-02 20:41:58 +01:00
Andreas Kling
812e7940e2 Add a simple /proc/cpuinfo that includes some info from CPUID. 2018-11-02 10:14:21 +01:00
Andreas Kling
90ddbca127 Free physical pages allocated for a process's page directory on exit.
Also use a ProcessPagingScope instead of region aliasing to implement
create-process ELF loading.
2018-11-01 23:08:10 +01:00
Andreas Kling
fce81d376c Move Region and Subregion out of Process and make them free classes. 2018-11-01 13:21:02 +01:00
Andreas Kling
3e532ac7b6 Process now maps regions immediately when they are allocated.
This avoids having to do a separate MM.mapRegionsForTask() pass.

Also, more Task => Process renaming that I apparently hadn't saved yet.
2018-11-01 13:15:46 +01:00
Andreas Kling
c178d7a9e3 Remove some unused MM functions.
These were just Q&D hacks I used while bringing up the paging code.
2018-11-01 12:51:54 +01:00
Andreas Kling
0f70b9105f Implement address validation by querying the task's page directory.
This is way better than walking the region lists. I suppose we could
even let the hardware trigger a page fault and handle that. That'll
be the next step in the evolution here I guess.
2018-11-01 12:45:51 +01:00
Andreas Kling
5891691640 Fix /proc/PID/stack in the new per-process page directory world.
I added an RAII helper called OtherTaskPagingScope. While present,
it switches the kernel over to using another task's page directory.
This is perfect for e.g walking the stack in /proc/PID/stack.
2018-11-01 11:44:21 +01:00
Andreas Kling
c45f166c63 More work on per-process page directories. It basically works now!
I spent some time stuck on a problem where processes would clobber each
other's stacks. Took me a moment to figure out that their stacks
were allocated in the sub-4MB linear address range which is shared
between all processes. Oops!
2018-11-01 11:36:25 +01:00
Andreas Kling
1da0a7c949 Give each task its own page directory.
This isn't finished but I'll commit as I go. We need to get to where context
switching only needs to change CR3 and everything's ready to go.

My basic idea is:
- The first 4 kB is off-limits. This catches null dereferences.
- Up to the 4 MB mark is identity-mapped and kernel-only.
- The rest is available to everyone!

While the first 4 MB is only available to the kernel, it's still mapped in
every process, for convenience when entering the kernel.
2018-11-01 09:01:51 +01:00
Andreas Kling
dec5683e9c Snazz up the kprintf() output a bit by giving it its own color. 2018-10-31 20:14:23 +01:00
Andreas Kling
819ce91395 Enough compatibility work to make figlet build and run!
I ran out of steam writing library routines and imported two
BSD-licensed libc routines: sscanf() and getopt().

I will most likely rewrite them sooner or later. For now
I just wanted to see figlet running.
2018-10-31 17:52:59 +01:00
Andreas Kling
c76dc9a047 Add /proc/mm and a /bin/mm utility that just dumps it.
This shows some info about the MM. Right now it's just the zone count
and the number of free physical pages. Lots more can be added.

Also added "exit" to sh so we can nest shells and exit from them.

I also noticed that we were leaking all the physical pages, so fixed that.
2018-10-28 10:28:21 +01:00
Andreas Kling
9a71c7759a Implement loading of linked ELF executables.
This took me a couple hours. :^)

The ELF loading code now allocates a single region for the entire
file and creates virtual memory mappings for the sections as needed.

Very nice!
2018-10-27 14:56:52 +02:00
Andreas Kling
1c49b34b93 Selectively disable interrupts in MM.
Also added an ASSERT_INTERRUPTS_DISABLED() macro.
2018-10-25 10:15:28 +02:00
Andreas Kling
44045b258c Teach MM to flush individual TLB entries only. 2018-10-23 15:53:11 +02:00
Andreas Kling
d133a2845f Add missing TLB flushes. 2018-10-23 13:03:11 +02:00
Andreas Kling
98f76f0153 Flush the TLB after modifying page tables.
This is a very inefficient naive implementation, but it gets us going.
Mapping tasks in and out of a shared address space now totally works.
2018-10-23 11:03:56 +02:00
Andreas Kling
fe237ee215 Lots of hacking:
- Turn Keyboard into a CharacterDevice (85,1) at /dev/keyboard.
- Implement MM::unmapRegionsForTask() and MM::unmapRegion()
- Save SS correctly on interrupt.
- Add a simple Spawn syscall for launching another process.
- Move a bunch of IO syscall debug output behind DEBUG_IO.
- Have ASSERT do a "cli" immediately when failing.
  This makes the output look proper every time.
- Implement a bunch of syscalls in LibC.
- Add a simple shell ("sh"). All it can do now is read a line
  of text from /dev/keyboard and then try launching the specified
  executable by calling spawn().

There are definitely bugs in here, but we're moving on forward.
2018-10-23 10:12:50 +02:00
Andreas Kling
b824f15619 Launching an arbitrary ELF executable from disk works! :^)
This is so cool! It's a bit messy now with two Task constructors,
but eventually they should fold into a single constructor somehow.
2018-10-22 15:43:02 +02:00
Andreas Kling
79ffdb7205 A lot of hacking:
- More work on funneling console output through Console.
- init() now breaks off into a separate task ASAP.
- ..this leaves the "colonel" task as a simple hlt idle loop.
- Mask all IRQs on startup (except IRQ2 for slave passthru)
- Fix underallocation bug in Task::allocateRegion().
- Remember how many times each Task has been scheduled.

The panel and scheduling banner are disabled until I get things
working nicely in the (brave) new Console world.
2018-10-22 11:15:16 +02:00
Andreas Kling
d5ec18027e Protect the first 4 KB of memory.
This makes null pointers crashy, tremendously useful :^)
2018-10-21 21:59:11 +02:00
Andreas Kling
3649638259 Add Regions concept to Task. 2018-10-18 14:55:20 +02:00
Andreas Kling
f67d695254 More paging stuff.
The test userspace process now runs at linear address 0x300000 which is
mapped to a dynamically allocated page from the MemoryManager. Cool!
2018-10-18 13:05:00 +02:00
Andreas Kling
77299cf54d Add basic paging. Only identity maps the bottom 4MB as a start. 2018-10-17 23:13:55 +02:00