Compare commits
1 Commits
vm/merged/
...
rox-check-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
987a71ec40 |
@@ -16,13 +16,18 @@ stages:
|
|||||||
script:
|
script:
|
||||||
- cd src/$DIR
|
- cd src/$DIR
|
||||||
- make check | tee build.log
|
- make check | tee build.log
|
||||||
- grep -vE "^FAIL $IGNORE\$" build.log | grep -q "FAIL tests/$DIR" && exit 1 || exit 0
|
- grep -q "FAIL tests/$DIR" build.log && exit 1 || exit 0
|
||||||
|
|
||||||
test_devices:
|
test_devices:
|
||||||
extends: .pintos_tests
|
extends: .pintos_tests
|
||||||
variables:
|
variables:
|
||||||
DIR: devices
|
DIR: devices
|
||||||
|
|
||||||
|
test_filesys:
|
||||||
|
extends: .pintos_tests
|
||||||
|
variables:
|
||||||
|
DIR: filesys
|
||||||
|
|
||||||
test_threads:
|
test_threads:
|
||||||
extends: .pintos_tests
|
extends: .pintos_tests
|
||||||
variables:
|
variables:
|
||||||
@@ -32,7 +37,6 @@ test_userprog:
|
|||||||
extends: .pintos_tests
|
extends: .pintos_tests
|
||||||
variables:
|
variables:
|
||||||
DIR: userprog
|
DIR: userprog
|
||||||
IGNORE: (tests/userprog/no-vm/multi-oom)
|
|
||||||
|
|
||||||
test_vm:
|
test_vm:
|
||||||
extends: .pintos_tests
|
extends: .pintos_tests
|
||||||
|
|||||||
@@ -62,9 +62,6 @@ userprog_SRC += userprog/gdt.c # GDT initialization.
|
|||||||
userprog_SRC += userprog/tss.c # TSS management.
|
userprog_SRC += userprog/tss.c # TSS management.
|
||||||
|
|
||||||
# Virtual memory code.
|
# Virtual memory code.
|
||||||
vm_SRC += vm/frame.c # Frame table manager.
|
|
||||||
vm_SRC += vm/page.c # Page table manager.
|
|
||||||
vm_SRC += vm/mmap.c # Memory-mapped files.
|
|
||||||
vm_SRC += devices/swap.c # Swap block manager.
|
vm_SRC += devices/swap.c # Swap block manager.
|
||||||
#vm_SRC = vm/file.c # Some other file.
|
#vm_SRC = vm/file.c # Some other file.
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ SRCDIR = ..
|
|||||||
# To add a new test, put its name on the PROGS list
|
# To add a new test, put its name on the PROGS list
|
||||||
# and then add a name_SRC line that lists its source files.
|
# and then add a name_SRC line that lists its source files.
|
||||||
PROGS = cat cmp cp echo halt hex-dump mcat mcp rm \
|
PROGS = cat cmp cp echo halt hex-dump mcat mcp rm \
|
||||||
bubsort insult lineup matmult recursor args-ovf
|
bubsort insult lineup matmult recursor
|
||||||
|
|
||||||
# Should work from task 2 onward.
|
# Should work from task 2 onward.
|
||||||
cat_SRC = cat.c
|
cat_SRC = cat.c
|
||||||
@@ -18,7 +18,6 @@ lineup_SRC = lineup.c
|
|||||||
ls_SRC = ls.c
|
ls_SRC = ls.c
|
||||||
recursor_SRC = recursor.c
|
recursor_SRC = recursor.c
|
||||||
rm_SRC = rm.c
|
rm_SRC = rm.c
|
||||||
args-ovf_SRC = args-ovf.c
|
|
||||||
|
|
||||||
# Should work in task 3; also in task 4 if VM is included.
|
# Should work in task 3; also in task 4 if VM is included.
|
||||||
bubsort_SRC = bubsort.c
|
bubsort_SRC = bubsort.c
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,7 +1,6 @@
|
|||||||
#include "filesys/inode.h"
|
#include "filesys/inode.h"
|
||||||
#include <list.h>
|
#include <list.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <round.h>
|
#include <round.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "filesys/filesys.h"
|
#include "filesys/filesys.h"
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ sc-bad-arg sc-bad-num sc-boundary sc-boundary-2 halt exit create-normal \
|
|||||||
create-empty create-null create-bad-ptr create-long create-exists \
|
create-empty create-null create-bad-ptr create-long create-exists \
|
||||||
create-bound open-normal open-missing open-boundary open-empty \
|
create-bound open-normal open-missing open-boundary open-empty \
|
||||||
open-null open-bad-ptr open-twice close-normal close-twice close-stdin \
|
open-null open-bad-ptr open-twice close-normal close-twice close-stdin \
|
||||||
close-stdout close-bad-fd read-normal read-bad-ptr read-bad-buf read-boundary \
|
close-stdout close-bad-fd read-normal read-bad-ptr read-boundary \
|
||||||
read-zero read-stdout read-bad-fd write-normal write-bad-ptr write-bad-buf \
|
read-zero read-stdout read-bad-fd write-normal write-bad-ptr \
|
||||||
write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \
|
write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \
|
||||||
exec-large-arg exec-multiple exec-missing exec-over-arg exec-over-args \
|
exec-large-arg exec-multiple exec-missing exec-over-arg exec-over-args \
|
||||||
exec-bad-ptr wait-simple wait-twice wait-killed wait-load-kill \
|
exec-bad-ptr wait-simple wait-twice wait-killed wait-load-kill \
|
||||||
wait-bad-pid wait-bad-child multi-recurse multi-child-fd rox-simple \
|
wait-bad-pid wait-bad-child multi-recurse multi-child-fd rox-simple \
|
||||||
rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \
|
rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \
|
||||||
bad-jump bad-jump2 bad-maths overflow-stack)
|
bad-jump bad-jump2 bad-maths)
|
||||||
|
|
||||||
tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \
|
tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \
|
||||||
tests/userprog/,child-simple child-args child-bad child-close child-rox exec-exit)
|
tests/userprog/,child-simple child-args child-bad child-close child-rox exec-exit)
|
||||||
@@ -36,7 +36,6 @@ tests/userprog/bad-read2_SRC = tests/userprog/bad-read2.c tests/main.c
|
|||||||
tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c
|
tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c
|
||||||
tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c
|
tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c
|
||||||
tests/userprog/bad-maths_SRC = tests/userprog/bad-maths.c tests/main.c
|
tests/userprog/bad-maths_SRC = tests/userprog/bad-maths.c tests/main.c
|
||||||
tests/userprog/overflow-stack_SRC = tests/userprog/overflow-stack.c tests/main.c
|
|
||||||
tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c \
|
tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c \
|
||||||
tests/userprog/boundary.c tests/main.c
|
tests/userprog/boundary.c tests/main.c
|
||||||
tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \
|
tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \
|
||||||
@@ -67,7 +66,6 @@ tests/userprog/close-stdout_SRC = tests/userprog/close-stdout.c tests/main.c
|
|||||||
tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c
|
tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c
|
||||||
tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c
|
tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c
|
||||||
tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c
|
tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c
|
||||||
tests/userprog/read-bad-buf_SRC = tests/userprog/read-bad-buf.c tests/main.c
|
|
||||||
tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c \
|
tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c \
|
||||||
tests/userprog/boundary.c tests/main.c
|
tests/userprog/boundary.c tests/main.c
|
||||||
tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c
|
tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c
|
||||||
@@ -75,7 +73,6 @@ tests/userprog/read-stdout_SRC = tests/userprog/read-stdout.c tests/main.c
|
|||||||
tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c
|
tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c
|
||||||
tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c
|
tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c
|
||||||
tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c
|
tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c
|
||||||
tests/userprog/write-bad-buf_SRC = tests/userprog/write-bad-buf.c tests/main.c
|
|
||||||
tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c \
|
tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c \
|
||||||
tests/userprog/boundary.c tests/main.c
|
tests/userprog/boundary.c tests/main.c
|
||||||
tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c
|
tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c
|
||||||
@@ -125,12 +122,10 @@ tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt
|
|||||||
tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/read-bad-buf_PUTFILES += tests/userprog/sample.txt
|
|
||||||
tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/write-bad-buf_PUTFILES += tests/userprog/sample.txt
|
|
||||||
tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt
|
||||||
tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt
|
tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
Full robustness of argument passing and syscall handling code:
|
Full robustness of argument passing code:
|
||||||
- Test user stack overflow robustness of "exec" system calls and user code.
|
- Test user stack overflow robustness of "exec" system calls.
|
||||||
5 exec-over-arg
|
5 exec-over-arg
|
||||||
5 exec-over-args
|
5 exec-over-args
|
||||||
5 overflow-stack
|
|
||||||
|
|
||||||
- Test syscall user provided buffer validity checks.
|
|
||||||
5 read-bad-buf
|
|
||||||
5 write-bad-buf
|
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use tests::tests;
|
use tests::tests;
|
||||||
check_expected ([<<'EOF']);
|
check_expected ([<<'EOF', <<'EOF']);
|
||||||
|
(exec-bad-ptr) begin
|
||||||
|
(exec-bad-ptr) end
|
||||||
|
exec-bad-ptr: exit(0)
|
||||||
|
EOF
|
||||||
(exec-bad-ptr) begin
|
(exec-bad-ptr) begin
|
||||||
exec-bad-ptr: exit(-1)
|
exec-bad-ptr: exit(-1)
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use tests::tests;
|
use tests::tests;
|
||||||
check_expected ([<<'EOF']);
|
check_expected ([<<'EOF', <<'EOF']);
|
||||||
|
(open-bad-ptr) begin
|
||||||
|
(open-bad-ptr) end
|
||||||
|
open-bad-ptr: exit(0)
|
||||||
|
EOF
|
||||||
(open-bad-ptr) begin
|
(open-bad-ptr) begin
|
||||||
open-bad-ptr: exit(-1)
|
open-bad-ptr: exit(-1)
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
/* Attempt to overflow the user stack by allocating a 4kB buffer and writing into it.
|
|
||||||
The process must be terminated with -1 exit code until stack growth has been implemented in Task 3
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <syscall.h>
|
|
||||||
#include "tests/lib.h"
|
|
||||||
#include "tests/main.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
test_main (void)
|
|
||||||
{
|
|
||||||
char stack_obj[4096];
|
|
||||||
memset (stack_obj, 'a', sizeof stack_obj);
|
|
||||||
memset (stack_obj+10, '\0', 1);
|
|
||||||
msg ("buffer: %s", stack_obj);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# -*- perl -*-
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use tests::tests;
|
|
||||||
check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF',<<'EOF']);
|
|
||||||
(overflow-stack) begin
|
|
||||||
overflow-stack: exit(-1)
|
|
||||||
EOF
|
|
||||||
(overflow-stack) begin
|
|
||||||
(overflow-stack) buffer: aaaaaaaaaa
|
|
||||||
(overflow-stack) end
|
|
||||||
overflow-stack: exit(0)
|
|
||||||
EOF
|
|
||||||
pass;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
/* Passes a buffer to the read system call that starts in valid memory, but runs into kernel space.
|
|
||||||
The process must be terminated with -1 exit code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <syscall.h>
|
|
||||||
#include "tests/lib.h"
|
|
||||||
#include "tests/main.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
test_main (void)
|
|
||||||
{
|
|
||||||
int handle;
|
|
||||||
CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
|
|
||||||
|
|
||||||
read (handle, (char *) 0xbfffffe0, 100);
|
|
||||||
fail ("should not have survived read()");
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# -*- perl -*-
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use tests::tests;
|
|
||||||
check_expected (IGNORE_KERNEL_FAULTS => 1, [<<'EOF']);
|
|
||||||
(read-bad-buf) begin
|
|
||||||
(read-bad-buf) open "sample.txt"
|
|
||||||
read-bad-buf: exit(-1)
|
|
||||||
EOF
|
|
||||||
pass;
|
|
||||||
@@ -2,7 +2,12 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use tests::tests;
|
use tests::tests;
|
||||||
check_expected ([<<'EOF']);
|
check_expected ([<<'EOF', <<'EOF']);
|
||||||
|
(read-bad-ptr) begin
|
||||||
|
(read-bad-ptr) open "sample.txt"
|
||||||
|
(read-bad-ptr) end
|
||||||
|
read-bad-ptr: exit(0)
|
||||||
|
EOF
|
||||||
(read-bad-ptr) begin
|
(read-bad-ptr) begin
|
||||||
(read-bad-ptr) open "sample.txt"
|
(read-bad-ptr) open "sample.txt"
|
||||||
read-bad-ptr: exit(-1)
|
read-bad-ptr: exit(-1)
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
/* Passes a buffer to the write system call that starts in valid memory, but runs into kernel space.
|
|
||||||
The process must be terminated with -1 exit code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <syscall.h>
|
|
||||||
#include "tests/lib.h"
|
|
||||||
#include "tests/main.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
test_main (void)
|
|
||||||
{
|
|
||||||
int handle;
|
|
||||||
CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
|
|
||||||
|
|
||||||
write (handle, (char *) 0xbffffff0, 32);
|
|
||||||
fail ("should have exited with -1");
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# -*- perl -*-
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use tests::tests;
|
|
||||||
check_expected (IGNORE_KERNEL_FAULTS => 1, [<<'EOF']);
|
|
||||||
(write-bad-buf) begin
|
|
||||||
(write-bad-buf) open "sample.txt"
|
|
||||||
write-bad-buf: exit(-1)
|
|
||||||
EOF
|
|
||||||
pass;
|
|
||||||
@@ -2,7 +2,12 @@
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use tests::tests;
|
use tests::tests;
|
||||||
check_expected ([<<'EOF']);
|
check_expected ([<<'EOF', <<'EOF']);
|
||||||
|
(write-bad-ptr) begin
|
||||||
|
(write-bad-ptr) open "sample.txt"
|
||||||
|
(write-bad-ptr) end
|
||||||
|
write-bad-ptr: exit(0)
|
||||||
|
EOF
|
||||||
(write-bad-ptr) begin
|
(write-bad-ptr) begin
|
||||||
(write-bad-ptr) open "sample.txt"
|
(write-bad-ptr) open "sample.txt"
|
||||||
write-bad-ptr: exit(-1)
|
write-bad-ptr: exit(-1)
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
#include "tests/threads/tests.h"
|
#include "tests/threads/tests.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef VM
|
#ifdef VM
|
||||||
#include "vm/frame.h"
|
|
||||||
#include "vm/page.h"
|
|
||||||
#include "devices/swap.h"
|
#include "devices/swap.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef FILESYS
|
#ifdef FILESYS
|
||||||
@@ -103,10 +101,6 @@ main (void)
|
|||||||
palloc_init (user_page_limit);
|
palloc_init (user_page_limit);
|
||||||
malloc_init ();
|
malloc_init ();
|
||||||
paging_init ();
|
paging_init ();
|
||||||
#ifdef VM
|
|
||||||
frame_init ();
|
|
||||||
shared_file_pages_init ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Segmentation. */
|
/* Segmentation. */
|
||||||
#ifdef USERPROG
|
#ifdef USERPROG
|
||||||
|
|||||||
@@ -119,14 +119,14 @@ sema_up (struct semaphore *sema)
|
|||||||
|
|
||||||
old_level = intr_disable ();
|
old_level = intr_disable ();
|
||||||
if (!list_empty (&sema->waiters))
|
if (!list_empty (&sema->waiters))
|
||||||
{
|
{
|
||||||
/* Enforces wake-up of the highest priority thread waiting for the
|
/* Enforces wake-up of the highest priority thread waiting for the
|
||||||
semaphore. */
|
semaphore. */
|
||||||
struct list_elem *e = list_max (&sema->waiters, priority_less, NULL);
|
struct list_elem *e = list_max (&sema->waiters, priority_less, NULL);
|
||||||
list_remove (e);
|
list_remove (e);
|
||||||
thread_unblock (list_entry (e, struct thread, elem));
|
thread_unblock (list_entry (e, struct thread, elem));
|
||||||
thread_unblocked = true;
|
thread_unblocked = true;
|
||||||
}
|
}
|
||||||
sema->value++;
|
sema->value++;
|
||||||
intr_set_level (old_level);
|
intr_set_level (old_level);
|
||||||
|
|
||||||
@@ -134,12 +134,12 @@ sema_up (struct semaphore *sema)
|
|||||||
priority that the current running thread, including the case when called
|
priority that the current running thread, including the case when called
|
||||||
within an interrupt handler. */
|
within an interrupt handler. */
|
||||||
if (thread_unblocked)
|
if (thread_unblocked)
|
||||||
{
|
{
|
||||||
if (intr_context ())
|
if (intr_context ())
|
||||||
intr_yield_on_return ();
|
intr_yield_on_return ();
|
||||||
else
|
else
|
||||||
thread_yield ();
|
thread_yield ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sema_test_helper (void *sema_);
|
static void sema_test_helper (void *sema_);
|
||||||
@@ -212,7 +212,6 @@ donate_priority (struct thread *donee) {
|
|||||||
ASSERT (intr_get_level () == INTR_OFF);
|
ASSERT (intr_get_level () == INTR_OFF);
|
||||||
|
|
||||||
struct thread *donor = thread_current ();
|
struct thread *donor = thread_current ();
|
||||||
list_remove (&donor->donor_elem);
|
|
||||||
list_push_back (&donee->donors_list, &donor->donor_elem);
|
list_push_back (&donee->donors_list, &donor->donor_elem);
|
||||||
|
|
||||||
while (donee != NULL)
|
while (donee != NULL)
|
||||||
@@ -261,7 +260,6 @@ lock_acquire (struct lock *lock)
|
|||||||
ASSERT (!lock_held_by_current_thread (lock));
|
ASSERT (!lock_held_by_current_thread (lock));
|
||||||
|
|
||||||
struct thread *t = thread_current ();
|
struct thread *t = thread_current ();
|
||||||
ASSERT (t->waiting_lock == NULL);
|
|
||||||
|
|
||||||
enum intr_level old_level = intr_disable ();
|
enum intr_level old_level = intr_disable ();
|
||||||
if (lock->holder != NULL)
|
if (lock->holder != NULL)
|
||||||
@@ -343,6 +341,7 @@ lock_release (struct lock *lock)
|
|||||||
released, transfer the remaining orphaned donors to its donor list. */
|
released, transfer the remaining orphaned donors to its donor list. */
|
||||||
if (max_donor != NULL)
|
if (max_donor != NULL)
|
||||||
{
|
{
|
||||||
|
list_remove (&max_donor->donor_elem);
|
||||||
while (!list_empty (&orphan_list))
|
while (!list_empty (&orphan_list))
|
||||||
list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list));
|
list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,6 @@
|
|||||||
#ifdef USERPROG
|
#ifdef USERPROG
|
||||||
#include "userprog/process.h"
|
#include "userprog/process.h"
|
||||||
#include "userprog/syscall.h"
|
#include "userprog/syscall.h"
|
||||||
#include "vm/page.h"
|
|
||||||
#endif
|
|
||||||
#ifdef VM
|
|
||||||
#include "vm/mmap.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Random value for struct thread's `magic' member.
|
/* Random value for struct thread's `magic' member.
|
||||||
@@ -75,7 +71,7 @@ static void kernel_thread (thread_func *, void *aux);
|
|||||||
static void idle (void *aux UNUSED);
|
static void idle (void *aux UNUSED);
|
||||||
static struct thread *running_thread (void);
|
static struct thread *running_thread (void);
|
||||||
static struct thread *next_thread_to_run (void);
|
static struct thread *next_thread_to_run (void);
|
||||||
static bool init_process_result (struct thread *t);
|
static void init_process_result (struct thread *t);
|
||||||
static void init_thread (struct thread *, const char *name, int nice,
|
static void init_thread (struct thread *, const char *name, int nice,
|
||||||
int priority, fp32_t recent_cpu);
|
int priority, fp32_t recent_cpu);
|
||||||
static bool is_thread (struct thread *) UNUSED;
|
static bool is_thread (struct thread *) UNUSED;
|
||||||
@@ -88,10 +84,6 @@ void thread_schedule_tail (struct thread *prev);
|
|||||||
static tid_t allocate_tid (void);
|
static tid_t allocate_tid (void);
|
||||||
static bool donor_priority_less (const struct list_elem *a_,
|
static bool donor_priority_less (const struct list_elem *a_,
|
||||||
const struct list_elem *b_, void *aux UNUSED);
|
const struct list_elem *b_, void *aux UNUSED);
|
||||||
static unsigned process_result_hash (const struct hash_elem *e,
|
|
||||||
void *aux UNUSED);
|
|
||||||
static bool process_result_less (const struct hash_elem *a,
|
|
||||||
const struct hash_elem *b, void *aux UNUSED);
|
|
||||||
|
|
||||||
/* Initializes the threading system by transforming the code
|
/* Initializes the threading system by transforming the code
|
||||||
that's currently running into a thread. This can't work in
|
that's currently running into a thread. This can't work in
|
||||||
@@ -130,13 +122,6 @@ thread_init (void)
|
|||||||
void
|
void
|
||||||
thread_start (void)
|
thread_start (void)
|
||||||
{
|
{
|
||||||
/* Malloc has been initalised, we can allocate the child results table
|
|
||||||
for the main thread. */
|
|
||||||
struct thread *t = thread_current ();
|
|
||||||
if (!hash_init (&t->child_results, process_result_hash, process_result_less,
|
|
||||||
t))
|
|
||||||
PANIC ("Failed to initialise child results table for main thread.");
|
|
||||||
|
|
||||||
/* Create the idle thread. */
|
/* Create the idle thread. */
|
||||||
struct semaphore idle_started;
|
struct semaphore idle_started;
|
||||||
sema_init (&idle_started, 0);
|
sema_init (&idle_started, 0);
|
||||||
@@ -256,43 +241,11 @@ thread_create (const char *name, int priority,
|
|||||||
struct thread *parent_thread = thread_current ();
|
struct thread *parent_thread = thread_current ();
|
||||||
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu);
|
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu);
|
||||||
tid = t->tid = allocate_tid ();
|
tid = t->tid = allocate_tid ();
|
||||||
if (!init_process_result (t))
|
init_process_result (t);
|
||||||
{
|
|
||||||
palloc_free_page (t);
|
|
||||||
return TID_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USERPROG
|
#ifdef USERPROG
|
||||||
/* Initialize the thread's file descriptor table. */
|
hash_init (&t->open_files, fd_hash, fd_less, NULL);
|
||||||
t->fd_counter = MINIMUM_USER_FD;
|
#endif
|
||||||
bool success = hash_init (&t->open_files, fd_hash, fd_less, NULL);
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
success = hash_init (&t->child_results, process_result_hash,
|
|
||||||
process_result_less, t);
|
|
||||||
if (!success)
|
|
||||||
hash_destroy (&t->open_files, NULL);
|
|
||||||
#ifdef VM
|
|
||||||
else
|
|
||||||
{
|
|
||||||
success = init_pages (&t->pages);
|
|
||||||
if (!success)
|
|
||||||
hash_destroy (&t->child_results, NULL);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
palloc_free_page (t);
|
|
||||||
free (t->result);
|
|
||||||
return TID_ERROR;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef VM
|
|
||||||
mmap_init (t);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Prepare thread for first run by initializing its stack.
|
/* Prepare thread for first run by initializing its stack.
|
||||||
Do this atomically so intermediate values for the 'stack'
|
Do this atomically so intermediate values for the 'stack'
|
||||||
@@ -316,7 +269,9 @@ thread_create (const char *name, int priority,
|
|||||||
|
|
||||||
intr_set_level (old_level);
|
intr_set_level (old_level);
|
||||||
|
|
||||||
hash_insert (&parent_thread->child_results, &t->result->elem);
|
/* No need to synchronise child_results since it is only ever accessed by one
|
||||||
|
thread. By the nature of increasing TIDs, this list is ordered. */
|
||||||
|
list_push_back (&parent_thread->child_results, &t->result->elem);
|
||||||
|
|
||||||
/* Add to run queue. */
|
/* Add to run queue. */
|
||||||
thread_unblock (t);
|
thread_unblock (t);
|
||||||
@@ -418,9 +373,7 @@ thread_exit (void)
|
|||||||
and schedule another process. That process will destroy us
|
and schedule another process. That process will destroy us
|
||||||
when it calls thread_schedule_tail(). */
|
when it calls thread_schedule_tail(). */
|
||||||
intr_disable ();
|
intr_disable ();
|
||||||
struct thread *t = thread_current ();
|
list_remove (&thread_current()->allelem);
|
||||||
list_remove (&t->allelem);
|
|
||||||
list_remove (&t->donor_elem);
|
|
||||||
thread_current ()->status = THREAD_DYING;
|
thread_current ()->status = THREAD_DYING;
|
||||||
schedule ();
|
schedule ();
|
||||||
NOT_REACHED ();
|
NOT_REACHED ();
|
||||||
@@ -694,18 +647,15 @@ is_thread (struct thread *t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and initialise a process result for given thread. */
|
/* Allocate and initialise a process result for given thread. */
|
||||||
static bool
|
static void
|
||||||
init_process_result (struct thread *t)
|
init_process_result (struct thread *t)
|
||||||
{
|
{
|
||||||
struct process_result *result = malloc (sizeof (struct process_result));
|
struct process_result *result = malloc (sizeof (struct process_result));
|
||||||
if (result == NULL)
|
|
||||||
return false;
|
|
||||||
result->tid = t->tid;
|
result->tid = t->tid;
|
||||||
result->exit_status = -1;
|
result->exit_status = t->exit_status;
|
||||||
lock_init (&result->lock);
|
lock_init (&result->lock);
|
||||||
sema_init (&result->sema, 0);
|
sema_init (&result->sema, 0);
|
||||||
t->result = result;
|
t->result = result;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Does basic initialization of T as a blocked thread named
|
/* Does basic initialization of T as a blocked thread named
|
||||||
@@ -729,13 +679,15 @@ init_thread (struct thread *t, const char *name, int nice, int priority,
|
|||||||
t->base_priority
|
t->base_priority
|
||||||
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
|
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
|
||||||
list_init (&t->donors_list);
|
list_init (&t->donors_list);
|
||||||
list_push_back (&t->donors_list, &t->donor_elem);
|
|
||||||
t->waiting_lock = NULL;
|
t->waiting_lock = NULL;
|
||||||
|
|
||||||
t->nice = nice;
|
t->nice = nice;
|
||||||
t->recent_cpu = recent_cpu;
|
t->recent_cpu = recent_cpu;
|
||||||
t->priority = t->base_priority;
|
t->priority = t->base_priority;
|
||||||
|
|
||||||
|
t->exit_status = -1;
|
||||||
|
list_init (&t->child_results);
|
||||||
|
|
||||||
old_level = intr_disable ();
|
old_level = intr_disable ();
|
||||||
list_push_back (&all_list, &t->allelem);
|
list_push_back (&all_list, &t->allelem);
|
||||||
intr_set_level (old_level);
|
intr_set_level (old_level);
|
||||||
@@ -866,29 +818,6 @@ allocate_tid (void)
|
|||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hashing function needed for child_results table.
|
|
||||||
Returns hash of process_result's TID. */
|
|
||||||
static unsigned
|
|
||||||
process_result_hash (const struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
const struct process_result *result
|
|
||||||
= hash_entry (e, struct process_result, elem);
|
|
||||||
return hash_int (result->tid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Comparator function needed for child_results table.
|
|
||||||
Returns less than comparison on process_results' TIDs. */
|
|
||||||
static bool
|
|
||||||
process_result_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|
||||||
void *aux UNUSED)
|
|
||||||
{
|
|
||||||
const struct process_result *a
|
|
||||||
= hash_entry (a_, struct process_result, elem);
|
|
||||||
const struct process_result *b
|
|
||||||
= hash_entry (b_, struct process_result, elem);
|
|
||||||
return a->tid < b->tid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Offset of `stack' member within `struct thread'.
|
/* Offset of `stack' member within `struct thread'.
|
||||||
Used by switch.S, which can't figure it out on its own. */
|
Used by switch.S, which can't figure it out on its own. */
|
||||||
uint32_t thread_stack_ofs = offsetof (struct thread, stack);
|
uint32_t thread_stack_ofs = offsetof (struct thread, stack);
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ typedef int tid_t;
|
|||||||
#define NICE_DEFAULT 0 /* Default niceness. */
|
#define NICE_DEFAULT 0 /* Default niceness. */
|
||||||
#define NICE_MAX 20 /* Highest niceness. */
|
#define NICE_MAX 20 /* Highest niceness. */
|
||||||
|
|
||||||
/* File Descriptors. */
|
|
||||||
#define MINIMUM_USER_FD 2 /* Minimum file descriptor for user programs. */
|
|
||||||
|
|
||||||
/* A process result, synchronised between parent and child. */
|
/* A process result, synchronised between parent and child. */
|
||||||
struct process_result
|
struct process_result
|
||||||
{
|
{
|
||||||
@@ -44,7 +41,7 @@ struct process_result
|
|||||||
struct lock lock; /* Lock the exit_status and sema. */
|
struct lock lock; /* Lock the exit_status and sema. */
|
||||||
struct semaphore sema; /* Semaphore to signal the parent that the exit_status
|
struct semaphore sema; /* Semaphore to signal the parent that the exit_status
|
||||||
has been set. */
|
has been set. */
|
||||||
struct hash_elem elem; /* Hash element for the parent's children map. */
|
struct list_elem elem; /* List element for the parent's children list. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A kernel thread or user process.
|
/* A kernel thread or user process.
|
||||||
@@ -128,29 +125,21 @@ struct thread
|
|||||||
|
|
||||||
/* Process wait properties. */
|
/* Process wait properties. */
|
||||||
struct process_result *result; /* Result of the process. */
|
struct process_result *result; /* Result of the process. */
|
||||||
struct hash child_results; /* Map of children's of this thread
|
struct list child_results; /* List of children's of this thread
|
||||||
TID to process result. */
|
process results. */
|
||||||
struct file *exec_file; /* Thread's currently running file */
|
struct file *exec_file; /* Thread's currently running file */
|
||||||
|
|
||||||
/* Shared between thread.c and synch.c. */
|
/* Shared between thread.c and synch.c. */
|
||||||
struct list_elem elem; /* List element. */
|
struct list_elem elem; /* List element. */
|
||||||
|
|
||||||
struct hash pages; /* Table of open user pages. */
|
int exit_status; /* Exit Status: 0 = successful exit. */
|
||||||
|
|
||||||
/* Memory mapped files for user virtual memory. */
|
|
||||||
struct hash mmap_files; /* List of memory mapped files. */
|
|
||||||
unsigned int mmap_counter; /* Counter for memory mapped files. */
|
|
||||||
|
|
||||||
#ifdef USERPROG
|
#ifdef USERPROG
|
||||||
/* Owned by userprog/process.c. */
|
/* Owned by userprog/process.c. */
|
||||||
uint32_t *pagedir; /* Page directory. */
|
uint32_t *pagedir; /* Page directory. */
|
||||||
unsigned int fd_counter; /* File descriptor counter for thread's
|
struct hash open_files; /* Hash Table of FD -> Struct File */
|
||||||
open files. */
|
|
||||||
struct hash open_files; /* Hash Table of FD -> Struct File. */
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void *curr_esp;
|
|
||||||
|
|
||||||
/* Owned by thread.c. */
|
/* Owned by thread.c. */
|
||||||
unsigned magic; /* Detects stack overflow. */
|
unsigned magic; /* Detects stack overflow. */
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- makefile -*-
|
# -*- makefile -*-
|
||||||
|
|
||||||
kernel.bin: DEFINES = -DUSERPROG -DFILESYS -DVM
|
kernel.bin: DEFINES = -DUSERPROG -DFILESYS
|
||||||
KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys vm
|
KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
|
||||||
TEST_SUBDIRS = tests/userprog tests/userprog/no-vm tests/filesys/base
|
TEST_SUBDIRS = tests/userprog tests/userprog/no-vm tests/filesys/base
|
||||||
GRADING_FILE = $(SRCDIR)/tests/userprog/Grading
|
GRADING_FILE = $(SRCDIR)/tests/userprog/Grading
|
||||||
SIMULATOR = --qemu
|
SIMULATOR = --qemu
|
||||||
|
|||||||
@@ -1,20 +1,9 @@
|
|||||||
#include "userprog/exception.h"
|
#include "userprog/exception.h"
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "stdbool.h"
|
|
||||||
#include "userprog/gdt.h"
|
#include "userprog/gdt.h"
|
||||||
#include "threads/interrupt.h"
|
#include "threads/interrupt.h"
|
||||||
#include "threads/thread.h"
|
#include "threads/thread.h"
|
||||||
#ifdef VM
|
|
||||||
#include "vm/frame.h"
|
|
||||||
#include "vm/page.h"
|
|
||||||
#include "devices/swap.h"
|
|
||||||
#include "threads/vaddr.h"
|
|
||||||
#include "userprog/pagedir.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_STACK_SIZE (8 * 1024 * 1024) // 8MB
|
|
||||||
#define MAX_STACK_OFFSET 32 // 32 bytes offset below stack pointer (ESP)
|
|
||||||
|
|
||||||
/* Number of page faults processed. */
|
/* Number of page faults processed. */
|
||||||
static long long page_fault_cnt;
|
static long long page_fault_cnt;
|
||||||
@@ -22,10 +11,6 @@ static long long page_fault_cnt;
|
|||||||
static void kill (struct intr_frame *);
|
static void kill (struct intr_frame *);
|
||||||
static void page_fault (struct intr_frame *);
|
static void page_fault (struct intr_frame *);
|
||||||
|
|
||||||
static bool is_valid_stack_access (const void *fault_addr, const void *esp);
|
|
||||||
static bool grow_stack (void *upage);
|
|
||||||
bool fetch_page (void *upage, bool write);
|
|
||||||
|
|
||||||
/* Registers handlers for interrupts that can be caused by user
|
/* Registers handlers for interrupts that can be caused by user
|
||||||
programs.
|
programs.
|
||||||
|
|
||||||
@@ -160,34 +145,6 @@ page_fault (struct intr_frame *f)
|
|||||||
write = (f->error_code & PF_W) != 0;
|
write = (f->error_code & PF_W) != 0;
|
||||||
user = (f->error_code & PF_U) != 0;
|
user = (f->error_code & PF_U) != 0;
|
||||||
|
|
||||||
/* Select the appropriate stack pointer based on the context of the fault. */
|
|
||||||
void *esp = user ? f->esp : thread_current()->curr_esp;
|
|
||||||
|
|
||||||
/* If the fault address is in a user page that is not present, then it might
|
|
||||||
be just that the stack needs to grow or that it needs to be lazily loaded.
|
|
||||||
So we attempt to grow the stack. If this does not work, we check our SPT to
|
|
||||||
see if the page is expected to have data loaded in memory. */
|
|
||||||
void *upage = pg_round_down (fault_addr);
|
|
||||||
if (not_present && is_user_vaddr (upage) && upage != NULL)
|
|
||||||
{
|
|
||||||
if (fetch_page (upage, write))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (is_valid_stack_access (fault_addr, esp))
|
|
||||||
if (grow_stack (upage))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the page fault occurred in kernel mode, then we intentionally indicate
|
|
||||||
a fault (for get_user() etc). */
|
|
||||||
if (!user)
|
|
||||||
{
|
|
||||||
f->eip = (void *)f->eax;
|
|
||||||
f->eax = 0xffffffff;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* To implement virtual memory, delete the rest of the function
|
/* To implement virtual memory, delete the rest of the function
|
||||||
body, and replace it with code that brings in the page to
|
body, and replace it with code that brings in the page to
|
||||||
which fault_addr refers. */
|
which fault_addr refers. */
|
||||||
@@ -199,107 +156,3 @@ page_fault (struct intr_frame *f)
|
|||||||
kill (f);
|
kill (f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validates whether the fault address is a valid stack access. Access is a
|
|
||||||
valid stack access under the following two conditions:
|
|
||||||
1. The fault address must be within MAX_STACK_OFFSET (32) bytes below
|
|
||||||
the current stack pointer. (Accounts for both PUSH and PUSHA instructions)
|
|
||||||
2. Growing this stack to this address does not cause it to exceed the
|
|
||||||
MAX_STACK_SIZE (8MB) limit.
|
|
||||||
|
|
||||||
Returns true if both conditions are met, false otherwise.
|
|
||||||
|
|
||||||
Pre: fault_addr is a valid user virtual address (so also not NULL). */
|
|
||||||
static bool
|
|
||||||
is_valid_stack_access (const void *fault_addr, const void *esp)
|
|
||||||
{
|
|
||||||
uint32_t new_stack_size = PHYS_BASE - pg_round_down (fault_addr);
|
|
||||||
|
|
||||||
uint32_t *lowest_valid_push_addr = (uint32_t *)esp - MAX_STACK_OFFSET;
|
|
||||||
bool is_within_push_range = (uint32_t *)fault_addr >= lowest_valid_push_addr;
|
|
||||||
|
|
||||||
return is_within_push_range && new_stack_size <= MAX_STACK_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attempts to grow the stack by allocating and mapping a new page.
|
|
||||||
This involves:
|
|
||||||
1. Allocating a zeroed page from the user pool
|
|
||||||
2. Installing it into the page table with write permissions
|
|
||||||
|
|
||||||
Returns true if the stack was successfully grown, false if either
|
|
||||||
allocation or installation fails.
|
|
||||||
|
|
||||||
Pre: upage is a valid page-aligned address (so also not NULL). */
|
|
||||||
static bool
|
|
||||||
grow_stack (void *upage)
|
|
||||||
{
|
|
||||||
/* Allocate new page for stack */
|
|
||||||
void *new_page = frame_alloc (PAL_ZERO, upage, thread_current ());
|
|
||||||
if (new_page == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Install the page into user page table */
|
|
||||||
if (!pagedir_set_page (thread_current ()->pagedir, upage, new_page, true))
|
|
||||||
{
|
|
||||||
frame_free (new_page);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
fetch_page (void *upage, bool write)
|
|
||||||
{
|
|
||||||
/* Check if the page is in the supplemental page table. That is, it is a page
|
|
||||||
that is expected to be in memory. */
|
|
||||||
struct page_entry *page = page_get (thread_current (), upage);
|
|
||||||
if (page == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Check if the non-present user page is in the swap partition.
|
|
||||||
If so, swap it back into main memory, updating the PTE for
|
|
||||||
the faulted virtual address to point to the newly allocated
|
|
||||||
frame. */
|
|
||||||
struct thread *t = thread_current ();
|
|
||||||
if (page_in_swap (t, upage))
|
|
||||||
{
|
|
||||||
/* NOTE: This code should be refactored and moved into helper functions
|
|
||||||
within 'page.c'.*/
|
|
||||||
void *kpage = frame_alloc (0, upage, t);
|
|
||||||
lock_acquire (&page->lock);
|
|
||||||
|
|
||||||
size_t swap_slot = page_get_swap (t, upage);
|
|
||||||
swap_in (kpage, swap_slot);
|
|
||||||
|
|
||||||
lock_release (&page->lock);
|
|
||||||
|
|
||||||
bool writeable = pagedir_is_writable (t->pagedir, upage);
|
|
||||||
|
|
||||||
/* TODO: When this returns false we should quit the page fault,
|
|
||||||
but currently we continue and check the stack conditions in the
|
|
||||||
page fault handler. */
|
|
||||||
return pagedir_set_page (t->pagedir, upage, kpage, writeable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* An attempt to write to a non-writeable should fail. */
|
|
||||||
if (write && !page->writable)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* Load the page into memory based on the type of data it is expecting. */
|
|
||||||
bool success = false;
|
|
||||||
switch (page->type) {
|
|
||||||
case PAGE_MMAP:
|
|
||||||
case PAGE_EXECUTABLE:
|
|
||||||
case PAGE_SHARED:
|
|
||||||
success = page_load_file (page);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success && page->writable &&
|
|
||||||
!pagedir_is_writable(thread_current()->pagedir, upage))
|
|
||||||
pagedir_set_writable(thread_current()->pagedir, upage, true);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#ifndef USERPROG_EXCEPTION_H
|
#ifndef USERPROG_EXCEPTION_H
|
||||||
#define USERPROG_EXCEPTION_H
|
#define USERPROG_EXCEPTION_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
/* Page fault error code bits that describe the cause of the exception. */
|
/* Page fault error code bits that describe the cause of the exception. */
|
||||||
#define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */
|
#define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */
|
||||||
#define PF_W 0x2 /* 0: read, 1: write. */
|
#define PF_W 0x2 /* 0: read, 1: write. */
|
||||||
@@ -10,7 +8,5 @@
|
|||||||
|
|
||||||
void exception_init (void);
|
void exception_init (void);
|
||||||
void exception_print_stats (void);
|
void exception_print_stats (void);
|
||||||
bool
|
|
||||||
try_fetch_page (void *upage, bool write);
|
|
||||||
|
|
||||||
#endif /* userprog/exception.h */
|
#endif /* userprog/exception.h */
|
||||||
|
|||||||
@@ -2,14 +2,12 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "devices/swap.h"
|
|
||||||
#include "threads/init.h"
|
#include "threads/init.h"
|
||||||
#include "threads/pte.h"
|
#include "threads/pte.h"
|
||||||
#include "threads/palloc.h"
|
#include "threads/palloc.h"
|
||||||
#include "vm/frame.h"
|
|
||||||
#include "vm/page.h"
|
|
||||||
|
|
||||||
static uint32_t *active_pd (void);
|
static uint32_t *active_pd (void);
|
||||||
|
static void invalidate_pagedir (uint32_t *);
|
||||||
|
|
||||||
/* Creates a new page directory that has mappings for kernel
|
/* Creates a new page directory that has mappings for kernel
|
||||||
virtual addresses, but none for user virtual addresses.
|
virtual addresses, but none for user virtual addresses.
|
||||||
@@ -42,14 +40,8 @@ pagedir_destroy (uint32_t *pd)
|
|||||||
uint32_t *pte;
|
uint32_t *pte;
|
||||||
|
|
||||||
for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
|
for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
|
||||||
{
|
if (*pte & PTE_P)
|
||||||
if (page_is_shared_pte (pte))
|
palloc_free_page (pte_get_page (*pte));
|
||||||
continue;
|
|
||||||
else if (page_in_swap_pte (pte))
|
|
||||||
swap_drop (page_get_swap_pte (pte));
|
|
||||||
else if (*pte & PTE_P)
|
|
||||||
frame_free (pte_get_page (*pte));
|
|
||||||
}
|
|
||||||
palloc_free_page (pt);
|
palloc_free_page (pt);
|
||||||
}
|
}
|
||||||
palloc_free_page (pd);
|
palloc_free_page (pd);
|
||||||
@@ -61,7 +53,7 @@ pagedir_destroy (uint32_t *pd)
|
|||||||
on CREATE. If CREATE is true, then a new page table is
|
on CREATE. If CREATE is true, then a new page table is
|
||||||
created and a pointer into it is returned. Otherwise, a null
|
created and a pointer into it is returned. Otherwise, a null
|
||||||
pointer is returned. */
|
pointer is returned. */
|
||||||
uint32_t *
|
static uint32_t *
|
||||||
lookup_page (uint32_t *pd, const void *vaddr, bool create)
|
lookup_page (uint32_t *pd, const void *vaddr, bool create)
|
||||||
{
|
{
|
||||||
uint32_t *pt, *pde;
|
uint32_t *pt, *pde;
|
||||||
@@ -286,7 +278,7 @@ active_pd (void)
|
|||||||
This function invalidates the TLB if PD is the active page
|
This function invalidates the TLB if PD is the active page
|
||||||
directory. (If PD is not active then its entries are not in
|
directory. (If PD is not active then its entries are not in
|
||||||
the TLB, so there is no need to invalidate anything.) */
|
the TLB, so there is no need to invalidate anything.) */
|
||||||
void
|
static void
|
||||||
invalidate_pagedir (uint32_t *pd)
|
invalidate_pagedir (uint32_t *pd)
|
||||||
{
|
{
|
||||||
if (active_pd () == pd)
|
if (active_pd () == pd)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
uint32_t *pagedir_create (void);
|
uint32_t *pagedir_create (void);
|
||||||
void pagedir_destroy (uint32_t *pd);
|
void pagedir_destroy (uint32_t *pd);
|
||||||
uint32_t *lookup_page (uint32_t *pd, const void *vaddr, bool create);
|
|
||||||
bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw);
|
bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw);
|
||||||
void *pagedir_get_page (uint32_t *pd, const void *upage);
|
void *pagedir_get_page (uint32_t *pd, const void *upage);
|
||||||
void pagedir_clear_page (uint32_t *pd, void *upage);
|
void pagedir_clear_page (uint32_t *pd, void *upage);
|
||||||
@@ -17,6 +16,5 @@ void pagedir_set_accessed (uint32_t *pd, const void *upage, bool accessed);
|
|||||||
bool pagedir_is_writable (uint32_t *pd, const void *upage);
|
bool pagedir_is_writable (uint32_t *pd, const void *upage);
|
||||||
void pagedir_set_writable (uint32_t *pd, const void *upage, bool writable);
|
void pagedir_set_writable (uint32_t *pd, const void *upage, bool writable);
|
||||||
void pagedir_activate (uint32_t *pd);
|
void pagedir_activate (uint32_t *pd);
|
||||||
void invalidate_pagedir (uint32_t *pd);
|
|
||||||
|
|
||||||
#endif /* userprog/pagedir.h */
|
#endif /* userprog/pagedir.h */
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "userprog/process.h"
|
#include "userprog/process.h"
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <hash.h>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <list.h>
|
#include <list.h>
|
||||||
#include <round.h>
|
#include <round.h>
|
||||||
@@ -24,20 +23,11 @@
|
|||||||
#include "threads/vaddr.h"
|
#include "threads/vaddr.h"
|
||||||
#include "threads/synch.h"
|
#include "threads/synch.h"
|
||||||
#include "devices/timer.h"
|
#include "devices/timer.h"
|
||||||
#include "vm/page.h"
|
|
||||||
#include "vm/mmap.h"
|
|
||||||
#ifdef VM
|
|
||||||
#include "vm/frame.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Defines the native number of bytes processed by the processor
|
/* Defines the native number of bytes processed by the processor
|
||||||
(for the purposes of alignment). */
|
(for the purposes of alignment). */
|
||||||
#define WORD_SIZE 4
|
#define WORD_SIZE 4
|
||||||
|
|
||||||
/* Defines non-negative integer division wherein the result is always rounded
|
|
||||||
up. */
|
|
||||||
#define DIV_CEIL(x, y) ((x + (y - 1)) / y)
|
|
||||||
|
|
||||||
/* Keeps track of the position of pointers to user program arguments
|
/* Keeps track of the position of pointers to user program arguments
|
||||||
within a linked list. */
|
within a linked list. */
|
||||||
struct arg_elem
|
struct arg_elem
|
||||||
@@ -50,18 +40,16 @@ struct arg_elem
|
|||||||
that executes process_start for the purpose of starting a user process. */
|
that executes process_start for the purpose of starting a user process. */
|
||||||
struct process_start_data
|
struct process_start_data
|
||||||
{
|
{
|
||||||
|
char *cmd; /* Pointer to a copy of the command used to execute the process.
|
||||||
|
Allocated a page that must be freed by process_start. */
|
||||||
char *cmd_saveptr; /* Value pointed to by 'saveptr' argument used by
|
char *cmd_saveptr; /* Value pointed to by 'saveptr' argument used by
|
||||||
successive calls to strtok_r to split 'cmd' into
|
successive calls to strtok_r to split 'cmd' into
|
||||||
tokens while maintaining state. */
|
tokens while maintaining state. */
|
||||||
char file_name[FNAME_MAX_LEN + 1]; /* Name of the file of the process to
|
char file_name[FNAME_MAX_LEN + 1]; /* Name of the file of the process to
|
||||||
be started. */
|
be started. */
|
||||||
bool success; /* Indicates whether the process was successfully loaded. */
|
|
||||||
struct semaphore loaded; /* Semaphore used to signal that the process has
|
|
||||||
finished attempting to load. */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static thread_func start_process NO_RETURN;
|
static thread_func start_process NO_RETURN;
|
||||||
static void destruct_process_result (struct hash_elem *e, void *aux UNUSED);
|
|
||||||
static bool load (const char *cmdline, void (**eip) (void), void **esp);
|
static bool load (const char *cmdline, void (**eip) (void), void **esp);
|
||||||
|
|
||||||
/* Starts a new thread running a user program executed via
|
/* Starts a new thread running a user program executed via
|
||||||
@@ -73,7 +61,12 @@ process_execute (const char *cmd)
|
|||||||
{
|
{
|
||||||
char *cmd_copy;
|
char *cmd_copy;
|
||||||
tid_t tid;
|
tid_t tid;
|
||||||
struct process_start_data data;
|
|
||||||
|
struct process_start_data *data = malloc (sizeof (struct process_start_data));
|
||||||
|
if (data == NULL)
|
||||||
|
{
|
||||||
|
return TID_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
/* Make a copy of command.
|
/* Make a copy of command.
|
||||||
Otherwise there's a race between the caller and load(). */
|
Otherwise there's a race between the caller and load(). */
|
||||||
@@ -87,41 +80,25 @@ process_execute (const char *cmd)
|
|||||||
|
|
||||||
/* Retrieve first argument of command, which is the file name
|
/* Retrieve first argument of command, which is the file name
|
||||||
of the process. */
|
of the process. */
|
||||||
char *file_name = strtok_r (cmd_copy, " ", &data.cmd_saveptr);
|
char *file_name = strtok_r (cmd_copy, " ", &data->cmd_saveptr);
|
||||||
|
|
||||||
/* Validates that the current file to be executed can be opened/exists. */
|
/* Validates that the current file to be executed is a valid file */
|
||||||
lock_acquire (&filesys_lock);
|
if (filesys_open (file_name) == NULL)
|
||||||
struct file *file = filesys_open (file_name);
|
|
||||||
lock_release (&filesys_lock);
|
|
||||||
if (file == NULL)
|
|
||||||
return TID_ERROR;
|
return TID_ERROR;
|
||||||
|
|
||||||
/* Create a new thread to execute the command, by initializing
|
/* Create a new thread to execute the command, by initializing
|
||||||
it running the function 'start_process' with the appropriate
|
it running the function 'start_process' with the appropriate
|
||||||
arguments. For details of arguments, see 'start_process'. */
|
arguments. For details of arguments, see 'start_process'. */
|
||||||
strlcpy (data.file_name, file_name, FNAME_MAX_LEN + 1);
|
data->cmd = cmd_copy;
|
||||||
sema_init (&data.loaded, 0);
|
strlcpy (data->file_name, file_name, FNAME_MAX_LEN + 1);
|
||||||
data.success = false;
|
|
||||||
|
|
||||||
tid = thread_create (file_name, PRI_DEFAULT, start_process, &data);
|
tid = thread_create (file_name, PRI_DEFAULT, start_process, data);
|
||||||
|
if (tid == TID_ERROR)
|
||||||
/* Wait until process file has finished attempting to load via the child
|
palloc_free_page (cmd_copy);
|
||||||
thread before reporting success of starting execution. */
|
|
||||||
if (tid != TID_ERROR)
|
|
||||||
{
|
|
||||||
sema_down (&data.loaded);
|
|
||||||
if (!data.success)
|
|
||||||
tid = TID_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
palloc_free_page (cmd_copy);
|
|
||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *get_usr_kpage (enum palloc_flags flags, void *upage);
|
static bool install_page (void *upage, void *kpage, bool writable);
|
||||||
static void free_usr_kpage (void *kpage);
|
|
||||||
bool install_page (void *upage, void *kpage, bool writable);
|
|
||||||
|
|
||||||
static bool process_init_stack (char *cmd_saveptr, void **esp, char *file_name);
|
static bool process_init_stack (char *cmd_saveptr, void **esp, char *file_name);
|
||||||
static void *push_to_stack (void **esp, void *data, size_t data_size);
|
static void *push_to_stack (void **esp, void *data, size_t data_size);
|
||||||
#define push_var_to_stack(esp, var) (push_to_stack (esp, &var, sizeof (var)))
|
#define push_var_to_stack(esp, var) (push_to_stack (esp, &var, sizeof (var)))
|
||||||
@@ -129,15 +106,14 @@ static void *push_to_stack (void **esp, void *data, size_t data_size);
|
|||||||
/* Make the current thread execute 'cmd', passing in a copy of the
|
/* Make the current thread execute 'cmd', passing in a copy of the
|
||||||
command string used for processing, the saveptr used by strtok_r
|
command string used for processing, the saveptr used by strtok_r
|
||||||
(in order to further tokenize the same command and retrieve its
|
(in order to further tokenize the same command and retrieve its
|
||||||
arguments), the name of the file being executed, and a semaphore that
|
arguments), as well as the name of the file being executed. This
|
||||||
calls sema_up to indicate that the 'success' variable passed to it
|
involves loading the specified file and starting it running. */
|
||||||
has been updated to indicate whether the process file loading succeeded.
|
|
||||||
This involves loading the specified file and calling its main () function
|
|
||||||
with the specified command arguments. */
|
|
||||||
static void
|
static void
|
||||||
start_process (void *proc_start_data)
|
start_process (void *proc_start_data)
|
||||||
{
|
{
|
||||||
struct intr_frame if_;
|
struct intr_frame if_;
|
||||||
|
bool success;
|
||||||
|
|
||||||
struct process_start_data *data = proc_start_data;
|
struct process_start_data *data = proc_start_data;
|
||||||
|
|
||||||
/* Initialize interrupt frame and load executable. */
|
/* Initialize interrupt frame and load executable. */
|
||||||
@@ -145,46 +121,33 @@ start_process (void *proc_start_data)
|
|||||||
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
|
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
|
||||||
if_.cs = SEL_UCSEG;
|
if_.cs = SEL_UCSEG;
|
||||||
if_.eflags = FLAG_IF | FLAG_MBS;
|
if_.eflags = FLAG_IF | FLAG_MBS;
|
||||||
|
success = load (data->file_name, &if_.eip, &if_.esp);
|
||||||
|
|
||||||
/* Acquire the file system lock to prevent race conditions. */
|
/* If load failed, quit. */
|
||||||
lock_acquire (&filesys_lock);
|
if (!success)
|
||||||
|
|
||||||
struct file *exec_file = filesys_open (data->file_name);
|
|
||||||
if (exec_file == NULL)
|
|
||||||
{
|
{
|
||||||
/* If the executable file cannot be opened, free resources and quit. */
|
palloc_free_page (data->cmd);
|
||||||
lock_release (&filesys_lock);
|
goto fail;
|
||||||
sema_up (&data->loaded);
|
|
||||||
thread_exit ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deny write to the executable file to prevent writing to it and release the
|
/* Initialize user process stack and free page used to store the
|
||||||
file system lock. */
|
command that executed the process. */
|
||||||
file_deny_write (exec_file);
|
success = process_init_stack (data->cmd_saveptr, &if_.esp, data->file_name);
|
||||||
lock_release (&filesys_lock);
|
palloc_free_page (data->cmd);
|
||||||
|
|
||||||
thread_current ()->exec_file = exec_file;
|
|
||||||
|
|
||||||
/* Load the ELF executable file, and store the success of the operation in
|
|
||||||
the 'success' variable in data. */
|
|
||||||
data->success = load (data->file_name, &if_.eip, &if_.esp);
|
|
||||||
|
|
||||||
/* If load was sucessful, initialize user process stack and free page used
|
|
||||||
to store the command that executed the process. */
|
|
||||||
if (data->success)
|
|
||||||
{
|
|
||||||
data->success =
|
|
||||||
process_init_stack (data->cmd_saveptr, &if_.esp, data->file_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signal that the process has finished attempting to load. */
|
|
||||||
bool success = data->success;
|
|
||||||
sema_up (&data->loaded);
|
|
||||||
|
|
||||||
/* If the load was unsuccessful or if it was but the stack initialization
|
/* If stack initialization failed, free resources and quit. */
|
||||||
failed, exit the thread. */
|
|
||||||
if (!success)
|
if (!success)
|
||||||
thread_exit ();
|
{
|
||||||
|
process_exit ();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTE: Currently, the file being executed is closed in load () and then
|
||||||
|
reopened here. Because load is an exported public function, this
|
||||||
|
might be necessary. */
|
||||||
|
struct file *exec_file = filesys_open (data->file_name);
|
||||||
|
thread_current ()->exec_file = exec_file;
|
||||||
|
file_deny_write (exec_file);
|
||||||
|
|
||||||
/* Start the user process by simulating a return from an
|
/* Start the user process by simulating a return from an
|
||||||
interrupt, implemented by intr_exit (in
|
interrupt, implemented by intr_exit (in
|
||||||
@@ -194,6 +157,11 @@ start_process (void *proc_start_data)
|
|||||||
and jump to it. */
|
and jump to it. */
|
||||||
asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
|
asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
|
||||||
NOT_REACHED ();
|
NOT_REACHED ();
|
||||||
|
|
||||||
|
/* If starting the process failed, free its common resources and exit. */
|
||||||
|
fail:
|
||||||
|
free (data);
|
||||||
|
thread_exit ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper function that initializes the stack of a newly created
|
/* Helper function that initializes the stack of a newly created
|
||||||
@@ -201,10 +169,6 @@ start_process (void *proc_start_data)
|
|||||||
static bool
|
static bool
|
||||||
process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
|
process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
|
||||||
{
|
{
|
||||||
ASSERT (cmd_saveptr != NULL);
|
|
||||||
ASSERT (esp != NULL);
|
|
||||||
ASSERT (file_name != NULL);
|
|
||||||
|
|
||||||
/* Load command line argument *data* to user process stack.
|
/* Load command line argument *data* to user process stack.
|
||||||
This can't cause overflow due to enforcing that the size of
|
This can't cause overflow due to enforcing that the size of
|
||||||
command line input must fit in a page. Also keep track
|
command line input must fit in a page. Also keep track
|
||||||
@@ -216,12 +180,8 @@ process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
|
|||||||
int arg_count = 0;
|
int arg_count = 0;
|
||||||
while (arg != NULL)
|
while (arg != NULL)
|
||||||
{
|
{
|
||||||
/* filename has already been validated to be a safe-to-access string,
|
|
||||||
so we can safely use strlen here. Filename has already been
|
|
||||||
split from the command line arguments. */
|
|
||||||
push_to_stack (esp, arg, (strlen (arg) + 1) * sizeof (char));
|
push_to_stack (esp, arg, (strlen (arg) + 1) * sizeof (char));
|
||||||
|
|
||||||
/* Try to allocate memory for the argument pointer. */
|
|
||||||
struct arg_elem *arg_elem = malloc (sizeof (struct arg_elem));
|
struct arg_elem *arg_elem = malloc (sizeof (struct arg_elem));
|
||||||
if (arg_elem == NULL)
|
if (arg_elem == NULL)
|
||||||
{
|
{
|
||||||
@@ -230,11 +190,9 @@ process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store the argument pointer in the linked list. */
|
|
||||||
arg_elem->arg = *esp;
|
arg_elem->arg = *esp;
|
||||||
list_push_front (&arg_list, &arg_elem->elem);
|
list_push_front (&arg_list, &arg_elem->elem);
|
||||||
|
|
||||||
/* Increment the argument count and get the next argument. */
|
|
||||||
arg_count++;
|
arg_count++;
|
||||||
arg = strtok_r (NULL, " ", &cmd_saveptr);
|
arg = strtok_r (NULL, " ", &cmd_saveptr);
|
||||||
}
|
}
|
||||||
@@ -250,23 +208,13 @@ process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
|
|||||||
+ return_addr_size;
|
+ return_addr_size;
|
||||||
|
|
||||||
/* If pushing the rest of the data required for the stack would cause
|
/* If pushing the rest of the data required for the stack would cause
|
||||||
overflow, allocate as many extra pages as needed to the user process
|
overflow, allocate an extra page that is contiguous within the
|
||||||
contiguously in the virtual address space below the initial page. */
|
virtual address space (below the current address range). */
|
||||||
int overflow_bytes = (PHYS_BASE - *esp) + remaining_size - PGSIZE;
|
if (PHYS_BASE - *esp + remaining_size > PGSIZE)
|
||||||
if (overflow_bytes > 0)
|
|
||||||
{
|
{
|
||||||
/* Calculate the number of pages needed to allocate. */
|
uint8_t *kpage = palloc_get_page (PAL_USER | PAL_ZERO);
|
||||||
int pages_needed = DIV_CEIL (overflow_bytes, PGSIZE);
|
if (!install_page (((uint8_t *) PHYS_BASE) - PGSIZE * 2, kpage, true))
|
||||||
|
return false;
|
||||||
/* Allocate the pages and map them to the user process. */
|
|
||||||
void *upage;
|
|
||||||
uint8_t *kpage;
|
|
||||||
for (int i = 1; i < pages_needed + 1; i++)
|
|
||||||
{
|
|
||||||
upage = ((uint8_t *) PHYS_BASE) - PGSIZE * (i + 1);
|
|
||||||
kpage = get_usr_kpage (PAL_ZERO, upage);
|
|
||||||
if (!install_page (upage, kpage, true)) return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Align stack pointer to word size before pushing argv elements for
|
/* Align stack pointer to word size before pushing argv elements for
|
||||||
@@ -324,35 +272,39 @@ push_to_stack (void **esp, void *data, size_t data_size)
|
|||||||
* This function will be implemented in task 2.
|
* This function will be implemented in task 2.
|
||||||
* For now, it does nothing. */
|
* For now, it does nothing. */
|
||||||
int
|
int
|
||||||
process_wait (tid_t child_tid)
|
process_wait (tid_t child_tid UNUSED)
|
||||||
{
|
{
|
||||||
struct thread *t = thread_current ();
|
struct process_result *child_result = NULL;
|
||||||
struct process_result fake_result;
|
struct list_elem *e;
|
||||||
fake_result.tid = child_tid;
|
struct thread *cur = thread_current ();
|
||||||
struct hash_elem *e = hash_find (&t->child_results, &fake_result.elem);
|
for (e = list_begin (&cur->child_results);
|
||||||
if (e == NULL)
|
e != list_end (&cur->child_results); e = list_next (e))
|
||||||
|
{
|
||||||
|
struct process_result *result
|
||||||
|
= list_entry (e, struct process_result, elem);
|
||||||
|
if (result->tid == child_tid)
|
||||||
|
{
|
||||||
|
child_result = result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* List is ordered, allowing us to break early. */
|
||||||
|
else if (result->tid > child_tid)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (child_result == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
struct process_result *child_result
|
|
||||||
= hash_entry (e, struct process_result, elem);
|
|
||||||
/* Wait for child to die. */
|
/* Wait for child to die. */
|
||||||
sema_down (&child_result->sema);
|
sema_down (&child_result->sema);
|
||||||
|
|
||||||
/* We need lock release in process_exit, so we need to acquire (and possibly
|
/* We need lock release in process_exit, so we need to acquire (and possibly
|
||||||
wait) for it here to ensure we don't free the lock memory before it is
|
wait) for it here to ensure we don't free the lock memory before it is
|
||||||
released in process_exit. */
|
released in process_exit. */
|
||||||
lock_acquire (&child_result->lock);
|
lock_acquire (&child_result->lock);
|
||||||
/* To prevent waiting for child twice, remove it from the table.
|
/* To prevent waiting for child twice, remove it from the list.
|
||||||
No need to use lock since this is the only thread with access to
|
No need to use lock since this is the only thread with access to
|
||||||
the struct process_result now. */
|
the struct process_result now. */
|
||||||
hash_delete (&t->child_results, &child_result->elem);
|
list_remove (&child_result->elem);
|
||||||
|
|
||||||
/* Get the exit status of the child */
|
|
||||||
int exit_status = child_result->exit_status;
|
int exit_status = child_result->exit_status;
|
||||||
|
|
||||||
/* Release the lock */
|
|
||||||
lock_release (&child_result->lock);
|
lock_release (&child_result->lock);
|
||||||
/* Result no-longer used by parent, nor child. Deallocate it. */
|
|
||||||
free (child_result);
|
free (child_result);
|
||||||
return exit_status;
|
return exit_status;
|
||||||
}
|
}
|
||||||
@@ -364,33 +316,50 @@ process_exit (void)
|
|||||||
struct thread *cur = thread_current ();
|
struct thread *cur = thread_current ();
|
||||||
uint32_t *pd;
|
uint32_t *pd;
|
||||||
|
|
||||||
/* Unmap all memory mapped files */
|
printf ("%s: exit(%d)\n", cur->name, cur->exit_status);
|
||||||
mmap_destroy ();
|
file_close (cur->exec_file);
|
||||||
|
|
||||||
/* Clean up all open files */
|
|
||||||
hash_destroy (&cur->open_files, fd_cleanup);
|
|
||||||
|
|
||||||
/* Clean up the thread's supplemental page table. */
|
|
||||||
hash_destroy (&cur->pages, page_cleanup);
|
|
||||||
|
|
||||||
/* Close the executable file, implicitly allowing it to be written to. */
|
|
||||||
if (cur->exec_file != NULL)
|
|
||||||
{
|
|
||||||
/* Acquire the file system lock to prevent race conditions. */
|
|
||||||
lock_acquire (&filesys_lock);
|
|
||||||
file_close (cur->exec_file);
|
|
||||||
lock_release (&filesys_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Update process result. */
|
||||||
if (cur->result != NULL)
|
if (cur->result != NULL)
|
||||||
{
|
{
|
||||||
printf ("%s: exit(%d)\n", cur->name, cur->result->exit_status);
|
lock_acquire (&cur->result->lock);
|
||||||
/* Update own process result. */
|
cur->result->exit_status = cur->exit_status;
|
||||||
destruct_process_result (&cur->result->elem, cur);
|
/* Parent has died, child has to free the struct process_result * */
|
||||||
|
if (sema_try_down (&cur->result->sema))
|
||||||
|
{
|
||||||
|
lock_release (&cur->result->lock);
|
||||||
|
free (cur->result);
|
||||||
|
}
|
||||||
|
/* Parent is still alive and will be the one to free the
|
||||||
|
struct process_result *, and may be waiting so call sema_up */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sema_up (&cur->result->sema);
|
||||||
|
lock_release (&cur->result->lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free child process results or signal parent's death. */
|
/* Free child process results or signal parent's death. */
|
||||||
hash_destroy (&cur->child_results, destruct_process_result);
|
struct list_elem *e;
|
||||||
|
for (e = list_begin (&cur->child_results);
|
||||||
|
e != list_end (&cur->child_results); e = list_next (e))
|
||||||
|
{
|
||||||
|
struct process_result *result
|
||||||
|
= list_entry (e, struct process_result, elem);
|
||||||
|
lock_acquire (&result->lock);
|
||||||
|
/* Child has died (and was not waited for). Free the result. */
|
||||||
|
if (sema_try_down (&result->sema))
|
||||||
|
{
|
||||||
|
lock_release (&result->lock);
|
||||||
|
free (result);
|
||||||
|
}
|
||||||
|
/* Child is still alive, signal via sema that parent has died. */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sema_up (&result->sema);
|
||||||
|
lock_release (&result->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Destroy the current process's page directory and switch back
|
/* Destroy the current process's page directory and switch back
|
||||||
to the kernel-only page directory. */
|
to the kernel-only page directory. */
|
||||||
@@ -410,28 +379,6 @@ process_exit (void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Destruct a process_result, with multi-thread awareness.
|
|
||||||
If the other thread is running, simply signals death. Otherwise
|
|
||||||
frees the result. */
|
|
||||||
static void
|
|
||||||
destruct_process_result (struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct process_result *result = hash_entry (e, struct process_result, elem);
|
|
||||||
lock_acquire (&result->lock);
|
|
||||||
/* Other thread has died (and was not waited for). Free the result. */
|
|
||||||
if (sema_try_down (&result->sema))
|
|
||||||
{
|
|
||||||
lock_release (&result->lock);
|
|
||||||
free (result);
|
|
||||||
}
|
|
||||||
/* Other thread is still alive, signal via sema that parent has died. */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sema_up (&result->sema);
|
|
||||||
lock_release (&result->lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sets up the CPU for running user code in the current
|
/* Sets up the CPU for running user code in the current
|
||||||
thread.
|
thread.
|
||||||
This function is called on every context switch. */
|
This function is called on every context switch. */
|
||||||
@@ -530,7 +477,6 @@ load (const char *file_name, void (**eip) (void), void **esp)
|
|||||||
off_t file_ofs;
|
off_t file_ofs;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
int i;
|
int i;
|
||||||
lock_acquire (&filesys_lock);
|
|
||||||
|
|
||||||
/* Allocate and activate page directory. */
|
/* Allocate and activate page directory. */
|
||||||
t->pagedir = pagedir_create ();
|
t->pagedir = pagedir_create ();
|
||||||
@@ -629,10 +575,7 @@ load (const char *file_name, void (**eip) (void), void **esp)
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
/* We arrive here whether the load is successful or not. */
|
/* We arrive here whether the load is successful or not. */
|
||||||
#ifndef VM
|
|
||||||
file_close (file);
|
file_close (file);
|
||||||
#endif
|
|
||||||
lock_release (&filesys_lock);
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,29 +642,58 @@ validate_segment (const struct Elf32_Phdr *phdr, struct file *file)
|
|||||||
or disk read error occurs. */
|
or disk read error occurs. */
|
||||||
static bool
|
static bool
|
||||||
load_segment (struct file *file, off_t ofs, uint8_t *upage,
|
load_segment (struct file *file, off_t ofs, uint8_t *upage,
|
||||||
uint32_t read_bytes, uint32_t zero_bytes, bool writable)
|
uint32_t read_bytes, uint32_t zero_bytes, bool writable)
|
||||||
{
|
{
|
||||||
ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
|
ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
|
||||||
ASSERT (pg_ofs (upage) == 0);
|
ASSERT (pg_ofs (upage) == 0);
|
||||||
ASSERT (ofs % PGSIZE == 0);
|
ASSERT (ofs % PGSIZE == 0);
|
||||||
|
|
||||||
while (read_bytes > 0 || zero_bytes > 0)
|
file_seek (file, ofs);
|
||||||
|
while (read_bytes > 0 || zero_bytes > 0)
|
||||||
{
|
{
|
||||||
/* Calculate how to fill this page.
|
/* Calculate how to fill this page.
|
||||||
We will read PAGE_READ_BYTES bytes from FILE
|
We will read PAGE_READ_BYTES bytes from FILE
|
||||||
and zero the final PAGE_ZERO_BYTES bytes. */
|
and zero the final PAGE_ZERO_BYTES bytes. */
|
||||||
size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
|
size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
|
||||||
size_t page_zero_bytes = PGSIZE - page_read_bytes;
|
size_t page_zero_bytes = PGSIZE - page_read_bytes;
|
||||||
|
|
||||||
|
/* Check if virtual page already allocated */
|
||||||
|
struct thread *t = thread_current ();
|
||||||
|
uint8_t *kpage = pagedir_get_page (t->pagedir, upage);
|
||||||
|
|
||||||
|
if (kpage == NULL){
|
||||||
|
|
||||||
|
/* Get a new page of memory. */
|
||||||
|
kpage = palloc_get_page (PAL_USER);
|
||||||
|
if (kpage == NULL){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the page to the process's address space. */
|
||||||
|
if (!install_page (upage, kpage, writable))
|
||||||
|
{
|
||||||
|
palloc_free_page (kpage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* Check if writable flag for the page should be updated */
|
||||||
|
if(writable && !pagedir_is_writable(t->pagedir, upage)){
|
||||||
|
pagedir_set_writable(t->pagedir, upage, writable);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Add the page metadata to the SPT to be lazy loaded later on */
|
/* Load data into the page. */
|
||||||
if (page_insert_file (file, ofs, upage, page_read_bytes, page_zero_bytes,
|
if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes){
|
||||||
writable, PAGE_EXECUTABLE) == NULL)
|
return false;
|
||||||
return false;
|
}
|
||||||
|
memset (kpage + page_read_bytes, 0, page_zero_bytes);
|
||||||
|
|
||||||
/* Advance. */
|
/* Advance. */
|
||||||
read_bytes -= page_read_bytes;
|
read_bytes -= page_read_bytes;
|
||||||
zero_bytes -= page_zero_bytes;
|
zero_bytes -= page_zero_bytes;
|
||||||
ofs += PGSIZE;
|
|
||||||
upage += PGSIZE;
|
upage += PGSIZE;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -734,54 +706,19 @@ setup_stack (void **esp)
|
|||||||
{
|
{
|
||||||
uint8_t *kpage;
|
uint8_t *kpage;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
void *upage = ((uint8_t *) PHYS_BASE) - PGSIZE;
|
kpage = palloc_get_page (PAL_USER | PAL_ZERO);
|
||||||
|
|
||||||
kpage = get_usr_kpage (PAL_ZERO, upage);
|
|
||||||
if (kpage != NULL)
|
if (kpage != NULL)
|
||||||
{
|
{
|
||||||
success = install_page (upage, kpage, true);
|
success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true);
|
||||||
if (success)
|
if (success)
|
||||||
*esp = PHYS_BASE;
|
*esp = PHYS_BASE;
|
||||||
else
|
else
|
||||||
free_usr_kpage (kpage);
|
palloc_free_page (kpage);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Claims a page from the user pool for ownership by the current thread
|
|
||||||
and returns its kernel address, updating the frame table if VM
|
|
||||||
is enabled. Requires the intended virtual address for where the page
|
|
||||||
will be installed. */
|
|
||||||
static void *
|
|
||||||
get_usr_kpage (enum palloc_flags flags, void *upage)
|
|
||||||
{
|
|
||||||
void *page;
|
|
||||||
#ifdef VM
|
|
||||||
struct thread *t = thread_current ();
|
|
||||||
if (pagedir_get_page (t->pagedir, upage) != NULL)
|
|
||||||
return NULL;
|
|
||||||
else
|
|
||||||
page = frame_alloc (flags, upage, t);
|
|
||||||
pagedir_set_accessed (t->pagedir, upage, true);
|
|
||||||
#else
|
|
||||||
page = palloc_get_page (flags | PAL_USER);
|
|
||||||
#endif
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Frees a page belonging to a user process given its kernel address,
|
|
||||||
updating the frame table if VM is enabled. */
|
|
||||||
static void
|
|
||||||
free_usr_kpage (void *kpage)
|
|
||||||
{
|
|
||||||
#ifdef VM
|
|
||||||
frame_free (kpage);
|
|
||||||
#else
|
|
||||||
palloc_free_page (kpage);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adds a mapping from user virtual address UPAGE to kernel
|
/* Adds a mapping from user virtual address UPAGE to kernel
|
||||||
virtual address KPAGE to the page table.
|
virtual address KPAGE to the page table.
|
||||||
If WRITABLE is true, the user process may modify the page;
|
If WRITABLE is true, the user process may modify the page;
|
||||||
@@ -791,7 +728,7 @@ free_usr_kpage (void *kpage)
|
|||||||
with palloc_get_page().
|
with palloc_get_page().
|
||||||
Returns true on success, false if UPAGE is already mapped or
|
Returns true on success, false if UPAGE is already mapped or
|
||||||
if memory allocation fails. */
|
if memory allocation fails. */
|
||||||
bool
|
static bool
|
||||||
install_page (void *upage, void *kpage, bool writable)
|
install_page (void *upage, void *kpage, bool writable)
|
||||||
{
|
{
|
||||||
struct thread *t = thread_current ();
|
struct thread *t = thread_current ();
|
||||||
|
|||||||
@@ -8,6 +8,4 @@ int process_wait (tid_t);
|
|||||||
void process_exit (void);
|
void process_exit (void);
|
||||||
void process_activate (void);
|
void process_activate (void);
|
||||||
|
|
||||||
bool install_page (void *upage, void *kpage, bool writable);
|
|
||||||
|
|
||||||
#endif /* userprog/process.h */
|
#endif /* userprog/process.h */
|
||||||
|
|||||||
@@ -10,16 +10,10 @@
|
|||||||
#include "threads/synch.h"
|
#include "threads/synch.h"
|
||||||
#include "userprog/process.h"
|
#include "userprog/process.h"
|
||||||
#include "userprog/pagedir.h"
|
#include "userprog/pagedir.h"
|
||||||
#include "vm/frame.h"
|
|
||||||
#include "vm/page.h"
|
|
||||||
#include "vm/mmap.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
|
||||||
#include <syscall-nr.h>
|
#include <syscall-nr.h>
|
||||||
|
|
||||||
#define MAX_SYSCALL_ARGS 3
|
static unsigned fd_counter = MIN_USER_FD;
|
||||||
#define EXIT_FAILURE -1
|
|
||||||
#define MMAP_FAILURE -1
|
|
||||||
|
|
||||||
struct open_file
|
struct open_file
|
||||||
{
|
{
|
||||||
@@ -49,31 +43,20 @@ static int syscall_write (int fd, const void *buffer, unsigned size);
|
|||||||
static void syscall_seek (int fd, unsigned position);
|
static void syscall_seek (int fd, unsigned position);
|
||||||
static unsigned syscall_tell (int fd);
|
static unsigned syscall_tell (int fd);
|
||||||
static void syscall_close (int fd);
|
static void syscall_close (int fd);
|
||||||
static mapid_t syscall_mmap (int fd, void *addr);
|
|
||||||
static void syscall_munmap (mapid_t mapping);
|
|
||||||
|
|
||||||
static struct open_file *fd_get_file (int fd);
|
static struct open_file *fd_get_file (int fd);
|
||||||
static void validate_user_ptr (const void *start, size_t size,
|
static void *validate_user_pointer (const void *ptr, size_t size);
|
||||||
bool write);
|
|
||||||
static void validate_and_pin_user_ptr (const void *start, size_t size,
|
|
||||||
bool write);
|
|
||||||
static void validate_and_pin_user_str (const char *ptr);
|
|
||||||
static void unpin_user_ptr (const void *start, size_t size);
|
|
||||||
|
|
||||||
static void unpin_user_str (const char *ptr);
|
|
||||||
static int get_user (const uint8_t *);
|
|
||||||
static bool put_user (uint8_t *, uint8_t);
|
|
||||||
|
|
||||||
/* A struct defining a syscall_function pointer along with its arity. */
|
/* A struct defining a syscall_function pointer along with its arity. */
|
||||||
struct syscall_arguments
|
typedef struct
|
||||||
{
|
{
|
||||||
syscall_function function; /* Function pointer. */
|
syscall_function function; /* Function pointer. */
|
||||||
int arity; /* Number of arguments of the function. */
|
int arity; /* Number of arguments of the function. */
|
||||||
};
|
} syscall_arguments;
|
||||||
|
|
||||||
/* A look-up table mapping numbers to system call functions with their number of
|
/* A look-up table mapping numbers to system call functions with their number of
|
||||||
arguments. */
|
arguments. */
|
||||||
static const struct syscall_arguments syscall_lookup[] =
|
static const syscall_arguments syscall_lookup[] =
|
||||||
{
|
{
|
||||||
[SYS_HALT] = {(syscall_function) syscall_halt, 0},
|
[SYS_HALT] = {(syscall_function) syscall_halt, 0},
|
||||||
[SYS_EXIT] = {(syscall_function) syscall_exit, 1},
|
[SYS_EXIT] = {(syscall_function) syscall_exit, 1},
|
||||||
@@ -88,14 +71,13 @@ static const struct syscall_arguments syscall_lookup[] =
|
|||||||
[SYS_SEEK] = {(syscall_function) syscall_seek, 2},
|
[SYS_SEEK] = {(syscall_function) syscall_seek, 2},
|
||||||
[SYS_TELL] = {(syscall_function) syscall_tell, 1},
|
[SYS_TELL] = {(syscall_function) syscall_tell, 1},
|
||||||
[SYS_CLOSE] = {(syscall_function) syscall_close, 1},
|
[SYS_CLOSE] = {(syscall_function) syscall_close, 1},
|
||||||
[SYS_MMAP] = {(syscall_function) syscall_mmap, 2},
|
|
||||||
[SYS_MUNMAP] = {(syscall_function) syscall_munmap, 1}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The number of syscall functions (i.e, number of elements) within the
|
/* The number of syscall functions (i.e, number of elements) within the
|
||||||
syscall_lookup table. */
|
syscall_lookup table. */
|
||||||
static const int LOOKUP_SIZE
|
static const int LOOKUP_SIZE
|
||||||
= sizeof (syscall_lookup) / sizeof (struct syscall_arguments);
|
= sizeof (syscall_lookup) / sizeof (syscall_arguments);
|
||||||
|
|
||||||
|
|
||||||
/* Initialises the syscall handling system, as well as a global lock to
|
/* Initialises the syscall handling system, as well as a global lock to
|
||||||
synchronise all file access between processes. */
|
synchronise all file access between processes. */
|
||||||
@@ -106,29 +88,28 @@ syscall_init (void)
|
|||||||
lock_init (&filesys_lock);
|
lock_init (&filesys_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function that takes an interrupt frame containing a syscall and its args.
|
/* Function that takes a interrupt frame containing a syscall and its args.
|
||||||
Validates the arguments and pointers before calling the relevant
|
Validates the arguments and pointers before calling the relevant
|
||||||
high-level system call function, storing its output (if any) in f->eax */
|
high-level system call function, storing its output (if any) in f->eax */
|
||||||
static void
|
static void
|
||||||
syscall_handler (struct intr_frame *f)
|
syscall_handler (struct intr_frame *f)
|
||||||
{
|
{
|
||||||
/* First, read the system call number from the stack. */
|
/* First, read the system call number from the stack. */
|
||||||
validate_user_ptr (f->esp, sizeof (uintptr_t), false);
|
validate_user_pointer (f->esp, 1);
|
||||||
uintptr_t syscall_number = *(int *)f->esp;
|
unsigned syscall_number = *(int *) f->esp;
|
||||||
thread_current ()->curr_esp = f->esp;
|
|
||||||
|
|
||||||
/* Ensures the number corresponds to a system call that can be handled. */
|
/* Ensures the number corresponds to a system call that can be handled. */
|
||||||
if (syscall_number >= LOOKUP_SIZE)
|
if (syscall_number >= LOOKUP_SIZE)
|
||||||
syscall_exit (EXIT_FAILURE);
|
thread_exit ();
|
||||||
|
|
||||||
struct syscall_arguments syscall = syscall_lookup[syscall_number];
|
syscall_arguments syscall = syscall_lookup[syscall_number];
|
||||||
|
|
||||||
/* Next, read and copy the arguments from the stack pointer. */
|
/* Next, read and copy the arguments from the stack pointer. */
|
||||||
validate_user_ptr (f->esp + sizeof (uintptr_t),
|
validate_user_pointer (f->esp + sizeof (uintptr_t),
|
||||||
syscall.arity * sizeof (uintptr_t), false);
|
syscall.arity * sizeof (uintptr_t));
|
||||||
uintptr_t args[MAX_SYSCALL_ARGS] = { 0 };
|
uintptr_t args[3] = {0};
|
||||||
for (int i = 0; i < syscall.arity && i < MAX_SYSCALL_ARGS; i++)
|
for (int i=0; i < syscall.arity; i++)
|
||||||
args[i] = *(uintptr_t *)(f->esp + sizeof (uintptr_t) * (i + 1));
|
args[i] = *(uintptr_t *) (f->esp + sizeof (uintptr_t) * (i + 1));
|
||||||
|
|
||||||
/* Call the function that handles this system call with the arguments. When
|
/* Call the function that handles this system call with the arguments. When
|
||||||
there is a return value it is stored in f->eax. */
|
there is a return value it is stored in f->eax. */
|
||||||
@@ -148,18 +129,20 @@ syscall_exit (int status)
|
|||||||
{
|
{
|
||||||
/* Sets exit_status of the thread to status. thread_exit () will call
|
/* Sets exit_status of the thread to status. thread_exit () will call
|
||||||
process_exit () if user programs are allowed. */
|
process_exit () if user programs are allowed. */
|
||||||
thread_current ()->result->exit_status = status;
|
thread_current ()->exit_status = status;
|
||||||
thread_exit ();
|
thread_exit ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Executes a given command with the relevant args, by calling process_execute.
|
/* Executes a given command with the relevant args, by calling process_execute.
|
||||||
Returns PID for the process that is running the CMD_LINE. */
|
Acquires the filesystem lock as process_execute accesses the file system.
|
||||||
|
Returns PID for the process that is running the CMD_LINE
|
||||||
|
*/
|
||||||
static pid_t
|
static pid_t
|
||||||
syscall_exec (const char *cmd_line)
|
syscall_exec (const char *cmd_line)
|
||||||
{
|
{
|
||||||
validate_and_pin_user_str (cmd_line);
|
validate_user_pointer (cmd_line, 1);
|
||||||
pid_t pid = process_execute (cmd_line);
|
|
||||||
unpin_user_str (cmd_line);
|
pid_t pid = process_execute(cmd_line);
|
||||||
|
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
@@ -169,25 +152,21 @@ syscall_exec (const char *cmd_line)
|
|||||||
static int
|
static int
|
||||||
syscall_wait (pid_t pid)
|
syscall_wait (pid_t pid)
|
||||||
{
|
{
|
||||||
return process_wait (pid); /* Returns the exit status of the waited process */
|
return process_wait (pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handles the syscall for file creation. First validates the user file
|
/* Handles the syscall for file creation. First validates the user file
|
||||||
pointer. Acquires the file system lock to prevent synchronisation issues,
|
pointer. Acquires the file system lock to prevent synchronisation issues,
|
||||||
and then uses FILESYS_CREATE to create the file, returning the same status */
|
and then uses FILESYS_CREATE to create the file, returning the same status */
|
||||||
static bool
|
static bool
|
||||||
syscall_create (const char *file, unsigned initial_size)
|
syscall_create (const char *file UNUSED, unsigned initial_size UNUSED)
|
||||||
{
|
{
|
||||||
validate_and_pin_user_str (file);
|
validate_user_pointer (file, 1);
|
||||||
|
|
||||||
/* Acquire the file system lock to prevent race conditions. */
|
|
||||||
lock_acquire (&filesys_lock);
|
lock_acquire (&filesys_lock);
|
||||||
bool status = filesys_create (file, initial_size);
|
bool status = filesys_create (file, initial_size);
|
||||||
lock_release (&filesys_lock);
|
lock_release (&filesys_lock);
|
||||||
|
|
||||||
unpin_user_str (file);
|
|
||||||
|
|
||||||
/* Return the status of the file creation. */
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,16 +176,12 @@ syscall_create (const char *file, unsigned initial_size)
|
|||||||
static bool
|
static bool
|
||||||
syscall_remove (const char *file)
|
syscall_remove (const char *file)
|
||||||
{
|
{
|
||||||
validate_and_pin_user_str (file);
|
validate_user_pointer (file, 1);
|
||||||
|
|
||||||
/* Acquire the file system lock to prevent race conditions. */
|
|
||||||
lock_acquire (&filesys_lock);
|
lock_acquire (&filesys_lock);
|
||||||
bool status = filesys_remove (file);
|
bool status = filesys_remove (file);
|
||||||
lock_release (&filesys_lock);
|
lock_release (&filesys_lock);
|
||||||
|
|
||||||
unpin_user_str (file);
|
|
||||||
|
|
||||||
/* Return the status of the file removal. */
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,33 +192,23 @@ syscall_remove (const char *file)
|
|||||||
static int
|
static int
|
||||||
syscall_open (const char *file)
|
syscall_open (const char *file)
|
||||||
{
|
{
|
||||||
validate_and_pin_user_str (file);
|
validate_user_pointer (file, 1);
|
||||||
|
|
||||||
/* Acquire the file system lock to prevent race conditions. */
|
|
||||||
lock_acquire (&filesys_lock);
|
lock_acquire (&filesys_lock);
|
||||||
struct file *ptr = filesys_open (file);
|
struct file *ptr = filesys_open (file);
|
||||||
lock_release (&filesys_lock);
|
lock_release (&filesys_lock);
|
||||||
|
|
||||||
unpin_user_str (file);
|
|
||||||
|
|
||||||
/* If the file could not be opened, return failure. */
|
|
||||||
if (ptr == NULL)
|
if (ptr == NULL)
|
||||||
return EXIT_FAILURE;
|
return -1;
|
||||||
|
|
||||||
/* Allocate space for a struct representing a mapping from an FD to a struct
|
/* Allocate space for a struct representing a mapping from an FD to a struct
|
||||||
file. */
|
file. */
|
||||||
struct open_file *file_info
|
struct open_file *file_info
|
||||||
= (struct open_file*) malloc (sizeof (struct open_file));
|
= (struct open_file*) malloc (sizeof (struct open_file));
|
||||||
if (file_info == NULL)
|
if (file_info == NULL)
|
||||||
{
|
return -1;
|
||||||
/* If we could not allocate memory for the file_info struct, close the
|
|
||||||
file and return failure. */
|
|
||||||
file_close (ptr);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Populate the above struct, with a unique FD and the current open file */
|
/* Populate the above struct, with a unique FD and the current open file */
|
||||||
file_info->fd = thread_current ()->fd_counter++;
|
file_info->fd = fd_counter++;
|
||||||
file_info->file = ptr;
|
file_info->file = ptr;
|
||||||
|
|
||||||
/* Add the new FD->file mapping to the hashtable for the current thread */
|
/* Add the new FD->file mapping to the hashtable for the current thread */
|
||||||
@@ -259,17 +224,14 @@ syscall_open (const char *file)
|
|||||||
static int
|
static int
|
||||||
syscall_filesize (int fd)
|
syscall_filesize (int fd)
|
||||||
{
|
{
|
||||||
/* Try to get the file from the FD. If it does not exist, return failure. */
|
|
||||||
struct open_file *file_info = fd_get_file (fd);
|
struct open_file *file_info = fd_get_file (fd);
|
||||||
if (file_info == NULL)
|
if (file_info == NULL)
|
||||||
return EXIT_FAILURE;
|
return -1;
|
||||||
|
|
||||||
/* Acquire the file system lock to prevent any race conditions. */
|
|
||||||
lock_acquire (&filesys_lock);
|
lock_acquire (&filesys_lock);
|
||||||
int bytes = file_length (file_info->file);
|
int bytes = file_length (file_info->file);
|
||||||
lock_release (&filesys_lock);
|
lock_release (&filesys_lock);
|
||||||
|
|
||||||
/* Return the number of bytes in the file. */
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,44 +244,31 @@ syscall_read (int fd, void *buffer, unsigned size)
|
|||||||
{
|
{
|
||||||
/* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are
|
/* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are
|
||||||
allowed. */
|
allowed. */
|
||||||
if (fd < STDIN_FILENO || fd == STDOUT_FILENO)
|
if (fd < 0 || fd == STDOUT_FILENO)
|
||||||
return EXIT_FAILURE;
|
return -1;
|
||||||
|
|
||||||
|
validate_user_pointer (buffer, size);
|
||||||
|
|
||||||
if (fd == STDIN_FILENO)
|
if (fd == STDIN_FILENO)
|
||||||
{
|
{
|
||||||
/* Validate the user buffer. */
|
|
||||||
validate_user_ptr (buffer, size, true);
|
|
||||||
|
|
||||||
/* Reading from the console. */
|
/* Reading from the console. */
|
||||||
char *write_buffer = buffer;
|
char *write_buffer = buffer;
|
||||||
for (unsigned i = 0; i < size; i++)
|
for (int i = 0; i < size; i++)
|
||||||
write_buffer[i] = input_getc ();
|
write_buffer[i] = input_getc ();
|
||||||
|
|
||||||
/* In case of console, read is always (eventually) successful. So return
|
|
||||||
the size for the number of bytes read. */
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Reading from a file. */
|
/* Reading from a file. */
|
||||||
/* Find the file from the FD. If it does not exist, return failure. */
|
|
||||||
struct open_file *file_info = fd_get_file (fd);
|
struct open_file *file_info = fd_get_file (fd);
|
||||||
if (file_info == NULL)
|
if (file_info == NULL)
|
||||||
return EXIT_FAILURE;
|
return -1;
|
||||||
|
|
||||||
/* Validate the user buffer, and pin the pages to prevent eviction. */
|
|
||||||
validate_and_pin_user_ptr (buffer, size, true);
|
|
||||||
|
|
||||||
/* Acquire the file system lock to prevent race-conditions. */
|
|
||||||
lock_acquire (&filesys_lock);
|
lock_acquire (&filesys_lock);
|
||||||
int bytes_read = file_read (file_info->file, buffer, size);
|
int bytes_written = file_read (file_info->file, buffer, size);
|
||||||
lock_release (&filesys_lock);
|
lock_release (&filesys_lock);
|
||||||
|
return bytes_written;
|
||||||
/* Unpin the pages to allow eviction. */
|
|
||||||
unpin_user_ptr (buffer, size);
|
|
||||||
|
|
||||||
/* Return the number of bytes read. */
|
|
||||||
return bytes_read;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,39 +284,26 @@ syscall_write (int fd, const void *buffer, unsigned size)
|
|||||||
if (fd <= 0)
|
if (fd <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
validate_user_pointer (buffer, size);
|
||||||
|
|
||||||
if (fd == STDOUT_FILENO)
|
if (fd == STDOUT_FILENO)
|
||||||
{
|
{
|
||||||
/* Validate the user buffer. */
|
|
||||||
validate_user_ptr (buffer, size, false);
|
|
||||||
|
|
||||||
/* Writing to the console. */
|
/* Writing to the console. */
|
||||||
putbuf (buffer, size);
|
putbuf (buffer, size);
|
||||||
|
|
||||||
/* In case of console, write is always successful. So return the size for
|
|
||||||
the number of bytes written. */
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Writing to a file. */
|
/* Writing to a file. */
|
||||||
/* Find the file from the FD. If it does not exist, return failure. */
|
|
||||||
struct open_file *file_info = fd_get_file (fd);
|
struct open_file *file_info = fd_get_file (fd);
|
||||||
if (file_info == NULL)
|
if (file_info == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Validate the user buffer, and pin the pages to prevent eviction. */
|
|
||||||
validate_and_pin_user_ptr (buffer, size, false);
|
|
||||||
|
|
||||||
/* Acquire the file system lock to prevent race conditions. */
|
|
||||||
lock_acquire (&filesys_lock);
|
lock_acquire (&filesys_lock);
|
||||||
int bytes_written = file_write (file_info->file, buffer, size);
|
int bytes = file_write (file_info->file, buffer, size);
|
||||||
lock_release (&filesys_lock);
|
lock_release (&filesys_lock);
|
||||||
|
|
||||||
/* Unpin the pages to allow eviction. */
|
return bytes;
|
||||||
unpin_user_ptr (buffer, size);
|
|
||||||
|
|
||||||
/* Return the number of bytes written. */
|
|
||||||
return bytes_written;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,10 +314,13 @@ syscall_write (int fd, const void *buffer, unsigned size)
|
|||||||
static void
|
static void
|
||||||
syscall_seek (int fd, unsigned position)
|
syscall_seek (int fd, unsigned position)
|
||||||
{
|
{
|
||||||
/* Find the file from the FD. If it does not exist, do nothing. */
|
|
||||||
struct open_file *file_info = fd_get_file (fd);
|
struct open_file *file_info = fd_get_file (fd);
|
||||||
if (file_info != NULL)
|
if (file_info != NULL)
|
||||||
|
{
|
||||||
|
lock_acquire (&filesys_lock);
|
||||||
file_seek (file_info->file, position);
|
file_seek (file_info->file, position);
|
||||||
|
lock_release (&filesys_lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handles the syscall for returning the next byte in a file referenced by
|
/* Handles the syscall for returning the next byte in a file referenced by
|
||||||
@@ -390,14 +329,14 @@ syscall_seek (int fd, unsigned position)
|
|||||||
static unsigned
|
static unsigned
|
||||||
syscall_tell (int fd)
|
syscall_tell (int fd)
|
||||||
{
|
{
|
||||||
/* Find the file from the FD. If it does not exist, return 0. */
|
|
||||||
struct open_file *file_info = fd_get_file (fd);
|
struct open_file *file_info = fd_get_file (fd);
|
||||||
if (file_info == NULL)
|
if (file_info == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
lock_acquire (&filesys_lock);
|
||||||
unsigned pos = file_tell (file_info->file);
|
unsigned pos = file_tell (file_info->file);
|
||||||
|
lock_release (&filesys_lock);
|
||||||
|
|
||||||
/* Return the current position in the file. */
|
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,111 +346,24 @@ syscall_tell (int fd)
|
|||||||
static void
|
static void
|
||||||
syscall_close (int fd)
|
syscall_close (int fd)
|
||||||
{
|
{
|
||||||
/* Find the file from the FD. If it does not exist, do nothing. */
|
|
||||||
struct open_file *file_info = fd_get_file (fd);
|
struct open_file *file_info = fd_get_file (fd);
|
||||||
if (file_info != NULL)
|
if (file_info != NULL)
|
||||||
{
|
{
|
||||||
/* File exists */
|
|
||||||
/* First, remove the file from the hash table of open files. */
|
|
||||||
hash_delete (&thread_current ()->open_files, &file_info->elem);
|
hash_delete (&thread_current ()->open_files, &file_info->elem);
|
||||||
|
|
||||||
/* Then, close the file, acquiring the file system lock to prevent race
|
|
||||||
conditions. */
|
|
||||||
lock_acquire (&filesys_lock);
|
lock_acquire (&filesys_lock);
|
||||||
file_close (file_info->file);
|
file_close (file_info->file);
|
||||||
lock_release (&filesys_lock);
|
lock_release (&filesys_lock);
|
||||||
|
|
||||||
/* Free the memory allocated for the file_info struct. */
|
|
||||||
free (file_info);
|
free (file_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handles the syscall for memory mapping a file. */
|
|
||||||
static mapid_t
|
|
||||||
syscall_mmap (int fd, void *addr)
|
|
||||||
{
|
|
||||||
/* Ensure the FD is for a file in the filesystem (not STDIN or STDOUT). */
|
|
||||||
if (fd == STDOUT_FILENO || fd == STDIN_FILENO)
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
|
|
||||||
/* Validate that there is a file associated with the given FD. */
|
|
||||||
struct open_file *file_info = fd_get_file (fd);
|
|
||||||
if (file_info == NULL)
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
|
|
||||||
/* Ensure that the address is page-aligned and it's neither NULL nor zero. */
|
|
||||||
if (addr == 0 || addr == NULL || pg_ofs (addr) != 0)
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
|
|
||||||
/* Reopen the file to obtain a separate and independent reference to the file
|
|
||||||
for the mapping. */
|
|
||||||
struct file *file = file_reopen (file_info->file);
|
|
||||||
if (file == NULL)
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
|
|
||||||
/* Get the size of the file. Mmap fails if the file is empty. */
|
|
||||||
off_t file_size = file_length (file);
|
|
||||||
if (file_size == 0)
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
|
|
||||||
/* ensures the page for mmap does not overlap with the stack */
|
|
||||||
if (addr >= (thread_current ()->curr_esp - PGSIZE))
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
|
|
||||||
/* Check and ensure that there is enough space in the user virtual memory to
|
|
||||||
hold the entire file. */
|
|
||||||
for (off_t ofs = 0; ofs < file_size; ofs += PGSIZE)
|
|
||||||
if (page_get (thread_current (), addr + ofs) != NULL)
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
|
|
||||||
|
|
||||||
/* Map the file data into the user virtual memory starting from addr. */
|
|
||||||
for (off_t ofs = 0; ofs < file_size; ofs += PGSIZE)
|
|
||||||
{
|
|
||||||
off_t read_bytes = file_size - ofs < PGSIZE ? file_size - ofs : PGSIZE;
|
|
||||||
off_t zero_bytes = PGSIZE - read_bytes;
|
|
||||||
|
|
||||||
if (page_insert_file (file, ofs, addr + ofs, read_bytes, zero_bytes, true,
|
|
||||||
PAGE_MMAP) == NULL)
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create a new mapping for the file. */
|
|
||||||
struct mmap_entry *mmap = mmap_insert (file, addr);
|
|
||||||
if (mmap == NULL)
|
|
||||||
return MMAP_FAILURE;
|
|
||||||
|
|
||||||
return mmap->mapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles the syscall for unmapping a memory mapped file.
|
|
||||||
|
|
||||||
Pre: mapping is a valid mapping identifier returned by mmap syscall. */
|
|
||||||
static void
|
|
||||||
syscall_munmap (mapid_t mapping)
|
|
||||||
{
|
|
||||||
/* Get the mmap entry from the mapping identifier. */
|
|
||||||
struct mmap_entry *mmap = mmap_get (mapping);
|
|
||||||
|
|
||||||
/* Delete the mmap entry from the hash table. */
|
|
||||||
hash_delete (&thread_current ()->mmap_files, &mmap->elem);
|
|
||||||
|
|
||||||
/* Unmap the mmap entry: free the pages and write back to the file if
|
|
||||||
necessary. NOTE. freeing and cleaning up is also handled by mmap_unmap. */
|
|
||||||
mmap_unmap (mmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hashing function needed for the open_file table. Returns a hash for an entry,
|
/* Hashing function needed for the open_file table. Returns a hash for an entry,
|
||||||
based on its FD. */
|
based on its FD. */
|
||||||
unsigned
|
unsigned
|
||||||
fd_hash (const struct hash_elem *element, void *aux UNUSED)
|
fd_hash (const struct hash_elem *element, void *aux UNUSED)
|
||||||
{
|
{
|
||||||
/* We use the FD as the hash value. This is because the FD is incremented
|
return hash_int (hash_entry (element, struct open_file, elem)->fd);
|
||||||
sequentially and is therefore unique for each file. It positively affects
|
|
||||||
the performance of the hash table: 1. It is unique so no need to call
|
|
||||||
expensive hash functions. 2. It being sequential means that the hash table
|
|
||||||
is more likely to be weight balanced. */
|
|
||||||
return hash_entry (element, struct open_file, elem)->fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Comparator function for the open_file table. Compares two entries based on
|
/* Comparator function for the open_file table. Compares two entries based on
|
||||||
@@ -526,20 +378,6 @@ fd_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|||||||
return a->fd < b->fd;
|
return a->fd < b->fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Function to clean up an open file entry. Closes the file and frees the
|
|
||||||
associated memory. */
|
|
||||||
void
|
|
||||||
fd_cleanup (struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct open_file *file_info = hash_entry (e, struct open_file, elem);
|
|
||||||
|
|
||||||
lock_acquire (&filesys_lock);
|
|
||||||
file_close (file_info->file);
|
|
||||||
lock_release (&filesys_lock);
|
|
||||||
|
|
||||||
free (file_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gets a file from its descriptor (FD number). If there is no file with the fd
|
/* Gets a file from its descriptor (FD number). If there is no file with the fd
|
||||||
FD it returns NULL. */
|
FD it returns NULL. */
|
||||||
static struct open_file *
|
static struct open_file *
|
||||||
@@ -559,193 +397,18 @@ fd_get_file (int fd)
|
|||||||
return hash_entry (e, struct open_file, elem);
|
return hash_entry (e, struct open_file, elem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper function that validates a block of memory and optionally pins frames.
|
|
||||||
thread_exit() if the memory is invalid. Used only by the two helper functions
|
|
||||||
validate_user_ptr and validate_and_pin_user_ptr. See the comments for those
|
|
||||||
functions for more details on each. */
|
|
||||||
static void
|
|
||||||
validate_user_ptr_helper (const void *start, size_t size, bool write, bool pin)
|
|
||||||
{
|
|
||||||
if (size == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* ptr < ptr + size - 1, so sufficient to check that (ptr + size -1) is a
|
|
||||||
valid user virtual memory address. */
|
|
||||||
void *end = start + size - 1;
|
|
||||||
if (!is_user_vaddr (end))
|
|
||||||
syscall_exit (EXIT_FAILURE);
|
|
||||||
|
|
||||||
for (const void *ptr = pg_round_down (start); ptr <= end; ptr += PGSIZE)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
|
|
||||||
/* Check read access to pointer. */
|
|
||||||
if ((result = get_user (ptr)) == -1)
|
|
||||||
syscall_exit (EXIT_FAILURE);
|
|
||||||
|
|
||||||
/* Check write access to pointer (if required). */
|
|
||||||
if (write && !put_user ((uint8_t *)ptr, result))
|
|
||||||
syscall_exit (EXIT_FAILURE);
|
|
||||||
|
|
||||||
/* If pin is set, pin the frame to prevent eviction. */
|
|
||||||
if (pin)
|
|
||||||
{
|
|
||||||
void *kpage = pagedir_get_page(thread_current()->pagedir, ptr);
|
|
||||||
if (kpage == NULL)
|
|
||||||
{
|
|
||||||
// If it was evicted, try to load it back in.
|
|
||||||
ptr -= PGSIZE;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_pin(kpage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validates if a block of memory starting at PTR and of size SIZE bytes is
|
/* Validates if a block of memory starting at PTR and of size SIZE bytes is
|
||||||
fully contained within valid user virtual memory. thread_exit () if the
|
fully contained within user virtual memory. Kills the thread (by calling
|
||||||
memory is invalid.
|
thread_exit) if the memory is invalid. Otherwise, returns the PTR given.
|
||||||
If the size is 0, the function does no checks and returns PTR. */
|
If the size is 0, the function does no checks and returns PTR.*/
|
||||||
static void
|
static void *
|
||||||
validate_user_ptr (const void *start, size_t size, bool write)
|
validate_user_pointer (const void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
validate_user_ptr_helper (start, size, write, false);
|
if (size > 0 && (ptr == NULL ||
|
||||||
}
|
!is_user_vaddr (ptr) ||
|
||||||
|
!is_user_vaddr (ptr + size - 1) ||
|
||||||
/* Validates if a block of memory starting at PTR and of size SIZE bytes is
|
pagedir_get_page (thread_current()->pagedir, ptr) == NULL))
|
||||||
fully contained within valid user virtual memory. thread_exit () if the
|
thread_exit ();
|
||||||
memory is invalid. The function also checks if the memory is writable if
|
|
||||||
WRITE flag is set.
|
return (void *) ptr;
|
||||||
|
|
||||||
The function attempts to preload the pages in case they are not in memory
|
|
||||||
yet (e.g., in a swap, lazy loading). If this is successful, the frame pages
|
|
||||||
are pinned to prevent eviction prior to access.
|
|
||||||
|
|
||||||
As such, a call to this function MUST be followed by a call to
|
|
||||||
unpin_user_ptr (START, SIZE) to unpin the pages and allow eviction.
|
|
||||||
|
|
||||||
If the size is 0, the function does no checks and returns PTR. */
|
|
||||||
static void
|
|
||||||
validate_and_pin_user_ptr (const void *start, size_t size, bool write)
|
|
||||||
{
|
|
||||||
validate_user_ptr_helper (start, size, write, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unpins all the pages containing a block of memory starting at START and of
|
|
||||||
size SIZE bytes.
|
|
||||||
|
|
||||||
Pre: The pages were previously pinned by validate_and_pin_user_ptr (START,
|
|
||||||
SIZE). */
|
|
||||||
static void
|
|
||||||
unpin_user_ptr (const void *start, size_t size)
|
|
||||||
{
|
|
||||||
void *end = start + size - 1;
|
|
||||||
|
|
||||||
/* We don't need to do any checks as this function is always called after
|
|
||||||
validate_and_pin_user_ptr. */
|
|
||||||
/* Go through all pages in the block range, unpinning the frames. */
|
|
||||||
for (void *ptr = pg_round_down (start); ptr <= end; ptr += PGSIZE)
|
|
||||||
{
|
|
||||||
void *kpage = pagedir_get_page (thread_current ()->pagedir, ptr);
|
|
||||||
ASSERT (kpage != NULL);
|
|
||||||
|
|
||||||
frame_unpin (kpage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validates of a C-string starting at ptr is fully contained within valid
|
|
||||||
user virtual memory. thread_exit () if the memory is invalid. */
|
|
||||||
static void
|
|
||||||
validate_and_pin_user_str (const char *ptr)
|
|
||||||
{
|
|
||||||
size_t offset = (uintptr_t) ptr % PGSIZE;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (!is_user_vaddr (ptr))
|
|
||||||
syscall_exit (EXIT_FAILURE);
|
|
||||||
|
|
||||||
if (get_user ((const uint8_t *)ptr) == -1)
|
|
||||||
syscall_exit (EXIT_FAILURE);
|
|
||||||
|
|
||||||
/* Pin the frame to prevent eviction. */
|
|
||||||
void *page = pg_round_down (ptr);
|
|
||||||
void *kpage = pagedir_get_page (thread_current ()->pagedir, page);
|
|
||||||
if (kpage == NULL)
|
|
||||||
{
|
|
||||||
// If it was evicted, attempt to reload.
|
|
||||||
ptr -= PGSIZE;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_pin (kpage);
|
|
||||||
|
|
||||||
while (offset < PGSIZE)
|
|
||||||
{
|
|
||||||
if (*ptr == '\0')
|
|
||||||
return; /* We reached the end of the string without issues. */
|
|
||||||
|
|
||||||
ptr++;
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unpins all the pages containing a C-string starting at PTR.
|
|
||||||
|
|
||||||
Pre: The pages were previously pinned by validate_and_pin_user_str (PTR).
|
|
||||||
PTR points to a valid C string that ends with '\0'. */
|
|
||||||
static void
|
|
||||||
unpin_user_str (const char *ptr)
|
|
||||||
{
|
|
||||||
size_t offset = (uintptr_t)ptr % PGSIZE;
|
|
||||||
const char *str_ptr = ptr;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
void *page = pg_round_down(str_ptr);
|
|
||||||
void *kpage = pagedir_get_page(thread_current()->pagedir, page);
|
|
||||||
ASSERT(kpage != NULL);
|
|
||||||
frame_unpin (kpage);
|
|
||||||
|
|
||||||
/* Scan until end of string or page */
|
|
||||||
while (offset < PGSIZE)
|
|
||||||
{
|
|
||||||
if (*str_ptr == '\0')
|
|
||||||
return; /* Found end of string */
|
|
||||||
str_ptr++;
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PROVIDED BY SPEC.
|
|
||||||
Reads a byte at user virtual address UADDR.
|
|
||||||
UADDR must be below PHYS_BASE.
|
|
||||||
Returns the byte value if successful, -1 if a segfault occurred. */
|
|
||||||
static int
|
|
||||||
get_user (const uint8_t *uaddr)
|
|
||||||
{
|
|
||||||
int result;
|
|
||||||
asm ("movl $1f, %0; movzbl %1, %0; 1:" : "=&a"(result) : "m"(*uaddr));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PROVIDED BY SPEC.
|
|
||||||
Writes BYTE to user address UDST.
|
|
||||||
UDST must be below PHYS_BASE.
|
|
||||||
Returns true if successful, false if a segfault occurred. */
|
|
||||||
static bool
|
|
||||||
put_user (uint8_t *udst, uint8_t byte)
|
|
||||||
{
|
|
||||||
int error_code;
|
|
||||||
asm ("movl $1f, %0; movb %b2, %1; 1:"
|
|
||||||
: "=&a"(error_code), "=m"(*udst)
|
|
||||||
: "q"(byte));
|
|
||||||
return error_code != -1;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include "threads/synch.h"
|
#include "threads/synch.h"
|
||||||
|
|
||||||
|
#define MIN_USER_FD 2
|
||||||
|
|
||||||
typedef int pid_t;
|
typedef int pid_t;
|
||||||
|
|
||||||
struct lock filesys_lock;
|
struct lock filesys_lock;
|
||||||
@@ -12,6 +14,5 @@ void syscall_init (void);
|
|||||||
|
|
||||||
unsigned fd_hash (const struct hash_elem *element, void *aux);
|
unsigned fd_hash (const struct hash_elem *element, void *aux);
|
||||||
bool fd_less (const struct hash_elem *a, const struct hash_elem *b, void *aux);
|
bool fd_less (const struct hash_elem *a, const struct hash_elem *b, void *aux);
|
||||||
void fd_cleanup (struct hash_elem *e, void *aux);
|
|
||||||
|
|
||||||
#endif /* userprog/syscall.h */
|
#endif /* userprog/syscall.h */
|
||||||
|
|||||||
402
src/vm/frame.c
402
src/vm/frame.c
@@ -1,402 +0,0 @@
|
|||||||
#include <debug.h>
|
|
||||||
#include <hash.h>
|
|
||||||
#include <list.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "frame.h"
|
|
||||||
#include "page.h"
|
|
||||||
#include "filesys/file.h"
|
|
||||||
#include "threads/malloc.h"
|
|
||||||
#include "threads/vaddr.h"
|
|
||||||
#include "userprog/pagedir.h"
|
|
||||||
#include "userprog/syscall.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
|
|
||||||
/* Hash table that maps every active frame's kernel virtual address
|
|
||||||
to its corresponding 'frame_metadata'.*/
|
|
||||||
struct hash frame_table;
|
|
||||||
|
|
||||||
/* Linked list used to represent the circular queue in the 'clock'
|
|
||||||
algorithm for page eviction. Iterating from the element that is
|
|
||||||
currently pointed at by 'next_victim' yields an ordering of the entries
|
|
||||||
from oldest to newest (in terms of when they were added or checked
|
|
||||||
for having been referenced by a process). */
|
|
||||||
struct list lru_list;
|
|
||||||
|
|
||||||
/* The next element in lru_list to be considered for eviction (oldest added
|
|
||||||
or referenced page in the circular queue). If this page has has an
|
|
||||||
'accessed' bit of 0 when considering eviction, then it will be the next
|
|
||||||
victim. Otherwise, the next element in the queue is similarly considered. */
|
|
||||||
struct list_elem *next_victim = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
struct frame_metadata
|
|
||||||
{
|
|
||||||
void *frame; /* The kernel virtual address holding the frame. */
|
|
||||||
void *upage; /* The user virtual address pointing to the frame. */
|
|
||||||
struct list owners; /* List of threads that own the frame. */
|
|
||||||
bool pinned; /* Indicates wheter the frame should be
|
|
||||||
considered as an eviction candidate.*/
|
|
||||||
struct hash_elem hash_elem; /* Tracks the position of the frame metadata
|
|
||||||
within 'frame_table', whose key is the
|
|
||||||
kernel virtual address of the frame. */
|
|
||||||
struct list_elem list_elem; /* Tracks the position of the frame metadata
|
|
||||||
within 'lru_list', so a victim can be
|
|
||||||
chosen for eviction. */
|
|
||||||
};
|
|
||||||
|
|
||||||
hash_hash_func frame_metadata_hash;
|
|
||||||
hash_less_func frame_metadata_less;
|
|
||||||
|
|
||||||
static struct list_elem *lru_next (struct list_elem *e);
|
|
||||||
static struct list_elem *lru_prev (struct list_elem *e);
|
|
||||||
static struct frame_metadata *frame_metadata_get (void *frame);
|
|
||||||
static struct frame_metadata *get_victim (void);
|
|
||||||
static void free_owners (struct list *owners);
|
|
||||||
static struct frame_metadata *frame_metadata_find (void *frame);
|
|
||||||
|
|
||||||
/* Initialize the frame system by initializing the frame (hash) table with
|
|
||||||
the frame_metadata hashing and comparison functions, as well as initializing
|
|
||||||
'lru_list' and its associated synchronisation primitives. */
|
|
||||||
void
|
|
||||||
frame_init (void)
|
|
||||||
{
|
|
||||||
hash_init (&frame_table, frame_metadata_hash, frame_metadata_less, NULL);
|
|
||||||
|
|
||||||
list_init (&lru_list);
|
|
||||||
lock_init (&ftable_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Consider synchronisation more closely (i.e. just for hash
|
|
||||||
table). */
|
|
||||||
/* Attempt to allocate a frame for a user process, either by direct
|
|
||||||
allocation of a user page if there is sufficient RAM, or by
|
|
||||||
evicting a currently active page if memory allocated for user
|
|
||||||
processes is fulled and storing it in swap. If swap is full in
|
|
||||||
the former case, panic the kernel. */
|
|
||||||
void *
|
|
||||||
frame_alloc (enum palloc_flags flags, void *upage, struct thread *owner)
|
|
||||||
{
|
|
||||||
struct frame_metadata *frame_metadata;
|
|
||||||
flags |= PAL_USER;
|
|
||||||
|
|
||||||
lock_acquire (&ftable_lock);
|
|
||||||
void *frame = palloc_get_page (flags);
|
|
||||||
|
|
||||||
/* If a frame couldn't be allocated we must be out of main memory. Thus,
|
|
||||||
obtain a victim page to replace with our page, and swap the victim
|
|
||||||
into disk. */
|
|
||||||
if (frame == NULL)
|
|
||||||
{
|
|
||||||
/* 1. Obtain victim. */
|
|
||||||
if (next_victim == NULL)
|
|
||||||
PANIC ("Couldn't allocate a single page to main memory!\n");
|
|
||||||
|
|
||||||
struct frame_metadata *victim = get_victim ();
|
|
||||||
ASSERT (victim != NULL); /* get_victim () should never return null. */
|
|
||||||
|
|
||||||
/* 2. Handle victim page writing based on its type. */
|
|
||||||
struct page_entry *victim_page = page_get (thread_current (), victim->upage);
|
|
||||||
if (victim_page != NULL && victim_page->type == PAGE_MMAP)
|
|
||||||
{
|
|
||||||
/* If it was a memory-mapped file page, we just write it back
|
|
||||||
to the file if it was dirty. */
|
|
||||||
if (pagedir_is_dirty(owner->pagedir, victim->upage))
|
|
||||||
{
|
|
||||||
lock_acquire (&filesys_lock);
|
|
||||||
file_write_at (victim_page->file, victim->upage,
|
|
||||||
victim_page->read_bytes, victim_page->offset);
|
|
||||||
lock_release (&filesys_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Otherwise, insert the page into swap. */
|
|
||||||
page_insert_swapped (victim->upage, victim->frame, &victim->owners);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free victim's owners. */
|
|
||||||
free_owners (&victim->owners);
|
|
||||||
|
|
||||||
/* If zero flag is set, zero out the victim page. */
|
|
||||||
if (flags & PAL_ZERO)
|
|
||||||
memset (victim->frame, 0, PGSIZE);
|
|
||||||
|
|
||||||
/* 3. Indicate that the new frame's metadata will be stored
|
|
||||||
inside the same structure that stored the victim's metadata.frame.c
|
|
||||||
As both the new frame and the victim frame share the same kernel
|
|
||||||
virtual address, the hash map need not be updated, and neither
|
|
||||||
the list_elem value as both share the same lru_list position. */
|
|
||||||
frame_metadata = victim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If sufficient main memory allows the frame to be directly allocated,
|
|
||||||
we must update the frame table with a new entry, and grow lru_list. */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Must own ftable_lock here, as otherwise there is a race condition
|
|
||||||
with next_victim either being NULL or uninitialized. */
|
|
||||||
frame_metadata = malloc (sizeof (struct frame_metadata));
|
|
||||||
if (frame_metadata == NULL)
|
|
||||||
PANIC ("Couldn't allocate memory for frame metadata!\n");
|
|
||||||
frame_metadata->frame = frame;
|
|
||||||
|
|
||||||
/* Newly allocated frames are pushed to the back of the circular queue
|
|
||||||
represented by lru_list. Must explicitly handle the case where the
|
|
||||||
circular queue is empty (when next_victim == NULL). */
|
|
||||||
if (next_victim == NULL)
|
|
||||||
{
|
|
||||||
list_push_back (&lru_list, &frame_metadata->list_elem);
|
|
||||||
next_victim = &frame_metadata->list_elem;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct list_elem *lru_tail = lru_prev (next_victim);
|
|
||||||
list_insert (lru_tail, &frame_metadata->list_elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_insert (&frame_table, &frame_metadata->hash_elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct frame_owner *frame_owner = malloc (sizeof (struct frame_owner));
|
|
||||||
if (frame_owner == NULL)
|
|
||||||
PANIC ("Couldn't allocate memory for frame owner!\n");
|
|
||||||
frame_owner->owner = owner;
|
|
||||||
list_init (&frame_metadata->owners);
|
|
||||||
list_push_back (&frame_metadata->owners, &frame_owner->elem);
|
|
||||||
frame_metadata->upage = upage;
|
|
||||||
frame_metadata->pinned = false;
|
|
||||||
lock_release (&ftable_lock);
|
|
||||||
return frame_metadata->frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
frame_pin (void *frame)
|
|
||||||
{
|
|
||||||
ASSERT (frame != NULL);
|
|
||||||
lock_acquire (&ftable_lock);
|
|
||||||
struct frame_metadata *frame_metadata = frame_metadata_get (frame);
|
|
||||||
if (frame_metadata == NULL)
|
|
||||||
PANIC ("Attempted to pin a frame at an unallocated kernel address '%p'\n",
|
|
||||||
frame);
|
|
||||||
|
|
||||||
frame_metadata->pinned = true;
|
|
||||||
lock_release (&ftable_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
frame_unpin (void *frame)
|
|
||||||
{
|
|
||||||
ASSERT (frame != NULL);
|
|
||||||
lock_acquire (&ftable_lock);
|
|
||||||
struct frame_metadata *frame_metadata = frame_metadata_get (frame);
|
|
||||||
if (frame_metadata == NULL)
|
|
||||||
PANIC ("Attempted to unpin a frame at an unallocated kernel address '%p'\n",
|
|
||||||
frame);
|
|
||||||
|
|
||||||
frame_metadata->pinned = false;
|
|
||||||
lock_release (&ftable_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attempt to deallocate a frame for a user process by removing it from the
|
|
||||||
frame table as well as lru_list, and freeing the underlying page
|
|
||||||
memory & metadata struct. Panics if the frame isn't active in memory. */
|
|
||||||
void
|
|
||||||
frame_free (void *frame)
|
|
||||||
{
|
|
||||||
struct frame_metadata *frame_metadata = frame_metadata_find (frame);
|
|
||||||
if (frame_metadata == NULL)
|
|
||||||
PANIC ("Attempted to free a frame at kernel address %p, "
|
|
||||||
"but this address is not allocated!\n",
|
|
||||||
frame);
|
|
||||||
|
|
||||||
free_owners (&frame_metadata->owners);
|
|
||||||
lock_acquire (&ftable_lock);
|
|
||||||
hash_delete (&frame_table, &frame_metadata->hash_elem);
|
|
||||||
list_remove (&frame_metadata->list_elem);
|
|
||||||
|
|
||||||
/* If we're freeing the frame marked as the next victim, update
|
|
||||||
next_victim to either be the next least recently used page, or NULL
|
|
||||||
if no pages are loaded in main memory. */
|
|
||||||
if (&frame_metadata->list_elem == next_victim)
|
|
||||||
{
|
|
||||||
if (list_empty (&lru_list))
|
|
||||||
next_victim = NULL;
|
|
||||||
else
|
|
||||||
next_victim = lru_next (next_victim);
|
|
||||||
}
|
|
||||||
lock_release (&ftable_lock);
|
|
||||||
|
|
||||||
free (frame_metadata);
|
|
||||||
palloc_free_page (frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a thread to a frame's frame_metadata owners list. */
|
|
||||||
bool
|
|
||||||
frame_owner_insert (void *frame, struct thread *owner)
|
|
||||||
{
|
|
||||||
struct frame_metadata *frame_metadata = frame_metadata_find (frame);
|
|
||||||
if (frame_metadata == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
struct frame_owner *frame_owner = malloc (sizeof (struct frame_owner));
|
|
||||||
if (frame_owner == NULL)
|
|
||||||
return false;
|
|
||||||
frame_owner->owner = owner;
|
|
||||||
list_push_back (&frame_metadata->owners, &frame_owner->elem);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove and deallocate a frame owner from the frame_metadata owners list.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
frame_owner_remove (void *frame, struct thread *owner)
|
|
||||||
{
|
|
||||||
struct frame_metadata *frame_metadata = frame_metadata_find (frame);
|
|
||||||
if (frame_metadata == NULL)
|
|
||||||
PANIC ("Attempted to remove an owner from a frame at kernel "
|
|
||||||
"address %p, but this address is not allocated!\n",
|
|
||||||
frame);
|
|
||||||
|
|
||||||
struct list_elem *oe;
|
|
||||||
for (oe = list_begin (&frame_metadata->owners);
|
|
||||||
oe != list_end (&frame_metadata->owners);)
|
|
||||||
{
|
|
||||||
struct frame_owner *frame_owner
|
|
||||||
= list_entry (oe, struct frame_owner, elem);
|
|
||||||
oe = list_next (oe);
|
|
||||||
if (frame_owner->owner == owner)
|
|
||||||
{
|
|
||||||
list_remove (&frame_owner->elem);
|
|
||||||
free (frame_owner);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NOT_REACHED ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find a frame_metadata entry in the frame table. */
|
|
||||||
static struct frame_metadata *
|
|
||||||
frame_metadata_find (void *frame)
|
|
||||||
{
|
|
||||||
struct frame_metadata key_metadata;
|
|
||||||
key_metadata.frame = frame;
|
|
||||||
|
|
||||||
struct hash_elem *e = hash_find (&frame_table, &key_metadata.hash_elem);
|
|
||||||
if (e == NULL)
|
|
||||||
return NULL;
|
|
||||||
return hash_entry (e, struct frame_metadata, hash_elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A pre-condition for calling this function is that the calling thread
|
|
||||||
owns ftable_lock and that lru_list is non-empty. */
|
|
||||||
static struct frame_metadata *
|
|
||||||
get_victim (void)
|
|
||||||
{
|
|
||||||
struct list_elem *ve = next_victim;
|
|
||||||
struct frame_metadata *frame_metadata;
|
|
||||||
bool found = false;
|
|
||||||
while (!found)
|
|
||||||
{
|
|
||||||
frame_metadata = list_entry (ve, struct frame_metadata, list_elem);
|
|
||||||
ve = lru_next (ve);
|
|
||||||
struct list_elem *oe;
|
|
||||||
|
|
||||||
/* Skip pinned frames */
|
|
||||||
if (frame_metadata->pinned)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Returns once a frame that was not accessed by any owner is found. */
|
|
||||||
found = true;
|
|
||||||
for (oe = list_begin (&frame_metadata->owners);
|
|
||||||
oe != list_end (&frame_metadata->owners); oe = list_next (oe))
|
|
||||||
{
|
|
||||||
struct frame_owner *frame_owner
|
|
||||||
= list_entry (oe, struct frame_owner, elem);
|
|
||||||
uint32_t *pd = frame_owner->owner->pagedir;
|
|
||||||
void *upage = frame_metadata->upage;
|
|
||||||
|
|
||||||
if (pagedir_is_accessed (pd, upage))
|
|
||||||
{
|
|
||||||
found = false;
|
|
||||||
pagedir_set_accessed (pd, upage, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next_victim = ve;
|
|
||||||
return frame_metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
free_owners (struct list *owners)
|
|
||||||
{
|
|
||||||
struct list_elem *oe;
|
|
||||||
for (oe = list_begin (owners); oe != list_end (owners);)
|
|
||||||
{
|
|
||||||
struct frame_owner *frame_owner
|
|
||||||
= list_entry (oe, struct frame_owner, elem);
|
|
||||||
oe = list_remove (oe);
|
|
||||||
free (frame_owner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hash function for frame metadata, used for storing entries in the
|
|
||||||
frame table. */
|
|
||||||
unsigned
|
|
||||||
frame_metadata_hash (const struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct frame_metadata *frame_metadata =
|
|
||||||
hash_entry (e, struct frame_metadata, hash_elem);
|
|
||||||
|
|
||||||
return hash_bytes (&frame_metadata->frame, sizeof (frame_metadata->frame));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 'less_func' comparison function for frame metadata, used for comparing
|
|
||||||
the keys of the frame table. Returns true iff the kernel virtual address
|
|
||||||
of the first frame is less than that of the second frame. */
|
|
||||||
bool
|
|
||||||
frame_metadata_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|
||||||
void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct frame_metadata *a =
|
|
||||||
hash_entry (a_, struct frame_metadata, hash_elem);
|
|
||||||
struct frame_metadata *b =
|
|
||||||
hash_entry (b_, struct frame_metadata, hash_elem);
|
|
||||||
|
|
||||||
return a->frame < b->frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct frame_metadata *
|
|
||||||
frame_metadata_get (void *frame)
|
|
||||||
{
|
|
||||||
struct frame_metadata key_metadata;
|
|
||||||
key_metadata.frame = frame;
|
|
||||||
|
|
||||||
struct hash_elem *e = hash_find (&frame_table, &key_metadata.hash_elem);
|
|
||||||
if (e == NULL) return NULL;
|
|
||||||
return hash_entry (e, struct frame_metadata, hash_elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the next recently used element after the one provided, which
|
|
||||||
is achieved by iterating through lru_list like a circular queue
|
|
||||||
(wrapping around the list at the tail). */
|
|
||||||
static struct list_elem *
|
|
||||||
lru_next (struct list_elem *e)
|
|
||||||
{
|
|
||||||
if (!list_empty (&lru_list) && e == list_back (&lru_list))
|
|
||||||
return list_front (&lru_list);
|
|
||||||
|
|
||||||
return list_next (e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the previous recently used element after the one provided, which
|
|
||||||
is achieved by iterating through lru_list like a circular queue
|
|
||||||
(wrapping around the list at the head). */
|
|
||||||
static struct list_elem *
|
|
||||||
lru_prev (struct list_elem *e)
|
|
||||||
{
|
|
||||||
if (!list_empty (&lru_list) && e == list_front (&lru_list))
|
|
||||||
return list_back (&lru_list);
|
|
||||||
|
|
||||||
return list_prev (e);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#ifndef VM_FRAME_H
|
|
||||||
#define VM_FRAME_H
|
|
||||||
|
|
||||||
#include "threads/thread.h"
|
|
||||||
#include "threads/palloc.h"
|
|
||||||
|
|
||||||
struct frame_owner
|
|
||||||
{
|
|
||||||
struct thread *owner; /* The thread that owns the frame. */
|
|
||||||
struct list_elem elem; /* List element for the list of owners. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Synchronisation variables. */
|
|
||||||
/* Protects access to the frame table and its related components. */
|
|
||||||
struct lock ftable_lock;
|
|
||||||
|
|
||||||
void frame_init (void);
|
|
||||||
|
|
||||||
void *frame_alloc (enum palloc_flags, void *, struct thread *);
|
|
||||||
void frame_pin (void *frame);
|
|
||||||
void frame_unpin (void *frame);
|
|
||||||
void frame_free (void *frame);
|
|
||||||
|
|
||||||
bool frame_owner_insert (void *frame, struct thread *owner);
|
|
||||||
void frame_owner_remove (void *frame, struct thread *owner);
|
|
||||||
|
|
||||||
#endif /* vm/frame.h */
|
|
||||||
129
src/vm/mmap.c
129
src/vm/mmap.c
@@ -1,129 +0,0 @@
|
|||||||
#include "mmap.h"
|
|
||||||
#include "page.h"
|
|
||||||
#include "threads/thread.h"
|
|
||||||
#include "threads/vaddr.h"
|
|
||||||
#include "threads/malloc.h"
|
|
||||||
#include "userprog/syscall.h"
|
|
||||||
#include "userprog/pagedir.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
static unsigned mmap_hash (const struct hash_elem *e, void *aux);
|
|
||||||
static bool mmap_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|
||||||
void *aux);
|
|
||||||
static void mmap_cleanup(struct hash_elem *e, void *aux);
|
|
||||||
|
|
||||||
/* Initializes the mmap table for the given thread, setting the mmap counter to
|
|
||||||
0 and initializing the hash table. */
|
|
||||||
bool
|
|
||||||
mmap_init (struct thread *t)
|
|
||||||
{
|
|
||||||
t->mmap_counter = 0;
|
|
||||||
return hash_init (&t->mmap_files, mmap_hash, mmap_less, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mmap_entry *
|
|
||||||
mmap_get (mapid_t mapping)
|
|
||||||
{
|
|
||||||
struct mmap_entry fake_mmap_entry;
|
|
||||||
fake_mmap_entry.mapping = mapping;
|
|
||||||
|
|
||||||
struct hash_elem *e
|
|
||||||
= hash_find (&thread_current ()->mmap_files, &fake_mmap_entry.elem);
|
|
||||||
|
|
||||||
if (e == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return hash_entry (e, struct mmap_entry, elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inserts a new mmap entry into the mmap table for the current thread. Upage
|
|
||||||
is the start address of the file data in the user VM. */
|
|
||||||
struct mmap_entry *
|
|
||||||
mmap_insert (struct file *file, void *upage)
|
|
||||||
{
|
|
||||||
if (file == NULL || upage == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
struct mmap_entry *mmap = malloc (sizeof (struct mmap_entry));
|
|
||||||
if (mmap == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
mmap->mapping = thread_current ()->mmap_counter++;
|
|
||||||
mmap->file = file;
|
|
||||||
mmap->upage = upage;
|
|
||||||
|
|
||||||
hash_insert (&thread_current ()->mmap_files, &mmap->elem);
|
|
||||||
return mmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unmaps the given mmap entry from the current thread's mmap table. */
|
|
||||||
void
|
|
||||||
mmap_unmap (struct mmap_entry *mmap)
|
|
||||||
{
|
|
||||||
if (mmap == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Free all the pages associated with the mapping, writing back to the file
|
|
||||||
if necessary. */
|
|
||||||
off_t length = file_length (mmap->file);
|
|
||||||
for (off_t ofs = 0; ofs < length; ofs += PGSIZE)
|
|
||||||
{
|
|
||||||
void *upage = mmap->upage + ofs;
|
|
||||||
|
|
||||||
/* Get the SPT page entry for this page. */
|
|
||||||
struct page_entry *page = page_get(thread_current (), upage);
|
|
||||||
if (page == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Write the page back to the file if it is dirty. */
|
|
||||||
if (pagedir_is_dirty (thread_current ()->pagedir, upage))
|
|
||||||
{
|
|
||||||
lock_acquire (&filesys_lock);
|
|
||||||
file_write_at (mmap->file, upage, page->read_bytes, ofs);
|
|
||||||
lock_release (&filesys_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the page from the supplemental page table. */
|
|
||||||
hash_delete (&thread_current ()->pages, &page->elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
file_close (mmap->file);
|
|
||||||
free (mmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Destroys the mmap table for the current thread. Frees all the memory
|
|
||||||
allocated for the mmap entries. */
|
|
||||||
void
|
|
||||||
mmap_destroy (void)
|
|
||||||
{
|
|
||||||
hash_destroy (&thread_current ()->mmap_files, mmap_cleanup);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A hash function for the mmap table. Returns a hash for an entry, based on its
|
|
||||||
mapping. */
|
|
||||||
static unsigned
|
|
||||||
mmap_hash (const struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
return hash_entry (e, struct mmap_entry, elem)->mapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A comparator function for the mmap table. Compares two entries based on their
|
|
||||||
mappings. */
|
|
||||||
static bool
|
|
||||||
mmap_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|
||||||
void *aux UNUSED)
|
|
||||||
{
|
|
||||||
const struct mmap_entry *a = hash_entry (a_, struct mmap_entry, elem);
|
|
||||||
const struct mmap_entry *b = hash_entry (b_, struct mmap_entry, elem);
|
|
||||||
|
|
||||||
return a->mapping < b->mapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cleans up the mmap table for the current thread. Implicitly unmaps the mmap
|
|
||||||
entry, freeing pages and writing back to the file if necessary. */
|
|
||||||
static void
|
|
||||||
mmap_cleanup (struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct mmap_entry *mmap = hash_entry (e, struct mmap_entry, elem);
|
|
||||||
mmap_unmap (mmap);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#ifndef VM_MMAP_H
|
|
||||||
#define VM_MMAP_H
|
|
||||||
|
|
||||||
#include <hash.h>
|
|
||||||
#include "threads/thread.h"
|
|
||||||
#include "filesys/file.h"
|
|
||||||
|
|
||||||
/* A mapping identifier type. */
|
|
||||||
typedef unsigned mapid_t;
|
|
||||||
|
|
||||||
/* A structure to represent a memory mapped file. */
|
|
||||||
struct mmap_entry {
|
|
||||||
mapid_t mapping; /* The mapping identifier of the mapped file. */
|
|
||||||
struct file *file; /* A pointer to the file that is being mapped. */
|
|
||||||
void *upage; /* The start address of the file data in the user VM. */
|
|
||||||
|
|
||||||
struct hash_elem elem; /* An elem for the hash table. */
|
|
||||||
};
|
|
||||||
|
|
||||||
bool mmap_init (struct thread *t);
|
|
||||||
struct mmap_entry *mmap_get (mapid_t mapping);
|
|
||||||
struct mmap_entry *mmap_insert (struct file *file, void *upage);
|
|
||||||
void mmap_unmap (struct mmap_entry *mmap);
|
|
||||||
void mmap_umap_all (void);
|
|
||||||
void mmap_destroy (void);
|
|
||||||
|
|
||||||
#endif /* vm/mmap.h */
|
|
||||||
446
src/vm/page.c
446
src/vm/page.c
@@ -1,446 +0,0 @@
|
|||||||
#include "page.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "filesys/file.h"
|
|
||||||
#include "threads/pte.h"
|
|
||||||
#include "threads/malloc.h"
|
|
||||||
#include "threads/palloc.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
#include "devices/swap.h"
|
|
||||||
#include "userprog/process.h"
|
|
||||||
#include "userprog/pagedir.h"
|
|
||||||
#include "vm/frame.h"
|
|
||||||
|
|
||||||
#define SWAP_FLAG_BIT 9
|
|
||||||
#define SHARED_FLAG_BIT 10
|
|
||||||
#define ADDR_START_BIT 12
|
|
||||||
|
|
||||||
struct hash shared_file_pages;
|
|
||||||
struct lock shared_file_pages_lock;
|
|
||||||
|
|
||||||
static unsigned page_hash (const struct hash_elem *e, void *aux UNUSED);
|
|
||||||
static bool page_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|
||||||
void *aux UNUSED);
|
|
||||||
|
|
||||||
static void page_flag_shared (struct thread *owner, void *upage, bool shared);
|
|
||||||
static unsigned shared_file_page_hash (const struct hash_elem *e,
|
|
||||||
void *aux UNUSED);
|
|
||||||
static bool shared_file_page_less (const struct hash_elem *a_,
|
|
||||||
const struct hash_elem *b_,
|
|
||||||
void *aux UNUSED);
|
|
||||||
static struct shared_file_page *shared_file_page_get (struct file *file,
|
|
||||||
void *upage);
|
|
||||||
|
|
||||||
/* Initialise a supplementary page table. */
|
|
||||||
bool
|
|
||||||
init_pages (struct hash *pages)
|
|
||||||
{
|
|
||||||
ASSERT (pages != NULL);
|
|
||||||
return hash_init (pages, page_hash, page_less, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hashing function needed for the SPT table. Returns a hash for an entry,
|
|
||||||
based on its upage. */
|
|
||||||
static unsigned
|
|
||||||
page_hash (const struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct page_entry *page = hash_entry (e, struct page_entry, elem);
|
|
||||||
return hash_ptr (page->upage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Comparator function for the SPT table. Compares two entries based on their
|
|
||||||
upages. */
|
|
||||||
static bool
|
|
||||||
page_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|
||||||
void *aux UNUSED)
|
|
||||||
{
|
|
||||||
const struct page_entry *a = hash_entry (a_, struct page_entry, elem);
|
|
||||||
const struct page_entry *b = hash_entry (b_, struct page_entry, elem);
|
|
||||||
|
|
||||||
return a->upage < b->upage;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void page_flag_swap (uint32_t *pte, bool set);
|
|
||||||
static void page_set_swap (struct thread *owner, uint32_t *pte,
|
|
||||||
size_t swap_slot);
|
|
||||||
|
|
||||||
// TODO: Deal with NULL malloc returns
|
|
||||||
/* Swap out 'owner' process's 'upage' stored at 'kpage'. Then, allocate and
|
|
||||||
insert a new page entry into the user process thread's SPT representing
|
|
||||||
this swapped out page. */
|
|
||||||
bool
|
|
||||||
page_insert_swapped (void *upage, void *kpage, struct list *owners)
|
|
||||||
{
|
|
||||||
struct file *exec_file = NULL;
|
|
||||||
struct list_elem *e;
|
|
||||||
for (e = list_begin (owners); e != list_end (owners); e = list_next (e))
|
|
||||||
{
|
|
||||||
struct thread *owner = list_entry (e, struct frame_owner, elem)->owner;
|
|
||||||
uint32_t *pte = lookup_page (owner->pagedir, upage, false);
|
|
||||||
if (exec_file != NULL || page_is_shared_pte (pte))
|
|
||||||
{
|
|
||||||
ASSERT (page_is_shared_pte (pte));
|
|
||||||
pagedir_clear_page (owner->pagedir, upage);
|
|
||||||
exec_file = owner->exec_file;
|
|
||||||
ASSERT (exec_file != NULL);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ASSERT (list_size (owners) == 1);
|
|
||||||
|
|
||||||
/* 1. Initialize swapped page entry. */
|
|
||||||
struct page_entry *page = page_get (owner, upage);
|
|
||||||
if (page == NULL)
|
|
||||||
{
|
|
||||||
page = malloc (sizeof (struct page_entry));
|
|
||||||
if (page == NULL)
|
|
||||||
return NULL;
|
|
||||||
page->upage = upage;
|
|
||||||
lock_init (&page->lock);
|
|
||||||
hash_insert (&owner->pages, &page->elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark page as 'swapped' and flag the page directory as having
|
|
||||||
been modified *before* eviction begins to prevent the owner of the
|
|
||||||
victim page from accessing/modifying it mid-eviction. */
|
|
||||||
/* TODO: We need to stop the process from destroying pagedir mid-eviction,
|
|
||||||
as this could render the page table entry invalid. */
|
|
||||||
page_flag_swap (pte, true);
|
|
||||||
lock_acquire (&page->lock);
|
|
||||||
pagedir_clear_page (owner->pagedir, upage);
|
|
||||||
|
|
||||||
size_t swap_slot = swap_out (kpage);
|
|
||||||
page_set_swap (owner, pte, swap_slot);
|
|
||||||
|
|
||||||
lock_release (&page->lock);
|
|
||||||
}
|
|
||||||
if (exec_file != NULL)
|
|
||||||
{
|
|
||||||
lock_acquire (&shared_file_pages_lock);
|
|
||||||
struct shared_file_page *sfp = shared_file_page_get (exec_file, upage);
|
|
||||||
sfp->frame = NULL;
|
|
||||||
sfp->swap_slot = swap_out (kpage);
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate and insert a new page entry into the user process thread's
|
|
||||||
SPT representing a file page. */
|
|
||||||
struct page_entry *
|
|
||||||
page_insert_file (struct file *file, off_t ofs, void *upage,
|
|
||||||
uint32_t read_bytes, uint32_t zero_bytes, bool writable,
|
|
||||||
enum page_type type)
|
|
||||||
{
|
|
||||||
/* If page exists, just update it. */
|
|
||||||
struct page_entry *existing = page_get (thread_current (), upage);
|
|
||||||
if (existing != NULL)
|
|
||||||
{
|
|
||||||
ASSERT (existing->read_bytes == read_bytes);
|
|
||||||
ASSERT (existing->zero_bytes == zero_bytes);
|
|
||||||
existing->writable = existing->writable || writable;
|
|
||||||
return existing;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct page_entry *page = malloc(sizeof (struct page_entry));
|
|
||||||
if (page == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
page->type = type;
|
|
||||||
page->file = file;
|
|
||||||
page->offset = ofs;
|
|
||||||
page->upage = upage;
|
|
||||||
page->read_bytes = read_bytes;
|
|
||||||
page->zero_bytes = zero_bytes;
|
|
||||||
page->writable = writable;
|
|
||||||
lock_init (&page->lock);
|
|
||||||
|
|
||||||
hash_insert (&thread_current ()->pages, &page->elem);
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gets a page_entry from the starting address of the page. Returns NULL if no
|
|
||||||
such page_entry exists in the hash map.*/
|
|
||||||
struct page_entry *
|
|
||||||
page_get (struct thread *thread, void *upage)
|
|
||||||
{
|
|
||||||
struct page_entry fake_page_entry;
|
|
||||||
fake_page_entry.upage = upage;
|
|
||||||
|
|
||||||
struct hash_elem *e
|
|
||||||
= hash_find (&thread->pages, &fake_page_entry.elem);
|
|
||||||
if (e == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return hash_entry (e, struct page_entry, elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
page_load_file (struct page_entry *page)
|
|
||||||
{
|
|
||||||
/* Allocate a frame for the page. If a frame allocation fails, then
|
|
||||||
frame_alloc should try to evict a page. If it is still NULL, the OS
|
|
||||||
panics as this should not happen if eviction is working correctly. */
|
|
||||||
struct thread *t = thread_current ();
|
|
||||||
bool shareable = !page->writable && file_compare (page->file, t->exec_file);
|
|
||||||
if (shareable)
|
|
||||||
{
|
|
||||||
lock_acquire (&shared_file_pages_lock);
|
|
||||||
struct shared_file_page *sfp
|
|
||||||
= shared_file_page_get (page->file, page->upage);
|
|
||||||
if (sfp != NULL)
|
|
||||||
{
|
|
||||||
/* Frame exists, just install it. */
|
|
||||||
if (sfp->frame != NULL)
|
|
||||||
{
|
|
||||||
if (!install_page (page->upage, sfp->frame, page->writable))
|
|
||||||
{
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
frame_owner_insert (sfp->frame, t);
|
|
||||||
}
|
|
||||||
/* Shared page is in swap. Load it. */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
void *frame = frame_alloc (PAL_USER, page->upage, t);
|
|
||||||
if (frame == NULL)
|
|
||||||
PANIC (
|
|
||||||
"Could not allocate a frame to load page into memory.");
|
|
||||||
swap_in (frame, sfp->swap_slot);
|
|
||||||
|
|
||||||
if (!install_page (page->upage, frame, false))
|
|
||||||
{
|
|
||||||
frame_free (frame);
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
page_flag_shared (t, page->upage, true);
|
|
||||||
if (page->type != PAGE_SHARED)
|
|
||||||
{
|
|
||||||
sfp->ref_count++;
|
|
||||||
page->type = PAGE_SHARED;
|
|
||||||
}
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *frame = frame_alloc (PAL_USER, page->upage, t);
|
|
||||||
pagedir_set_accessed (t->pagedir, page->upage, true);
|
|
||||||
if (frame == NULL)
|
|
||||||
PANIC ("Could not allocate a frame to load page into memory.");
|
|
||||||
|
|
||||||
/* Map the page to the frame. */
|
|
||||||
if (!install_page (page->upage, frame, page->writable))
|
|
||||||
{
|
|
||||||
if (shareable)
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
frame_free (frame);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Move the file pointer to the correct location in the file. Then, read the
|
|
||||||
data from the file into the frame. Checks that we were able to read the
|
|
||||||
expected number of bytes. */
|
|
||||||
file_seek (page->file, page->offset);
|
|
||||||
if (file_read (page->file, frame, page->read_bytes) != (int) page->read_bytes)
|
|
||||||
{
|
|
||||||
if (shareable)
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
frame_free (frame);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Zero out the remaining bytes in the frame. */
|
|
||||||
memset (frame + page->read_bytes, 0, page->zero_bytes);
|
|
||||||
|
|
||||||
/* If file page is read-only, make it shared. */
|
|
||||||
if (shareable)
|
|
||||||
{
|
|
||||||
struct shared_file_page *sfp = malloc (sizeof (struct shared_file_page));
|
|
||||||
if (sfp == NULL)
|
|
||||||
{
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
frame_free (frame);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sfp->file = page->file;
|
|
||||||
sfp->upage = page->upage;
|
|
||||||
sfp->frame = frame;
|
|
||||||
sfp->swap_slot = 0;
|
|
||||||
sfp->ref_count = 1;
|
|
||||||
hash_insert (&shared_file_pages, &sfp->elem);
|
|
||||||
page_flag_shared (t, page->upage, true);
|
|
||||||
page->type = PAGE_SHARED;
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark the page as loaded successfully. */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Function to clean up a page_entry. Given the elem of that page_entry, frees
|
|
||||||
the page_entry itself. */
|
|
||||||
void
|
|
||||||
page_cleanup (struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct page_entry *page = hash_entry (e, struct page_entry, elem);
|
|
||||||
if (page->type == PAGE_SHARED)
|
|
||||||
{
|
|
||||||
lock_acquire (&shared_file_pages_lock);
|
|
||||||
struct shared_file_page *sfp
|
|
||||||
= shared_file_page_get (page->file, page->upage);
|
|
||||||
ASSERT (sfp != NULL);
|
|
||||||
if (sfp->frame != NULL)
|
|
||||||
frame_owner_remove (sfp->frame, thread_current ());
|
|
||||||
sfp->ref_count--;
|
|
||||||
if (sfp->ref_count == 0)
|
|
||||||
{
|
|
||||||
hash_delete (&shared_file_pages, &sfp->elem);
|
|
||||||
if (sfp->frame != NULL)
|
|
||||||
frame_free (sfp->frame);
|
|
||||||
else
|
|
||||||
swap_drop (sfp->swap_slot);
|
|
||||||
free (sfp);
|
|
||||||
}
|
|
||||||
lock_release (&shared_file_pages_lock);
|
|
||||||
}
|
|
||||||
free (page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flags the provided page table entry as representing a swapped out page. */
|
|
||||||
void
|
|
||||||
page_flag_swap (uint32_t *pte, bool set)
|
|
||||||
{
|
|
||||||
if (set)
|
|
||||||
*pte |= (1 << SWAP_FLAG_BIT);
|
|
||||||
else
|
|
||||||
*pte &= ~(1 << SWAP_FLAG_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sets the address bits of the page table entry to the provided swap slot
|
|
||||||
value. To be used for later retrieval of the swap slot when page faulting. */
|
|
||||||
static void
|
|
||||||
page_set_swap (struct thread *owner, uint32_t *pte, size_t swap_slot)
|
|
||||||
{
|
|
||||||
/* Store the provided swap slot in the address bits of the page table
|
|
||||||
entry, truncating excess bits. */
|
|
||||||
*pte |= (1 << SWAP_FLAG_BIT);
|
|
||||||
uint32_t swap_slot_bits = (swap_slot << ADDR_START_BIT) & PTE_ADDR;
|
|
||||||
*pte = (*pte & PTE_FLAGS) | swap_slot_bits;
|
|
||||||
|
|
||||||
invalidate_pagedir (owner->pagedir);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true iff the page with user address 'upage' owned by 'owner'
|
|
||||||
is flagged to be in the swap disk via the owner's page table. */
|
|
||||||
bool
|
|
||||||
page_in_swap (struct thread *owner, void *upage)
|
|
||||||
{
|
|
||||||
uint32_t *pte = lookup_page (owner->pagedir, upage, false);
|
|
||||||
return page_in_swap_pte (pte);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true iff the page table entry is marked to be in the swap disk. */
|
|
||||||
bool
|
|
||||||
page_in_swap_pte (uint32_t *pte)
|
|
||||||
{
|
|
||||||
return pte != NULL && (*pte & (1 << SWAP_FLAG_BIT)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Given that the page with user address 'upage' owned by 'owner' is flagged
|
|
||||||
to be in the swap disk via the owner's page table, returns its stored
|
|
||||||
swap slot and marks the PTE as not being in swap. */
|
|
||||||
size_t
|
|
||||||
page_get_swap (struct thread *owner, void *upage)
|
|
||||||
{
|
|
||||||
uint32_t *pte = lookup_page (owner->pagedir, upage, false);
|
|
||||||
|
|
||||||
ASSERT (pte != NULL);
|
|
||||||
ASSERT ((*pte & PTE_P) == 0);
|
|
||||||
|
|
||||||
/* Masks the address bits and returns truncated value. */
|
|
||||||
page_flag_swap (pte, false);
|
|
||||||
return ((*pte & PTE_ADDR) >> ADDR_START_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the swap slot stored in a PTE. */
|
|
||||||
size_t
|
|
||||||
page_get_swap_pte (uint32_t *pte)
|
|
||||||
{
|
|
||||||
ASSERT (pte != NULL);
|
|
||||||
ASSERT ((*pte & PTE_P) == 0);
|
|
||||||
return ((*pte & PTE_ADDR) >> ADDR_START_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flags the provided page table entry as representing a shared page. */
|
|
||||||
static void
|
|
||||||
page_flag_shared (struct thread *owner, void *upage, bool shared)
|
|
||||||
{
|
|
||||||
uint32_t *pte = lookup_page (owner->pagedir, upage, false);
|
|
||||||
ASSERT (pte != NULL);
|
|
||||||
|
|
||||||
if (shared)
|
|
||||||
*pte |= (1 << SHARED_FLAG_BIT);
|
|
||||||
else
|
|
||||||
*pte &= ~(1 << SHARED_FLAG_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true iff the page table entry is marked to be shared. */
|
|
||||||
bool
|
|
||||||
page_is_shared_pte (uint32_t *pte)
|
|
||||||
{
|
|
||||||
return pte != NULL && (*pte & (1 << SHARED_FLAG_BIT)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initializes the shared file pages hash table. */
|
|
||||||
void
|
|
||||||
shared_file_pages_init ()
|
|
||||||
{
|
|
||||||
if (!hash_init (&shared_file_pages, shared_file_page_hash,
|
|
||||||
shared_file_page_less, NULL))
|
|
||||||
PANIC ("Failed to initialize shared file pages hash table.");
|
|
||||||
lock_init (&shared_file_pages_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hash function for shared file pages, used for storing entries in the
|
|
||||||
shared file pages table. */
|
|
||||||
static unsigned
|
|
||||||
shared_file_page_hash (const struct hash_elem *e, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct shared_file_page *sfp = hash_entry (e, struct shared_file_page, elem);
|
|
||||||
void *inode = file_get_inode (sfp->file);
|
|
||||||
void *upage = sfp->upage;
|
|
||||||
void *bytes[2] = { inode, upage };
|
|
||||||
return hash_bytes (bytes, sizeof (bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 'less_func' comparison function for shared file pages, used for comparing
|
|
||||||
the keys of the shared file pages table. */
|
|
||||||
static bool
|
|
||||||
shared_file_page_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|
||||||
void *aux UNUSED)
|
|
||||||
{
|
|
||||||
const struct shared_file_page *a
|
|
||||||
= hash_entry (a_, struct shared_file_page, elem);
|
|
||||||
const struct shared_file_page *b
|
|
||||||
= hash_entry (b_, struct shared_file_page, elem);
|
|
||||||
|
|
||||||
return !file_compare (a->file, b->file) || a->upage < b->upage;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct shared_file_page *
|
|
||||||
shared_file_page_get (struct file *file, void *upage)
|
|
||||||
{
|
|
||||||
struct shared_file_page fake_sfp;
|
|
||||||
fake_sfp.file = file;
|
|
||||||
fake_sfp.upage = upage;
|
|
||||||
|
|
||||||
struct hash_elem *e = hash_find (&shared_file_pages, &fake_sfp.elem);
|
|
||||||
if (e == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return hash_entry (e, struct shared_file_page, elem);
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
#ifndef VM_PAGE_H
|
|
||||||
#define VM_PAGE_H
|
|
||||||
|
|
||||||
#include "threads/thread.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
#include "filesys/off_t.h"
|
|
||||||
|
|
||||||
enum page_type
|
|
||||||
{
|
|
||||||
PAGE_EXECUTABLE,
|
|
||||||
PAGE_MMAP,
|
|
||||||
PAGE_SHARED
|
|
||||||
};
|
|
||||||
|
|
||||||
struct page_entry
|
|
||||||
{
|
|
||||||
enum page_type type; /* Type of Data that should go into the page */
|
|
||||||
void *upage; /* Start Address of the User Page (Key of hash table). */
|
|
||||||
|
|
||||||
/* Data for swapped pages */
|
|
||||||
struct lock lock; /* Enforces mutual exclusion in accessing the page
|
|
||||||
referenced by the entry between its owning process
|
|
||||||
and any thread performing page eviction. */
|
|
||||||
|
|
||||||
/* File Data */
|
|
||||||
struct file *file; /* Pointer to the file for executables. */
|
|
||||||
off_t offset; /* Offset of the page content within the file. */
|
|
||||||
uint32_t read_bytes; /* Number of bytes to read within the page. */
|
|
||||||
uint32_t zero_bytes; /* Number of bytes to zero within the page. */
|
|
||||||
bool writable; /* Flag for whether this page is writable or not. */
|
|
||||||
|
|
||||||
struct hash_elem elem; /* An elem for the hash table. */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct shared_file_page
|
|
||||||
{
|
|
||||||
struct file *file; /* The shared file page's source file, used for indexing
|
|
||||||
the table. */
|
|
||||||
void *upage; /* The shared page's upage which is the same across all process
|
|
||||||
using it. Used for indexing the table. */
|
|
||||||
void *frame; /* Set to the frame address of the page when it is in memory.
|
|
||||||
Set to NULL when the page is in swap. */
|
|
||||||
size_t swap_slot; /* Set to the swap_slot of the shared paged if it is
|
|
||||||
currently in swap. Should not be used when frame is not
|
|
||||||
NULL.*/
|
|
||||||
int ref_count; /* Number of processes that are using this shared page. */
|
|
||||||
|
|
||||||
struct hash_elem elem; /* AN elem for the hash table. */
|
|
||||||
};
|
|
||||||
|
|
||||||
bool init_pages (struct hash *pages);
|
|
||||||
bool page_insert_swapped (void *upage, void *kpage, struct list *owners);
|
|
||||||
struct page_entry *page_insert_file (struct file *file, off_t ofs, void *upage,
|
|
||||||
uint32_t read_bytes, uint32_t zero_bytes,
|
|
||||||
bool writable, enum page_type);
|
|
||||||
struct page_entry *page_get (struct thread *thread, void *upage);
|
|
||||||
bool page_load_file (struct page_entry *page);
|
|
||||||
void page_cleanup (struct hash_elem *e, void *aux);
|
|
||||||
|
|
||||||
bool page_in_swap (struct thread *, void *);
|
|
||||||
bool page_in_swap_pte (uint32_t *pte);
|
|
||||||
size_t page_get_swap (struct thread *owner, void *upage);
|
|
||||||
size_t page_get_swap_pte (uint32_t *pte);
|
|
||||||
|
|
||||||
bool page_is_shared_pte (uint32_t *pte);
|
|
||||||
void shared_file_pages_init (void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
4
tests/devices/src/.gitignore
vendored
4
tests/devices/src/.gitignore
vendored
@@ -1,4 +0,0 @@
|
|||||||
cscope.files
|
|
||||||
cscope.out
|
|
||||||
TAGS
|
|
||||||
tags
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
PintOS, including its documentation, is subject to the following
|
|
||||||
license:
|
|
||||||
|
|
||||||
Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford
|
|
||||||
Jr. University. All rights reserved.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
A few individual files in PintOS were originally derived from other
|
|
||||||
projects, but they have been extensively modified for use in PintOS.
|
|
||||||
The original code falls under the original license, and modifications
|
|
||||||
for PintOS are additionally covered by the PintOS license above.
|
|
||||||
|
|
||||||
In particular, code derived from Nachos is subject to the following
|
|
||||||
license:
|
|
||||||
|
|
||||||
/* Copyright (c) 1992-1996 The Regents of the University of California.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software
|
|
||||||
and its documentation for any purpose, without fee, and
|
|
||||||
without written agreement is hereby granted, provided that the
|
|
||||||
above copyright notice and the following two paragraphs appear
|
|
||||||
in all copies of this software.
|
|
||||||
|
|
||||||
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
|
|
||||||
ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
|
||||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
|
|
||||||
AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
|
|
||||||
HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
|
|
||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
|
|
||||||
BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
|
|
||||||
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
|
||||||
MODIFICATIONS.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Also, code derived from MIT's 6.828 course code is subject to the
|
|
||||||
following license:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 1997 Massachusetts Institute of Technology
|
|
||||||
*
|
|
||||||
* This software is being provided by the copyright holders under the
|
|
||||||
* following license. By obtaining, using and/or copying this software,
|
|
||||||
* you agree that you have read, understood, and will comply with the
|
|
||||||
* following terms and conditions:
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, distribute, and sell this software
|
|
||||||
* and its documentation for any purpose and without fee or royalty is
|
|
||||||
* hereby granted, provided that the full text of this NOTICE appears on
|
|
||||||
* ALL copies of the software and documentation or portions thereof,
|
|
||||||
* including modifications, that you make.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
|
|
||||||
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
|
|
||||||
* BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
|
|
||||||
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
|
||||||
* THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
|
||||||
* THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
|
|
||||||
* HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
|
|
||||||
* DOCUMENTATION.
|
|
||||||
*
|
|
||||||
* The name and trademarks of copyright holders may NOT be used in
|
|
||||||
* advertising or publicity pertaining to the software without specific,
|
|
||||||
* written prior permission. Title to copyright in this software and any
|
|
||||||
* associated documentation will at all times remain with copyright
|
|
||||||
* holders. See the file AUTHORS which should have accompanied this software
|
|
||||||
* for a list of all copyright holders.
|
|
||||||
*
|
|
||||||
* This file may be derived from previously copyrighted software. This
|
|
||||||
* copyright applies only to those changes made by the copyright
|
|
||||||
* holders listed in the AUTHORS file. The rest of this file is covered by
|
|
||||||
* the copyright notices, if any, listed below.
|
|
||||||
*/
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
# -*- makefile -*-
|
|
||||||
|
|
||||||
SHELL = /bin/sh
|
|
||||||
|
|
||||||
VPATH = $(SRCDIR)
|
|
||||||
|
|
||||||
# Binary utilities.
|
|
||||||
# If the host appears to be x86, use the normal tools.
|
|
||||||
# If it's x86-64, use the compiler and linker in 32-bit mode.
|
|
||||||
# Otherwise assume cross-tools are installed as i386-elf-*.
|
|
||||||
X86 = i.86\|pentium.*\|[pk][56]\|nexgen\|viac3\|6x86\|athlon.*\|i86pc
|
|
||||||
X86_64 = x86_64
|
|
||||||
ifneq (0, $(shell expr `uname -m` : '$(X86)'))
|
|
||||||
CC = gcc
|
|
||||||
LD = ld
|
|
||||||
OBJCOPY = objcopy
|
|
||||||
else
|
|
||||||
ifneq (0, $(shell expr `uname -m` : '$(X86_64)'))
|
|
||||||
CC = gcc -m32
|
|
||||||
LD = ld -melf_i386
|
|
||||||
OBJCOPY = objcopy
|
|
||||||
else
|
|
||||||
CC = i386-elf-gcc
|
|
||||||
LD = i386-elf-ld
|
|
||||||
OBJCOPY = i386-elf-objcopy
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
# by default randomizing static libraries can be done using the host compiler
|
|
||||||
RANLIB = ranlib
|
|
||||||
|
|
||||||
# macOS: force compiling with the i686-elf cross compiler suite
|
|
||||||
UNAME_S = $(shell uname -s)
|
|
||||||
ifeq ($(UNAME_S),Darwin)
|
|
||||||
CC = i686-elf-gcc
|
|
||||||
LD = i686-elf-ld
|
|
||||||
OBJCOPY = i686-elf-objcopy
|
|
||||||
RANLIB = i686-elf-ranlib
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(strip $(shell command -v $(CC) 2> /dev/null)),)
|
|
||||||
$(warning *** Compiler ($(CC)) not found. Did you set $$PATH properly? Please refer to the Getting Started section in the documentation for details. ***)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Compiler and assembler invocation.
|
|
||||||
DEFINES =
|
|
||||||
WARNINGS = -Wall -W -Wstrict-prototypes -Wmissing-prototypes -Wsystem-headers -Wno-frame-address
|
|
||||||
CFLAGS = -g -msoft-float -O -fno-omit-frame-pointer -ffreestanding -fno-pic -fcommon -mno-sse
|
|
||||||
CPPFLAGS = -nostdinc -I$(SRCDIR) -I$(SRCDIR)/lib
|
|
||||||
ASFLAGS = -Wa,--gstabs
|
|
||||||
LDFLAGS =
|
|
||||||
DEPS = -MMD -MF $(@:.o=.d)
|
|
||||||
|
|
||||||
# Turn off -fstack-protector, which we don't support.
|
|
||||||
ifeq ($(strip $(shell echo | $(CC) -fno-stack-protector -E - > /dev/null 2>&1; echo $$?)),0)
|
|
||||||
CFLAGS += -fno-stack-protector
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Turn off --build-id in the linker, which confuses the PintOS loader.
|
|
||||||
ifeq ($(strip $(shell $(LD) --help | grep -q build-id; echo $$?)),0)
|
|
||||||
LDFLAGS += -Wl,--build-id=none
|
|
||||||
endif
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
$(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(DEFINES) $(DEPS)
|
|
||||||
|
|
||||||
%.o: %.S
|
|
||||||
$(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) $(DEPS)
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
BUILD_SUBDIRS = devices threads userprog vm filesys
|
|
||||||
|
|
||||||
all::
|
|
||||||
@echo "Run 'make' in subdirectories: $(BUILD_SUBDIRS)."
|
|
||||||
@echo "This top-level make has only 'clean' targets."
|
|
||||||
|
|
||||||
CLEAN_SUBDIRS = $(BUILD_SUBDIRS) examples utils
|
|
||||||
|
|
||||||
clean::
|
|
||||||
for d in $(CLEAN_SUBDIRS); do $(MAKE) -C $$d $@; done
|
|
||||||
rm -f TAGS tags
|
|
||||||
|
|
||||||
distclean:: clean
|
|
||||||
find . -name '*~' -exec rm '{}' \;
|
|
||||||
|
|
||||||
TAGS_SUBDIRS = $(BUILD_SUBDIRS) devices lib
|
|
||||||
TAGS_SOURCES = find $(TAGS_SUBDIRS) -name \*.[chS] -print
|
|
||||||
|
|
||||||
TAGS::
|
|
||||||
etags --members `$(TAGS_SOURCES)`
|
|
||||||
|
|
||||||
tags::
|
|
||||||
ctags -T --no-warn `$(TAGS_SOURCES)`
|
|
||||||
|
|
||||||
cscope.files::
|
|
||||||
$(TAGS_SOURCES) > cscope.files
|
|
||||||
|
|
||||||
cscope:: cscope.files
|
|
||||||
cscope -b -q -k
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
# -*- makefile -*-
|
|
||||||
|
|
||||||
SRCDIR = ../..
|
|
||||||
|
|
||||||
all: kernel.bin loader.bin
|
|
||||||
|
|
||||||
include ../../Make.config
|
|
||||||
include ../Make.vars
|
|
||||||
include ../../tests/Make.tests
|
|
||||||
|
|
||||||
# Compiler and assembler options.
|
|
||||||
kernel.bin: CPPFLAGS += -I$(SRCDIR)/lib/kernel
|
|
||||||
|
|
||||||
# Core kernel.
|
|
||||||
threads_SRC = threads/start.S # Startup code.
|
|
||||||
threads_SRC += threads/init.c # Main program.
|
|
||||||
threads_SRC += threads/thread.c # Thread management core.
|
|
||||||
threads_SRC += threads/switch.S # Thread switch routine.
|
|
||||||
threads_SRC += threads/interrupt.c # Interrupt core.
|
|
||||||
threads_SRC += threads/intr-stubs.S # Interrupt stubs.
|
|
||||||
threads_SRC += threads/synch.c # Synchronization.
|
|
||||||
threads_SRC += threads/palloc.c # Page allocator.
|
|
||||||
threads_SRC += threads/malloc.c # Subpage allocator.
|
|
||||||
|
|
||||||
# Device driver code.
|
|
||||||
devices_SRC = devices/pit.c # Programmable interrupt timer chip.
|
|
||||||
devices_SRC += devices/timer.c # Periodic timer device.
|
|
||||||
devices_SRC += devices/kbd.c # Keyboard device.
|
|
||||||
devices_SRC += devices/vga.c # Video device.
|
|
||||||
devices_SRC += devices/serial.c # Serial port device.
|
|
||||||
devices_SRC += devices/block.c # Block device abstraction layer.
|
|
||||||
devices_SRC += devices/partition.c # Partition block device.
|
|
||||||
devices_SRC += devices/ide.c # IDE disk block device.
|
|
||||||
devices_SRC += devices/input.c # Serial and keyboard input.
|
|
||||||
devices_SRC += devices/intq.c # Interrupt queue.
|
|
||||||
devices_SRC += devices/rtc.c # Real-time clock.
|
|
||||||
devices_SRC += devices/shutdown.c # Reboot and power off.
|
|
||||||
devices_SRC += devices/speaker.c # PC speaker.
|
|
||||||
|
|
||||||
# Library code shared between kernel and user programs.
|
|
||||||
lib_SRC = lib/debug.c # Debug helpers.
|
|
||||||
lib_SRC += lib/random.c # Pseudo-random numbers.
|
|
||||||
lib_SRC += lib/stdio.c # I/O library.
|
|
||||||
lib_SRC += lib/stdlib.c # Utility functions.
|
|
||||||
lib_SRC += lib/string.c # String functions.
|
|
||||||
lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC.
|
|
||||||
lib_SRC += lib/ustar.c # Unix standard tar format utilities.
|
|
||||||
|
|
||||||
# Kernel-specific library code.
|
|
||||||
lib/kernel_SRC = lib/kernel/debug.c # Debug helpers.
|
|
||||||
lib/kernel_SRC += lib/kernel/list.c # Doubly-linked lists.
|
|
||||||
lib/kernel_SRC += lib/kernel/bitmap.c # Bitmaps.
|
|
||||||
lib/kernel_SRC += lib/kernel/hash.c # Hash tables.
|
|
||||||
lib/kernel_SRC += lib/kernel/console.c # printf(), putchar().
|
|
||||||
|
|
||||||
# User process code.
|
|
||||||
userprog_SRC = userprog/process.c # Process loading.
|
|
||||||
userprog_SRC += userprog/pagedir.c # Page directories.
|
|
||||||
userprog_SRC += userprog/exception.c # User exception handler.
|
|
||||||
userprog_SRC += userprog/syscall.c # System call handler.
|
|
||||||
userprog_SRC += userprog/gdt.c # GDT initialization.
|
|
||||||
userprog_SRC += userprog/tss.c # TSS management.
|
|
||||||
|
|
||||||
# Virtual memory code.
|
|
||||||
vm_SRC += vm/frame.c # Frame table manager.
|
|
||||||
vm_SRC += vm/page.c # Page table manager.
|
|
||||||
vm_SRC += devices/swap.c # Swap block manager.
|
|
||||||
|
|
||||||
# Filesystem code.
|
|
||||||
filesys_SRC = filesys/filesys.c # Filesystem core.
|
|
||||||
filesys_SRC += filesys/free-map.c # Free sector bitmap.
|
|
||||||
filesys_SRC += filesys/file.c # Files.
|
|
||||||
filesys_SRC += filesys/directory.c # Directories.
|
|
||||||
filesys_SRC += filesys/inode.c # File headers.
|
|
||||||
filesys_SRC += filesys/fsutil.c # Utilities.
|
|
||||||
|
|
||||||
SOURCES = $(foreach dir,$(KERNEL_SUBDIRS),$($(dir)_SRC))
|
|
||||||
OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES)))
|
|
||||||
DEPENDS = $(patsubst %.o,%.d,$(OBJECTS))
|
|
||||||
|
|
||||||
threads/kernel.lds.s: CPPFLAGS += -P
|
|
||||||
threads/kernel.lds.s: threads/kernel.lds.S threads/loader.h
|
|
||||||
|
|
||||||
kernel.o: threads/kernel.lds.s $(OBJECTS)
|
|
||||||
$(LD) -T $< -o $@ $(OBJECTS)
|
|
||||||
|
|
||||||
kernel.bin: kernel.o
|
|
||||||
$(OBJCOPY) -R .note -R .comment -S $< $@
|
|
||||||
|
|
||||||
threads/loader.o: threads/loader.S
|
|
||||||
$(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES)
|
|
||||||
|
|
||||||
loader.bin: threads/loader.o loader.ld
|
|
||||||
$(LD) -N -e 0 -T $(word 2, $^) -o $@ $<
|
|
||||||
|
|
||||||
os.dsk: kernel.bin
|
|
||||||
cat $^ > $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
rm -f $(OBJECTS) $(DEPENDS)
|
|
||||||
rm -f threads/loader.o threads/kernel.lds.s threads/loader.d
|
|
||||||
rm -f kernel.bin.tmp
|
|
||||||
rm -f kernel.o kernel.lds.s
|
|
||||||
rm -f kernel.bin loader.bin
|
|
||||||
rm -f bochsout.txt bochsrc.txt
|
|
||||||
rm -f results grade
|
|
||||||
|
|
||||||
Makefile: $(SRCDIR)/Makefile.build
|
|
||||||
cp $< $@
|
|
||||||
|
|
||||||
-include $(DEPENDS)
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# -*- makefile -*-
|
|
||||||
|
|
||||||
all:
|
|
||||||
|
|
||||||
include Make.vars
|
|
||||||
|
|
||||||
DIRS = $(sort $(addprefix build/,$(KERNEL_SUBDIRS) $(TEST_SUBDIRS) lib/user))
|
|
||||||
|
|
||||||
all grade check: $(DIRS) build/Makefile
|
|
||||||
cd build && $(MAKE) $@
|
|
||||||
$(DIRS):
|
|
||||||
mkdir -p $@
|
|
||||||
build/Makefile: ../Makefile.build
|
|
||||||
cp $< $@
|
|
||||||
|
|
||||||
build/%: $(DIRS) build/Makefile
|
|
||||||
cd build && $(MAKE) $*
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf build
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# -*- makefile -*-
|
|
||||||
|
|
||||||
$(PROGS): CPPFLAGS += -I$(SRCDIR)/lib/user -I.
|
|
||||||
|
|
||||||
# Linker flags.
|
|
||||||
$(PROGS): LDFLAGS += -nostdlib -static -Wl,-T,$(LDSCRIPT)
|
|
||||||
$(PROGS): LDSCRIPT = $(SRCDIR)/lib/user/user.lds
|
|
||||||
|
|
||||||
# Library code shared between kernel and user programs.
|
|
||||||
lib_SRC = lib/debug.c # Debug code.
|
|
||||||
lib_SRC += lib/random.c # Pseudo-random numbers.
|
|
||||||
lib_SRC += lib/stdio.c # I/O library.
|
|
||||||
lib_SRC += lib/stdlib.c # Utility functions.
|
|
||||||
lib_SRC += lib/string.c # String functions.
|
|
||||||
lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC.
|
|
||||||
lib_SRC += lib/ustar.c # Unix standard tar format utilities.
|
|
||||||
|
|
||||||
# User level only library code.
|
|
||||||
lib/user_SRC = lib/user/debug.c # Debug helpers.
|
|
||||||
lib/user_SRC += lib/user/syscall.c # System calls.
|
|
||||||
lib/user_SRC += lib/user/console.c # Console code.
|
|
||||||
|
|
||||||
LIB_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(lib_SRC) $(lib/user_SRC)))
|
|
||||||
LIB_DEP = $(patsubst %.o,%.d,$(LIB_OBJ))
|
|
||||||
LIB = lib/user/entry.o libc.a
|
|
||||||
|
|
||||||
PROGS_SRC = $(foreach prog,$(PROGS),$($(prog)_SRC))
|
|
||||||
PROGS_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(PROGS_SRC)))
|
|
||||||
PROGS_DEP = $(patsubst %.o,%.d,$(PROGS_OBJ))
|
|
||||||
|
|
||||||
all: $(PROGS)
|
|
||||||
|
|
||||||
define TEMPLATE
|
|
||||||
$(1)_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$($(1)_SRC)))
|
|
||||||
$(1): $$($(1)_OBJ) $$(LIB) $$(LDSCRIPT)
|
|
||||||
$$(CC) $$(LDFLAGS) $$($(1)_OBJ) $$(LIB) -o $$@
|
|
||||||
endef
|
|
||||||
|
|
||||||
$(foreach prog,$(PROGS),$(eval $(call TEMPLATE,$(prog))))
|
|
||||||
|
|
||||||
libc.a: $(LIB_OBJ)
|
|
||||||
rm -f $@
|
|
||||||
ar r $@ $^
|
|
||||||
$(RANLIB) $@
|
|
||||||
|
|
||||||
clean::
|
|
||||||
rm -f $(PROGS) $(PROGS_OBJ) $(PROGS_DEP)
|
|
||||||
rm -f $(LIB_DEP) $(LIB_OBJ) lib/user/entry.[do] libc.a
|
|
||||||
|
|
||||||
.PHONY: all clean
|
|
||||||
|
|
||||||
-include $(LIB_DEP) $(PROGS_DEP)
|
|
||||||
3
tests/devices/src/devices/.gitignore
vendored
3
tests/devices/src/devices/.gitignore
vendored
@@ -1,3 +0,0 @@
|
|||||||
build
|
|
||||||
bochsrc.txt
|
|
||||||
bochsout.txt
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# -*- makefile -*-
|
|
||||||
|
|
||||||
kernel.bin: DEFINES =
|
|
||||||
KERNEL_SUBDIRS = threads devices lib lib/kernel $(TEST_SUBDIRS)
|
|
||||||
TEST_SUBDIRS = tests/devices
|
|
||||||
GRADING_FILE = $(SRCDIR)/tests/devices/Grading
|
|
||||||
SIMULATOR = --qemu
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
include ../Makefile.kernel
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
#include "devices/block.h"
|
|
||||||
#include <list.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "devices/ide.h"
|
|
||||||
#include "threads/malloc.h"
|
|
||||||
|
|
||||||
/* A block device. */
|
|
||||||
struct block
|
|
||||||
{
|
|
||||||
struct list_elem list_elem; /* Element in all_blocks. */
|
|
||||||
|
|
||||||
char name[16]; /* Block device name. */
|
|
||||||
enum block_type type; /* Type of block device. */
|
|
||||||
block_sector_t size; /* Size in sectors. */
|
|
||||||
|
|
||||||
const struct block_operations *ops; /* Driver operations. */
|
|
||||||
void *aux; /* Extra data owned by driver. */
|
|
||||||
|
|
||||||
unsigned long long read_cnt; /* Number of sectors read. */
|
|
||||||
unsigned long long write_cnt; /* Number of sectors written. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* List of all block devices. */
|
|
||||||
static struct list all_blocks = LIST_INITIALIZER (all_blocks);
|
|
||||||
|
|
||||||
/* The block block assigned to each PintOS role. */
|
|
||||||
static struct block *block_by_role[BLOCK_ROLE_CNT];
|
|
||||||
|
|
||||||
static struct block *list_elem_to_block (struct list_elem *);
|
|
||||||
|
|
||||||
/* Returns a human-readable name for the given block device
|
|
||||||
TYPE. */
|
|
||||||
const char *
|
|
||||||
block_type_name (enum block_type type)
|
|
||||||
{
|
|
||||||
static const char *block_type_names[BLOCK_CNT] =
|
|
||||||
{
|
|
||||||
"kernel",
|
|
||||||
"filesys",
|
|
||||||
"scratch",
|
|
||||||
"swap",
|
|
||||||
"raw",
|
|
||||||
"foreign",
|
|
||||||
};
|
|
||||||
|
|
||||||
ASSERT (type < BLOCK_CNT);
|
|
||||||
return block_type_names[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the block device fulfilling the given ROLE, or a null
|
|
||||||
pointer if no block device has been assigned that role. */
|
|
||||||
struct block *
|
|
||||||
block_get_role (enum block_type role)
|
|
||||||
{
|
|
||||||
ASSERT (role < BLOCK_ROLE_CNT);
|
|
||||||
return block_by_role[role];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assigns BLOCK the given ROLE. */
|
|
||||||
void
|
|
||||||
block_set_role (enum block_type role, struct block *block)
|
|
||||||
{
|
|
||||||
ASSERT (role < BLOCK_ROLE_CNT);
|
|
||||||
block_by_role[role] = block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the first block device in kernel probe order, or a
|
|
||||||
null pointer if no block devices are registered. */
|
|
||||||
struct block *
|
|
||||||
block_first (void)
|
|
||||||
{
|
|
||||||
return list_elem_to_block (list_begin (&all_blocks));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the block device following BLOCK in kernel probe
|
|
||||||
order, or a null pointer if BLOCK is the last block device. */
|
|
||||||
struct block *
|
|
||||||
block_next (struct block *block)
|
|
||||||
{
|
|
||||||
return list_elem_to_block (list_next (&block->list_elem));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the block device with the given NAME, or a null
|
|
||||||
pointer if no block device has that name. */
|
|
||||||
struct block *
|
|
||||||
block_get_by_name (const char *name)
|
|
||||||
{
|
|
||||||
struct list_elem *e;
|
|
||||||
|
|
||||||
for (e = list_begin (&all_blocks); e != list_end (&all_blocks);
|
|
||||||
e = list_next (e))
|
|
||||||
{
|
|
||||||
struct block *block = list_entry (e, struct block, list_elem);
|
|
||||||
if (!strcmp (name, block->name))
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Verifies that SECTOR is a valid offset within BLOCK.
|
|
||||||
Panics if not. */
|
|
||||||
static void
|
|
||||||
check_sector (struct block *block, block_sector_t sector)
|
|
||||||
{
|
|
||||||
if (sector >= block->size)
|
|
||||||
{
|
|
||||||
/* We do not use ASSERT because we want to panic here
|
|
||||||
regardless of whether NDEBUG is defined. */
|
|
||||||
PANIC ("Access past end of device %s (sector=%"PRDSNu", "
|
|
||||||
"size=%"PRDSNu")\n", block_name (block), sector, block->size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads sector SECTOR from BLOCK into BUFFER, which must
|
|
||||||
have room for BLOCK_SECTOR_SIZE bytes.
|
|
||||||
Internally synchronizes accesses to block devices, so external
|
|
||||||
per-block device locking is unneeded. */
|
|
||||||
void
|
|
||||||
block_read (struct block *block, block_sector_t sector, void *buffer)
|
|
||||||
{
|
|
||||||
check_sector (block, sector);
|
|
||||||
block->ops->read (block->aux, sector, buffer);
|
|
||||||
block->read_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write sector SECTOR to BLOCK from BUFFER, which must contain
|
|
||||||
BLOCK_SECTOR_SIZE bytes. Returns after the block device has
|
|
||||||
acknowledged receiving the data.
|
|
||||||
Internally synchronizes accesses to block devices, so external
|
|
||||||
per-block device locking is unneeded. */
|
|
||||||
void
|
|
||||||
block_write (struct block *block, block_sector_t sector, const void *buffer)
|
|
||||||
{
|
|
||||||
check_sector (block, sector);
|
|
||||||
ASSERT (block->type != BLOCK_FOREIGN);
|
|
||||||
block->ops->write (block->aux, sector, buffer);
|
|
||||||
block->write_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of sectors in BLOCK. */
|
|
||||||
block_sector_t
|
|
||||||
block_size (struct block *block)
|
|
||||||
{
|
|
||||||
return block->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns BLOCK's name (e.g. "hda"). */
|
|
||||||
const char *
|
|
||||||
block_name (struct block *block)
|
|
||||||
{
|
|
||||||
return block->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns BLOCK's type. */
|
|
||||||
enum block_type
|
|
||||||
block_type (struct block *block)
|
|
||||||
{
|
|
||||||
return block->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prints statistics for each block device used for a PintOS role. */
|
|
||||||
void
|
|
||||||
block_print_stats (void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < BLOCK_ROLE_CNT; i++)
|
|
||||||
{
|
|
||||||
struct block *block = block_by_role[i];
|
|
||||||
if (block != NULL)
|
|
||||||
{
|
|
||||||
printf ("%s (%s): %llu reads, %llu writes\n",
|
|
||||||
block->name, block_type_name (block->type),
|
|
||||||
block->read_cnt, block->write_cnt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Registers a new block device with the given NAME. If
|
|
||||||
EXTRA_INFO is non-null, it is printed as part of a user
|
|
||||||
message. The block device's SIZE in sectors and its TYPE must
|
|
||||||
be provided, as well as the it operation functions OPS, which
|
|
||||||
will be passed AUX in each function call. */
|
|
||||||
struct block *
|
|
||||||
block_register (const char *name, enum block_type type,
|
|
||||||
const char *extra_info, block_sector_t size,
|
|
||||||
const struct block_operations *ops, void *aux)
|
|
||||||
{
|
|
||||||
struct block *block = malloc (sizeof *block);
|
|
||||||
if (block == NULL)
|
|
||||||
PANIC ("Failed to allocate memory for block device descriptor");
|
|
||||||
|
|
||||||
list_push_back (&all_blocks, &block->list_elem);
|
|
||||||
strlcpy (block->name, name, sizeof block->name);
|
|
||||||
block->type = type;
|
|
||||||
block->size = size;
|
|
||||||
block->ops = ops;
|
|
||||||
block->aux = aux;
|
|
||||||
block->read_cnt = 0;
|
|
||||||
block->write_cnt = 0;
|
|
||||||
|
|
||||||
printf ("%s: %'"PRDSNu" sectors (", block->name, block->size);
|
|
||||||
print_human_readable_size ((uint64_t) block->size * BLOCK_SECTOR_SIZE);
|
|
||||||
printf (")");
|
|
||||||
if (extra_info != NULL)
|
|
||||||
printf (", %s", extra_info);
|
|
||||||
printf ("\n");
|
|
||||||
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the block device corresponding to LIST_ELEM, or a null
|
|
||||||
pointer if LIST_ELEM is the list end of all_blocks. */
|
|
||||||
static struct block *
|
|
||||||
list_elem_to_block (struct list_elem *list_elem)
|
|
||||||
{
|
|
||||||
return (list_elem != list_end (&all_blocks)
|
|
||||||
? list_entry (list_elem, struct block, list_elem)
|
|
||||||
: NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
#ifndef DEVICES_BLOCK_H
|
|
||||||
#define DEVICES_BLOCK_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
/* Size of a block device sector in bytes.
|
|
||||||
All IDE disks use this sector size, as do most USB and SCSI
|
|
||||||
disks. It's not worth it to try to cater to other sector
|
|
||||||
sizes in PintOS (yet). */
|
|
||||||
#define BLOCK_SECTOR_SIZE 512
|
|
||||||
|
|
||||||
/* Index of a block device sector.
|
|
||||||
Good enough for devices up to 2 TB. */
|
|
||||||
typedef uint32_t block_sector_t;
|
|
||||||
|
|
||||||
/* Format specifier for printf(), e.g.:
|
|
||||||
printf ("sector=%"PRDSNu"\n", sector); */
|
|
||||||
#define PRDSNu PRIu32
|
|
||||||
|
|
||||||
/* Higher-level interface for file systems, etc. */
|
|
||||||
|
|
||||||
struct block;
|
|
||||||
|
|
||||||
/* Type of a block device. */
|
|
||||||
enum block_type
|
|
||||||
{
|
|
||||||
/* Block device types that play a role in PintOS. */
|
|
||||||
BLOCK_KERNEL, /* PintOS OS kernel. */
|
|
||||||
BLOCK_FILESYS, /* File system. */
|
|
||||||
BLOCK_SCRATCH, /* Scratch. */
|
|
||||||
BLOCK_SWAP, /* Swap. */
|
|
||||||
BLOCK_ROLE_CNT,
|
|
||||||
|
|
||||||
/* Other kinds of block devices that PintOS may see but does
|
|
||||||
not interact with. */
|
|
||||||
BLOCK_RAW = BLOCK_ROLE_CNT, /* "Raw" device with unidentified contents. */
|
|
||||||
BLOCK_FOREIGN, /* Owned by non-PintOS operating system. */
|
|
||||||
BLOCK_CNT /* Number of PintOS block types. */
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *block_type_name (enum block_type);
|
|
||||||
|
|
||||||
/* Finding block devices. */
|
|
||||||
struct block *block_get_role (enum block_type);
|
|
||||||
void block_set_role (enum block_type, struct block *);
|
|
||||||
struct block *block_get_by_name (const char *name);
|
|
||||||
|
|
||||||
struct block *block_first (void);
|
|
||||||
struct block *block_next (struct block *);
|
|
||||||
|
|
||||||
/* Block device operations. */
|
|
||||||
block_sector_t block_size (struct block *);
|
|
||||||
void block_read (struct block *, block_sector_t, void *);
|
|
||||||
void block_write (struct block *, block_sector_t, const void *);
|
|
||||||
const char *block_name (struct block *);
|
|
||||||
enum block_type block_type (struct block *);
|
|
||||||
|
|
||||||
/* Statistics. */
|
|
||||||
void block_print_stats (void);
|
|
||||||
|
|
||||||
/* Lower-level interface to block device drivers. */
|
|
||||||
|
|
||||||
struct block_operations
|
|
||||||
{
|
|
||||||
void (*read) (void *aux, block_sector_t, void *buffer);
|
|
||||||
void (*write) (void *aux, block_sector_t, const void *buffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct block *block_register (const char *name, enum block_type,
|
|
||||||
const char *extra_info, block_sector_t size,
|
|
||||||
const struct block_operations *, void *aux);
|
|
||||||
|
|
||||||
#endif /* devices/block.h */
|
|
||||||
@@ -1,527 +0,0 @@
|
|||||||
#include "devices/ide.h"
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <debug.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "devices/block.h"
|
|
||||||
#include "devices/partition.h"
|
|
||||||
#include "devices/timer.h"
|
|
||||||
#include "threads/io.h"
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
|
|
||||||
/* The code in this file is an interface to an ATA (IDE)
|
|
||||||
controller. It attempts to comply to [ATA-3]. */
|
|
||||||
|
|
||||||
/* ATA command block port addresses. */
|
|
||||||
#define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) /* Data. */
|
|
||||||
#define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) /* Error. */
|
|
||||||
#define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2) /* Sector Count. */
|
|
||||||
#define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3) /* LBA 0:7. */
|
|
||||||
#define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4) /* LBA 15:8. */
|
|
||||||
#define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5) /* LBA 23:16. */
|
|
||||||
#define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6) /* Device/LBA 27:24. */
|
|
||||||
#define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) /* Status (r/o). */
|
|
||||||
#define reg_command(CHANNEL) reg_status (CHANNEL) /* Command (w/o). */
|
|
||||||
|
|
||||||
/* ATA control block port addresses.
|
|
||||||
(If we supported non-legacy ATA controllers this would not be
|
|
||||||
flexible enough, but it's fine for what we do.) */
|
|
||||||
#define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) /* Control (w/o). */
|
|
||||||
#define reg_alt_status(CHANNEL) reg_ctl (CHANNEL) /* Alt Status (r/o). */
|
|
||||||
|
|
||||||
/* Alternate Status Register bits. */
|
|
||||||
#define STA_BSY 0x80 /* Busy. */
|
|
||||||
#define STA_DRDY 0x40 /* Device Ready. */
|
|
||||||
#define STA_DRQ 0x08 /* Data Request. */
|
|
||||||
|
|
||||||
/* Control Register bits. */
|
|
||||||
#define CTL_SRST 0x04 /* Software Reset. */
|
|
||||||
|
|
||||||
/* Device Register bits. */
|
|
||||||
#define DEV_MBS 0xa0 /* Must be set. */
|
|
||||||
#define DEV_LBA 0x40 /* Linear based addressing. */
|
|
||||||
#define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */
|
|
||||||
|
|
||||||
/* Commands.
|
|
||||||
Many more are defined but this is the small subset that we
|
|
||||||
use. */
|
|
||||||
#define CMD_IDENTIFY_DEVICE 0xec /* IDENTIFY DEVICE. */
|
|
||||||
#define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */
|
|
||||||
#define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */
|
|
||||||
|
|
||||||
/* An ATA device. */
|
|
||||||
struct ata_disk
|
|
||||||
{
|
|
||||||
char name[8]; /* Name, e.g. "hda". */
|
|
||||||
struct channel *channel; /* Channel that disk is attached to. */
|
|
||||||
int dev_no; /* Device 0 or 1 for master or slave. */
|
|
||||||
bool is_ata; /* Is device an ATA disk? */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* An ATA channel (aka controller).
|
|
||||||
Each channel can control up to two disks. */
|
|
||||||
struct channel
|
|
||||||
{
|
|
||||||
char name[8]; /* Name, e.g. "ide0". */
|
|
||||||
uint16_t reg_base; /* Base I/O port. */
|
|
||||||
uint8_t irq; /* Interrupt in use. */
|
|
||||||
|
|
||||||
struct lock lock; /* Must acquire to access the controller. */
|
|
||||||
bool expecting_interrupt; /* True if an interrupt is expected, false if
|
|
||||||
any interrupt would be spurious. */
|
|
||||||
struct semaphore completion_wait; /* Up'd by interrupt handler. */
|
|
||||||
|
|
||||||
struct ata_disk devices[2]; /* The devices on this channel. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* We support the two "legacy" ATA channels found in a standard PC. */
|
|
||||||
#define CHANNEL_CNT 2
|
|
||||||
static struct channel channels[CHANNEL_CNT];
|
|
||||||
|
|
||||||
static struct block_operations ide_operations;
|
|
||||||
|
|
||||||
static void reset_channel (struct channel *);
|
|
||||||
static bool check_device_type (struct ata_disk *);
|
|
||||||
static void identify_ata_device (struct ata_disk *);
|
|
||||||
|
|
||||||
static void select_sector (struct ata_disk *, block_sector_t);
|
|
||||||
static void issue_pio_command (struct channel *, uint8_t command);
|
|
||||||
static void input_sector (struct channel *, void *);
|
|
||||||
static void output_sector (struct channel *, const void *);
|
|
||||||
|
|
||||||
static void wait_until_idle (const struct ata_disk *);
|
|
||||||
static bool wait_while_busy (const struct ata_disk *);
|
|
||||||
static void select_device (const struct ata_disk *);
|
|
||||||
static void select_device_wait (const struct ata_disk *);
|
|
||||||
|
|
||||||
static void interrupt_handler (struct intr_frame *);
|
|
||||||
|
|
||||||
/* Initialize the disk subsystem and detect disks. */
|
|
||||||
void
|
|
||||||
ide_init (void)
|
|
||||||
{
|
|
||||||
size_t chan_no;
|
|
||||||
|
|
||||||
for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++)
|
|
||||||
{
|
|
||||||
struct channel *c = &channels[chan_no];
|
|
||||||
int dev_no;
|
|
||||||
|
|
||||||
/* Initialize channel. */
|
|
||||||
snprintf (c->name, sizeof c->name, "ide%zu", chan_no);
|
|
||||||
switch (chan_no)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
c->reg_base = 0x1f0;
|
|
||||||
c->irq = 14 + 0x20;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
c->reg_base = 0x170;
|
|
||||||
c->irq = 15 + 0x20;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
NOT_REACHED ();
|
|
||||||
}
|
|
||||||
lock_init (&c->lock);
|
|
||||||
c->expecting_interrupt = false;
|
|
||||||
sema_init (&c->completion_wait, 0);
|
|
||||||
|
|
||||||
/* Initialize devices. */
|
|
||||||
for (dev_no = 0; dev_no < 2; dev_no++)
|
|
||||||
{
|
|
||||||
struct ata_disk *d = &c->devices[dev_no];
|
|
||||||
snprintf (d->name, sizeof d->name,
|
|
||||||
"hd%c", 'a' + chan_no * 2 + dev_no);
|
|
||||||
d->channel = c;
|
|
||||||
d->dev_no = dev_no;
|
|
||||||
d->is_ata = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Register interrupt handler. */
|
|
||||||
intr_register_ext (c->irq, interrupt_handler, c->name);
|
|
||||||
|
|
||||||
/* Reset hardware. */
|
|
||||||
reset_channel (c);
|
|
||||||
|
|
||||||
/* Distinguish ATA hard disks from other devices. */
|
|
||||||
if (check_device_type (&c->devices[0]))
|
|
||||||
check_device_type (&c->devices[1]);
|
|
||||||
|
|
||||||
/* Read hard disk identity information. */
|
|
||||||
for (dev_no = 0; dev_no < 2; dev_no++)
|
|
||||||
if (c->devices[dev_no].is_ata)
|
|
||||||
identify_ata_device (&c->devices[dev_no]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disk detection and identification. */
|
|
||||||
|
|
||||||
static char *descramble_ata_string (char *, int size);
|
|
||||||
|
|
||||||
/* Resets an ATA channel and waits for any devices present on it
|
|
||||||
to finish the reset. */
|
|
||||||
static void
|
|
||||||
reset_channel (struct channel *c)
|
|
||||||
{
|
|
||||||
bool present[2];
|
|
||||||
int dev_no;
|
|
||||||
|
|
||||||
/* The ATA reset sequence depends on which devices are present,
|
|
||||||
so we start by detecting device presence. */
|
|
||||||
for (dev_no = 0; dev_no < 2; dev_no++)
|
|
||||||
{
|
|
||||||
struct ata_disk *d = &c->devices[dev_no];
|
|
||||||
|
|
||||||
select_device (d);
|
|
||||||
|
|
||||||
outb (reg_nsect (c), 0x55);
|
|
||||||
outb (reg_lbal (c), 0xaa);
|
|
||||||
|
|
||||||
outb (reg_nsect (c), 0xaa);
|
|
||||||
outb (reg_lbal (c), 0x55);
|
|
||||||
|
|
||||||
outb (reg_nsect (c), 0x55);
|
|
||||||
outb (reg_lbal (c), 0xaa);
|
|
||||||
|
|
||||||
present[dev_no] = (inb (reg_nsect (c)) == 0x55
|
|
||||||
&& inb (reg_lbal (c)) == 0xaa);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Issue soft reset sequence, which selects device 0 as a side effect.
|
|
||||||
Also enable interrupts. */
|
|
||||||
outb (reg_ctl (c), 0);
|
|
||||||
timer_usleep (10);
|
|
||||||
outb (reg_ctl (c), CTL_SRST);
|
|
||||||
timer_usleep (10);
|
|
||||||
outb (reg_ctl (c), 0);
|
|
||||||
|
|
||||||
timer_msleep (150);
|
|
||||||
|
|
||||||
/* Wait for device 0 to clear BSY. */
|
|
||||||
if (present[0])
|
|
||||||
{
|
|
||||||
select_device (&c->devices[0]);
|
|
||||||
wait_while_busy (&c->devices[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait for device 1 to clear BSY. */
|
|
||||||
if (present[1])
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
select_device (&c->devices[1]);
|
|
||||||
for (i = 0; i < 3000; i++)
|
|
||||||
{
|
|
||||||
if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1)
|
|
||||||
break;
|
|
||||||
timer_msleep (10);
|
|
||||||
}
|
|
||||||
wait_while_busy (&c->devices[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Checks whether device D is an ATA disk and sets D's is_ata
|
|
||||||
member appropriately. If D is device 0 (master), returns true
|
|
||||||
if it's possible that a slave (device 1) exists on this
|
|
||||||
channel. If D is device 1 (slave), the return value is not
|
|
||||||
meaningful. */
|
|
||||||
static bool
|
|
||||||
check_device_type (struct ata_disk *d)
|
|
||||||
{
|
|
||||||
struct channel *c = d->channel;
|
|
||||||
uint8_t error, lbam, lbah, status;
|
|
||||||
|
|
||||||
select_device (d);
|
|
||||||
|
|
||||||
error = inb (reg_error (c));
|
|
||||||
lbam = inb (reg_lbam (c));
|
|
||||||
lbah = inb (reg_lbah (c));
|
|
||||||
status = inb (reg_status (c));
|
|
||||||
|
|
||||||
if ((error != 1 && (error != 0x81 || d->dev_no == 1))
|
|
||||||
|| (status & STA_DRDY) == 0
|
|
||||||
|| (status & STA_BSY) != 0)
|
|
||||||
{
|
|
||||||
d->is_ata = false;
|
|
||||||
return error != 0x81;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sends an IDENTIFY DEVICE command to disk D and reads the
|
|
||||||
response. Registers the disk with the block device
|
|
||||||
layer. */
|
|
||||||
static void
|
|
||||||
identify_ata_device (struct ata_disk *d)
|
|
||||||
{
|
|
||||||
struct channel *c = d->channel;
|
|
||||||
char id[BLOCK_SECTOR_SIZE];
|
|
||||||
block_sector_t capacity;
|
|
||||||
char *model, *serial;
|
|
||||||
char extra_info[128];
|
|
||||||
struct block *block;
|
|
||||||
|
|
||||||
ASSERT (d->is_ata);
|
|
||||||
|
|
||||||
/* Send the IDENTIFY DEVICE command, wait for an interrupt
|
|
||||||
indicating the device's response is ready, and read the data
|
|
||||||
into our buffer. */
|
|
||||||
select_device_wait (d);
|
|
||||||
issue_pio_command (c, CMD_IDENTIFY_DEVICE);
|
|
||||||
sema_down (&c->completion_wait);
|
|
||||||
if (!wait_while_busy (d))
|
|
||||||
{
|
|
||||||
d->is_ata = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
input_sector (c, id);
|
|
||||||
|
|
||||||
/* Calculate capacity.
|
|
||||||
Read model name and serial number. */
|
|
||||||
capacity = *(uint32_t *) &id[60 * 2];
|
|
||||||
model = descramble_ata_string (&id[10 * 2], 20);
|
|
||||||
serial = descramble_ata_string (&id[27 * 2], 40);
|
|
||||||
snprintf (extra_info, sizeof extra_info,
|
|
||||||
"model \"%s\", serial \"%s\"", model, serial);
|
|
||||||
|
|
||||||
/* Disable access to IDE disks over 1 GB, which are likely
|
|
||||||
physical IDE disks rather than virtual ones. If we don't
|
|
||||||
allow access to those, we're less likely to scribble on
|
|
||||||
someone's important data. You can disable this check by
|
|
||||||
hand if you really want to do so. */
|
|
||||||
if (capacity >= 1024 * 1024 * 1024 / BLOCK_SECTOR_SIZE)
|
|
||||||
{
|
|
||||||
printf ("%s: ignoring ", d->name);
|
|
||||||
print_human_readable_size (capacity * 512);
|
|
||||||
printf ("disk for safety\n");
|
|
||||||
d->is_ata = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Register. */
|
|
||||||
block = block_register (d->name, BLOCK_RAW, extra_info, capacity,
|
|
||||||
&ide_operations, d);
|
|
||||||
partition_scan (block);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Translates STRING, which consists of SIZE bytes in a funky
|
|
||||||
format, into a null-terminated string in-place. Drops
|
|
||||||
trailing whitespace and null bytes. Returns STRING. */
|
|
||||||
static char *
|
|
||||||
descramble_ata_string (char *string, int size)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Swap all pairs of bytes. */
|
|
||||||
for (i = 0; i + 1 < size; i += 2)
|
|
||||||
{
|
|
||||||
char tmp = string[i];
|
|
||||||
string[i] = string[i + 1];
|
|
||||||
string[i + 1] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the last non-white, non-null character. */
|
|
||||||
for (size--; size > 0; size--)
|
|
||||||
{
|
|
||||||
int c = string[size - 1];
|
|
||||||
if (c != '\0' && !isspace (c))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
string[size] = '\0';
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads sector SEC_NO from disk D into BUFFER, which must have
|
|
||||||
room for BLOCK_SECTOR_SIZE bytes.
|
|
||||||
Internally synchronizes accesses to disks, so external
|
|
||||||
per-disk locking is unneeded. */
|
|
||||||
static void
|
|
||||||
ide_read (void *d_, block_sector_t sec_no, void *buffer)
|
|
||||||
{
|
|
||||||
struct ata_disk *d = d_;
|
|
||||||
struct channel *c = d->channel;
|
|
||||||
lock_acquire (&c->lock);
|
|
||||||
select_sector (d, sec_no);
|
|
||||||
issue_pio_command (c, CMD_READ_SECTOR_RETRY);
|
|
||||||
sema_down (&c->completion_wait);
|
|
||||||
if (!wait_while_busy (d))
|
|
||||||
PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
|
|
||||||
input_sector (c, buffer);
|
|
||||||
lock_release (&c->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write sector SEC_NO to disk D from BUFFER, which must contain
|
|
||||||
BLOCK_SECTOR_SIZE bytes. Returns after the disk has
|
|
||||||
acknowledged receiving the data.
|
|
||||||
Internally synchronizes accesses to disks, so external
|
|
||||||
per-disk locking is unneeded. */
|
|
||||||
static void
|
|
||||||
ide_write (void *d_, block_sector_t sec_no, const void *buffer)
|
|
||||||
{
|
|
||||||
struct ata_disk *d = d_;
|
|
||||||
struct channel *c = d->channel;
|
|
||||||
lock_acquire (&c->lock);
|
|
||||||
select_sector (d, sec_no);
|
|
||||||
issue_pio_command (c, CMD_WRITE_SECTOR_RETRY);
|
|
||||||
if (!wait_while_busy (d))
|
|
||||||
PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
|
|
||||||
output_sector (c, buffer);
|
|
||||||
sema_down (&c->completion_wait);
|
|
||||||
lock_release (&c->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct block_operations ide_operations =
|
|
||||||
{
|
|
||||||
ide_read,
|
|
||||||
ide_write
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Selects device D, waiting for it to become ready, and then
|
|
||||||
writes SEC_NO to the disk's sector selection registers. (We
|
|
||||||
use LBA mode.) */
|
|
||||||
static void
|
|
||||||
select_sector (struct ata_disk *d, block_sector_t sec_no)
|
|
||||||
{
|
|
||||||
struct channel *c = d->channel;
|
|
||||||
|
|
||||||
ASSERT (sec_no < (1UL << 28));
|
|
||||||
|
|
||||||
select_device_wait (d);
|
|
||||||
outb (reg_nsect (c), 1);
|
|
||||||
outb (reg_lbal (c), sec_no);
|
|
||||||
outb (reg_lbam (c), sec_no >> 8);
|
|
||||||
outb (reg_lbah (c), (sec_no >> 16));
|
|
||||||
outb (reg_device (c),
|
|
||||||
DEV_MBS | DEV_LBA | (d->dev_no == 1 ? DEV_DEV : 0) | (sec_no >> 24));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes COMMAND to channel C and prepares for receiving a
|
|
||||||
completion interrupt. */
|
|
||||||
static void
|
|
||||||
issue_pio_command (struct channel *c, uint8_t command)
|
|
||||||
{
|
|
||||||
/* Interrupts must be enabled or our semaphore will never be
|
|
||||||
up'd by the completion handler. */
|
|
||||||
ASSERT (intr_get_level () == INTR_ON);
|
|
||||||
|
|
||||||
c->expecting_interrupt = true;
|
|
||||||
outb (reg_command (c), command);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads a sector from channel C's data register in PIO mode into
|
|
||||||
SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. */
|
|
||||||
static void
|
|
||||||
input_sector (struct channel *c, void *sector)
|
|
||||||
{
|
|
||||||
insw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes SECTOR to channel C's data register in PIO mode.
|
|
||||||
SECTOR must contain BLOCK_SECTOR_SIZE bytes. */
|
|
||||||
static void
|
|
||||||
output_sector (struct channel *c, const void *sector)
|
|
||||||
{
|
|
||||||
outsw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Low-level ATA primitives. */
|
|
||||||
|
|
||||||
/* Wait up to 10 seconds for the controller to become idle, that
|
|
||||||
is, for the BSY and DRQ bits to clear in the status register.
|
|
||||||
|
|
||||||
As a side effect, reading the status register clears any
|
|
||||||
pending interrupt. */
|
|
||||||
static void
|
|
||||||
wait_until_idle (const struct ata_disk *d)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 1000; i++)
|
|
||||||
{
|
|
||||||
if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0)
|
|
||||||
return;
|
|
||||||
timer_usleep (10);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf ("%s: idle timeout\n", d->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait up to 30 seconds for disk D to clear BSY,
|
|
||||||
and then return the status of the DRQ bit.
|
|
||||||
The ATA standards say that a disk may take as long as that to
|
|
||||||
complete its reset. */
|
|
||||||
static bool
|
|
||||||
wait_while_busy (const struct ata_disk *d)
|
|
||||||
{
|
|
||||||
struct channel *c = d->channel;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 3000; i++)
|
|
||||||
{
|
|
||||||
if (i == 700)
|
|
||||||
printf ("%s: busy, waiting...", d->name);
|
|
||||||
if (!(inb (reg_alt_status (c)) & STA_BSY))
|
|
||||||
{
|
|
||||||
if (i >= 700)
|
|
||||||
printf ("ok\n");
|
|
||||||
return (inb (reg_alt_status (c)) & STA_DRQ) != 0;
|
|
||||||
}
|
|
||||||
timer_msleep (10);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf ("failed\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Program D's channel so that D is now the selected disk. */
|
|
||||||
static void
|
|
||||||
select_device (const struct ata_disk *d)
|
|
||||||
{
|
|
||||||
struct channel *c = d->channel;
|
|
||||||
uint8_t dev = DEV_MBS;
|
|
||||||
if (d->dev_no == 1)
|
|
||||||
dev |= DEV_DEV;
|
|
||||||
outb (reg_device (c), dev);
|
|
||||||
inb (reg_alt_status (c));
|
|
||||||
timer_nsleep (400);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select disk D in its channel, as select_device(), but wait for
|
|
||||||
the channel to become idle before and after. */
|
|
||||||
static void
|
|
||||||
select_device_wait (const struct ata_disk *d)
|
|
||||||
{
|
|
||||||
wait_until_idle (d);
|
|
||||||
select_device (d);
|
|
||||||
wait_until_idle (d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ATA interrupt handler. */
|
|
||||||
static void
|
|
||||||
interrupt_handler (struct intr_frame *f)
|
|
||||||
{
|
|
||||||
struct channel *c;
|
|
||||||
|
|
||||||
for (c = channels; c < channels + CHANNEL_CNT; c++)
|
|
||||||
if (f->vec_no == c->irq)
|
|
||||||
{
|
|
||||||
if (c->expecting_interrupt)
|
|
||||||
{
|
|
||||||
inb (reg_status (c)); /* Acknowledge interrupt. */
|
|
||||||
sema_up (&c->completion_wait); /* Wake up waiter. */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
printf ("%s: unexpected interrupt\n", c->name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NOT_REACHED ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef DEVICES_IDE_H
|
|
||||||
#define DEVICES_IDE_H
|
|
||||||
|
|
||||||
void ide_init (void);
|
|
||||||
|
|
||||||
#endif /* devices/ide.h */
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#include "devices/input.h"
|
|
||||||
#include <debug.h>
|
|
||||||
#include "devices/intq.h"
|
|
||||||
#include "devices/serial.h"
|
|
||||||
|
|
||||||
/* Stores keys from the keyboard and serial port. */
|
|
||||||
static struct intq buffer;
|
|
||||||
|
|
||||||
/* Initializes the input buffer. */
|
|
||||||
void
|
|
||||||
input_init (void)
|
|
||||||
{
|
|
||||||
intq_init (&buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adds a key to the input buffer.
|
|
||||||
Interrupts must be off and the buffer must not be full. */
|
|
||||||
void
|
|
||||||
input_putc (uint8_t key)
|
|
||||||
{
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
ASSERT (!intq_full (&buffer));
|
|
||||||
|
|
||||||
intq_putc (&buffer, key);
|
|
||||||
serial_notify ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Retrieves a key from the input buffer.
|
|
||||||
If the buffer is empty, waits for a key to be pressed. */
|
|
||||||
uint8_t
|
|
||||||
input_getc (void)
|
|
||||||
{
|
|
||||||
enum intr_level old_level;
|
|
||||||
uint8_t key;
|
|
||||||
|
|
||||||
old_level = intr_disable ();
|
|
||||||
key = intq_getc (&buffer);
|
|
||||||
serial_notify ();
|
|
||||||
intr_set_level (old_level);
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if the input buffer is full,
|
|
||||||
false otherwise.
|
|
||||||
Interrupts must be off. */
|
|
||||||
bool
|
|
||||||
input_full (void)
|
|
||||||
{
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
return intq_full (&buffer);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#ifndef DEVICES_INPUT_H
|
|
||||||
#define DEVICES_INPUT_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void input_init (void);
|
|
||||||
void input_putc (uint8_t);
|
|
||||||
uint8_t input_getc (void);
|
|
||||||
bool input_full (void);
|
|
||||||
|
|
||||||
#endif /* devices/input.h */
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
#include "devices/intq.h"
|
|
||||||
#include <debug.h>
|
|
||||||
#include "threads/thread.h"
|
|
||||||
|
|
||||||
static int next (int pos);
|
|
||||||
static void wait (struct intq *q, struct thread **waiter);
|
|
||||||
static void signal (struct intq *q, struct thread **waiter);
|
|
||||||
|
|
||||||
/* Initializes interrupt queue Q. */
|
|
||||||
void
|
|
||||||
intq_init (struct intq *q)
|
|
||||||
{
|
|
||||||
lock_init (&q->lock);
|
|
||||||
q->not_full = q->not_empty = NULL;
|
|
||||||
q->head = q->tail = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if Q is empty, false otherwise. */
|
|
||||||
bool
|
|
||||||
intq_empty (const struct intq *q)
|
|
||||||
{
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
return q->head == q->tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if Q is full, false otherwise. */
|
|
||||||
bool
|
|
||||||
intq_full (const struct intq *q)
|
|
||||||
{
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
return next (q->head) == q->tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes a byte from Q and returns it.
|
|
||||||
If Q is empty, sleeps until a byte is added.
|
|
||||||
When called from an interrupt handler, Q must not be empty. */
|
|
||||||
uint8_t
|
|
||||||
intq_getc (struct intq *q)
|
|
||||||
{
|
|
||||||
uint8_t byte;
|
|
||||||
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
while (intq_empty (q))
|
|
||||||
{
|
|
||||||
ASSERT (!intr_context ());
|
|
||||||
lock_acquire (&q->lock);
|
|
||||||
wait (q, &q->not_empty);
|
|
||||||
lock_release (&q->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte = q->buf[q->tail];
|
|
||||||
q->tail = next (q->tail);
|
|
||||||
signal (q, &q->not_full);
|
|
||||||
return byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adds BYTE to the end of Q.
|
|
||||||
If Q is full, sleeps until a byte is removed.
|
|
||||||
When called from an interrupt handler, Q must not be full. */
|
|
||||||
void
|
|
||||||
intq_putc (struct intq *q, uint8_t byte)
|
|
||||||
{
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
while (intq_full (q))
|
|
||||||
{
|
|
||||||
ASSERT (!intr_context ());
|
|
||||||
lock_acquire (&q->lock);
|
|
||||||
wait (q, &q->not_full);
|
|
||||||
lock_release (&q->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
q->buf[q->head] = byte;
|
|
||||||
q->head = next (q->head);
|
|
||||||
signal (q, &q->not_empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the position after POS within an intq. */
|
|
||||||
static int
|
|
||||||
next (int pos)
|
|
||||||
{
|
|
||||||
return (pos + 1) % INTQ_BUFSIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* WAITER must be the address of Q's not_empty or not_full
|
|
||||||
member. Waits until the given condition is true. */
|
|
||||||
static void
|
|
||||||
wait (struct intq *q UNUSED, struct thread **waiter)
|
|
||||||
{
|
|
||||||
ASSERT (!intr_context ());
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
ASSERT ((waiter == &q->not_empty && intq_empty (q))
|
|
||||||
|| (waiter == &q->not_full && intq_full (q)));
|
|
||||||
|
|
||||||
*waiter = thread_current ();
|
|
||||||
thread_block ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* WAITER must be the address of Q's not_empty or not_full
|
|
||||||
member, and the associated condition must be true. If a
|
|
||||||
thread is waiting for the condition, wakes it up and resets
|
|
||||||
the waiting thread. */
|
|
||||||
static void
|
|
||||||
signal (struct intq *q UNUSED, struct thread **waiter)
|
|
||||||
{
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
ASSERT ((waiter == &q->not_empty && !intq_empty (q))
|
|
||||||
|| (waiter == &q->not_full && !intq_full (q)));
|
|
||||||
|
|
||||||
if (*waiter != NULL)
|
|
||||||
{
|
|
||||||
thread_unblock (*waiter);
|
|
||||||
*waiter = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#ifndef DEVICES_INTQ_H
|
|
||||||
#define DEVICES_INTQ_H
|
|
||||||
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
|
|
||||||
/* An "interrupt queue", a circular buffer shared between
|
|
||||||
kernel threads and external interrupt handlers.
|
|
||||||
|
|
||||||
Interrupt queue functions can be called from kernel threads or
|
|
||||||
from external interrupt handlers. Except for intq_init(),
|
|
||||||
interrupts must be off in either case.
|
|
||||||
|
|
||||||
The interrupt queue has the structure of a "monitor". Locks
|
|
||||||
and condition variables from threads/synch.h cannot be used in
|
|
||||||
this case, as they normally would, because they can only
|
|
||||||
protect kernel threads from one another, not from interrupt
|
|
||||||
handlers. */
|
|
||||||
|
|
||||||
/* Queue buffer size, in bytes. */
|
|
||||||
#define INTQ_BUFSIZE 64
|
|
||||||
|
|
||||||
/* A circular queue of bytes. */
|
|
||||||
struct intq
|
|
||||||
{
|
|
||||||
/* Waiting threads. */
|
|
||||||
struct lock lock; /* Only one thread may wait at once. */
|
|
||||||
struct thread *not_full; /* Thread waiting for not-full condition. */
|
|
||||||
struct thread *not_empty; /* Thread waiting for not-empty condition. */
|
|
||||||
|
|
||||||
/* Queue. */
|
|
||||||
uint8_t buf[INTQ_BUFSIZE]; /* Buffer. */
|
|
||||||
int head; /* New data is written here. */
|
|
||||||
int tail; /* Old data is read here. */
|
|
||||||
};
|
|
||||||
|
|
||||||
void intq_init (struct intq *);
|
|
||||||
bool intq_empty (const struct intq *);
|
|
||||||
bool intq_full (const struct intq *);
|
|
||||||
uint8_t intq_getc (struct intq *);
|
|
||||||
void intq_putc (struct intq *, uint8_t);
|
|
||||||
|
|
||||||
#endif /* devices/intq.h */
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
#include "devices/kbd.h"
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <debug.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "devices/input.h"
|
|
||||||
#include "devices/shutdown.h"
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/io.h"
|
|
||||||
|
|
||||||
/* Keyboard data register port. */
|
|
||||||
#define DATA_REG 0x60
|
|
||||||
|
|
||||||
/* Current state of shift keys.
|
|
||||||
True if depressed, false otherwise. */
|
|
||||||
static bool left_shift, right_shift; /* Left and right Shift keys. */
|
|
||||||
static bool left_alt, right_alt; /* Left and right Alt keys. */
|
|
||||||
static bool left_ctrl, right_ctrl; /* Left and right Ctl keys. */
|
|
||||||
|
|
||||||
/* Status of Caps Lock.
|
|
||||||
True when on, false when off. */
|
|
||||||
static bool caps_lock;
|
|
||||||
|
|
||||||
/* Number of keys pressed. */
|
|
||||||
static int64_t key_cnt;
|
|
||||||
|
|
||||||
static intr_handler_func keyboard_interrupt;
|
|
||||||
|
|
||||||
/* Initializes the keyboard. */
|
|
||||||
void
|
|
||||||
kbd_init (void)
|
|
||||||
{
|
|
||||||
intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prints keyboard statistics. */
|
|
||||||
void
|
|
||||||
kbd_print_stats (void)
|
|
||||||
{
|
|
||||||
printf ("Keyboard: %lld keys pressed\n", key_cnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Maps a set of contiguous scancodes into characters. */
|
|
||||||
struct keymap
|
|
||||||
{
|
|
||||||
uint8_t first_scancode; /* First scancode. */
|
|
||||||
const char *chars; /* chars[0] has scancode first_scancode,
|
|
||||||
chars[1] has scancode first_scancode + 1,
|
|
||||||
and so on to the end of the string. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Keys that produce the same characters regardless of whether
|
|
||||||
the Shift keys are down. Case of letters is an exception
|
|
||||||
that we handle elsewhere. */
|
|
||||||
static const struct keymap invariant_keymap[] =
|
|
||||||
{
|
|
||||||
{0x01, "\033"}, /* Escape. */
|
|
||||||
{0x0e, "\b"},
|
|
||||||
{0x0f, "\tQWERTYUIOP"},
|
|
||||||
{0x1c, "\r"},
|
|
||||||
{0x1e, "ASDFGHJKL"},
|
|
||||||
{0x2c, "ZXCVBNM"},
|
|
||||||
{0x37, "*"},
|
|
||||||
{0x39, " "},
|
|
||||||
{0x53, "\177"}, /* Delete. */
|
|
||||||
{0, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Characters for keys pressed without Shift, for those keys
|
|
||||||
where it matters. */
|
|
||||||
static const struct keymap unshifted_keymap[] =
|
|
||||||
{
|
|
||||||
{0x02, "1234567890-="},
|
|
||||||
{0x1a, "[]"},
|
|
||||||
{0x27, ";'`"},
|
|
||||||
{0x2b, "\\"},
|
|
||||||
{0x33, ",./"},
|
|
||||||
{0, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Characters for keys pressed with Shift, for those keys where
|
|
||||||
it matters. */
|
|
||||||
static const struct keymap shifted_keymap[] =
|
|
||||||
{
|
|
||||||
{0x02, "!@#$%^&*()_+"},
|
|
||||||
{0x1a, "{}"},
|
|
||||||
{0x27, ":\"~"},
|
|
||||||
{0x2b, "|"},
|
|
||||||
{0x33, "<>?"},
|
|
||||||
{0, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
|
|
||||||
|
|
||||||
static void
|
|
||||||
keyboard_interrupt (struct intr_frame *args UNUSED)
|
|
||||||
{
|
|
||||||
/* Status of shift keys. */
|
|
||||||
bool shift = left_shift || right_shift;
|
|
||||||
bool alt = left_alt || right_alt;
|
|
||||||
bool ctrl = left_ctrl || right_ctrl;
|
|
||||||
|
|
||||||
/* Keyboard scancode. */
|
|
||||||
unsigned code;
|
|
||||||
|
|
||||||
/* False if key pressed, true if key released. */
|
|
||||||
bool release;
|
|
||||||
|
|
||||||
/* Character that corresponds to `code'. */
|
|
||||||
uint8_t c;
|
|
||||||
|
|
||||||
/* Read scancode, including second byte if prefix code. */
|
|
||||||
code = inb (DATA_REG);
|
|
||||||
if (code == 0xe0)
|
|
||||||
code = (code << 8) | inb (DATA_REG);
|
|
||||||
|
|
||||||
/* Bit 0x80 distinguishes key press from key release
|
|
||||||
(even if there's a prefix). */
|
|
||||||
release = (code & 0x80) != 0;
|
|
||||||
code &= ~0x80u;
|
|
||||||
|
|
||||||
/* Interpret key. */
|
|
||||||
if (code == 0x3a)
|
|
||||||
{
|
|
||||||
/* Caps Lock. */
|
|
||||||
if (!release)
|
|
||||||
caps_lock = !caps_lock;
|
|
||||||
}
|
|
||||||
else if (map_key (invariant_keymap, code, &c)
|
|
||||||
|| (!shift && map_key (unshifted_keymap, code, &c))
|
|
||||||
|| (shift && map_key (shifted_keymap, code, &c)))
|
|
||||||
{
|
|
||||||
/* Ordinary character. */
|
|
||||||
if (!release)
|
|
||||||
{
|
|
||||||
/* Reboot if Ctrl+Alt+Del pressed. */
|
|
||||||
if (c == 0177 && ctrl && alt)
|
|
||||||
shutdown_reboot ();
|
|
||||||
|
|
||||||
/* Handle Ctrl, Shift.
|
|
||||||
Note that Ctrl overrides Shift. */
|
|
||||||
if (ctrl && c >= 0x40 && c < 0x60)
|
|
||||||
{
|
|
||||||
/* A is 0x41, Ctrl+A is 0x01, etc. */
|
|
||||||
c -= 0x40;
|
|
||||||
}
|
|
||||||
else if (shift == caps_lock)
|
|
||||||
c = tolower (c);
|
|
||||||
|
|
||||||
/* Handle Alt by setting the high bit.
|
|
||||||
This 0x80 is unrelated to the one used to
|
|
||||||
distinguish key press from key release. */
|
|
||||||
if (alt)
|
|
||||||
c += 0x80;
|
|
||||||
|
|
||||||
/* Append to keyboard buffer. */
|
|
||||||
if (!input_full ())
|
|
||||||
{
|
|
||||||
key_cnt++;
|
|
||||||
input_putc (c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Maps a keycode into a shift state variable. */
|
|
||||||
struct shift_key
|
|
||||||
{
|
|
||||||
unsigned scancode;
|
|
||||||
bool *state_var;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Table of shift keys. */
|
|
||||||
static const struct shift_key shift_keys[] =
|
|
||||||
{
|
|
||||||
{ 0x2a, &left_shift},
|
|
||||||
{ 0x36, &right_shift},
|
|
||||||
{ 0x38, &left_alt},
|
|
||||||
{0xe038, &right_alt},
|
|
||||||
{ 0x1d, &left_ctrl},
|
|
||||||
{0xe01d, &right_ctrl},
|
|
||||||
{0, NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct shift_key *key;
|
|
||||||
|
|
||||||
/* Scan the table. */
|
|
||||||
for (key = shift_keys; key->scancode != 0; key++)
|
|
||||||
if (key->scancode == code)
|
|
||||||
{
|
|
||||||
*key->state_var = !release;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scans the array of keymaps K for SCANCODE.
|
|
||||||
If found, sets *C to the corresponding character and returns
|
|
||||||
true.
|
|
||||||
If not found, returns false and C is ignored. */
|
|
||||||
static bool
|
|
||||||
map_key (const struct keymap k[], unsigned scancode, uint8_t *c)
|
|
||||||
{
|
|
||||||
for (; k->first_scancode != 0; k++)
|
|
||||||
if (scancode >= k->first_scancode
|
|
||||||
&& scancode < k->first_scancode + strlen (k->chars))
|
|
||||||
{
|
|
||||||
*c = k->chars[scancode - k->first_scancode];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#ifndef DEVICES_KBD_H
|
|
||||||
#define DEVICES_KBD_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void kbd_init (void);
|
|
||||||
void kbd_print_stats (void);
|
|
||||||
|
|
||||||
#endif /* devices/kbd.h */
|
|
||||||
@@ -1,324 +0,0 @@
|
|||||||
#include "devices/partition.h"
|
|
||||||
#include <packed.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "devices/block.h"
|
|
||||||
#include "threads/malloc.h"
|
|
||||||
|
|
||||||
/* A partition of a block device. */
|
|
||||||
struct partition
|
|
||||||
{
|
|
||||||
struct block *block; /* Underlying block device. */
|
|
||||||
block_sector_t start; /* First sector within device. */
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct block_operations partition_operations;
|
|
||||||
|
|
||||||
static void read_partition_table (struct block *, block_sector_t sector,
|
|
||||||
block_sector_t primary_extended_sector,
|
|
||||||
int *part_nr);
|
|
||||||
static void found_partition (struct block *, uint8_t type,
|
|
||||||
block_sector_t start, block_sector_t size,
|
|
||||||
int part_nr);
|
|
||||||
static const char *partition_type_name (uint8_t);
|
|
||||||
|
|
||||||
/* Scans BLOCK for partitions of interest to PintOS. */
|
|
||||||
void
|
|
||||||
partition_scan (struct block *block)
|
|
||||||
{
|
|
||||||
int part_nr = 0;
|
|
||||||
read_partition_table (block, 0, 0, &part_nr);
|
|
||||||
if (part_nr == 0)
|
|
||||||
printf ("%s: Device contains no partitions\n", block_name (block));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads the partition table in the given SECTOR of BLOCK and
|
|
||||||
scans it for partitions of interest to PintOS.
|
|
||||||
|
|
||||||
If SECTOR is 0, so that this is the top-level partition table
|
|
||||||
on BLOCK, then PRIMARY_EXTENDED_SECTOR is not meaningful;
|
|
||||||
otherwise, it should designate the sector of the top-level
|
|
||||||
extended partition table that was traversed to arrive at
|
|
||||||
SECTOR, for use in finding logical partitions (see the large
|
|
||||||
comment below).
|
|
||||||
|
|
||||||
PART_NR points to the number of non-empty primary or logical
|
|
||||||
partitions already encountered on BLOCK. It is incremented as
|
|
||||||
partitions are found. */
|
|
||||||
static void
|
|
||||||
read_partition_table (struct block *block, block_sector_t sector,
|
|
||||||
block_sector_t primary_extended_sector,
|
|
||||||
int *part_nr)
|
|
||||||
{
|
|
||||||
/* Format of a partition table entry. See [Partitions]. */
|
|
||||||
struct partition_table_entry
|
|
||||||
{
|
|
||||||
uint8_t bootable; /* 0x00=not bootable, 0x80=bootable. */
|
|
||||||
uint8_t start_chs[3]; /* Encoded starting cylinder, head, sector. */
|
|
||||||
uint8_t type; /* Partition type (see partition_type_name). */
|
|
||||||
uint8_t end_chs[3]; /* Encoded ending cylinder, head, sector. */
|
|
||||||
uint32_t offset; /* Start sector offset from partition table. */
|
|
||||||
uint32_t size; /* Number of sectors. */
|
|
||||||
}
|
|
||||||
PACKED;
|
|
||||||
|
|
||||||
/* Partition table sector. */
|
|
||||||
struct partition_table
|
|
||||||
{
|
|
||||||
uint8_t loader[446]; /* Loader, in top-level partition table. */
|
|
||||||
struct partition_table_entry partitions[4]; /* Table entries. */
|
|
||||||
uint16_t signature; /* Should be 0xaa55. */
|
|
||||||
}
|
|
||||||
PACKED;
|
|
||||||
|
|
||||||
struct partition_table *pt;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
/* Check SECTOR validity. */
|
|
||||||
if (sector >= block_size (block))
|
|
||||||
{
|
|
||||||
printf ("%s: Partition table at sector %"PRDSNu" past end of device.\n",
|
|
||||||
block_name (block), sector);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read sector. */
|
|
||||||
ASSERT (sizeof *pt == BLOCK_SECTOR_SIZE);
|
|
||||||
pt = malloc (sizeof *pt);
|
|
||||||
if (pt == NULL)
|
|
||||||
PANIC ("Failed to allocate memory for partition table.");
|
|
||||||
block_read (block, 0, pt);
|
|
||||||
|
|
||||||
/* Check signature. */
|
|
||||||
if (pt->signature != 0xaa55)
|
|
||||||
{
|
|
||||||
if (primary_extended_sector == 0)
|
|
||||||
printf ("%s: Invalid partition table signature\n", block_name (block));
|
|
||||||
else
|
|
||||||
printf ("%s: Invalid extended partition table in sector %"PRDSNu"\n",
|
|
||||||
block_name (block), sector);
|
|
||||||
free (pt);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse partitions. */
|
|
||||||
for (i = 0; i < sizeof pt->partitions / sizeof *pt->partitions; i++)
|
|
||||||
{
|
|
||||||
struct partition_table_entry *e = &pt->partitions[i];
|
|
||||||
|
|
||||||
if (e->size == 0 || e->type == 0)
|
|
||||||
{
|
|
||||||
/* Ignore empty partition. */
|
|
||||||
}
|
|
||||||
else if (e->type == 0x05 /* Extended partition. */
|
|
||||||
|| e->type == 0x0f /* Windows 98 extended partition. */
|
|
||||||
|| e->type == 0x85 /* Linux extended partition. */
|
|
||||||
|| e->type == 0xc5) /* DR-DOS extended partition. */
|
|
||||||
{
|
|
||||||
printf ("%s: Extended partition in sector %"PRDSNu"\n",
|
|
||||||
block_name (block), sector);
|
|
||||||
|
|
||||||
/* The interpretation of the offset field for extended
|
|
||||||
partitions is bizarre. When the extended partition
|
|
||||||
table entry is in the master boot record, that is,
|
|
||||||
the device's primary partition table in sector 0, then
|
|
||||||
the offset is an absolute sector number. Otherwise,
|
|
||||||
no matter how deep the partition table we're reading
|
|
||||||
is nested, the offset is relative to the start of
|
|
||||||
the extended partition that the MBR points to. */
|
|
||||||
if (sector == 0)
|
|
||||||
read_partition_table (block, e->offset, e->offset, part_nr);
|
|
||||||
else
|
|
||||||
read_partition_table (block, e->offset + primary_extended_sector,
|
|
||||||
primary_extended_sector, part_nr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++*part_nr;
|
|
||||||
|
|
||||||
found_partition (block, e->type, e->offset + sector,
|
|
||||||
e->size, *part_nr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free (pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We have found a primary or logical partition of the given TYPE
|
|
||||||
on BLOCK, starting at sector START and continuing for SIZE
|
|
||||||
sectors, which we are giving the partition number PART_NR.
|
|
||||||
Check whether this is a partition of interest to PintOS, and
|
|
||||||
if so then add it to the proper element of partitions[]. */
|
|
||||||
static void
|
|
||||||
found_partition (struct block *block, uint8_t part_type,
|
|
||||||
block_sector_t start, block_sector_t size,
|
|
||||||
int part_nr)
|
|
||||||
{
|
|
||||||
if (start >= block_size (block))
|
|
||||||
printf ("%s%d: Partition starts past end of device (sector %"PRDSNu")\n",
|
|
||||||
block_name (block), part_nr, start);
|
|
||||||
else if (start + size < start || start + size > block_size (block))
|
|
||||||
printf ("%s%d: Partition end (%"PRDSNu") past end of device (%"PRDSNu")\n",
|
|
||||||
block_name (block), part_nr, start + size, block_size (block));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
enum block_type type = (part_type == 0x20 ? BLOCK_KERNEL
|
|
||||||
: part_type == 0x21 ? BLOCK_FILESYS
|
|
||||||
: part_type == 0x22 ? BLOCK_SCRATCH
|
|
||||||
: part_type == 0x23 ? BLOCK_SWAP
|
|
||||||
: BLOCK_FOREIGN);
|
|
||||||
struct partition *p;
|
|
||||||
char extra_info[128];
|
|
||||||
char name[16];
|
|
||||||
|
|
||||||
p = malloc (sizeof *p);
|
|
||||||
if (p == NULL)
|
|
||||||
PANIC ("Failed to allocate memory for partition descriptor");
|
|
||||||
p->block = block;
|
|
||||||
p->start = start;
|
|
||||||
|
|
||||||
snprintf (name, sizeof name, "%s%d", block_name (block), part_nr);
|
|
||||||
snprintf (extra_info, sizeof extra_info, "%s (%02x)",
|
|
||||||
partition_type_name (part_type), part_type);
|
|
||||||
block_register (name, type, extra_info, size, &partition_operations, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a human-readable name for the given partition TYPE. */
|
|
||||||
static const char *
|
|
||||||
partition_type_name (uint8_t type)
|
|
||||||
{
|
|
||||||
/* Name of each known type of partition.
|
|
||||||
From util-linux-2.12r/fdisk/i386_sys_types.c.
|
|
||||||
This initializer makes use of a C99 feature that allows
|
|
||||||
array elements to be initialized by index. */
|
|
||||||
static const char *type_names[256] =
|
|
||||||
{
|
|
||||||
[0x00] = "Empty",
|
|
||||||
[0x01] = "FAT12",
|
|
||||||
[0x02] = "XENIX root",
|
|
||||||
[0x03] = "XENIX usr",
|
|
||||||
[0x04] = "FAT16 <32M",
|
|
||||||
[0x05] = "Extended",
|
|
||||||
[0x06] = "FAT16",
|
|
||||||
[0x07] = "HPFS/NTFS",
|
|
||||||
[0x08] = "AIX",
|
|
||||||
[0x09] = "AIX bootable",
|
|
||||||
[0x0a] = "OS/2 Boot Manager",
|
|
||||||
[0x0b] = "W95 FAT32",
|
|
||||||
[0x0c] = "W95 FAT32 (LBA)",
|
|
||||||
[0x0e] = "W95 FAT16 (LBA)",
|
|
||||||
[0x0f] = "W95 Ext'd (LBA)",
|
|
||||||
[0x10] = "OPUS",
|
|
||||||
[0x11] = "Hidden FAT12",
|
|
||||||
[0x12] = "Compaq diagnostics",
|
|
||||||
[0x14] = "Hidden FAT16 <32M",
|
|
||||||
[0x16] = "Hidden FAT16",
|
|
||||||
[0x17] = "Hidden HPFS/NTFS",
|
|
||||||
[0x18] = "AST SmartSleep",
|
|
||||||
[0x1b] = "Hidden W95 FAT32",
|
|
||||||
[0x1c] = "Hidden W95 FAT32 (LBA)",
|
|
||||||
[0x1e] = "Hidden W95 FAT16 (LBA)",
|
|
||||||
[0x20] = "PintOS OS kernel",
|
|
||||||
[0x21] = "PintOS file system",
|
|
||||||
[0x22] = "PintOS scratch",
|
|
||||||
[0x23] = "PintOS swap",
|
|
||||||
[0x24] = "NEC DOS",
|
|
||||||
[0x39] = "Plan 9",
|
|
||||||
[0x3c] = "PartitionMagic recovery",
|
|
||||||
[0x40] = "Venix 80286",
|
|
||||||
[0x41] = "PPC PReP Boot",
|
|
||||||
[0x42] = "SFS",
|
|
||||||
[0x4d] = "QNX4.x",
|
|
||||||
[0x4e] = "QNX4.x 2nd part",
|
|
||||||
[0x4f] = "QNX4.x 3rd part",
|
|
||||||
[0x50] = "OnTrack DM",
|
|
||||||
[0x51] = "OnTrack DM6 Aux1",
|
|
||||||
[0x52] = "CP/M",
|
|
||||||
[0x53] = "OnTrack DM6 Aux3",
|
|
||||||
[0x54] = "OnTrackDM6",
|
|
||||||
[0x55] = "EZ-Drive",
|
|
||||||
[0x56] = "Golden Bow",
|
|
||||||
[0x5c] = "Priam Edisk",
|
|
||||||
[0x61] = "SpeedStor",
|
|
||||||
[0x63] = "GNU HURD or SysV",
|
|
||||||
[0x64] = "Novell Netware 286",
|
|
||||||
[0x65] = "Novell Netware 386",
|
|
||||||
[0x70] = "DiskSecure Multi-Boot",
|
|
||||||
[0x75] = "PC/IX",
|
|
||||||
[0x80] = "Old Minix",
|
|
||||||
[0x81] = "Minix / old Linux",
|
|
||||||
[0x82] = "Linux swap / Solaris",
|
|
||||||
[0x83] = "Linux",
|
|
||||||
[0x84] = "OS/2 hidden C: drive",
|
|
||||||
[0x85] = "Linux extended",
|
|
||||||
[0x86] = "NTFS volume set",
|
|
||||||
[0x87] = "NTFS volume set",
|
|
||||||
[0x88] = "Linux plaintext",
|
|
||||||
[0x8e] = "Linux LVM",
|
|
||||||
[0x93] = "Amoeba",
|
|
||||||
[0x94] = "Amoeba BBT",
|
|
||||||
[0x9f] = "BSD/OS",
|
|
||||||
[0xa0] = "IBM Thinkpad hibernation",
|
|
||||||
[0xa5] = "FreeBSD",
|
|
||||||
[0xa6] = "OpenBSD",
|
|
||||||
[0xa7] = "NeXTSTEP",
|
|
||||||
[0xa8] = "Darwin UFS",
|
|
||||||
[0xa9] = "NetBSD",
|
|
||||||
[0xab] = "Darwin boot",
|
|
||||||
[0xb7] = "BSDI fs",
|
|
||||||
[0xb8] = "BSDI swap",
|
|
||||||
[0xbb] = "Boot Wizard hidden",
|
|
||||||
[0xbe] = "Solaris boot",
|
|
||||||
[0xbf] = "Solaris",
|
|
||||||
[0xc1] = "DRDOS/sec (FAT-12)",
|
|
||||||
[0xc4] = "DRDOS/sec (FAT-16 < 32M)",
|
|
||||||
[0xc6] = "DRDOS/sec (FAT-16)",
|
|
||||||
[0xc7] = "Syrinx",
|
|
||||||
[0xda] = "Non-FS data",
|
|
||||||
[0xdb] = "CP/M / CTOS / ...",
|
|
||||||
[0xde] = "Dell Utility",
|
|
||||||
[0xdf] = "BootIt",
|
|
||||||
[0xe1] = "DOS access",
|
|
||||||
[0xe3] = "DOS R/O",
|
|
||||||
[0xe4] = "SpeedStor",
|
|
||||||
[0xeb] = "BeOS fs",
|
|
||||||
[0xee] = "EFI GPT",
|
|
||||||
[0xef] = "EFI (FAT-12/16/32)",
|
|
||||||
[0xf0] = "Linux/PA-RISC boot",
|
|
||||||
[0xf1] = "SpeedStor",
|
|
||||||
[0xf4] = "SpeedStor",
|
|
||||||
[0xf2] = "DOS secondary",
|
|
||||||
[0xfd] = "Linux raid autodetect",
|
|
||||||
[0xfe] = "LANstep",
|
|
||||||
[0xff] = "BBT",
|
|
||||||
};
|
|
||||||
|
|
||||||
return type_names[type] != NULL ? type_names[type] : "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads sector SECTOR from partition P into BUFFER, which must
|
|
||||||
have room for BLOCK_SECTOR_SIZE bytes. */
|
|
||||||
static void
|
|
||||||
partition_read (void *p_, block_sector_t sector, void *buffer)
|
|
||||||
{
|
|
||||||
struct partition *p = p_;
|
|
||||||
block_read (p->block, p->start + sector, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write sector SECTOR to partition P from BUFFER, which must
|
|
||||||
contain BLOCK_SECTOR_SIZE bytes. Returns after the block has
|
|
||||||
acknowledged receiving the data. */
|
|
||||||
static void
|
|
||||||
partition_write (void *p_, block_sector_t sector, const void *buffer)
|
|
||||||
{
|
|
||||||
struct partition *p = p_;
|
|
||||||
block_write (p->block, p->start + sector, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct block_operations partition_operations =
|
|
||||||
{
|
|
||||||
partition_read,
|
|
||||||
partition_write
|
|
||||||
};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#ifndef DEVICES_PARTITION_H
|
|
||||||
#define DEVICES_PARTITION_H
|
|
||||||
|
|
||||||
struct block;
|
|
||||||
|
|
||||||
void partition_scan (struct block *);
|
|
||||||
|
|
||||||
#endif /* devices/partition.h */
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
#include "devices/pit.h"
|
|
||||||
#include <debug.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/io.h"
|
|
||||||
|
|
||||||
/* Interface to 8254 Programmable Interrupt Timer (PIT).
|
|
||||||
Refer to [8254] for details. */
|
|
||||||
|
|
||||||
/* 8254 registers. */
|
|
||||||
#define PIT_PORT_CONTROL 0x43 /* Control port. */
|
|
||||||
#define PIT_PORT_COUNTER(CHANNEL) (0x40 + (CHANNEL)) /* Counter port. */
|
|
||||||
|
|
||||||
/* PIT cycles per second. */
|
|
||||||
#define PIT_HZ 1193180
|
|
||||||
|
|
||||||
/* Configure the given CHANNEL in the PIT. In a PC, the PIT's
|
|
||||||
three output channels are hooked up like this:
|
|
||||||
|
|
||||||
- Channel 0 is connected to interrupt line 0, so that it can
|
|
||||||
be used as a periodic timer interrupt, as implemented in
|
|
||||||
PintOS in devices/timer.c.
|
|
||||||
|
|
||||||
- Channel 1 is used for dynamic RAM refresh (in older PCs).
|
|
||||||
No good can come of messing with this.
|
|
||||||
|
|
||||||
- Channel 2 is connected to the PC speaker, so that it can
|
|
||||||
be used to play a tone, as implemented in PintOS in
|
|
||||||
devices/speaker.c.
|
|
||||||
|
|
||||||
MODE specifies the form of output:
|
|
||||||
|
|
||||||
- Mode 2 is a periodic pulse: the channel's output is 1 for
|
|
||||||
most of the period, but drops to 0 briefly toward the end
|
|
||||||
of the period. This is useful for hooking up to an
|
|
||||||
interrupt controller to generate a periodic interrupt.
|
|
||||||
|
|
||||||
- Mode 3 is a square wave: for the first half of the period
|
|
||||||
it is 1, for the second half it is 0. This is useful for
|
|
||||||
generating a tone on a speaker.
|
|
||||||
|
|
||||||
- Other modes are less useful.
|
|
||||||
|
|
||||||
FREQUENCY is the number of periods per second, in Hz. */
|
|
||||||
void
|
|
||||||
pit_configure_channel (int channel, int mode, int frequency)
|
|
||||||
{
|
|
||||||
uint16_t count;
|
|
||||||
enum intr_level old_level;
|
|
||||||
|
|
||||||
ASSERT (channel == 0 || channel == 2);
|
|
||||||
ASSERT (mode == 2 || mode == 3);
|
|
||||||
|
|
||||||
/* Convert FREQUENCY to a PIT counter value. The PIT has a
|
|
||||||
clock that runs at PIT_HZ cycles per second. We must
|
|
||||||
translate FREQUENCY into a number of these cycles. */
|
|
||||||
if (frequency < 19)
|
|
||||||
{
|
|
||||||
/* Frequency is too low: the quotient would overflow the
|
|
||||||
16-bit counter. Force it to 0, which the PIT treats as
|
|
||||||
65536, the highest possible count. This yields a 18.2
|
|
||||||
Hz timer, approximately. */
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
else if (frequency > PIT_HZ)
|
|
||||||
{
|
|
||||||
/* Frequency is too high: the quotient would underflow to
|
|
||||||
0, which the PIT would interpret as 65536. A count of 1
|
|
||||||
is illegal in mode 2, so we force it to 2, which yields
|
|
||||||
a 596.590 kHz timer, approximately. (This timer rate is
|
|
||||||
probably too fast to be useful anyhow.) */
|
|
||||||
count = 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
count = (PIT_HZ + frequency / 2) / frequency;
|
|
||||||
|
|
||||||
/* Configure the PIT mode and load its counters. */
|
|
||||||
old_level = intr_disable ();
|
|
||||||
outb (PIT_PORT_CONTROL, (channel << 6) | 0x30 | (mode << 1));
|
|
||||||
outb (PIT_PORT_COUNTER (channel), count);
|
|
||||||
outb (PIT_PORT_COUNTER (channel), count >> 8);
|
|
||||||
intr_set_level (old_level);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#ifndef DEVICES_PIT_H
|
|
||||||
#define DEVICES_PIT_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void pit_configure_channel (int channel, int mode, int frequency);
|
|
||||||
|
|
||||||
#endif /* devices/pit.h */
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
#include "devices/rtc.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "threads/io.h"
|
|
||||||
|
|
||||||
/* This code is an interface to the MC146818A-compatible real
|
|
||||||
time clock found on PC motherboards. See [MC146818A] for
|
|
||||||
hardware details. */
|
|
||||||
|
|
||||||
/* I/O register addresses. */
|
|
||||||
#define CMOS_REG_SET 0x70 /* Selects CMOS register exposed by REG_IO. */
|
|
||||||
#define CMOS_REG_IO 0x71 /* Contains the selected data byte. */
|
|
||||||
|
|
||||||
/* Indexes of CMOS registers with real-time clock functions.
|
|
||||||
Note that all of these registers are in BCD format,
|
|
||||||
so that 0x59 means 59, not 89. */
|
|
||||||
#define RTC_REG_SEC 0 /* Second: 0x00...0x59. */
|
|
||||||
#define RTC_REG_MIN 2 /* Minute: 0x00...0x59. */
|
|
||||||
#define RTC_REG_HOUR 4 /* Hour: 0x00...0x23. */
|
|
||||||
#define RTC_REG_MDAY 7 /* Day of the month: 0x01...0x31. */
|
|
||||||
#define RTC_REG_MON 8 /* Month: 0x01...0x12. */
|
|
||||||
#define RTC_REG_YEAR 9 /* Year: 0x00...0x99. */
|
|
||||||
|
|
||||||
/* Indexes of CMOS control registers. */
|
|
||||||
#define RTC_REG_A 0x0a /* Register A: update-in-progress. */
|
|
||||||
#define RTC_REG_B 0x0b /* Register B: 24/12 hour time, irq enables. */
|
|
||||||
#define RTC_REG_C 0x0c /* Register C: pending interrupts. */
|
|
||||||
#define RTC_REG_D 0x0d /* Register D: valid time? */
|
|
||||||
|
|
||||||
/* Register A. */
|
|
||||||
#define RTCSA_UIP 0x80 /* Set while time update in progress. */
|
|
||||||
|
|
||||||
/* Register B. */
|
|
||||||
#define RTCSB_SET 0x80 /* Disables update to let time be set. */
|
|
||||||
#define RTCSB_DM 0x04 /* 0 = BCD time format, 1 = binary format. */
|
|
||||||
#define RTCSB_24HR 0x02 /* 0 = 12-hour format, 1 = 24-hour format. */
|
|
||||||
|
|
||||||
static int bcd_to_bin (uint8_t);
|
|
||||||
static uint8_t cmos_read (uint8_t index);
|
|
||||||
|
|
||||||
/* Returns number of seconds since Unix epoch of January 1,
|
|
||||||
1970. */
|
|
||||||
time_t
|
|
||||||
rtc_get_time (void)
|
|
||||||
{
|
|
||||||
static const int days_per_month[12] =
|
|
||||||
{
|
|
||||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
||||||
};
|
|
||||||
int sec, min, hour, mday, mon, year;
|
|
||||||
time_t time;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Get time components.
|
|
||||||
|
|
||||||
We repeatedly read the time until it is stable from one read
|
|
||||||
to another, in case we start our initial read in the middle
|
|
||||||
of an update. This strategy is not recommended by the
|
|
||||||
MC146818A datasheet, but it is simpler than any of their
|
|
||||||
suggestions and, furthermore, it is also used by Linux.
|
|
||||||
|
|
||||||
The MC146818A can be configured for BCD or binary format,
|
|
||||||
but for historical reasons everyone always uses BCD format
|
|
||||||
except on obscure non-PC platforms, so we don't bother
|
|
||||||
trying to detect the format in use. */
|
|
||||||
do
|
|
||||||
{
|
|
||||||
sec = bcd_to_bin (cmos_read (RTC_REG_SEC));
|
|
||||||
min = bcd_to_bin (cmos_read (RTC_REG_MIN));
|
|
||||||
hour = bcd_to_bin (cmos_read (RTC_REG_HOUR));
|
|
||||||
mday = bcd_to_bin (cmos_read (RTC_REG_MDAY));
|
|
||||||
mon = bcd_to_bin (cmos_read (RTC_REG_MON));
|
|
||||||
year = bcd_to_bin (cmos_read (RTC_REG_YEAR));
|
|
||||||
}
|
|
||||||
while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC)));
|
|
||||||
|
|
||||||
/* Translate years-since-1900 into years-since-1970.
|
|
||||||
If it's before the epoch, assume that it has passed 2000.
|
|
||||||
This will break at 2070, but that's long after our 31-bit
|
|
||||||
time_t breaks in 2038. */
|
|
||||||
if (year < 70)
|
|
||||||
year += 100;
|
|
||||||
year -= 70;
|
|
||||||
|
|
||||||
/* Break down all components into seconds. */
|
|
||||||
time = (year * 365 + (year - 1) / 4) * 24 * 60 * 60;
|
|
||||||
for (i = 1; i <= mon; i++)
|
|
||||||
time += days_per_month[i - 1] * 24 * 60 * 60;
|
|
||||||
if (mon > 2 && year % 4 == 0)
|
|
||||||
time += 24 * 60 * 60;
|
|
||||||
time += (mday - 1) * 24 * 60 * 60;
|
|
||||||
time += hour * 60 * 60;
|
|
||||||
time += min * 60;
|
|
||||||
time += sec;
|
|
||||||
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the integer value of the given BCD byte. */
|
|
||||||
static int
|
|
||||||
bcd_to_bin (uint8_t x)
|
|
||||||
{
|
|
||||||
return (x & 0x0f) + ((x >> 4) * 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads a byte from the CMOS register with the given INDEX and
|
|
||||||
returns the byte read. */
|
|
||||||
static uint8_t
|
|
||||||
cmos_read (uint8_t index)
|
|
||||||
{
|
|
||||||
outb (CMOS_REG_SET, index);
|
|
||||||
return inb (CMOS_REG_IO);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#ifndef RTC_H
|
|
||||||
#define RTC_H
|
|
||||||
|
|
||||||
typedef unsigned long time_t;
|
|
||||||
|
|
||||||
time_t rtc_get_time (void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
#include "devices/serial.h"
|
|
||||||
#include <debug.h>
|
|
||||||
#include "devices/input.h"
|
|
||||||
#include "devices/intq.h"
|
|
||||||
#include "devices/timer.h"
|
|
||||||
#include "threads/io.h"
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
#include "threads/thread.h"
|
|
||||||
|
|
||||||
/* Register definitions for the 16550A UART used in PCs.
|
|
||||||
The 16550A has a lot more going on than shown here, but this
|
|
||||||
is all we need.
|
|
||||||
|
|
||||||
Refer to [PC16650D] for hardware information. */
|
|
||||||
|
|
||||||
/* I/O port base address for the first serial port. */
|
|
||||||
#define IO_BASE 0x3f8
|
|
||||||
|
|
||||||
/* DLAB=0 registers. */
|
|
||||||
#define RBR_REG (IO_BASE + 0) /* Receiver Buffer Reg. (read-only). */
|
|
||||||
#define THR_REG (IO_BASE + 0) /* Transmitter Holding Reg. (write-only). */
|
|
||||||
#define IER_REG (IO_BASE + 1) /* Interrupt Enable Reg.. */
|
|
||||||
|
|
||||||
/* DLAB=1 registers. */
|
|
||||||
#define LS_REG (IO_BASE + 0) /* Divisor Latch (LSB). */
|
|
||||||
#define MS_REG (IO_BASE + 1) /* Divisor Latch (MSB). */
|
|
||||||
|
|
||||||
/* DLAB-insensitive registers. */
|
|
||||||
#define IIR_REG (IO_BASE + 2) /* Interrupt Identification Reg. (read-only) */
|
|
||||||
#define FCR_REG (IO_BASE + 2) /* FIFO Control Reg. (write-only). */
|
|
||||||
#define LCR_REG (IO_BASE + 3) /* Line Control Register. */
|
|
||||||
#define MCR_REG (IO_BASE + 4) /* MODEM Control Register. */
|
|
||||||
#define LSR_REG (IO_BASE + 5) /* Line Status Register (read-only). */
|
|
||||||
|
|
||||||
/* Interrupt Enable Register bits. */
|
|
||||||
#define IER_RECV 0x01 /* Interrupt when data received. */
|
|
||||||
#define IER_XMIT 0x02 /* Interrupt when transmit finishes. */
|
|
||||||
|
|
||||||
/* Line Control Register bits. */
|
|
||||||
#define LCR_N81 0x03 /* No parity, 8 data bits, 1 stop bit. */
|
|
||||||
#define LCR_DLAB 0x80 /* Divisor Latch Access Bit (DLAB). */
|
|
||||||
|
|
||||||
/* MODEM Control Register. */
|
|
||||||
#define MCR_OUT2 0x08 /* Output line 2. */
|
|
||||||
|
|
||||||
/* Line Status Register. */
|
|
||||||
#define LSR_DR 0x01 /* Data Ready: received data byte is in RBR. */
|
|
||||||
#define LSR_THRE 0x20 /* THR Empty. */
|
|
||||||
|
|
||||||
/* Transmission mode. */
|
|
||||||
static enum { UNINIT, POLL, QUEUE } mode;
|
|
||||||
|
|
||||||
/* Data to be transmitted. */
|
|
||||||
static struct intq txq;
|
|
||||||
|
|
||||||
static void set_serial (int bps);
|
|
||||||
static void putc_poll (uint8_t);
|
|
||||||
static void write_ier (void);
|
|
||||||
static intr_handler_func serial_interrupt;
|
|
||||||
|
|
||||||
/* Initializes the serial port device for polling mode.
|
|
||||||
Polling mode busy-waits for the serial port to become free
|
|
||||||
before writing to it. It's slow, but until interrupts have
|
|
||||||
been initialized it's all we can do. */
|
|
||||||
static void
|
|
||||||
init_poll (void)
|
|
||||||
{
|
|
||||||
ASSERT (mode == UNINIT);
|
|
||||||
outb (IER_REG, 0); /* Turn off all interrupts. */
|
|
||||||
outb (FCR_REG, 0); /* Disable FIFO. */
|
|
||||||
set_serial (9600); /* 9.6 kbps, N-8-1. */
|
|
||||||
outb (MCR_REG, MCR_OUT2); /* Required to enable interrupts. */
|
|
||||||
intq_init (&txq);
|
|
||||||
mode = POLL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initializes the serial port device for queued interrupt-driven
|
|
||||||
I/O. With interrupt-driven I/O we don't waste CPU time
|
|
||||||
waiting for the serial device to become ready. */
|
|
||||||
void
|
|
||||||
serial_init_queue (void)
|
|
||||||
{
|
|
||||||
enum intr_level old_level;
|
|
||||||
|
|
||||||
if (mode == UNINIT)
|
|
||||||
init_poll ();
|
|
||||||
ASSERT (mode == POLL);
|
|
||||||
|
|
||||||
intr_register_ext (0x20 + 4, serial_interrupt, "serial");
|
|
||||||
mode = QUEUE;
|
|
||||||
old_level = intr_disable ();
|
|
||||||
write_ier ();
|
|
||||||
intr_set_level (old_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sends BYTE to the serial port. */
|
|
||||||
void
|
|
||||||
serial_putc (uint8_t byte)
|
|
||||||
{
|
|
||||||
enum intr_level old_level = intr_disable ();
|
|
||||||
|
|
||||||
if (mode != QUEUE)
|
|
||||||
{
|
|
||||||
/* If we're not set up for interrupt-driven I/O yet,
|
|
||||||
use dumb polling to transmit a byte. */
|
|
||||||
if (mode == UNINIT)
|
|
||||||
init_poll ();
|
|
||||||
putc_poll (byte);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Otherwise, queue a byte and update the interrupt enable
|
|
||||||
register. */
|
|
||||||
if (old_level == INTR_OFF && intq_full (&txq))
|
|
||||||
{
|
|
||||||
/* Interrupts are off and the transmit queue is full.
|
|
||||||
If we wanted to wait for the queue to empty,
|
|
||||||
we'd have to reenable interrupts.
|
|
||||||
That's impolite, so we'll send a character via
|
|
||||||
polling instead. */
|
|
||||||
putc_poll (intq_getc (&txq));
|
|
||||||
}
|
|
||||||
|
|
||||||
intq_putc (&txq, byte);
|
|
||||||
write_ier ();
|
|
||||||
}
|
|
||||||
|
|
||||||
intr_set_level (old_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flushes anything in the serial buffer out the port in polling
|
|
||||||
mode. */
|
|
||||||
void
|
|
||||||
serial_flush (void)
|
|
||||||
{
|
|
||||||
enum intr_level old_level = intr_disable ();
|
|
||||||
while (!intq_empty (&txq))
|
|
||||||
putc_poll (intq_getc (&txq));
|
|
||||||
intr_set_level (old_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The fullness of the input buffer may have changed. Reassess
|
|
||||||
whether we should block receive interrupts.
|
|
||||||
Called by the input buffer routines when characters are added
|
|
||||||
to or removed from the buffer. */
|
|
||||||
void
|
|
||||||
serial_notify (void)
|
|
||||||
{
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
if (mode == QUEUE)
|
|
||||||
write_ier ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configures the serial port for BPS bits per second. */
|
|
||||||
static void
|
|
||||||
set_serial (int bps)
|
|
||||||
{
|
|
||||||
int base_rate = 1843200 / 16; /* Base rate of 16550A, in Hz. */
|
|
||||||
uint16_t divisor = base_rate / bps; /* Clock rate divisor. */
|
|
||||||
|
|
||||||
ASSERT (bps >= 300 && bps <= 115200);
|
|
||||||
|
|
||||||
/* Enable DLAB. */
|
|
||||||
outb (LCR_REG, LCR_N81 | LCR_DLAB);
|
|
||||||
|
|
||||||
/* Set data rate. */
|
|
||||||
outb (LS_REG, divisor & 0xff);
|
|
||||||
outb (MS_REG, divisor >> 8);
|
|
||||||
|
|
||||||
/* Reset DLAB. */
|
|
||||||
outb (LCR_REG, LCR_N81);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update interrupt enable register. */
|
|
||||||
static void
|
|
||||||
write_ier (void)
|
|
||||||
{
|
|
||||||
uint8_t ier = 0;
|
|
||||||
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
|
|
||||||
/* Enable transmit interrupt if we have any characters to
|
|
||||||
transmit. */
|
|
||||||
if (!intq_empty (&txq))
|
|
||||||
ier |= IER_XMIT;
|
|
||||||
|
|
||||||
/* Enable receive interrupt if we have room to store any
|
|
||||||
characters we receive. */
|
|
||||||
if (!input_full ())
|
|
||||||
ier |= IER_RECV;
|
|
||||||
|
|
||||||
outb (IER_REG, ier);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Polls the serial port until it's ready,
|
|
||||||
and then transmits BYTE. */
|
|
||||||
static void
|
|
||||||
putc_poll (uint8_t byte)
|
|
||||||
{
|
|
||||||
ASSERT (intr_get_level () == INTR_OFF);
|
|
||||||
|
|
||||||
while ((inb (LSR_REG) & LSR_THRE) == 0)
|
|
||||||
continue;
|
|
||||||
outb (THR_REG, byte);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Serial interrupt handler. */
|
|
||||||
static void
|
|
||||||
serial_interrupt (struct intr_frame *f UNUSED)
|
|
||||||
{
|
|
||||||
/* Inquire about interrupt in UART. Without this, we can
|
|
||||||
occasionally miss an interrupt running under QEMU. */
|
|
||||||
inb (IIR_REG);
|
|
||||||
|
|
||||||
/* As long as we have room to receive a byte, and the hardware
|
|
||||||
has a byte for us, receive a byte. */
|
|
||||||
while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0)
|
|
||||||
input_putc (inb (RBR_REG));
|
|
||||||
|
|
||||||
/* As long as we have a byte to transmit, and the hardware is
|
|
||||||
ready to accept a byte for transmission, transmit a byte. */
|
|
||||||
while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0)
|
|
||||||
outb (THR_REG, intq_getc (&txq));
|
|
||||||
|
|
||||||
/* Update interrupt enable register based on queue status. */
|
|
||||||
write_ier ();
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#ifndef DEVICES_SERIAL_H
|
|
||||||
#define DEVICES_SERIAL_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void serial_init_queue (void);
|
|
||||||
void serial_putc (uint8_t);
|
|
||||||
void serial_flush (void);
|
|
||||||
void serial_notify (void);
|
|
||||||
|
|
||||||
#endif /* devices/serial.h */
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
#include "devices/shutdown.h"
|
|
||||||
#include <console.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "devices/kbd.h"
|
|
||||||
#include "devices/serial.h"
|
|
||||||
#include "devices/timer.h"
|
|
||||||
#include "threads/io.h"
|
|
||||||
#include "threads/thread.h"
|
|
||||||
#ifdef USERPROG
|
|
||||||
#include "userprog/exception.h"
|
|
||||||
#endif
|
|
||||||
#ifdef FILESYS
|
|
||||||
#include "devices/block.h"
|
|
||||||
#include "filesys/filesys.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Keyboard control register port. */
|
|
||||||
#define CONTROL_REG 0x64
|
|
||||||
|
|
||||||
/* How to shut down when shutdown() is called. */
|
|
||||||
static enum shutdown_type how = SHUTDOWN_NONE;
|
|
||||||
|
|
||||||
static void print_stats (void);
|
|
||||||
|
|
||||||
/* Shuts down the machine in the way configured by
|
|
||||||
shutdown_configure(). If the shutdown type is SHUTDOWN_NONE
|
|
||||||
(which is the default), returns without doing anything. */
|
|
||||||
void
|
|
||||||
shutdown (void)
|
|
||||||
{
|
|
||||||
switch (how)
|
|
||||||
{
|
|
||||||
case SHUTDOWN_POWER_OFF:
|
|
||||||
shutdown_power_off ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SHUTDOWN_REBOOT:
|
|
||||||
shutdown_reboot ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* Nothing to do. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sets TYPE as the way that machine will shut down when PintOS
|
|
||||||
execution is complete. */
|
|
||||||
void
|
|
||||||
shutdown_configure (enum shutdown_type type)
|
|
||||||
{
|
|
||||||
how = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reboots the machine via the keyboard controller. */
|
|
||||||
void
|
|
||||||
shutdown_reboot (void)
|
|
||||||
{
|
|
||||||
printf ("Rebooting...\n");
|
|
||||||
|
|
||||||
/* See [kbd] for details on how to program the keyboard
|
|
||||||
* controller. */
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Poll keyboard controller's status byte until
|
|
||||||
* 'input buffer empty' is reported. */
|
|
||||||
for (i = 0; i < 0x10000; i++)
|
|
||||||
{
|
|
||||||
if ((inb (CONTROL_REG) & 0x02) == 0)
|
|
||||||
break;
|
|
||||||
timer_udelay (2);
|
|
||||||
}
|
|
||||||
|
|
||||||
timer_udelay (50);
|
|
||||||
|
|
||||||
/* Pulse bit 0 of the output port P2 of the keyboard controller.
|
|
||||||
* This will reset the CPU. */
|
|
||||||
outb (CONTROL_REG, 0xfe);
|
|
||||||
timer_udelay (50);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Powers down the machine we're running on,
|
|
||||||
as long as we're running on Bochs or QEMU. */
|
|
||||||
void
|
|
||||||
shutdown_power_off (void)
|
|
||||||
{
|
|
||||||
const char s[] = "Shutdown";
|
|
||||||
const char *p;
|
|
||||||
|
|
||||||
#ifdef FILESYS
|
|
||||||
filesys_done ();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
print_stats ();
|
|
||||||
|
|
||||||
printf ("Powering off...\n");
|
|
||||||
serial_flush ();
|
|
||||||
|
|
||||||
/* This is a special power-off sequence supported by Bochs and
|
|
||||||
QEMU, but not by physical hardware. */
|
|
||||||
for (p = s; *p != '\0'; p++){
|
|
||||||
outb (0x8900, *p);
|
|
||||||
}
|
|
||||||
outw (0x604, 0x00 | 0x2000);
|
|
||||||
|
|
||||||
/* This will power off a VMware VM if "gui.exitOnCLIHLT = TRUE"
|
|
||||||
is set in its configuration file. (The "pintos" script does
|
|
||||||
that automatically.) */
|
|
||||||
asm volatile ("cli; hlt" : : : "memory");
|
|
||||||
|
|
||||||
/* None of those worked. */
|
|
||||||
printf ("still running...\n");
|
|
||||||
for (;;);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print statistics about PintOS execution. */
|
|
||||||
static void
|
|
||||||
print_stats (void)
|
|
||||||
{
|
|
||||||
timer_print_stats ();
|
|
||||||
thread_print_stats ();
|
|
||||||
#ifdef FILESYS
|
|
||||||
block_print_stats ();
|
|
||||||
#endif
|
|
||||||
console_print_stats ();
|
|
||||||
kbd_print_stats ();
|
|
||||||
#ifdef USERPROG
|
|
||||||
exception_print_stats ();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#ifndef DEVICES_SHUTDOWN_H
|
|
||||||
#define DEVICES_SHUTDOWN_H
|
|
||||||
|
|
||||||
#include <debug.h>
|
|
||||||
|
|
||||||
/* How to shut down when PintOS has nothing left to do. */
|
|
||||||
enum shutdown_type
|
|
||||||
{
|
|
||||||
SHUTDOWN_NONE, /* Loop forever. */
|
|
||||||
SHUTDOWN_POWER_OFF, /* Power off the machine (if possible). */
|
|
||||||
SHUTDOWN_REBOOT, /* Reboot the machine (if possible). */
|
|
||||||
};
|
|
||||||
|
|
||||||
void shutdown (void);
|
|
||||||
void shutdown_configure (enum shutdown_type);
|
|
||||||
void shutdown_reboot (void) NO_RETURN;
|
|
||||||
void shutdown_power_off (void) NO_RETURN;
|
|
||||||
|
|
||||||
#endif /* devices/shutdown.h */
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
#include "devices/speaker.h"
|
|
||||||
#include "devices/pit.h"
|
|
||||||
#include "threads/io.h"
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "devices/timer.h"
|
|
||||||
|
|
||||||
/* Speaker port enable I/O register. */
|
|
||||||
#define SPEAKER_PORT_GATE 0x61
|
|
||||||
|
|
||||||
/* Speaker port enable bits. */
|
|
||||||
#define SPEAKER_GATE_ENABLE 0x03
|
|
||||||
|
|
||||||
/* Sets the PC speaker to emit a tone at the given FREQUENCY, in
|
|
||||||
Hz. */
|
|
||||||
void
|
|
||||||
speaker_on (int frequency)
|
|
||||||
{
|
|
||||||
if (frequency >= 20 && frequency <= 20000)
|
|
||||||
{
|
|
||||||
/* Set the timer channel that's connected to the speaker to
|
|
||||||
output a square wave at the given FREQUENCY, then
|
|
||||||
connect the timer channel output to the speaker. */
|
|
||||||
enum intr_level old_level = intr_disable ();
|
|
||||||
pit_configure_channel (2, 3, frequency);
|
|
||||||
outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) | SPEAKER_GATE_ENABLE);
|
|
||||||
intr_set_level (old_level);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* FREQUENCY is outside the range of normal human hearing.
|
|
||||||
Just turn off the speaker. */
|
|
||||||
speaker_off ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Turn off the PC speaker, by disconnecting the timer channel's
|
|
||||||
output from the speaker. */
|
|
||||||
void
|
|
||||||
speaker_off (void)
|
|
||||||
{
|
|
||||||
enum intr_level old_level = intr_disable ();
|
|
||||||
outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) & ~SPEAKER_GATE_ENABLE);
|
|
||||||
intr_set_level (old_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Briefly beep the PC speaker. */
|
|
||||||
void
|
|
||||||
speaker_beep (void)
|
|
||||||
{
|
|
||||||
/* Only attempt to beep the speaker if interrupts are enabled,
|
|
||||||
because we don't want to freeze the machine during the beep.
|
|
||||||
We could add a hook to the timer interrupt to avoid that
|
|
||||||
problem, but then we'd risk failing to ever stop the beep if
|
|
||||||
PintOS crashes for some unrelated reason. There's nothing
|
|
||||||
more annoying than a machine whose beeping you can't stop
|
|
||||||
without a power cycle.
|
|
||||||
|
|
||||||
We can't just enable interrupts while we sleep. For one
|
|
||||||
thing, we get called (indirectly) from printf, which should
|
|
||||||
always work, even during boot before we're ready to enable
|
|
||||||
interrupts. */
|
|
||||||
if (intr_get_level () == INTR_ON)
|
|
||||||
{
|
|
||||||
speaker_on (440);
|
|
||||||
timer_msleep (250);
|
|
||||||
speaker_off ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#ifndef DEVICES_SPEAKER_H
|
|
||||||
#define DEVICES_SPEAKER_H
|
|
||||||
|
|
||||||
void speaker_on (int frequency);
|
|
||||||
void speaker_off (void);
|
|
||||||
void speaker_beep (void);
|
|
||||||
|
|
||||||
#endif /* devices/speaker.h */
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
#include "devices/swap.h"
|
|
||||||
#include "devices/block.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
#include "threads/vaddr.h"
|
|
||||||
#include <bitmap.h>
|
|
||||||
#include <debug.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* Pointer to the swap device */
|
|
||||||
static struct block *swap_device;
|
|
||||||
|
|
||||||
/* Pointer to a bitmap to track used swap pages */
|
|
||||||
static struct bitmap *swap_bitmap;
|
|
||||||
|
|
||||||
/* Lock that protects swap_bitmap from unsynchronised access */
|
|
||||||
static struct lock swap_lock;
|
|
||||||
|
|
||||||
/* Number of sectors needed to store a page */
|
|
||||||
#define PAGE_SECTORS (PGSIZE / BLOCK_SECTOR_SIZE)
|
|
||||||
|
|
||||||
/* Sets up the swap space */
|
|
||||||
void
|
|
||||||
swap_init (void)
|
|
||||||
{
|
|
||||||
// locate the swap block allocated to the kernel
|
|
||||||
swap_device = block_get_role (BLOCK_SWAP);
|
|
||||||
if (swap_device == NULL) {
|
|
||||||
printf ("no swap device--swap disabled\n");
|
|
||||||
swap_bitmap = bitmap_create (0);
|
|
||||||
} else {
|
|
||||||
// create a bitmap with 1 slot per page-sized chunk of memory on the swap block
|
|
||||||
swap_bitmap = bitmap_create (block_size (swap_device) / PAGE_SECTORS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (swap_bitmap == NULL){
|
|
||||||
PANIC ("couldn't create swap bitmap");
|
|
||||||
}
|
|
||||||
lock_init (&swap_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swaps page at VADDR out of memory, returns the swap-slot used */
|
|
||||||
size_t
|
|
||||||
swap_out (const void *vaddr)
|
|
||||||
{
|
|
||||||
// find available swap-slot for the page to be swapped out
|
|
||||||
lock_acquire (&swap_lock);
|
|
||||||
size_t slot = bitmap_scan_and_flip (swap_bitmap, 0, 1, false);
|
|
||||||
lock_release (&swap_lock);
|
|
||||||
if (slot == BITMAP_ERROR)
|
|
||||||
return BITMAP_ERROR;
|
|
||||||
|
|
||||||
// calculate block sector from swap-slot number
|
|
||||||
size_t sector = slot * PAGE_SECTORS;
|
|
||||||
|
|
||||||
// loop over each sector of the page, copying it from memory into swap
|
|
||||||
for (size_t i = 0; i < PAGE_SECTORS; i++)
|
|
||||||
block_write (swap_device, sector + i, vaddr + i * BLOCK_SECTOR_SIZE);
|
|
||||||
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swaps page on disk in swap-slot SLOT into memory at VADDR */
|
|
||||||
void
|
|
||||||
swap_in (void *vaddr, size_t slot)
|
|
||||||
{
|
|
||||||
// calculate block sector from swap-slot number
|
|
||||||
size_t sector = slot * PAGE_SECTORS;
|
|
||||||
|
|
||||||
// loop over each sector of the page, copying it from swap into memory
|
|
||||||
for (size_t i = 0; i < PAGE_SECTORS; i++)
|
|
||||||
block_read (swap_device, sector + i, vaddr + i * BLOCK_SECTOR_SIZE);
|
|
||||||
|
|
||||||
// clear the swap-slot previously used by this page
|
|
||||||
swap_drop (slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clears the swap-slot SLOT so that it can be used for another page */
|
|
||||||
void
|
|
||||||
swap_drop (size_t slot)
|
|
||||||
{
|
|
||||||
bitmap_reset (swap_bitmap, slot);
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#ifndef DEVICES_SWAP_H
|
|
||||||
#define DEVICES_SWAP_H 1
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
void swap_init (void);
|
|
||||||
size_t swap_out (const void *vaddr);
|
|
||||||
void swap_in (void *vaddr, size_t slot);
|
|
||||||
void swap_drop (size_t slot);
|
|
||||||
|
|
||||||
#endif /* devices/swap.h */
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
#include "devices/timer.h"
|
|
||||||
#include <debug.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <round.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <list.h>
|
|
||||||
#include "devices/pit.h"
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
#include "threads/thread.h"
|
|
||||||
|
|
||||||
/* See [8254] for hardware details of the 8254 timer chip. */
|
|
||||||
|
|
||||||
#if TIMER_FREQ < 19
|
|
||||||
#error 8254 timer requires TIMER_FREQ >= 19
|
|
||||||
#endif
|
|
||||||
#if TIMER_FREQ > 1000
|
|
||||||
#error TIMER_FREQ <= 1000 recommended
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct asleep_thread
|
|
||||||
{
|
|
||||||
int64_t end_at; /* Number of timer ticks to stop sleeping at. */
|
|
||||||
struct semaphore semaphore; /* Semaphore used to block the thread. */
|
|
||||||
struct list_elem elem; /* List element. */
|
|
||||||
};
|
|
||||||
/* List of threads that are sleeping. */
|
|
||||||
static struct list sleeping_threads;
|
|
||||||
|
|
||||||
/* Number of timer ticks since OS booted. */
|
|
||||||
static int64_t ticks;
|
|
||||||
|
|
||||||
/* Number of loops per timer tick.
|
|
||||||
Initialized by timer_calibrate(). */
|
|
||||||
static unsigned loops_per_tick;
|
|
||||||
|
|
||||||
static intr_handler_func timer_interrupt;
|
|
||||||
static bool too_many_loops (unsigned loops);
|
|
||||||
static void busy_wait (int64_t loops);
|
|
||||||
static void real_time_sleep (int64_t num, int32_t denom);
|
|
||||||
static void real_time_delay (int64_t num, int32_t denom);
|
|
||||||
static bool sleeping_threads_less (const struct list_elem *a,
|
|
||||||
const struct list_elem *b,
|
|
||||||
void *aux UNUSED);
|
|
||||||
|
|
||||||
/* Sets up the timer to interrupt TIMER_FREQ times per second,
|
|
||||||
and registers the corresponding interrupt. */
|
|
||||||
void
|
|
||||||
timer_init (void)
|
|
||||||
{
|
|
||||||
pit_configure_channel (0, 2, TIMER_FREQ);
|
|
||||||
list_init (&sleeping_threads);
|
|
||||||
intr_register_ext (0x20, timer_interrupt, "8254 Timer");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calibrates loops_per_tick, used to implement brief delays. */
|
|
||||||
void
|
|
||||||
timer_calibrate (void)
|
|
||||||
{
|
|
||||||
unsigned high_bit, test_bit;
|
|
||||||
|
|
||||||
ASSERT (intr_get_level () == INTR_ON);
|
|
||||||
printf ("Calibrating timer... ");
|
|
||||||
|
|
||||||
/* Approximate loops_per_tick as the largest power-of-two
|
|
||||||
still less than one timer tick. */
|
|
||||||
loops_per_tick = 1u << 10;
|
|
||||||
while (!too_many_loops (loops_per_tick << 1))
|
|
||||||
{
|
|
||||||
loops_per_tick <<= 1;
|
|
||||||
ASSERT (loops_per_tick != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Refine the next 8 bits of loops_per_tick. */
|
|
||||||
high_bit = loops_per_tick;
|
|
||||||
for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1)
|
|
||||||
if (!too_many_loops (high_bit | test_bit))
|
|
||||||
loops_per_tick |= test_bit;
|
|
||||||
|
|
||||||
printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of timer ticks since the OS booted. */
|
|
||||||
int64_t
|
|
||||||
timer_ticks (void)
|
|
||||||
{
|
|
||||||
enum intr_level old_level = intr_disable ();
|
|
||||||
int64_t t = ticks;
|
|
||||||
intr_set_level (old_level);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of timer ticks elapsed since THEN, which
|
|
||||||
should be a value once returned by timer_ticks(). */
|
|
||||||
int64_t
|
|
||||||
timer_elapsed (int64_t then)
|
|
||||||
{
|
|
||||||
return timer_ticks () - then;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sleeps for approximately TICKS timer ticks. Interrupts must
|
|
||||||
be turned on. */
|
|
||||||
void
|
|
||||||
timer_sleep (int64_t ticks)
|
|
||||||
{
|
|
||||||
enum intr_level old_level;
|
|
||||||
int64_t start = timer_ticks ();
|
|
||||||
|
|
||||||
ASSERT (intr_get_level () == INTR_ON);
|
|
||||||
if (ticks < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
struct asleep_thread st;
|
|
||||||
st.end_at = start + ticks;
|
|
||||||
sema_init (&st.semaphore, 0);
|
|
||||||
|
|
||||||
old_level = intr_disable ();
|
|
||||||
list_insert_ordered (&sleeping_threads, &st.elem, &sleeping_threads_less,
|
|
||||||
NULL);
|
|
||||||
intr_set_level (old_level);
|
|
||||||
sema_down (&st.semaphore);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sleeps for approximately MS milliseconds. Interrupts must be
|
|
||||||
turned on. */
|
|
||||||
void
|
|
||||||
timer_msleep (int64_t ms)
|
|
||||||
{
|
|
||||||
real_time_sleep (ms, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sleeps for approximately US microseconds. Interrupts must be
|
|
||||||
turned on. */
|
|
||||||
void
|
|
||||||
timer_usleep (int64_t us)
|
|
||||||
{
|
|
||||||
real_time_sleep (us, 1000 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sleeps for approximately NS nanoseconds. Interrupts must be
|
|
||||||
turned on. */
|
|
||||||
void
|
|
||||||
timer_nsleep (int64_t ns)
|
|
||||||
{
|
|
||||||
real_time_sleep (ns, 1000 * 1000 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Busy-waits for approximately MS milliseconds. Interrupts need
|
|
||||||
not be turned on.
|
|
||||||
|
|
||||||
Busy waiting wastes CPU cycles, and busy waiting with
|
|
||||||
interrupts off for the interval between timer ticks or longer
|
|
||||||
will cause timer ticks to be lost. Thus, use timer_msleep()
|
|
||||||
instead if interrupts are enabled. */
|
|
||||||
void
|
|
||||||
timer_mdelay (int64_t ms)
|
|
||||||
{
|
|
||||||
real_time_delay (ms, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sleeps for approximately US microseconds. Interrupts need not
|
|
||||||
be turned on.
|
|
||||||
|
|
||||||
Busy waiting wastes CPU cycles, and busy waiting with
|
|
||||||
interrupts off for the interval between timer ticks or longer
|
|
||||||
will cause timer ticks to be lost. Thus, use timer_usleep()
|
|
||||||
instead if interrupts are enabled. */
|
|
||||||
void
|
|
||||||
timer_udelay (int64_t us)
|
|
||||||
{
|
|
||||||
real_time_delay (us, 1000 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sleeps execution for approximately NS nanoseconds. Interrupts
|
|
||||||
need not be turned on.
|
|
||||||
|
|
||||||
Busy waiting wastes CPU cycles, and busy waiting with
|
|
||||||
interrupts off for the interval between timer ticks or longer
|
|
||||||
will cause timer ticks to be lost. Thus, use timer_nsleep()
|
|
||||||
instead if interrupts are enabled.*/
|
|
||||||
void
|
|
||||||
timer_ndelay (int64_t ns)
|
|
||||||
{
|
|
||||||
real_time_delay (ns, 1000 * 1000 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prints timer statistics. */
|
|
||||||
void
|
|
||||||
timer_print_stats (void)
|
|
||||||
{
|
|
||||||
printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Timer interrupt handler. */
|
|
||||||
static void
|
|
||||||
timer_interrupt (struct intr_frame *args UNUSED)
|
|
||||||
{
|
|
||||||
ticks++;
|
|
||||||
for (struct list_elem *e = list_begin (&sleeping_threads);
|
|
||||||
e != list_end (&sleeping_threads); e = list_next (e))
|
|
||||||
{
|
|
||||||
struct asleep_thread *st = list_entry (e, struct asleep_thread, elem);
|
|
||||||
if (ticks >= st->end_at)
|
|
||||||
{
|
|
||||||
list_remove (&st->elem);
|
|
||||||
sema_up (&st->semaphore);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
thread_tick ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if LOOPS iterations waits for more than one timer
|
|
||||||
tick, otherwise false. */
|
|
||||||
static bool
|
|
||||||
too_many_loops (unsigned loops)
|
|
||||||
{
|
|
||||||
/* Wait for a timer tick. */
|
|
||||||
int64_t start = ticks;
|
|
||||||
while (ticks == start)
|
|
||||||
barrier ();
|
|
||||||
|
|
||||||
/* Run LOOPS loops. */
|
|
||||||
start = ticks;
|
|
||||||
busy_wait (loops);
|
|
||||||
|
|
||||||
/* If the tick count changed, we iterated too long. */
|
|
||||||
barrier ();
|
|
||||||
return start != ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iterates through a simple loop LOOPS times, for implementing
|
|
||||||
brief delays.
|
|
||||||
|
|
||||||
Marked NO_INLINE because code alignment can significantly
|
|
||||||
affect timings, so that if this function was inlined
|
|
||||||
differently in different places the results would be difficult
|
|
||||||
to predict. */
|
|
||||||
static void NO_INLINE
|
|
||||||
busy_wait (int64_t loops)
|
|
||||||
{
|
|
||||||
while (loops-- > 0)
|
|
||||||
barrier ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sleep for approximately NUM/DENOM seconds. */
|
|
||||||
static void
|
|
||||||
real_time_sleep (int64_t num, int32_t denom)
|
|
||||||
{
|
|
||||||
/* Convert NUM/DENOM seconds into timer ticks, rounding down.
|
|
||||||
|
|
||||||
(NUM / DENOM) s
|
|
||||||
---------------------- = NUM * TIMER_FREQ / DENOM ticks.
|
|
||||||
1 s / TIMER_FREQ ticks
|
|
||||||
*/
|
|
||||||
int64_t ticks = num * TIMER_FREQ / denom;
|
|
||||||
|
|
||||||
ASSERT (intr_get_level () == INTR_ON);
|
|
||||||
if (ticks > 0)
|
|
||||||
{
|
|
||||||
/* We're waiting for at least one full timer tick. Use
|
|
||||||
timer_sleep() because it will yield the CPU to other
|
|
||||||
processes. */
|
|
||||||
timer_sleep (ticks);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Otherwise, use a busy-wait loop for more accurate
|
|
||||||
sub-tick timing. */
|
|
||||||
real_time_delay (num, denom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Busy-wait for approximately NUM/DENOM seconds. */
|
|
||||||
static void
|
|
||||||
real_time_delay (int64_t num, int32_t denom)
|
|
||||||
{
|
|
||||||
/* Scale the numerator and denominator down by 1000 to avoid
|
|
||||||
the possibility of overflow. */
|
|
||||||
ASSERT (denom % 1000 == 0);
|
|
||||||
busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* list_less_func for sleeping_threads list */
|
|
||||||
bool
|
|
||||||
sleeping_threads_less (const struct list_elem *a, const struct list_elem *b,
|
|
||||||
void *aux UNUSED)
|
|
||||||
{
|
|
||||||
struct asleep_thread *sta = list_entry (a, struct asleep_thread, elem);
|
|
||||||
struct asleep_thread *stb = list_entry (b, struct asleep_thread, elem);
|
|
||||||
return sta->end_at < stb->end_at;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#ifndef DEVICES_TIMER_H
|
|
||||||
#define DEVICES_TIMER_H
|
|
||||||
|
|
||||||
#include <round.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* Number of timer interrupts per second. */
|
|
||||||
#define TIMER_FREQ 100
|
|
||||||
|
|
||||||
void timer_init (void);
|
|
||||||
void timer_calibrate (void);
|
|
||||||
|
|
||||||
int64_t timer_ticks (void);
|
|
||||||
int64_t timer_elapsed (int64_t);
|
|
||||||
|
|
||||||
/* Sleep and yield the CPU to other threads. */
|
|
||||||
void timer_sleep (int64_t ticks);
|
|
||||||
void timer_msleep (int64_t milliseconds);
|
|
||||||
void timer_usleep (int64_t microseconds);
|
|
||||||
void timer_nsleep (int64_t nanoseconds);
|
|
||||||
|
|
||||||
/* Busy waits. */
|
|
||||||
void timer_mdelay (int64_t milliseconds);
|
|
||||||
void timer_udelay (int64_t microseconds);
|
|
||||||
void timer_ndelay (int64_t nanoseconds);
|
|
||||||
|
|
||||||
void timer_print_stats (void);
|
|
||||||
|
|
||||||
#endif /* devices/timer.h */
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
#include "devices/vga.h"
|
|
||||||
#include <round.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "devices/speaker.h"
|
|
||||||
#include "threads/io.h"
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/vaddr.h"
|
|
||||||
|
|
||||||
/* VGA text screen support. See [FREEVGA] for more information. */
|
|
||||||
|
|
||||||
/* Number of columns and rows on the text display. */
|
|
||||||
#define COL_CNT 80
|
|
||||||
#define ROW_CNT 25
|
|
||||||
|
|
||||||
/* Current cursor position. (0,0) is in the upper left corner of
|
|
||||||
the display. */
|
|
||||||
static size_t cx, cy;
|
|
||||||
|
|
||||||
/* Attribute value for gray text on a black background. */
|
|
||||||
#define GRAY_ON_BLACK 0x07
|
|
||||||
|
|
||||||
/* Framebuffer. See [FREEVGA] under "VGA Text Mode Operation".
|
|
||||||
The character at (x,y) is fb[y][x][0].
|
|
||||||
The attribute at (x,y) is fb[y][x][1]. */
|
|
||||||
static uint8_t (*fb)[COL_CNT][2];
|
|
||||||
|
|
||||||
static void clear_row (size_t y);
|
|
||||||
static void cls (void);
|
|
||||||
static void newline (void);
|
|
||||||
static void move_cursor (void);
|
|
||||||
static void find_cursor (size_t *x, size_t *y);
|
|
||||||
|
|
||||||
/* Initializes the VGA text display. */
|
|
||||||
static void
|
|
||||||
init (void)
|
|
||||||
{
|
|
||||||
/* Already initialized? */
|
|
||||||
static bool inited;
|
|
||||||
if (!inited)
|
|
||||||
{
|
|
||||||
fb = ptov (0xb8000);
|
|
||||||
find_cursor (&cx, &cy);
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes C to the VGA text display, interpreting control
|
|
||||||
characters in the conventional ways. */
|
|
||||||
void
|
|
||||||
vga_putc (int c)
|
|
||||||
{
|
|
||||||
/* Disable interrupts to lock out interrupt handlers
|
|
||||||
that might write to the console. */
|
|
||||||
enum intr_level old_level = intr_disable ();
|
|
||||||
|
|
||||||
init ();
|
|
||||||
|
|
||||||
switch (c)
|
|
||||||
{
|
|
||||||
case '\n':
|
|
||||||
newline ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\f':
|
|
||||||
cls ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\b':
|
|
||||||
if (cx > 0)
|
|
||||||
cx--;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\r':
|
|
||||||
cx = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\t':
|
|
||||||
cx = ROUND_UP (cx + 1, 8);
|
|
||||||
if (cx >= COL_CNT)
|
|
||||||
newline ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\a':
|
|
||||||
intr_set_level (old_level);
|
|
||||||
speaker_beep ();
|
|
||||||
intr_disable ();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fb[cy][cx][0] = c;
|
|
||||||
fb[cy][cx][1] = GRAY_ON_BLACK;
|
|
||||||
if (++cx >= COL_CNT)
|
|
||||||
newline ();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update cursor position. */
|
|
||||||
move_cursor ();
|
|
||||||
|
|
||||||
intr_set_level (old_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clears the screen and moves the cursor to the upper left. */
|
|
||||||
static void
|
|
||||||
cls (void)
|
|
||||||
{
|
|
||||||
size_t y;
|
|
||||||
|
|
||||||
for (y = 0; y < ROW_CNT; y++)
|
|
||||||
clear_row (y);
|
|
||||||
|
|
||||||
cx = cy = 0;
|
|
||||||
move_cursor ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clears row Y to spaces. */
|
|
||||||
static void
|
|
||||||
clear_row (size_t y)
|
|
||||||
{
|
|
||||||
size_t x;
|
|
||||||
|
|
||||||
for (x = 0; x < COL_CNT; x++)
|
|
||||||
{
|
|
||||||
fb[y][x][0] = ' ';
|
|
||||||
fb[y][x][1] = GRAY_ON_BLACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advances the cursor to the first column in the next line on
|
|
||||||
the screen. If the cursor is already on the last line on the
|
|
||||||
screen, scrolls the screen upward one line. */
|
|
||||||
static void
|
|
||||||
newline (void)
|
|
||||||
{
|
|
||||||
cx = 0;
|
|
||||||
cy++;
|
|
||||||
if (cy >= ROW_CNT)
|
|
||||||
{
|
|
||||||
cy = ROW_CNT - 1;
|
|
||||||
memmove (&fb[0], &fb[1], sizeof fb[0] * (ROW_CNT - 1));
|
|
||||||
clear_row (ROW_CNT - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Moves the hardware cursor to (cx,cy). */
|
|
||||||
static void
|
|
||||||
move_cursor (void)
|
|
||||||
{
|
|
||||||
/* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
|
|
||||||
uint16_t cp = cx + COL_CNT * cy;
|
|
||||||
outw (0x3d4, 0x0e | (cp & 0xff00));
|
|
||||||
outw (0x3d4, 0x0f | (cp << 8));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads the current hardware cursor position into (*X,*Y). */
|
|
||||||
static void
|
|
||||||
find_cursor (size_t *x, size_t *y)
|
|
||||||
{
|
|
||||||
/* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
|
|
||||||
uint16_t cp;
|
|
||||||
|
|
||||||
outb (0x3d4, 0x0e);
|
|
||||||
cp = inb (0x3d5) << 8;
|
|
||||||
|
|
||||||
outb (0x3d4, 0x0f);
|
|
||||||
cp |= inb (0x3d5);
|
|
||||||
|
|
||||||
*x = cp % COL_CNT;
|
|
||||||
*y = cp / COL_CNT;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef DEVICES_VGA_H
|
|
||||||
#define DEVICES_VGA_H
|
|
||||||
|
|
||||||
void vga_putc (int);
|
|
||||||
|
|
||||||
#endif /* devices/vga.h */
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* On x86, division of one 64-bit integer by another cannot be
|
|
||||||
done with a single instruction or a short sequence. Thus, GCC
|
|
||||||
implements 64-bit division and remainder operations through
|
|
||||||
function calls. These functions are normally obtained from
|
|
||||||
libgcc, which is automatically included by GCC in any link
|
|
||||||
that it does.
|
|
||||||
|
|
||||||
Some x86-64 machines, however, have a compiler and utilities
|
|
||||||
that can generate 32-bit x86 code without having any of the
|
|
||||||
necessary libraries, including libgcc. Thus, we can make
|
|
||||||
PintOS work on these machines by simply implementing our own
|
|
||||||
64-bit division routines, which are the only routines from
|
|
||||||
libgcc that PintOS requires.
|
|
||||||
|
|
||||||
Completeness is another reason to include these routines. If
|
|
||||||
PintOS is completely self-contained, then that makes it that
|
|
||||||
much less mysterious. */
|
|
||||||
|
|
||||||
/* Uses x86 DIVL instruction to divide 64-bit N by 32-bit D to
|
|
||||||
yield a 32-bit quotient. Returns the quotient.
|
|
||||||
Traps with a divide error (#DE) if the quotient does not fit
|
|
||||||
in 32 bits. */
|
|
||||||
static inline uint32_t
|
|
||||||
divl (uint64_t n, uint32_t d)
|
|
||||||
{
|
|
||||||
uint32_t n1 = n >> 32;
|
|
||||||
uint32_t n0 = n;
|
|
||||||
uint32_t q, r;
|
|
||||||
|
|
||||||
asm ("divl %4"
|
|
||||||
: "=d" (r), "=a" (q)
|
|
||||||
: "0" (n1), "1" (n0), "rm" (d));
|
|
||||||
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of leading zero bits in X,
|
|
||||||
which must be nonzero. */
|
|
||||||
static int
|
|
||||||
nlz (uint32_t x)
|
|
||||||
{
|
|
||||||
/* This technique is portable, but there are better ways to do
|
|
||||||
it on particular systems. With sufficiently new enough GCC,
|
|
||||||
you can use __builtin_clz() to take advantage of GCC's
|
|
||||||
knowledge of how to do it. Or you can use the x86 BSR
|
|
||||||
instruction directly. */
|
|
||||||
int n = 0;
|
|
||||||
if (x <= 0x0000FFFF)
|
|
||||||
{
|
|
||||||
n += 16;
|
|
||||||
x <<= 16;
|
|
||||||
}
|
|
||||||
if (x <= 0x00FFFFFF)
|
|
||||||
{
|
|
||||||
n += 8;
|
|
||||||
x <<= 8;
|
|
||||||
}
|
|
||||||
if (x <= 0x0FFFFFFF)
|
|
||||||
{
|
|
||||||
n += 4;
|
|
||||||
x <<= 4;
|
|
||||||
}
|
|
||||||
if (x <= 0x3FFFFFFF)
|
|
||||||
{
|
|
||||||
n += 2;
|
|
||||||
x <<= 2;
|
|
||||||
}
|
|
||||||
if (x <= 0x7FFFFFFF)
|
|
||||||
n++;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
|
|
||||||
quotient. */
|
|
||||||
static uint64_t
|
|
||||||
udiv64 (uint64_t n, uint64_t d)
|
|
||||||
{
|
|
||||||
if ((d >> 32) == 0)
|
|
||||||
{
|
|
||||||
/* Proof of correctness:
|
|
||||||
|
|
||||||
Let n, d, b, n1, and n0 be defined as in this function.
|
|
||||||
Let [x] be the "floor" of x. Let T = b[n1/d]. Assume d
|
|
||||||
nonzero. Then:
|
|
||||||
[n/d] = [n/d] - T + T
|
|
||||||
= [n/d - T] + T by (1) below
|
|
||||||
= [(b*n1 + n0)/d - T] + T by definition of n
|
|
||||||
= [(b*n1 + n0)/d - dT/d] + T
|
|
||||||
= [(b(n1 - d[n1/d]) + n0)/d] + T
|
|
||||||
= [(b[n1 % d] + n0)/d] + T, by definition of %
|
|
||||||
which is the expression calculated below.
|
|
||||||
|
|
||||||
(1) Note that for any real x, integer i: [x] + i = [x + i].
|
|
||||||
|
|
||||||
To prevent divl() from trapping, [(b[n1 % d] + n0)/d] must
|
|
||||||
be less than b. Assume that [n1 % d] and n0 take their
|
|
||||||
respective maximum values of d - 1 and b - 1:
|
|
||||||
[(b(d - 1) + (b - 1))/d] < b
|
|
||||||
<=> [(bd - 1)/d] < b
|
|
||||||
<=> [b - 1/d] < b
|
|
||||||
which is a tautology.
|
|
||||||
|
|
||||||
Therefore, this code is correct and will not trap. */
|
|
||||||
uint64_t b = 1ULL << 32;
|
|
||||||
uint32_t n1 = n >> 32;
|
|
||||||
uint32_t n0 = n;
|
|
||||||
uint32_t d0 = d;
|
|
||||||
|
|
||||||
return divl (b * (n1 % d0) + n0, d0) + b * (n1 / d0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Based on the algorithm and proof available from
|
|
||||||
http://www.hackersdelight.org/revisions.pdf. */
|
|
||||||
if (n < d)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uint32_t d1 = d >> 32;
|
|
||||||
int s = nlz (d1);
|
|
||||||
uint64_t q = divl (n >> 1, (d << s) >> 32) >> (31 - s);
|
|
||||||
return n - (q - 1) * d < d ? q - 1 : q;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
|
|
||||||
remainder. */
|
|
||||||
static uint32_t
|
|
||||||
umod64 (uint64_t n, uint64_t d)
|
|
||||||
{
|
|
||||||
return n - d * udiv64 (n, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Divides signed 64-bit N by signed 64-bit D and returns the
|
|
||||||
quotient. */
|
|
||||||
static int64_t
|
|
||||||
sdiv64 (int64_t n, int64_t d)
|
|
||||||
{
|
|
||||||
uint64_t n_abs = n >= 0 ? (uint64_t) n : -(uint64_t) n;
|
|
||||||
uint64_t d_abs = d >= 0 ? (uint64_t) d : -(uint64_t) d;
|
|
||||||
uint64_t q_abs = udiv64 (n_abs, d_abs);
|
|
||||||
return (n < 0) == (d < 0) ? (int64_t) q_abs : -(int64_t) q_abs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Divides signed 64-bit N by signed 64-bit D and returns the
|
|
||||||
remainder. */
|
|
||||||
static int32_t
|
|
||||||
smod64 (int64_t n, int64_t d)
|
|
||||||
{
|
|
||||||
return n - d * sdiv64 (n, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* These are the routines that GCC calls. */
|
|
||||||
|
|
||||||
long long __divdi3 (long long n, long long d);
|
|
||||||
long long __moddi3 (long long n, long long d);
|
|
||||||
unsigned long long __udivdi3 (unsigned long long n, unsigned long long d);
|
|
||||||
unsigned long long __umoddi3 (unsigned long long n, unsigned long long d);
|
|
||||||
|
|
||||||
/* Signed 64-bit division. */
|
|
||||||
long long
|
|
||||||
__divdi3 (long long n, long long d)
|
|
||||||
{
|
|
||||||
return sdiv64 (n, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Signed 64-bit remainder. */
|
|
||||||
long long
|
|
||||||
__moddi3 (long long n, long long d)
|
|
||||||
{
|
|
||||||
return smod64 (n, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unsigned 64-bit division. */
|
|
||||||
unsigned long long
|
|
||||||
__udivdi3 (unsigned long long n, unsigned long long d)
|
|
||||||
{
|
|
||||||
return udiv64 (n, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unsigned 64-bit remainder. */
|
|
||||||
unsigned long long
|
|
||||||
__umoddi3 (unsigned long long n, unsigned long long d)
|
|
||||||
{
|
|
||||||
return umod64 (n, d);
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef __LIB_CTYPE_H
|
|
||||||
#define __LIB_CTYPE_H
|
|
||||||
|
|
||||||
static inline int islower (int c) { return c >= 'a' && c <= 'z'; }
|
|
||||||
static inline int isupper (int c) { return c >= 'A' && c <= 'Z'; }
|
|
||||||
static inline int isalpha (int c) { return islower (c) || isupper (c); }
|
|
||||||
static inline int isdigit (int c) { return c >= '0' && c <= '9'; }
|
|
||||||
static inline int isalnum (int c) { return isalpha (c) || isdigit (c); }
|
|
||||||
static inline int isxdigit (int c) {
|
|
||||||
return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
|
||||||
}
|
|
||||||
static inline int isspace (int c) {
|
|
||||||
return (c == ' ' || c == '\f' || c == '\n'
|
|
||||||
|| c == '\r' || c == '\t' || c == '\v');
|
|
||||||
}
|
|
||||||
static inline int isblank (int c) { return c == ' ' || c == '\t'; }
|
|
||||||
static inline int isgraph (int c) { return c > 32 && c < 127; }
|
|
||||||
static inline int isprint (int c) { return c >= 32 && c < 127; }
|
|
||||||
static inline int iscntrl (int c) { return (c >= 0 && c < 32) || c == 127; }
|
|
||||||
static inline int isascii (int c) { return c >= 0 && c < 128; }
|
|
||||||
static inline int ispunct (int c) {
|
|
||||||
return isprint (c) && !isalnum (c) && !isspace (c);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int tolower (int c) { return isupper (c) ? c - 'A' + 'a' : c; }
|
|
||||||
static inline int toupper (int c) { return islower (c) ? c - 'a' + 'A' : c; }
|
|
||||||
|
|
||||||
#endif /* lib/ctype.h */
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#include <debug.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/* Prints the call stack, that is, a list of addresses, one in
|
|
||||||
each of the functions we are nested within. gdb or addr2line
|
|
||||||
may be applied to kernel.o to translate these into file names,
|
|
||||||
line numbers, and function names. */
|
|
||||||
void
|
|
||||||
debug_backtrace (void)
|
|
||||||
{
|
|
||||||
static bool explained;
|
|
||||||
void **frame;
|
|
||||||
|
|
||||||
printf ("Call stack: %p", __builtin_return_address (0));
|
|
||||||
for (frame = __builtin_frame_address (1);
|
|
||||||
(uintptr_t) frame >= 0x1000 && frame[0] != NULL;
|
|
||||||
frame = frame[0])
|
|
||||||
printf (" %p", frame[1]);
|
|
||||||
printf (".\n");
|
|
||||||
|
|
||||||
if (!explained)
|
|
||||||
{
|
|
||||||
explained = true;
|
|
||||||
printf ("The `backtrace' program can make call stacks useful.\n"
|
|
||||||
"Read \"Backtraces\" in the \"Debugging Tools\" chapter\n"
|
|
||||||
"of the PintOS documentation for more information.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#ifndef __LIB_DEBUG_H
|
|
||||||
#define __LIB_DEBUG_H
|
|
||||||
|
|
||||||
/* GCC lets us add "attributes" to functions, function
|
|
||||||
parameters, etc. to indicate their properties.
|
|
||||||
See the GCC manual for details. */
|
|
||||||
#define UNUSED __attribute__ ((unused))
|
|
||||||
#define NO_RETURN __attribute__ ((noreturn))
|
|
||||||
#define NO_INLINE __attribute__ ((noinline))
|
|
||||||
#define PRINTF_FORMAT(FMT, FIRST) __attribute__ ((format (printf, FMT, FIRST)))
|
|
||||||
|
|
||||||
/* Halts the OS, printing the source file name, line number, and
|
|
||||||
function name, plus a user-specific message. */
|
|
||||||
#define PANIC(...) debug_panic (__FILE__, __LINE__, __func__, __VA_ARGS__)
|
|
||||||
|
|
||||||
void debug_panic (const char *file, int line, const char *function,
|
|
||||||
const char *message, ...) PRINTF_FORMAT (4, 5) NO_RETURN;
|
|
||||||
void debug_backtrace (void);
|
|
||||||
void debug_backtrace_all (void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* This is outside the header guard so that debug.h may be
|
|
||||||
included multiple times with different settings of NDEBUG. */
|
|
||||||
#undef ASSERT
|
|
||||||
#undef NOT_REACHED
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#define ASSERT(CONDITION) \
|
|
||||||
if (CONDITION) { } else { \
|
|
||||||
PANIC ("assertion `%s' failed.", #CONDITION); \
|
|
||||||
}
|
|
||||||
#define NOT_REACHED() PANIC ("executed an unreachable statement");
|
|
||||||
#else
|
|
||||||
#define ASSERT(CONDITION) ((void) 0)
|
|
||||||
#define NOT_REACHED() for (;;)
|
|
||||||
#endif /* lib/debug.h */
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#ifndef __LIB_INTTYPES_H
|
|
||||||
#define __LIB_INTTYPES_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define PRId8 "hhd"
|
|
||||||
#define PRIi8 "hhi"
|
|
||||||
#define PRIo8 "hho"
|
|
||||||
#define PRIu8 "hhu"
|
|
||||||
#define PRIx8 "hhx"
|
|
||||||
#define PRIX8 "hhX"
|
|
||||||
|
|
||||||
#define PRId16 "hd"
|
|
||||||
#define PRIi16 "hi"
|
|
||||||
#define PRIo16 "ho"
|
|
||||||
#define PRIu16 "hu"
|
|
||||||
#define PRIx16 "hx"
|
|
||||||
#define PRIX16 "hX"
|
|
||||||
|
|
||||||
#define PRId32 "d"
|
|
||||||
#define PRIi32 "i"
|
|
||||||
#define PRIo32 "o"
|
|
||||||
#define PRIu32 "u"
|
|
||||||
#define PRIx32 "x"
|
|
||||||
#define PRIX32 "X"
|
|
||||||
|
|
||||||
#define PRId64 "lld"
|
|
||||||
#define PRIi64 "lli"
|
|
||||||
#define PRIo64 "llo"
|
|
||||||
#define PRIu64 "llu"
|
|
||||||
#define PRIx64 "llx"
|
|
||||||
#define PRIX64 "llX"
|
|
||||||
|
|
||||||
#define PRIdMAX "jd"
|
|
||||||
#define PRIiMAX "ji"
|
|
||||||
#define PRIoMAX "jo"
|
|
||||||
#define PRIuMAX "ju"
|
|
||||||
#define PRIxMAX "jx"
|
|
||||||
#define PRIXMAX "jX"
|
|
||||||
|
|
||||||
#define PRIdPTR "td"
|
|
||||||
#define PRIiPTR "ti"
|
|
||||||
#define PRIoPTR "to"
|
|
||||||
#define PRIuPTR "tu"
|
|
||||||
#define PRIxPTR "tx"
|
|
||||||
#define PRIXPTR "tX"
|
|
||||||
|
|
||||||
#endif /* lib/inttypes.h */
|
|
||||||
@@ -1,372 +0,0 @@
|
|||||||
#include "bitmap.h"
|
|
||||||
#include <debug.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <round.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "threads/malloc.h"
|
|
||||||
#ifdef FILESYS
|
|
||||||
#include "filesys/file.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Element type.
|
|
||||||
|
|
||||||
This must be an unsigned integer type at least as wide as int.
|
|
||||||
|
|
||||||
Each bit represents one bit in the bitmap.
|
|
||||||
If bit 0 in an element represents bit K in the bitmap,
|
|
||||||
then bit 1 in the element represents bit K+1 in the bitmap,
|
|
||||||
and so on. */
|
|
||||||
typedef unsigned long elem_type;
|
|
||||||
|
|
||||||
/* Number of bits in an element. */
|
|
||||||
#define ELEM_BITS (sizeof (elem_type) * CHAR_BIT)
|
|
||||||
|
|
||||||
/* From the outside, a bitmap is an array of bits. From the
|
|
||||||
inside, it's an array of elem_type (defined above) that
|
|
||||||
simulates an array of bits. */
|
|
||||||
struct bitmap
|
|
||||||
{
|
|
||||||
size_t bit_cnt; /* Number of bits. */
|
|
||||||
elem_type *bits; /* Elements that represent bits. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Returns the index of the element that contains the bit
|
|
||||||
numbered BIT_IDX. */
|
|
||||||
static inline size_t
|
|
||||||
elem_idx (size_t bit_idx)
|
|
||||||
{
|
|
||||||
return bit_idx / ELEM_BITS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns an elem_type where only the bit corresponding to
|
|
||||||
BIT_IDX is turned on. */
|
|
||||||
static inline elem_type
|
|
||||||
bit_mask (size_t bit_idx)
|
|
||||||
{
|
|
||||||
return (elem_type) 1 << (bit_idx % ELEM_BITS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of elements required for BIT_CNT bits. */
|
|
||||||
static inline size_t
|
|
||||||
elem_cnt (size_t bit_cnt)
|
|
||||||
{
|
|
||||||
return DIV_ROUND_UP (bit_cnt, ELEM_BITS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of bytes required for BIT_CNT bits. */
|
|
||||||
static inline size_t
|
|
||||||
byte_cnt (size_t bit_cnt)
|
|
||||||
{
|
|
||||||
return sizeof (elem_type) * elem_cnt (bit_cnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a bit mask in which the bits actually used in the last
|
|
||||||
element of B's bits are set to 1 and the rest are set to 0. */
|
|
||||||
static inline elem_type
|
|
||||||
last_mask (const struct bitmap *b)
|
|
||||||
{
|
|
||||||
int last_bits = b->bit_cnt % ELEM_BITS;
|
|
||||||
return last_bits ? ((elem_type) 1 << last_bits) - 1 : (elem_type) -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Creation and destruction. */
|
|
||||||
|
|
||||||
/* Initializes B to be a bitmap of BIT_CNT bits
|
|
||||||
and sets all of its bits to false.
|
|
||||||
Returns true if success, false if memory allocation
|
|
||||||
failed. */
|
|
||||||
struct bitmap *
|
|
||||||
bitmap_create (size_t bit_cnt)
|
|
||||||
{
|
|
||||||
struct bitmap *b = malloc (sizeof *b);
|
|
||||||
if (b != NULL)
|
|
||||||
{
|
|
||||||
b->bit_cnt = bit_cnt;
|
|
||||||
b->bits = malloc (byte_cnt (bit_cnt));
|
|
||||||
if (b->bits != NULL || bit_cnt == 0)
|
|
||||||
{
|
|
||||||
bitmap_set_all (b, false);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
free (b);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Creates and returns a bitmap with BIT_CNT bits in the
|
|
||||||
BLOCK_SIZE bytes of storage preallocated at BLOCK.
|
|
||||||
BLOCK_SIZE must be at least bitmap_needed_bytes(BIT_CNT). */
|
|
||||||
struct bitmap *
|
|
||||||
bitmap_create_in_buf (size_t bit_cnt, void *block, size_t block_size UNUSED)
|
|
||||||
{
|
|
||||||
struct bitmap *b = block;
|
|
||||||
|
|
||||||
ASSERT (block_size >= bitmap_buf_size (bit_cnt));
|
|
||||||
|
|
||||||
b->bit_cnt = bit_cnt;
|
|
||||||
b->bits = (elem_type *) (b + 1);
|
|
||||||
bitmap_set_all (b, false);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of bytes required to accomodate a bitmap
|
|
||||||
with BIT_CNT bits (for use with bitmap_create_in_buf()). */
|
|
||||||
size_t
|
|
||||||
bitmap_buf_size (size_t bit_cnt)
|
|
||||||
{
|
|
||||||
return sizeof (struct bitmap) + byte_cnt (bit_cnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Destroys bitmap B, freeing its storage.
|
|
||||||
Not for use on bitmaps created by
|
|
||||||
bitmap_create_preallocated(). */
|
|
||||||
void
|
|
||||||
bitmap_destroy (struct bitmap *b)
|
|
||||||
{
|
|
||||||
if (b != NULL)
|
|
||||||
{
|
|
||||||
free (b->bits);
|
|
||||||
free (b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bitmap size. */
|
|
||||||
|
|
||||||
/* Returns the number of bits in B. */
|
|
||||||
size_t
|
|
||||||
bitmap_size (const struct bitmap *b)
|
|
||||||
{
|
|
||||||
return b->bit_cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setting and testing single bits. */
|
|
||||||
|
|
||||||
/* Atomically sets the bit numbered IDX in B to VALUE. */
|
|
||||||
void
|
|
||||||
bitmap_set (struct bitmap *b, size_t idx, bool value)
|
|
||||||
{
|
|
||||||
ASSERT (b != NULL);
|
|
||||||
ASSERT (idx < b->bit_cnt);
|
|
||||||
if (value)
|
|
||||||
bitmap_mark (b, idx);
|
|
||||||
else
|
|
||||||
bitmap_reset (b, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Atomically sets the bit numbered BIT_IDX in B to true. */
|
|
||||||
void
|
|
||||||
bitmap_mark (struct bitmap *b, size_t bit_idx)
|
|
||||||
{
|
|
||||||
size_t idx = elem_idx (bit_idx);
|
|
||||||
elem_type mask = bit_mask (bit_idx);
|
|
||||||
|
|
||||||
/* This is equivalent to `b->bits[idx] |= mask' except that it
|
|
||||||
is guaranteed to be atomic on a uniprocessor machine. See
|
|
||||||
the description of the OR instruction in [IA32-v2b]. */
|
|
||||||
asm ("orl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Atomically sets the bit numbered BIT_IDX in B to false. */
|
|
||||||
void
|
|
||||||
bitmap_reset (struct bitmap *b, size_t bit_idx)
|
|
||||||
{
|
|
||||||
size_t idx = elem_idx (bit_idx);
|
|
||||||
elem_type mask = bit_mask (bit_idx);
|
|
||||||
|
|
||||||
/* This is equivalent to `b->bits[idx] &= ~mask' except that it
|
|
||||||
is guaranteed to be atomic on a uniprocessor machine. See
|
|
||||||
the description of the AND instruction in [IA32-v2a]. */
|
|
||||||
asm ("andl %1, %0" : "=m" (b->bits[idx]) : "r" (~mask) : "cc");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Atomically toggles the bit numbered IDX in B;
|
|
||||||
that is, if it is true, makes it false,
|
|
||||||
and if it is false, makes it true. */
|
|
||||||
void
|
|
||||||
bitmap_flip (struct bitmap *b, size_t bit_idx)
|
|
||||||
{
|
|
||||||
size_t idx = elem_idx (bit_idx);
|
|
||||||
elem_type mask = bit_mask (bit_idx);
|
|
||||||
|
|
||||||
/* This is equivalent to `b->bits[idx] ^= mask' except that it
|
|
||||||
is guaranteed to be atomic on a uniprocessor machine. See
|
|
||||||
the description of the XOR instruction in [IA32-v2b]. */
|
|
||||||
asm ("xorl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the value of the bit numbered IDX in B. */
|
|
||||||
bool
|
|
||||||
bitmap_test (const struct bitmap *b, size_t idx)
|
|
||||||
{
|
|
||||||
ASSERT (b != NULL);
|
|
||||||
ASSERT (idx < b->bit_cnt);
|
|
||||||
return (b->bits[elem_idx (idx)] & bit_mask (idx)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setting and testing multiple bits. */
|
|
||||||
|
|
||||||
/* Sets all bits in B to VALUE. */
|
|
||||||
void
|
|
||||||
bitmap_set_all (struct bitmap *b, bool value)
|
|
||||||
{
|
|
||||||
ASSERT (b != NULL);
|
|
||||||
|
|
||||||
bitmap_set_multiple (b, 0, bitmap_size (b), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sets the CNT bits starting at START in B to VALUE. */
|
|
||||||
void
|
|
||||||
bitmap_set_multiple (struct bitmap *b, size_t start, size_t cnt, bool value)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
ASSERT (b != NULL);
|
|
||||||
ASSERT (start <= b->bit_cnt);
|
|
||||||
ASSERT (start + cnt <= b->bit_cnt);
|
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++)
|
|
||||||
bitmap_set (b, start + i, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of bits in B between START and START + CNT,
|
|
||||||
exclusive, that are set to VALUE. */
|
|
||||||
size_t
|
|
||||||
bitmap_count (const struct bitmap *b, size_t start, size_t cnt, bool value)
|
|
||||||
{
|
|
||||||
size_t i, value_cnt;
|
|
||||||
|
|
||||||
ASSERT (b != NULL);
|
|
||||||
ASSERT (start <= b->bit_cnt);
|
|
||||||
ASSERT (start + cnt <= b->bit_cnt);
|
|
||||||
|
|
||||||
value_cnt = 0;
|
|
||||||
for (i = 0; i < cnt; i++)
|
|
||||||
if (bitmap_test (b, start + i) == value)
|
|
||||||
value_cnt++;
|
|
||||||
return value_cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if any bits in B between START and START + CNT,
|
|
||||||
exclusive, are set to VALUE, and false otherwise. */
|
|
||||||
bool
|
|
||||||
bitmap_contains (const struct bitmap *b, size_t start, size_t cnt, bool value)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
ASSERT (b != NULL);
|
|
||||||
ASSERT (start <= b->bit_cnt);
|
|
||||||
ASSERT (start + cnt <= b->bit_cnt);
|
|
||||||
|
|
||||||
for (i = 0; i < cnt; i++)
|
|
||||||
if (bitmap_test (b, start + i) == value)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if any bits in B between START and START + CNT,
|
|
||||||
exclusive, are set to true, and false otherwise.*/
|
|
||||||
bool
|
|
||||||
bitmap_any (const struct bitmap *b, size_t start, size_t cnt)
|
|
||||||
{
|
|
||||||
return bitmap_contains (b, start, cnt, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if no bits in B between START and START + CNT,
|
|
||||||
exclusive, are set to true, and false otherwise.*/
|
|
||||||
bool
|
|
||||||
bitmap_none (const struct bitmap *b, size_t start, size_t cnt)
|
|
||||||
{
|
|
||||||
return !bitmap_contains (b, start, cnt, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if every bit in B between START and START + CNT,
|
|
||||||
exclusive, is set to true, and false otherwise. */
|
|
||||||
bool
|
|
||||||
bitmap_all (const struct bitmap *b, size_t start, size_t cnt)
|
|
||||||
{
|
|
||||||
return !bitmap_contains (b, start, cnt, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finding set or unset bits. */
|
|
||||||
|
|
||||||
/* Finds and returns the starting index of the first group of CNT
|
|
||||||
consecutive bits in B at or after START that are all set to
|
|
||||||
VALUE.
|
|
||||||
If there is no such group, returns BITMAP_ERROR. */
|
|
||||||
size_t
|
|
||||||
bitmap_scan (const struct bitmap *b, size_t start, size_t cnt, bool value)
|
|
||||||
{
|
|
||||||
ASSERT (b != NULL);
|
|
||||||
ASSERT (start <= b->bit_cnt);
|
|
||||||
|
|
||||||
if (cnt <= b->bit_cnt)
|
|
||||||
{
|
|
||||||
size_t last = b->bit_cnt - cnt;
|
|
||||||
size_t i;
|
|
||||||
for (i = start; i <= last; i++)
|
|
||||||
if (!bitmap_contains (b, i, cnt, !value))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return BITMAP_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finds the first group of CNT consecutive bits in B at or after
|
|
||||||
START that are all set to VALUE, flips them all to !VALUE,
|
|
||||||
and returns the index of the first bit in the group.
|
|
||||||
If there is no such group, returns BITMAP_ERROR.
|
|
||||||
If CNT is zero, returns 0.
|
|
||||||
Bits are set atomically, but testing bits is not atomic with
|
|
||||||
setting them. */
|
|
||||||
size_t
|
|
||||||
bitmap_scan_and_flip (struct bitmap *b, size_t start, size_t cnt, bool value)
|
|
||||||
{
|
|
||||||
size_t idx = bitmap_scan (b, start, cnt, value);
|
|
||||||
if (idx != BITMAP_ERROR)
|
|
||||||
bitmap_set_multiple (b, idx, cnt, !value);
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* File input and output. */
|
|
||||||
|
|
||||||
#ifdef FILESYS
|
|
||||||
/* Returns the number of bytes needed to store B in a file. */
|
|
||||||
size_t
|
|
||||||
bitmap_file_size (const struct bitmap *b)
|
|
||||||
{
|
|
||||||
return byte_cnt (b->bit_cnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads B from FILE. Returns true if successful, false
|
|
||||||
otherwise. */
|
|
||||||
bool
|
|
||||||
bitmap_read (struct bitmap *b, struct file *file)
|
|
||||||
{
|
|
||||||
bool success = true;
|
|
||||||
if (b->bit_cnt > 0)
|
|
||||||
{
|
|
||||||
off_t size = byte_cnt (b->bit_cnt);
|
|
||||||
success = file_read_at (file, b->bits, size, 0) == size;
|
|
||||||
b->bits[elem_cnt (b->bit_cnt) - 1] &= last_mask (b);
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes B to FILE. Return true if successful, false
|
|
||||||
otherwise. */
|
|
||||||
bool
|
|
||||||
bitmap_write (const struct bitmap *b, struct file *file)
|
|
||||||
{
|
|
||||||
off_t size = byte_cnt (b->bit_cnt);
|
|
||||||
return file_write_at (file, b->bits, size, 0) == size;
|
|
||||||
}
|
|
||||||
#endif /* FILESYS */
|
|
||||||
|
|
||||||
/* Debugging. */
|
|
||||||
|
|
||||||
/* Dumps the contents of B to the console as hexadecimal. */
|
|
||||||
void
|
|
||||||
bitmap_dump (const struct bitmap *b)
|
|
||||||
{
|
|
||||||
hex_dump (0, b->bits, byte_cnt (b->bit_cnt), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#ifndef __LIB_KERNEL_BITMAP_H
|
|
||||||
#define __LIB_KERNEL_BITMAP_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
/* Bitmap abstract data type. */
|
|
||||||
|
|
||||||
/* Creation and destruction. */
|
|
||||||
struct bitmap *bitmap_create (size_t bit_cnt);
|
|
||||||
struct bitmap *bitmap_create_in_buf (size_t bit_cnt, void *, size_t byte_cnt);
|
|
||||||
size_t bitmap_buf_size (size_t bit_cnt);
|
|
||||||
void bitmap_destroy (struct bitmap *);
|
|
||||||
|
|
||||||
/* Bitmap size. */
|
|
||||||
size_t bitmap_size (const struct bitmap *);
|
|
||||||
|
|
||||||
/* Setting and testing single bits. */
|
|
||||||
void bitmap_set (struct bitmap *, size_t idx, bool);
|
|
||||||
void bitmap_mark (struct bitmap *, size_t idx);
|
|
||||||
void bitmap_reset (struct bitmap *, size_t idx);
|
|
||||||
void bitmap_flip (struct bitmap *, size_t idx);
|
|
||||||
bool bitmap_test (const struct bitmap *, size_t idx);
|
|
||||||
|
|
||||||
/* Setting and testing multiple bits. */
|
|
||||||
void bitmap_set_all (struct bitmap *, bool);
|
|
||||||
void bitmap_set_multiple (struct bitmap *, size_t start, size_t cnt, bool);
|
|
||||||
size_t bitmap_count (const struct bitmap *, size_t start, size_t cnt, bool);
|
|
||||||
bool bitmap_contains (const struct bitmap *, size_t start, size_t cnt, bool);
|
|
||||||
bool bitmap_any (const struct bitmap *, size_t start, size_t cnt);
|
|
||||||
bool bitmap_none (const struct bitmap *, size_t start, size_t cnt);
|
|
||||||
bool bitmap_all (const struct bitmap *, size_t start, size_t cnt);
|
|
||||||
|
|
||||||
/* Finding set or unset bits. */
|
|
||||||
#define BITMAP_ERROR SIZE_MAX
|
|
||||||
size_t bitmap_scan (const struct bitmap *, size_t start, size_t cnt, bool);
|
|
||||||
size_t bitmap_scan_and_flip (struct bitmap *, size_t start, size_t cnt, bool);
|
|
||||||
|
|
||||||
/* File input and output. */
|
|
||||||
#ifdef FILESYS
|
|
||||||
struct file;
|
|
||||||
size_t bitmap_file_size (const struct bitmap *);
|
|
||||||
bool bitmap_read (struct bitmap *, struct file *);
|
|
||||||
bool bitmap_write (const struct bitmap *, struct file *);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Debugging. */
|
|
||||||
void bitmap_dump (const struct bitmap *);
|
|
||||||
|
|
||||||
#endif /* lib/kernel/bitmap.h */
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
#include <console.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "devices/serial.h"
|
|
||||||
#include "devices/vga.h"
|
|
||||||
#include "threads/init.h"
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/synch.h"
|
|
||||||
|
|
||||||
static void vprintf_helper (char, void *);
|
|
||||||
static void putchar_have_lock (uint8_t c);
|
|
||||||
|
|
||||||
/* The console lock.
|
|
||||||
Both the vga and serial layers do their own locking, so it's
|
|
||||||
safe to call them at any time.
|
|
||||||
But this lock is useful to prevent simultaneous printf() calls
|
|
||||||
from mixing their output, which looks confusing. */
|
|
||||||
static struct lock console_lock;
|
|
||||||
|
|
||||||
/* True in ordinary circumstances: we want to use the console
|
|
||||||
lock to avoid mixing output between threads, as explained
|
|
||||||
above.
|
|
||||||
|
|
||||||
False in early boot before the point that locks are functional
|
|
||||||
or the console lock has been initialized, or after a kernel
|
|
||||||
panics. In the former case, taking the lock would cause an
|
|
||||||
assertion failure, which in turn would cause a panic, turning
|
|
||||||
it into the latter case. In the latter case, if it is a buggy
|
|
||||||
lock_acquire() implementation that caused the panic, we'll
|
|
||||||
likely just recurse. */
|
|
||||||
static bool use_console_lock;
|
|
||||||
|
|
||||||
/* It's possible, if you add enough debug output to PintOS, to
|
|
||||||
try to recursively grab console_lock from a single thread. As
|
|
||||||
a real example, I added a printf() call to palloc_free().
|
|
||||||
Here's a real backtrace that resulted:
|
|
||||||
|
|
||||||
lock_console()
|
|
||||||
vprintf()
|
|
||||||
printf() - palloc() tries to grab the lock again
|
|
||||||
palloc_free()
|
|
||||||
thread_schedule_tail() - another thread dying as we switch threads
|
|
||||||
schedule()
|
|
||||||
thread_yield()
|
|
||||||
intr_handler() - timer interrupt
|
|
||||||
intr_set_level()
|
|
||||||
serial_putc()
|
|
||||||
putchar_have_lock()
|
|
||||||
putbuf()
|
|
||||||
sys_write() - one process writing to the console
|
|
||||||
syscall_handler()
|
|
||||||
intr_handler()
|
|
||||||
|
|
||||||
This kind of thing is very difficult to debug, so we avoid the
|
|
||||||
problem by simulating a recursive lock with a depth
|
|
||||||
counter. */
|
|
||||||
static int console_lock_depth;
|
|
||||||
|
|
||||||
/* Number of characters written to console. */
|
|
||||||
static int64_t write_cnt;
|
|
||||||
|
|
||||||
/* Enable console locking. */
|
|
||||||
void
|
|
||||||
console_init (void)
|
|
||||||
{
|
|
||||||
lock_init (&console_lock);
|
|
||||||
use_console_lock = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Notifies the console that a kernel panic is underway,
|
|
||||||
which warns it to avoid trying to take the console lock from
|
|
||||||
now on. */
|
|
||||||
void
|
|
||||||
console_panic (void)
|
|
||||||
{
|
|
||||||
use_console_lock = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prints console statistics. */
|
|
||||||
void
|
|
||||||
console_print_stats (void)
|
|
||||||
{
|
|
||||||
printf ("Console: %lld characters output\n", write_cnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Acquires the console lock. */
|
|
||||||
static void
|
|
||||||
acquire_console (void)
|
|
||||||
{
|
|
||||||
if (!intr_context () && use_console_lock)
|
|
||||||
{
|
|
||||||
if (lock_held_by_current_thread (&console_lock))
|
|
||||||
console_lock_depth++;
|
|
||||||
else
|
|
||||||
lock_acquire (&console_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Releases the console lock. */
|
|
||||||
static void
|
|
||||||
release_console (void)
|
|
||||||
{
|
|
||||||
if (!intr_context () && use_console_lock)
|
|
||||||
{
|
|
||||||
if (console_lock_depth > 0)
|
|
||||||
console_lock_depth--;
|
|
||||||
else
|
|
||||||
lock_release (&console_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if the current thread has the console lock,
|
|
||||||
false otherwise. */
|
|
||||||
static bool
|
|
||||||
console_locked_by_current_thread (void)
|
|
||||||
{
|
|
||||||
return (intr_context ()
|
|
||||||
|| !use_console_lock
|
|
||||||
|| lock_held_by_current_thread (&console_lock));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The standard vprintf() function,
|
|
||||||
which is like printf() but uses a va_list.
|
|
||||||
Writes its output to both vga display and serial port. */
|
|
||||||
int
|
|
||||||
vprintf (const char *format, va_list args)
|
|
||||||
{
|
|
||||||
int char_cnt = 0;
|
|
||||||
|
|
||||||
acquire_console ();
|
|
||||||
__vprintf (format, args, vprintf_helper, &char_cnt);
|
|
||||||
release_console ();
|
|
||||||
|
|
||||||
return char_cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes string S to the console, followed by a new-line
|
|
||||||
character. */
|
|
||||||
int
|
|
||||||
puts (const char *s)
|
|
||||||
{
|
|
||||||
acquire_console ();
|
|
||||||
while (*s != '\0')
|
|
||||||
putchar_have_lock (*s++);
|
|
||||||
putchar_have_lock ('\n');
|
|
||||||
release_console ();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes the N characters in BUFFER to the console. */
|
|
||||||
void
|
|
||||||
putbuf (const char *buffer, size_t n)
|
|
||||||
{
|
|
||||||
acquire_console ();
|
|
||||||
while (n-- > 0)
|
|
||||||
putchar_have_lock (*buffer++);
|
|
||||||
release_console ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes C to the vga display and serial port. */
|
|
||||||
int
|
|
||||||
putchar (int c)
|
|
||||||
{
|
|
||||||
acquire_console ();
|
|
||||||
putchar_have_lock (c);
|
|
||||||
release_console ();
|
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function for vprintf(). */
|
|
||||||
static void
|
|
||||||
vprintf_helper (char c, void *char_cnt_)
|
|
||||||
{
|
|
||||||
int *char_cnt = char_cnt_;
|
|
||||||
(*char_cnt)++;
|
|
||||||
putchar_have_lock (c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes C to the vga display and serial port.
|
|
||||||
The caller has already acquired the console lock if
|
|
||||||
appropriate. */
|
|
||||||
static void
|
|
||||||
putchar_have_lock (uint8_t c)
|
|
||||||
{
|
|
||||||
ASSERT (console_locked_by_current_thread ());
|
|
||||||
write_cnt++;
|
|
||||||
serial_putc (c);
|
|
||||||
vga_putc (c);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#ifndef __LIB_KERNEL_CONSOLE_H
|
|
||||||
#define __LIB_KERNEL_CONSOLE_H
|
|
||||||
|
|
||||||
void console_init (void);
|
|
||||||
void console_panic (void);
|
|
||||||
void console_print_stats (void);
|
|
||||||
|
|
||||||
#endif /* lib/kernel/console.h */
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
#include <debug.h>
|
|
||||||
#include <console.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "threads/init.h"
|
|
||||||
#include "threads/interrupt.h"
|
|
||||||
#include "threads/thread.h"
|
|
||||||
#include "threads/switch.h"
|
|
||||||
#include "threads/vaddr.h"
|
|
||||||
#include "devices/serial.h"
|
|
||||||
#include "devices/shutdown.h"
|
|
||||||
|
|
||||||
/* Halts the OS, printing the source file name, line number, and
|
|
||||||
function name, plus a user-specific message. */
|
|
||||||
void
|
|
||||||
debug_panic (const char *file, int line, const char *function,
|
|
||||||
const char *message, ...)
|
|
||||||
{
|
|
||||||
static int level;
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
intr_disable ();
|
|
||||||
console_panic ();
|
|
||||||
|
|
||||||
level++;
|
|
||||||
if (level == 1)
|
|
||||||
{
|
|
||||||
printf ("Kernel PANIC at %s:%d in %s(): ", file, line, function);
|
|
||||||
|
|
||||||
va_start (args, message);
|
|
||||||
vprintf (message, args);
|
|
||||||
printf ("\n");
|
|
||||||
va_end (args);
|
|
||||||
|
|
||||||
debug_backtrace ();
|
|
||||||
}
|
|
||||||
else if (level == 2)
|
|
||||||
printf ("Kernel PANIC recursion at %s:%d in %s().\n",
|
|
||||||
file, line, function);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Don't print anything: that's probably why we recursed. */
|
|
||||||
}
|
|
||||||
|
|
||||||
serial_flush ();
|
|
||||||
shutdown ();
|
|
||||||
for (;;);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print call stack of a thread.
|
|
||||||
The thread may be running, ready, or blocked. */
|
|
||||||
static void
|
|
||||||
print_stacktrace(struct thread *t, void *aux UNUSED)
|
|
||||||
{
|
|
||||||
void *retaddr = NULL, **frame = NULL;
|
|
||||||
const char *status = "UNKNOWN";
|
|
||||||
|
|
||||||
switch (t->status) {
|
|
||||||
case THREAD_RUNNING:
|
|
||||||
status = "RUNNING";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case THREAD_READY:
|
|
||||||
status = "READY";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case THREAD_BLOCKED:
|
|
||||||
status = "BLOCKED";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf ("Call stack of thread `%s' (status %s):", t->name, status);
|
|
||||||
|
|
||||||
if (t == thread_current())
|
|
||||||
{
|
|
||||||
frame = __builtin_frame_address (1);
|
|
||||||
retaddr = __builtin_return_address (0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Retrieve the values of the base and instruction pointers
|
|
||||||
as they were saved when this thread called switch_threads. */
|
|
||||||
struct switch_threads_frame * saved_frame;
|
|
||||||
|
|
||||||
saved_frame = (struct switch_threads_frame *)t->stack;
|
|
||||||
|
|
||||||
/* Skip threads if they have been added to the all threads
|
|
||||||
list, but have never been scheduled.
|
|
||||||
We can identify because their `stack' member either points
|
|
||||||
at the top of their kernel stack page, or the
|
|
||||||
switch_threads_frame's 'eip' member points at switch_entry.
|
|
||||||
See also threads.c. */
|
|
||||||
if (t->stack == (uint8_t *)t + PGSIZE || saved_frame->eip == switch_entry)
|
|
||||||
{
|
|
||||||
printf (" thread was never scheduled.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame = (void **) saved_frame->ebp;
|
|
||||||
retaddr = (void *) saved_frame->eip;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf (" %p", retaddr);
|
|
||||||
for (; (uintptr_t) frame >= 0x1000 && frame[0] != NULL; frame = frame[0])
|
|
||||||
printf (" %p", frame[1]);
|
|
||||||
printf (".\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prints call stack of all threads. */
|
|
||||||
void
|
|
||||||
debug_backtrace_all (void)
|
|
||||||
{
|
|
||||||
enum intr_level oldlevel = intr_disable ();
|
|
||||||
|
|
||||||
thread_foreach (print_stacktrace, 0);
|
|
||||||
intr_set_level (oldlevel);
|
|
||||||
}
|
|
||||||
@@ -1,437 +0,0 @@
|
|||||||
/* Hash table.
|
|
||||||
|
|
||||||
This data structure is thoroughly documented in the Tour of
|
|
||||||
PintOS for Task 3.
|
|
||||||
|
|
||||||
See hash.h for basic information. */
|
|
||||||
|
|
||||||
#include "hash.h"
|
|
||||||
#include "../debug.h"
|
|
||||||
#include "threads/malloc.h"
|
|
||||||
|
|
||||||
#define list_elem_to_hash_elem(LIST_ELEM) \
|
|
||||||
list_entry(LIST_ELEM, struct hash_elem, list_elem)
|
|
||||||
|
|
||||||
static struct list *find_bucket (struct hash *, struct hash_elem *);
|
|
||||||
static struct hash_elem *find_elem (struct hash *, struct list *,
|
|
||||||
struct hash_elem *);
|
|
||||||
static void insert_elem (struct hash *, struct list *, struct hash_elem *);
|
|
||||||
static void remove_elem (struct hash *, struct hash_elem *);
|
|
||||||
static void rehash (struct hash *);
|
|
||||||
|
|
||||||
/* Initializes hash table H to compute hash values using HASH and
|
|
||||||
compare hash elements using LESS, given auxiliary data AUX. */
|
|
||||||
bool
|
|
||||||
hash_init (struct hash *h,
|
|
||||||
hash_hash_func *hash, hash_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
h->elem_cnt = 0;
|
|
||||||
h->bucket_cnt = 4;
|
|
||||||
h->buckets = malloc (sizeof *h->buckets * h->bucket_cnt);
|
|
||||||
h->hash = hash;
|
|
||||||
h->less = less;
|
|
||||||
h->aux = aux;
|
|
||||||
|
|
||||||
if (h->buckets != NULL)
|
|
||||||
{
|
|
||||||
hash_clear (h, NULL);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes all the elements from H.
|
|
||||||
|
|
||||||
If DESTRUCTOR is non-null, then it is called for each element
|
|
||||||
in the hash. DESTRUCTOR may, if appropriate, deallocate the
|
|
||||||
memory used by the hash element. However, modifying hash
|
|
||||||
table H while hash_clear() is running, using any of the
|
|
||||||
functions hash_clear(), hash_destroy(), hash_insert(),
|
|
||||||
hash_replace(), or hash_delete(), yields undefined behavior,
|
|
||||||
whether done in DESTRUCTOR or elsewhere. */
|
|
||||||
void
|
|
||||||
hash_clear (struct hash *h, hash_action_func *destructor)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < h->bucket_cnt; i++)
|
|
||||||
{
|
|
||||||
struct list *bucket = &h->buckets[i];
|
|
||||||
|
|
||||||
if (destructor != NULL)
|
|
||||||
while (!list_empty (bucket))
|
|
||||||
{
|
|
||||||
struct list_elem *list_elem = list_pop_front (bucket);
|
|
||||||
struct hash_elem *hash_elem = list_elem_to_hash_elem (list_elem);
|
|
||||||
destructor (hash_elem, h->aux);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_init (bucket);
|
|
||||||
}
|
|
||||||
|
|
||||||
h->elem_cnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Destroys hash table H.
|
|
||||||
|
|
||||||
If DESTRUCTOR is non-null, then it is first called for each
|
|
||||||
element in the hash. DESTRUCTOR may, if appropriate,
|
|
||||||
deallocate the memory used by the hash element. However,
|
|
||||||
modifying hash table H while hash_clear() is running, using
|
|
||||||
any of the functions hash_clear(), hash_destroy(),
|
|
||||||
hash_insert(), hash_replace(), or hash_delete(), yields
|
|
||||||
undefined behavior, whether done in DESTRUCTOR or
|
|
||||||
elsewhere. */
|
|
||||||
void
|
|
||||||
hash_destroy (struct hash *h, hash_action_func *destructor)
|
|
||||||
{
|
|
||||||
if (destructor != NULL)
|
|
||||||
hash_clear (h, destructor);
|
|
||||||
free (h->buckets);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inserts NEW into hash table H and returns a null pointer, if
|
|
||||||
no equal element is already in the table.
|
|
||||||
If an equal element is already in the table, returns it
|
|
||||||
without inserting NEW. */
|
|
||||||
struct hash_elem *
|
|
||||||
hash_insert (struct hash *h, struct hash_elem *new)
|
|
||||||
{
|
|
||||||
struct list *bucket = find_bucket (h, new);
|
|
||||||
struct hash_elem *old = find_elem (h, bucket, new);
|
|
||||||
|
|
||||||
if (old == NULL)
|
|
||||||
insert_elem (h, bucket, new);
|
|
||||||
|
|
||||||
rehash (h);
|
|
||||||
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inserts NEW into hash table H, replacing any equal element
|
|
||||||
already in the table, which is returned. */
|
|
||||||
struct hash_elem *
|
|
||||||
hash_replace (struct hash *h, struct hash_elem *new)
|
|
||||||
{
|
|
||||||
struct list *bucket = find_bucket (h, new);
|
|
||||||
struct hash_elem *old = find_elem (h, bucket, new);
|
|
||||||
|
|
||||||
if (old != NULL)
|
|
||||||
remove_elem (h, old);
|
|
||||||
insert_elem (h, bucket, new);
|
|
||||||
|
|
||||||
rehash (h);
|
|
||||||
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finds and returns an element equal to E in hash table H, or a
|
|
||||||
null pointer if no equal element exists in the table. */
|
|
||||||
struct hash_elem *
|
|
||||||
hash_find (struct hash *h, struct hash_elem *e)
|
|
||||||
{
|
|
||||||
return find_elem (h, find_bucket (h, e), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finds, removes, and returns an element equal to E in hash
|
|
||||||
table H. Returns a null pointer if no equal element existed
|
|
||||||
in the table.
|
|
||||||
|
|
||||||
If the elements of the hash table are dynamically allocated,
|
|
||||||
or own resources that are, then it is the caller's
|
|
||||||
responsibility to deallocate them. */
|
|
||||||
struct hash_elem *
|
|
||||||
hash_delete (struct hash *h, struct hash_elem *e)
|
|
||||||
{
|
|
||||||
struct hash_elem *found = find_elem (h, find_bucket (h, e), e);
|
|
||||||
if (found != NULL)
|
|
||||||
{
|
|
||||||
remove_elem (h, found);
|
|
||||||
rehash (h);
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calls ACTION for each element in hash table H in arbitrary
|
|
||||||
order.
|
|
||||||
Modifying hash table H while hash_apply() is running, using
|
|
||||||
any of the functions hash_clear(), hash_destroy(),
|
|
||||||
hash_insert(), hash_replace(), or hash_delete(), yields
|
|
||||||
undefined behavior, whether done from ACTION or elsewhere. */
|
|
||||||
void
|
|
||||||
hash_apply (struct hash *h, hash_action_func *action)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
ASSERT (action != NULL);
|
|
||||||
|
|
||||||
for (i = 0; i < h->bucket_cnt; i++)
|
|
||||||
{
|
|
||||||
struct list *bucket = &h->buckets[i];
|
|
||||||
struct list_elem *elem, *next;
|
|
||||||
|
|
||||||
for (elem = list_begin (bucket); elem != list_end (bucket); elem = next)
|
|
||||||
{
|
|
||||||
next = list_next (elem);
|
|
||||||
action (list_elem_to_hash_elem (elem), h->aux);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initializes I for iterating hash table H.
|
|
||||||
|
|
||||||
Iteration idiom:
|
|
||||||
|
|
||||||
struct hash_iterator i;
|
|
||||||
|
|
||||||
hash_first (&i, h);
|
|
||||||
while (hash_next (&i))
|
|
||||||
{
|
|
||||||
struct foo *f = hash_entry (hash_cur (&i), struct foo, elem);
|
|
||||||
...do something with f...
|
|
||||||
}
|
|
||||||
|
|
||||||
Modifying hash table H during iteration, using any of the
|
|
||||||
functions hash_clear(), hash_destroy(), hash_insert(),
|
|
||||||
hash_replace(), or hash_delete(), invalidates all
|
|
||||||
iterators. */
|
|
||||||
void
|
|
||||||
hash_first (struct hash_iterator *i, struct hash *h)
|
|
||||||
{
|
|
||||||
ASSERT (i != NULL);
|
|
||||||
ASSERT (h != NULL);
|
|
||||||
|
|
||||||
i->hash = h;
|
|
||||||
i->bucket = i->hash->buckets;
|
|
||||||
i->elem = list_elem_to_hash_elem (list_head (i->bucket));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advances I to the next element in the hash table and returns
|
|
||||||
it. Returns a null pointer if no elements are left. Elements
|
|
||||||
are returned in arbitrary order.
|
|
||||||
|
|
||||||
Modifying a hash table H during iteration, using any of the
|
|
||||||
functions hash_clear(), hash_destroy(), hash_insert(),
|
|
||||||
hash_replace(), or hash_delete(), invalidates all
|
|
||||||
iterators. */
|
|
||||||
struct hash_elem *
|
|
||||||
hash_next (struct hash_iterator *i)
|
|
||||||
{
|
|
||||||
ASSERT (i != NULL);
|
|
||||||
|
|
||||||
i->elem = list_elem_to_hash_elem (list_next (&i->elem->list_elem));
|
|
||||||
while (i->elem == list_elem_to_hash_elem (list_end (i->bucket)))
|
|
||||||
{
|
|
||||||
if (++i->bucket >= i->hash->buckets + i->hash->bucket_cnt)
|
|
||||||
{
|
|
||||||
i->elem = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i->elem = list_elem_to_hash_elem (list_begin (i->bucket));
|
|
||||||
}
|
|
||||||
|
|
||||||
return i->elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the current element in the hash table iteration, or a
|
|
||||||
null pointer at the end of the table. Undefined behavior
|
|
||||||
after calling hash_first() but before hash_next(). */
|
|
||||||
struct hash_elem *
|
|
||||||
hash_cur (struct hash_iterator *i)
|
|
||||||
{
|
|
||||||
return i->elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of elements in H. */
|
|
||||||
size_t
|
|
||||||
hash_size (struct hash *h)
|
|
||||||
{
|
|
||||||
return h->elem_cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if H contains no elements, false otherwise. */
|
|
||||||
bool
|
|
||||||
hash_empty (struct hash *h)
|
|
||||||
{
|
|
||||||
return h->elem_cnt == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */
|
|
||||||
#define FNV_32_PRIME 16777619u
|
|
||||||
#define FNV_32_BASIS 2166136261u
|
|
||||||
|
|
||||||
/* Returns a hash of the SIZE bytes in BUF. */
|
|
||||||
unsigned
|
|
||||||
hash_bytes (const void *buf_, size_t size)
|
|
||||||
{
|
|
||||||
/* Fowler-Noll-Vo 32-bit hash, for bytes. */
|
|
||||||
const unsigned char *buf = buf_;
|
|
||||||
unsigned hash;
|
|
||||||
|
|
||||||
ASSERT (buf != NULL);
|
|
||||||
|
|
||||||
hash = FNV_32_BASIS;
|
|
||||||
while (size-- > 0)
|
|
||||||
hash = (hash * FNV_32_PRIME) ^ *buf++;
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a hash of string S. */
|
|
||||||
unsigned
|
|
||||||
hash_string (const char *s_)
|
|
||||||
{
|
|
||||||
const unsigned char *s = (const unsigned char *) s_;
|
|
||||||
unsigned hash;
|
|
||||||
|
|
||||||
ASSERT (s != NULL);
|
|
||||||
|
|
||||||
hash = FNV_32_BASIS;
|
|
||||||
while (*s != '\0')
|
|
||||||
hash = (hash * FNV_32_PRIME) ^ *s++;
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a hash of integer I. */
|
|
||||||
unsigned
|
|
||||||
hash_int (int i)
|
|
||||||
{
|
|
||||||
return hash_bytes (&i, sizeof i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a hash of pointer P */
|
|
||||||
unsigned
|
|
||||||
hash_ptr (const void *p)
|
|
||||||
{
|
|
||||||
return hash_bytes (&p, sizeof p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the bucket in H that E belongs in. */
|
|
||||||
static struct list *
|
|
||||||
find_bucket (struct hash *h, struct hash_elem *e)
|
|
||||||
{
|
|
||||||
size_t bucket_idx = h->hash (e, h->aux) & (h->bucket_cnt - 1);
|
|
||||||
return &h->buckets[bucket_idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Searches BUCKET in H for a hash element equal to E. Returns
|
|
||||||
it if found or a null pointer otherwise. */
|
|
||||||
static struct hash_elem *
|
|
||||||
find_elem (struct hash *h, struct list *bucket, struct hash_elem *e)
|
|
||||||
{
|
|
||||||
struct list_elem *i;
|
|
||||||
|
|
||||||
for (i = list_begin (bucket); i != list_end (bucket); i = list_next (i))
|
|
||||||
{
|
|
||||||
struct hash_elem *hi = list_elem_to_hash_elem (i);
|
|
||||||
if (!h->less (hi, e, h->aux) && !h->less (e, hi, h->aux))
|
|
||||||
return hi;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns X with its lowest-order bit set to 1 turned off. */
|
|
||||||
static inline size_t
|
|
||||||
turn_off_least_1bit (size_t x)
|
|
||||||
{
|
|
||||||
return x & (x - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if X is a power of 2, otherwise false. */
|
|
||||||
static inline size_t
|
|
||||||
is_power_of_2 (size_t x)
|
|
||||||
{
|
|
||||||
return x != 0 && turn_off_least_1bit (x) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Element per bucket ratios. */
|
|
||||||
#define MIN_ELEMS_PER_BUCKET 1 /* Elems/bucket < 1: reduce # of buckets. */
|
|
||||||
#define BEST_ELEMS_PER_BUCKET 2 /* Ideal elems/bucket. */
|
|
||||||
#define MAX_ELEMS_PER_BUCKET 4 /* Elems/bucket > 4: increase # of buckets. */
|
|
||||||
|
|
||||||
/* Changes the number of buckets in hash table H to match the
|
|
||||||
ideal. This function can fail because of an out-of-memory
|
|
||||||
condition, but that'll just make hash accesses less efficient;
|
|
||||||
we can still continue. */
|
|
||||||
static void
|
|
||||||
rehash (struct hash *h)
|
|
||||||
{
|
|
||||||
size_t old_bucket_cnt, new_bucket_cnt;
|
|
||||||
struct list *new_buckets, *old_buckets;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
ASSERT (h != NULL);
|
|
||||||
|
|
||||||
/* Save old bucket info for later use. */
|
|
||||||
old_buckets = h->buckets;
|
|
||||||
old_bucket_cnt = h->bucket_cnt;
|
|
||||||
|
|
||||||
/* Calculate the number of buckets to use now.
|
|
||||||
We want one bucket for about every BEST_ELEMS_PER_BUCKET.
|
|
||||||
We must have at least four buckets, and the number of
|
|
||||||
buckets must be a power of 2. */
|
|
||||||
new_bucket_cnt = h->elem_cnt / BEST_ELEMS_PER_BUCKET;
|
|
||||||
if (new_bucket_cnt < 4)
|
|
||||||
new_bucket_cnt = 4;
|
|
||||||
while (!is_power_of_2 (new_bucket_cnt))
|
|
||||||
new_bucket_cnt = turn_off_least_1bit (new_bucket_cnt);
|
|
||||||
|
|
||||||
/* Don't do anything if the bucket count wouldn't change. */
|
|
||||||
if (new_bucket_cnt == old_bucket_cnt)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Allocate new buckets and initialize them as empty. */
|
|
||||||
new_buckets = malloc (sizeof *new_buckets * new_bucket_cnt);
|
|
||||||
if (new_buckets == NULL)
|
|
||||||
{
|
|
||||||
/* Allocation failed. This means that use of the hash table will
|
|
||||||
be less efficient. However, it is still usable, so
|
|
||||||
there's no reason for it to be an error. */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (i = 0; i < new_bucket_cnt; i++)
|
|
||||||
list_init (&new_buckets[i]);
|
|
||||||
|
|
||||||
/* Install new bucket info. */
|
|
||||||
h->buckets = new_buckets;
|
|
||||||
h->bucket_cnt = new_bucket_cnt;
|
|
||||||
|
|
||||||
/* Move each old element into the appropriate new bucket. */
|
|
||||||
for (i = 0; i < old_bucket_cnt; i++)
|
|
||||||
{
|
|
||||||
struct list *old_bucket;
|
|
||||||
struct list_elem *elem, *next;
|
|
||||||
|
|
||||||
old_bucket = &old_buckets[i];
|
|
||||||
for (elem = list_begin (old_bucket);
|
|
||||||
elem != list_end (old_bucket); elem = next)
|
|
||||||
{
|
|
||||||
struct list *new_bucket
|
|
||||||
= find_bucket (h, list_elem_to_hash_elem (elem));
|
|
||||||
next = list_next (elem);
|
|
||||||
list_remove (elem);
|
|
||||||
list_push_front (new_bucket, elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free (old_buckets);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inserts E into BUCKET (in hash table H). */
|
|
||||||
static void
|
|
||||||
insert_elem (struct hash *h, struct list *bucket, struct hash_elem *e)
|
|
||||||
{
|
|
||||||
h->elem_cnt++;
|
|
||||||
list_push_front (bucket, &e->list_elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes E from hash table H. */
|
|
||||||
static void
|
|
||||||
remove_elem (struct hash *h, struct hash_elem *e)
|
|
||||||
{
|
|
||||||
h->elem_cnt--;
|
|
||||||
list_remove (&e->list_elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
#ifndef __LIB_KERNEL_HASH_H
|
|
||||||
#define __LIB_KERNEL_HASH_H
|
|
||||||
|
|
||||||
/* Hash table.
|
|
||||||
|
|
||||||
This data structure is thoroughly documented in the PintOS
|
|
||||||
manual: Appendix A Reference Guide (A.8 Hash Table)
|
|
||||||
|
|
||||||
This is a standard hash table with chaining. To locate an
|
|
||||||
element in the table, we compute a hash function over the
|
|
||||||
element's data and use that as an index into an array of
|
|
||||||
doubly linked lists, then linearly search the list.
|
|
||||||
|
|
||||||
The chain lists do not use dynamic allocation. Instead, each
|
|
||||||
structure that can potentially be in a hash must embed a
|
|
||||||
struct hash_elem member. All of the hash functions operate on
|
|
||||||
these `struct hash_elem's. The hash_entry macro allows
|
|
||||||
conversion from a struct hash_elem back to a structure object
|
|
||||||
that contains it. This is the same technique used in the
|
|
||||||
linked list implementation. Refer to lib/kernel/list.h for a
|
|
||||||
detailed explanation. */
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "list.h"
|
|
||||||
|
|
||||||
/* Hash element. */
|
|
||||||
struct hash_elem
|
|
||||||
{
|
|
||||||
struct list_elem list_elem;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Converts pointer to hash element HASH_ELEM into a pointer to
|
|
||||||
the structure that HASH_ELEM is embedded inside. Supply the
|
|
||||||
name of the outer structure STRUCT and the member name MEMBER
|
|
||||||
of the hash element. See the big comment at the top of the
|
|
||||||
file for an example. */
|
|
||||||
#define hash_entry(HASH_ELEM, STRUCT, MEMBER) \
|
|
||||||
((STRUCT *) ((uint8_t *) &(HASH_ELEM)->list_elem \
|
|
||||||
- offsetof (STRUCT, MEMBER.list_elem)))
|
|
||||||
|
|
||||||
/* Computes and returns the hash value for hash element E, given
|
|
||||||
auxiliary data AUX. */
|
|
||||||
typedef unsigned hash_hash_func (const struct hash_elem *e, void *aux);
|
|
||||||
|
|
||||||
/* Compares the value of two hash elements A and B, given
|
|
||||||
auxiliary data AUX. Returns true if A is less than B, or
|
|
||||||
false if A is greater than or equal to B. */
|
|
||||||
typedef bool hash_less_func (const struct hash_elem *a,
|
|
||||||
const struct hash_elem *b,
|
|
||||||
void *aux);
|
|
||||||
|
|
||||||
/* Performs some operation on hash element E, given auxiliary
|
|
||||||
data AUX. */
|
|
||||||
typedef void hash_action_func (struct hash_elem *e, void *aux);
|
|
||||||
|
|
||||||
/* Hash table. */
|
|
||||||
struct hash
|
|
||||||
{
|
|
||||||
size_t elem_cnt; /* Number of elements in table. */
|
|
||||||
size_t bucket_cnt; /* Number of buckets, a power of 2. */
|
|
||||||
struct list *buckets; /* Array of `bucket_cnt' lists. */
|
|
||||||
hash_hash_func *hash; /* Hash function. */
|
|
||||||
hash_less_func *less; /* Comparison function. */
|
|
||||||
void *aux; /* Auxiliary data for `hash' and `less'. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* A hash table iterator. */
|
|
||||||
struct hash_iterator
|
|
||||||
{
|
|
||||||
struct hash *hash; /* The hash table. */
|
|
||||||
struct list *bucket; /* Current bucket. */
|
|
||||||
struct hash_elem *elem; /* Current hash element in current bucket. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Basic life cycle. */
|
|
||||||
bool hash_init (struct hash *, hash_hash_func *, hash_less_func *, void *aux);
|
|
||||||
void hash_clear (struct hash *, hash_action_func *);
|
|
||||||
void hash_destroy (struct hash *, hash_action_func *);
|
|
||||||
|
|
||||||
/* Search, insertion, deletion. */
|
|
||||||
struct hash_elem *hash_insert (struct hash *, struct hash_elem *);
|
|
||||||
struct hash_elem *hash_replace (struct hash *, struct hash_elem *);
|
|
||||||
struct hash_elem *hash_find (struct hash *, struct hash_elem *);
|
|
||||||
struct hash_elem *hash_delete (struct hash *, struct hash_elem *);
|
|
||||||
|
|
||||||
/* Iteration. */
|
|
||||||
void hash_apply (struct hash *, hash_action_func *);
|
|
||||||
void hash_first (struct hash_iterator *, struct hash *);
|
|
||||||
struct hash_elem *hash_next (struct hash_iterator *);
|
|
||||||
struct hash_elem *hash_cur (struct hash_iterator *);
|
|
||||||
|
|
||||||
/* Information. */
|
|
||||||
size_t hash_size (struct hash *);
|
|
||||||
bool hash_empty (struct hash *);
|
|
||||||
|
|
||||||
/* Sample hash functions. */
|
|
||||||
unsigned hash_bytes (const void *, size_t);
|
|
||||||
unsigned hash_string (const char *);
|
|
||||||
unsigned hash_int (int);
|
|
||||||
unsigned hash_ptr (const void *);
|
|
||||||
|
|
||||||
#endif /* lib/kernel/hash.h */
|
|
||||||
@@ -1,527 +0,0 @@
|
|||||||
#include "list.h"
|
|
||||||
#include "../debug.h"
|
|
||||||
|
|
||||||
/* Our doubly linked lists have two header elements: the "head"
|
|
||||||
just before the first element and the "tail" just after the
|
|
||||||
last element. The `prev' link of the front header is null, as
|
|
||||||
is the `next' link of the back header. Their other two links
|
|
||||||
point toward each other via the interior elements of the list.
|
|
||||||
|
|
||||||
An empty list looks like this:
|
|
||||||
|
|
||||||
+------+ +------+
|
|
||||||
<---| head |<--->| tail |--->
|
|
||||||
+------+ +------+
|
|
||||||
|
|
||||||
A list with two elements in it looks like this:
|
|
||||||
|
|
||||||
+------+ +-------+ +-------+ +------+
|
|
||||||
<---| head |<--->| 1 |<--->| 2 |<--->| tail |--->
|
|
||||||
+------+ +-------+ +-------+ +------+
|
|
||||||
|
|
||||||
The symmetry of this arrangement eliminates lots of special
|
|
||||||
cases in list processing. For example, take a look at
|
|
||||||
list_remove(): it takes only two pointer assignments and no
|
|
||||||
conditionals. That's a lot simpler than the code would be
|
|
||||||
without header elements.
|
|
||||||
|
|
||||||
(Because only one of the pointers in each header element is used,
|
|
||||||
we could in fact combine them into a single header element
|
|
||||||
without sacrificing this simplicity. But using two separate
|
|
||||||
elements allows us to do a little bit of checking on some
|
|
||||||
operations, which can be valuable.) */
|
|
||||||
|
|
||||||
static bool is_sorted (struct list_elem *a, struct list_elem *b,
|
|
||||||
list_less_func *less, void *aux) UNUSED;
|
|
||||||
|
|
||||||
/* Returns true if ELEM is a head, false otherwise. */
|
|
||||||
static inline bool
|
|
||||||
is_head (struct list_elem *elem)
|
|
||||||
{
|
|
||||||
return elem != NULL && elem->prev == NULL && elem->next != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if ELEM is an interior element,
|
|
||||||
false otherwise. */
|
|
||||||
static inline bool
|
|
||||||
is_interior (struct list_elem *elem)
|
|
||||||
{
|
|
||||||
return elem != NULL && elem->prev != NULL && elem->next != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if ELEM is a tail, false otherwise. */
|
|
||||||
static inline bool
|
|
||||||
is_tail (struct list_elem *elem)
|
|
||||||
{
|
|
||||||
return elem != NULL && elem->prev != NULL && elem->next == NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initializes LIST as an empty list. */
|
|
||||||
void
|
|
||||||
list_init (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
list->head.prev = NULL;
|
|
||||||
list->head.next = &list->tail;
|
|
||||||
list->tail.prev = &list->head;
|
|
||||||
list->tail.next = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the beginning of LIST. */
|
|
||||||
struct list_elem *
|
|
||||||
list_begin (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
return list->head.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the element after ELEM in its list. If ELEM is the
|
|
||||||
last element in its list, returns the list tail. Results are
|
|
||||||
undefined if ELEM is itself a list tail. */
|
|
||||||
struct list_elem *
|
|
||||||
list_next (struct list_elem *elem)
|
|
||||||
{
|
|
||||||
ASSERT (is_head (elem) || is_interior (elem));
|
|
||||||
return elem->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns LIST's tail.
|
|
||||||
|
|
||||||
list_end() is often used in iterating through a list from
|
|
||||||
front to back. See the big comment at the top of list.h for
|
|
||||||
an example. */
|
|
||||||
struct list_elem *
|
|
||||||
list_end (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
return &list->tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the LIST's reverse beginning, for iterating through
|
|
||||||
LIST in reverse order, from back to front. */
|
|
||||||
struct list_elem *
|
|
||||||
list_rbegin (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
return list->tail.prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the element before ELEM in its list. If ELEM is the
|
|
||||||
first element in its list, returns the list head. Results are
|
|
||||||
undefined if ELEM is itself a list head. */
|
|
||||||
struct list_elem *
|
|
||||||
list_prev (struct list_elem *elem)
|
|
||||||
{
|
|
||||||
ASSERT (is_interior (elem) || is_tail (elem));
|
|
||||||
return elem->prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns LIST's head.
|
|
||||||
|
|
||||||
list_rend() is often used in iterating through a list in
|
|
||||||
reverse order, from back to front. Here's typical usage,
|
|
||||||
following the example from the top of list.h:
|
|
||||||
|
|
||||||
for (e = list_rbegin (&foo_list); e != list_rend (&foo_list);
|
|
||||||
e = list_prev (e))
|
|
||||||
{
|
|
||||||
struct foo *f = list_entry (e, struct foo, elem);
|
|
||||||
...do something with f...
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
struct list_elem *
|
|
||||||
list_rend (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
return &list->head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return's LIST's head.
|
|
||||||
|
|
||||||
list_head() can be used for an alternate style of iterating
|
|
||||||
through a list, e.g.:
|
|
||||||
|
|
||||||
e = list_head (&list);
|
|
||||||
while ((e = list_next (e)) != list_end (&list))
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
struct list_elem *
|
|
||||||
list_head (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
return &list->head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return's LIST's tail. */
|
|
||||||
struct list_elem *
|
|
||||||
list_tail (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
return &list->tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inserts ELEM just before BEFORE, which may be either an
|
|
||||||
interior element or a tail. The latter case is equivalent to
|
|
||||||
list_push_back(). Undefined behavior if ELEM is already in the list. */
|
|
||||||
void
|
|
||||||
list_insert (struct list_elem *before, struct list_elem *elem)
|
|
||||||
{
|
|
||||||
ASSERT (is_interior (before) || is_tail (before));
|
|
||||||
ASSERT (elem != NULL);
|
|
||||||
// Sanity checks to prevent (some) loop lists
|
|
||||||
ASSERT (before != elem);
|
|
||||||
ASSERT (before->prev != elem);
|
|
||||||
|
|
||||||
elem->prev = before->prev;
|
|
||||||
elem->next = before;
|
|
||||||
before->prev->next = elem;
|
|
||||||
before->prev = elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes elements FIRST though LAST (exclusive) from their
|
|
||||||
current list, then inserts them just before BEFORE, which may
|
|
||||||
be either an interior element or a tail. */
|
|
||||||
void
|
|
||||||
list_splice (struct list_elem *before,
|
|
||||||
struct list_elem *first, struct list_elem *last)
|
|
||||||
{
|
|
||||||
ASSERT (is_interior (before) || is_tail (before));
|
|
||||||
if (first == last)
|
|
||||||
return;
|
|
||||||
last = list_prev (last);
|
|
||||||
|
|
||||||
ASSERT (is_interior (first));
|
|
||||||
ASSERT (is_interior (last));
|
|
||||||
|
|
||||||
/* Cleanly remove FIRST...LAST from its current list. */
|
|
||||||
first->prev->next = last->next;
|
|
||||||
last->next->prev = first->prev;
|
|
||||||
|
|
||||||
/* Splice FIRST...LAST into new list. */
|
|
||||||
first->prev = before->prev;
|
|
||||||
last->next = before;
|
|
||||||
before->prev->next = first;
|
|
||||||
before->prev = last;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inserts ELEM at the beginning of LIST, so that it becomes the
|
|
||||||
front in LIST. */
|
|
||||||
void
|
|
||||||
list_push_front (struct list *list, struct list_elem *elem)
|
|
||||||
{
|
|
||||||
list_insert (list_begin (list), elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inserts ELEM at the end of LIST, so that it becomes the
|
|
||||||
back in LIST. */
|
|
||||||
void
|
|
||||||
list_push_back (struct list *list, struct list_elem *elem)
|
|
||||||
{
|
|
||||||
list_insert (list_end (list), elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes ELEM from its list and returns the element that
|
|
||||||
followed it. Undefined behavior if ELEM is not in a list.
|
|
||||||
|
|
||||||
A list element must be treated very carefully after removing
|
|
||||||
it from its list. Calling list_next() or list_prev() on ELEM
|
|
||||||
will return the item that was previously before or after ELEM,
|
|
||||||
but, e.g., list_prev(list_next(ELEM)) is no longer ELEM!
|
|
||||||
|
|
||||||
The list_remove() return value provides a convenient way to
|
|
||||||
iterate and remove elements from a list:
|
|
||||||
|
|
||||||
for (e = list_begin (&list); e != list_end (&list); e = list_remove (e))
|
|
||||||
{
|
|
||||||
...do something with e...
|
|
||||||
}
|
|
||||||
|
|
||||||
If you need to free() elements of the list then you need to be
|
|
||||||
more conservative. Here's an alternate strategy that works
|
|
||||||
even in that case:
|
|
||||||
|
|
||||||
while (!list_empty (&list))
|
|
||||||
{
|
|
||||||
struct list_elem *e = list_pop_front (&list);
|
|
||||||
...do something with e...
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
struct list_elem *
|
|
||||||
list_remove (struct list_elem *elem)
|
|
||||||
{
|
|
||||||
ASSERT (is_interior (elem));
|
|
||||||
elem->prev->next = elem->next;
|
|
||||||
elem->next->prev = elem->prev;
|
|
||||||
return elem->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes the front element from LIST and returns it.
|
|
||||||
Undefined behavior if LIST is empty before removal. */
|
|
||||||
struct list_elem *
|
|
||||||
list_pop_front (struct list *list)
|
|
||||||
{
|
|
||||||
struct list_elem *front = list_front (list);
|
|
||||||
list_remove (front);
|
|
||||||
return front;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes the back element from LIST and returns it.
|
|
||||||
Undefined behavior if LIST is empty before removal. */
|
|
||||||
struct list_elem *
|
|
||||||
list_pop_back (struct list *list)
|
|
||||||
{
|
|
||||||
struct list_elem *back = list_back (list);
|
|
||||||
list_remove (back);
|
|
||||||
return back;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the front element in LIST.
|
|
||||||
Undefined behavior if LIST is empty. */
|
|
||||||
struct list_elem *
|
|
||||||
list_front (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (!list_empty (list));
|
|
||||||
return list->head.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the back element in LIST.
|
|
||||||
Undefined behavior if LIST is empty. */
|
|
||||||
struct list_elem *
|
|
||||||
list_back (struct list *list)
|
|
||||||
{
|
|
||||||
ASSERT (!list_empty (list));
|
|
||||||
return list->tail.prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the number of elements in LIST.
|
|
||||||
Runs in O(n) in the number of elements. */
|
|
||||||
size_t
|
|
||||||
list_size (struct list *list)
|
|
||||||
{
|
|
||||||
struct list_elem *e;
|
|
||||||
size_t cnt = 0;
|
|
||||||
|
|
||||||
for (e = list_begin (list); e != list_end (list); e = list_next (e))
|
|
||||||
cnt++;
|
|
||||||
return cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if LIST is empty, false otherwise. */
|
|
||||||
bool
|
|
||||||
list_empty (struct list *list)
|
|
||||||
{
|
|
||||||
return list_begin (list) == list_end (list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swaps the `struct list_elem *'s that A and B point to. */
|
|
||||||
static void
|
|
||||||
swap (struct list_elem **a, struct list_elem **b)
|
|
||||||
{
|
|
||||||
struct list_elem *t = *a;
|
|
||||||
*a = *b;
|
|
||||||
*b = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reverses the order of LIST. */
|
|
||||||
void
|
|
||||||
list_reverse (struct list *list)
|
|
||||||
{
|
|
||||||
if (!list_empty (list))
|
|
||||||
{
|
|
||||||
struct list_elem *e;
|
|
||||||
|
|
||||||
for (e = list_begin (list); e != list_end (list); e = e->prev)
|
|
||||||
swap (&e->prev, &e->next);
|
|
||||||
swap (&list->head.next, &list->tail.prev);
|
|
||||||
swap (&list->head.next->prev, &list->tail.prev->next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true only if the list elements A through B (exclusive)
|
|
||||||
are in order according to LESS given auxiliary data AUX. */
|
|
||||||
static bool
|
|
||||||
is_sorted (struct list_elem *a, struct list_elem *b,
|
|
||||||
list_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
if (a != b)
|
|
||||||
while ((a = list_next (a)) != b)
|
|
||||||
if (less (a, list_prev (a), aux))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finds a run, starting at A and ending not after B, of list
|
|
||||||
elements that are in nondecreasing order according to LESS
|
|
||||||
given auxiliary data AUX. Returns the (exclusive) end of the
|
|
||||||
run.
|
|
||||||
A through B (exclusive) must form a non-empty range. */
|
|
||||||
static struct list_elem *
|
|
||||||
find_end_of_run (struct list_elem *a, struct list_elem *b,
|
|
||||||
list_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
ASSERT (a != NULL);
|
|
||||||
ASSERT (b != NULL);
|
|
||||||
ASSERT (less != NULL);
|
|
||||||
ASSERT (a != b);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
a = list_next (a);
|
|
||||||
}
|
|
||||||
while (a != b && !less (a, list_prev (a), aux));
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Merges A0 through A1B0 (exclusive) with A1B0 through B1
|
|
||||||
(exclusive) to form a combined range also ending at B1
|
|
||||||
(exclusive). Both input ranges must be nonempty and sorted in
|
|
||||||
nondecreasing order according to LESS given auxiliary data
|
|
||||||
AUX. The output range will be sorted the same way. */
|
|
||||||
static void
|
|
||||||
inplace_merge (struct list_elem *a0, struct list_elem *a1b0,
|
|
||||||
struct list_elem *b1,
|
|
||||||
list_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
ASSERT (a0 != NULL);
|
|
||||||
ASSERT (a1b0 != NULL);
|
|
||||||
ASSERT (b1 != NULL);
|
|
||||||
ASSERT (less != NULL);
|
|
||||||
ASSERT (is_sorted (a0, a1b0, less, aux));
|
|
||||||
ASSERT (is_sorted (a1b0, b1, less, aux));
|
|
||||||
|
|
||||||
while (a0 != a1b0 && a1b0 != b1)
|
|
||||||
if (!less (a1b0, a0, aux))
|
|
||||||
a0 = list_next (a0);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
a1b0 = list_next (a1b0);
|
|
||||||
list_splice (a0, list_prev (a1b0), a1b0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sorts LIST according to LESS given auxiliary data AUX, using a
|
|
||||||
natural iterative merge sort that runs in O(n lg n) time and
|
|
||||||
O(1) space in the number of elements in LIST. */
|
|
||||||
void
|
|
||||||
list_sort (struct list *list, list_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
size_t output_run_cnt; /* Number of runs output in current pass. */
|
|
||||||
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
ASSERT (less != NULL);
|
|
||||||
|
|
||||||
/* Pass over the list repeatedly, merging adjacent runs of
|
|
||||||
nondecreasing elements, until only one run is left. */
|
|
||||||
do
|
|
||||||
{
|
|
||||||
struct list_elem *a0; /* Start of first run. */
|
|
||||||
struct list_elem *a1b0; /* End of first run, start of second. */
|
|
||||||
struct list_elem *b1; /* End of second run. */
|
|
||||||
|
|
||||||
output_run_cnt = 0;
|
|
||||||
for (a0 = list_begin (list); a0 != list_end (list); a0 = b1)
|
|
||||||
{
|
|
||||||
/* Each iteration produces one output run. */
|
|
||||||
output_run_cnt++;
|
|
||||||
|
|
||||||
/* Locate two adjacent runs of nondecreasing elements
|
|
||||||
A0...A1B0 and A1B0...B1. */
|
|
||||||
a1b0 = find_end_of_run (a0, list_end (list), less, aux);
|
|
||||||
if (a1b0 == list_end (list))
|
|
||||||
break;
|
|
||||||
b1 = find_end_of_run (a1b0, list_end (list), less, aux);
|
|
||||||
|
|
||||||
/* Merge the runs. */
|
|
||||||
inplace_merge (a0, a1b0, b1, less, aux);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (output_run_cnt > 1);
|
|
||||||
|
|
||||||
ASSERT (is_sorted (list_begin (list), list_end (list), less, aux));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inserts ELEM in the proper position in LIST, which must be
|
|
||||||
sorted according to LESS given auxiliary data AUX.
|
|
||||||
Runs in O(n) average case in the number of elements in LIST. */
|
|
||||||
void
|
|
||||||
list_insert_ordered (struct list *list, struct list_elem *elem,
|
|
||||||
list_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
struct list_elem *e;
|
|
||||||
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
ASSERT (elem != NULL);
|
|
||||||
ASSERT (less != NULL);
|
|
||||||
|
|
||||||
for (e = list_begin (list); e != list_end (list); e = list_next (e))
|
|
||||||
if (less (elem, e, aux))
|
|
||||||
break;
|
|
||||||
return list_insert (e, elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iterates through LIST and removes all but the first in each
|
|
||||||
set of adjacent elements that are equal according to LESS
|
|
||||||
given auxiliary data AUX. If DUPLICATES is non-null, then the
|
|
||||||
elements from LIST are appended to DUPLICATES. */
|
|
||||||
void
|
|
||||||
list_unique (struct list *list, struct list *duplicates,
|
|
||||||
list_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
struct list_elem *elem, *next;
|
|
||||||
|
|
||||||
ASSERT (list != NULL);
|
|
||||||
ASSERT (less != NULL);
|
|
||||||
if (list_empty (list))
|
|
||||||
return;
|
|
||||||
|
|
||||||
elem = list_begin (list);
|
|
||||||
while ((next = list_next (elem)) != list_end (list))
|
|
||||||
if (!less (elem, next, aux) && !less (next, elem, aux))
|
|
||||||
{
|
|
||||||
list_remove (next);
|
|
||||||
if (duplicates != NULL)
|
|
||||||
list_push_back (duplicates, next);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
elem = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the element in LIST with the largest value according
|
|
||||||
to LESS given auxiliary data AUX. If there is more than one
|
|
||||||
maximum, returns the one that appears earlier in the list. If
|
|
||||||
the list is empty, returns its tail. */
|
|
||||||
struct list_elem *
|
|
||||||
list_max (struct list *list, list_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
struct list_elem *max = list_begin (list);
|
|
||||||
if (max != list_end (list))
|
|
||||||
{
|
|
||||||
struct list_elem *e;
|
|
||||||
|
|
||||||
for (e = list_next (max); e != list_end (list); e = list_next (e))
|
|
||||||
if (less (max, e, aux))
|
|
||||||
max = e;
|
|
||||||
}
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the element in LIST with the smallest value according
|
|
||||||
to LESS given auxiliary data AUX. If there is more than one
|
|
||||||
minimum, returns the one that appears earlier in the list. If
|
|
||||||
the list is empty, returns its tail. */
|
|
||||||
struct list_elem *
|
|
||||||
list_min (struct list *list, list_less_func *less, void *aux)
|
|
||||||
{
|
|
||||||
struct list_elem *min = list_begin (list);
|
|
||||||
if (min != list_end (list))
|
|
||||||
{
|
|
||||||
struct list_elem *e;
|
|
||||||
|
|
||||||
for (e = list_next (min); e != list_end (list); e = list_next (e))
|
|
||||||
if (less (e, min, aux))
|
|
||||||
min = e;
|
|
||||||
}
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
#ifndef __LIB_KERNEL_LIST_H
|
|
||||||
#define __LIB_KERNEL_LIST_H
|
|
||||||
|
|
||||||
/* Doubly linked list.
|
|
||||||
|
|
||||||
This implementation of a doubly linked list does not require
|
|
||||||
use of dynamically allocated memory. Instead, each structure
|
|
||||||
that is a potential list element must embed a struct list_elem
|
|
||||||
member. All of the list functions operate on these `struct
|
|
||||||
list_elem's. The list_entry macro allows conversion from a
|
|
||||||
struct list_elem back to a structure object that contains it.
|
|
||||||
|
|
||||||
For example, suppose there is a need for a list of `struct
|
|
||||||
foo'. `struct foo' should contain a `struct list_elem'
|
|
||||||
member, like so:
|
|
||||||
|
|
||||||
struct foo
|
|
||||||
{
|
|
||||||
struct list_elem elem;
|
|
||||||
int bar;
|
|
||||||
...other members...
|
|
||||||
};
|
|
||||||
|
|
||||||
Then a list of `struct foo' can be be declared and initialized
|
|
||||||
like so:
|
|
||||||
|
|
||||||
struct list foo_list;
|
|
||||||
|
|
||||||
list_init (&foo_list);
|
|
||||||
|
|
||||||
Iteration is a typical situation where it is necessary to
|
|
||||||
convert from a struct list_elem back to its enclosing
|
|
||||||
structure. Here's an example using foo_list:
|
|
||||||
|
|
||||||
struct list_elem *e;
|
|
||||||
|
|
||||||
for (e = list_begin (&foo_list); e != list_end (&foo_list);
|
|
||||||
e = list_next (e))
|
|
||||||
{
|
|
||||||
struct foo *f = list_entry (e, struct foo, elem);
|
|
||||||
...do something with f...
|
|
||||||
}
|
|
||||||
|
|
||||||
You can find real examples of list usage throughout the
|
|
||||||
source; for example, malloc.c, palloc.c, and thread.c in the
|
|
||||||
threads directory all use lists.
|
|
||||||
|
|
||||||
The interface for this list is inspired by the list<> template
|
|
||||||
in the C++ STL. If you're familiar with list<>, you should
|
|
||||||
find this easy to use. However, it should be emphasized that
|
|
||||||
these lists do *no* type checking and can't do much other
|
|
||||||
correctness checking. If you screw up, it will bite you.
|
|
||||||
|
|
||||||
Glossary of list terms:
|
|
||||||
|
|
||||||
- "front": The first element in a list. Undefined in an
|
|
||||||
empty list. Returned by list_front().
|
|
||||||
|
|
||||||
- "back": The last element in a list. Undefined in an empty
|
|
||||||
list. Returned by list_back().
|
|
||||||
|
|
||||||
- "tail": The element figuratively just after the last
|
|
||||||
element of a list. Well defined even in an empty list.
|
|
||||||
Returned by list_end(). Used as the end sentinel for an
|
|
||||||
iteration from front to back.
|
|
||||||
|
|
||||||
- "beginning": In a non-empty list, the front. In an empty
|
|
||||||
list, the tail. Returned by list_begin(). Used as the
|
|
||||||
starting point for an iteration from front to back.
|
|
||||||
|
|
||||||
- "head": The element figuratively just before the first
|
|
||||||
element of a list. Well defined even in an empty list.
|
|
||||||
Returned by list_rend(). Used as the end sentinel for an
|
|
||||||
iteration from back to front.
|
|
||||||
|
|
||||||
- "reverse beginning": In a non-empty list, the back. In an
|
|
||||||
empty list, the head. Returned by list_rbegin(). Used as
|
|
||||||
the starting point for an iteration from back to front.
|
|
||||||
|
|
||||||
- "interior element": An element that is not the head or
|
|
||||||
tail, that is, a real list element. An empty list does
|
|
||||||
not have any interior elements.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* List element. */
|
|
||||||
struct list_elem
|
|
||||||
{
|
|
||||||
struct list_elem *prev; /* Previous list element. */
|
|
||||||
struct list_elem *next; /* Next list element. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* List. */
|
|
||||||
struct list
|
|
||||||
{
|
|
||||||
struct list_elem head; /* List head. */
|
|
||||||
struct list_elem tail; /* List tail. */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Converts pointer to list element LIST_ELEM into a pointer to
|
|
||||||
the structure that LIST_ELEM is embedded inside. Supply the
|
|
||||||
name of the outer structure STRUCT and the member name MEMBER
|
|
||||||
of the list element. See the big comment at the top of the
|
|
||||||
file for an example. */
|
|
||||||
#define list_entry(LIST_ELEM, STRUCT, MEMBER) \
|
|
||||||
((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next \
|
|
||||||
- offsetof (STRUCT, MEMBER.next)))
|
|
||||||
|
|
||||||
/* List initialization.
|
|
||||||
|
|
||||||
A list may be initialized by calling list_init():
|
|
||||||
|
|
||||||
struct list my_list;
|
|
||||||
list_init (&my_list);
|
|
||||||
|
|
||||||
or with an initializer using LIST_INITIALIZER:
|
|
||||||
|
|
||||||
struct list my_list = LIST_INITIALIZER (my_list); */
|
|
||||||
#define LIST_INITIALIZER(NAME) { { NULL, &(NAME).tail }, \
|
|
||||||
{ &(NAME).head, NULL } }
|
|
||||||
|
|
||||||
void list_init (struct list *);
|
|
||||||
|
|
||||||
/* List traversal. */
|
|
||||||
struct list_elem *list_begin (struct list *);
|
|
||||||
struct list_elem *list_next (struct list_elem *);
|
|
||||||
struct list_elem *list_end (struct list *);
|
|
||||||
|
|
||||||
struct list_elem *list_rbegin (struct list *);
|
|
||||||
struct list_elem *list_prev (struct list_elem *);
|
|
||||||
struct list_elem *list_rend (struct list *);
|
|
||||||
|
|
||||||
struct list_elem *list_head (struct list *);
|
|
||||||
struct list_elem *list_tail (struct list *);
|
|
||||||
|
|
||||||
/* List insertion. */
|
|
||||||
void list_insert (struct list_elem *, struct list_elem *);
|
|
||||||
void list_splice (struct list_elem *before,
|
|
||||||
struct list_elem *first, struct list_elem *last);
|
|
||||||
void list_push_front (struct list *, struct list_elem *);
|
|
||||||
void list_push_back (struct list *, struct list_elem *);
|
|
||||||
|
|
||||||
/* List removal. */
|
|
||||||
struct list_elem *list_remove (struct list_elem *);
|
|
||||||
struct list_elem *list_pop_front (struct list *);
|
|
||||||
struct list_elem *list_pop_back (struct list *);
|
|
||||||
|
|
||||||
/* List elements. */
|
|
||||||
struct list_elem *list_front (struct list *);
|
|
||||||
struct list_elem *list_back (struct list *);
|
|
||||||
|
|
||||||
/* List properties. */
|
|
||||||
size_t list_size (struct list *);
|
|
||||||
bool list_empty (struct list *);
|
|
||||||
|
|
||||||
/* Miscellaneous. */
|
|
||||||
void list_reverse (struct list *);
|
|
||||||
|
|
||||||
/* Compares the value of two list elements A and B, given
|
|
||||||
auxiliary data AUX. Returns true if A is less than B, or
|
|
||||||
false if A is greater than or equal to B. */
|
|
||||||
typedef bool list_less_func (const struct list_elem *a,
|
|
||||||
const struct list_elem *b,
|
|
||||||
void *aux);
|
|
||||||
|
|
||||||
/* Operations on lists with ordered elements. */
|
|
||||||
void list_sort (struct list *,
|
|
||||||
list_less_func *, void *aux);
|
|
||||||
void list_insert_ordered (struct list *, struct list_elem *,
|
|
||||||
list_less_func *, void *aux);
|
|
||||||
void list_unique (struct list *, struct list *duplicates,
|
|
||||||
list_less_func *, void *aux);
|
|
||||||
|
|
||||||
/* Max and min. */
|
|
||||||
struct list_elem *list_max (struct list *, list_less_func *, void *aux);
|
|
||||||
struct list_elem *list_min (struct list *, list_less_func *, void *aux);
|
|
||||||
|
|
||||||
#endif /* lib/kernel/list.h */
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef __LIB_KERNEL_STDIO_H
|
|
||||||
#define __LIB_KERNEL_STDIO_H
|
|
||||||
|
|
||||||
void putbuf (const char *, size_t);
|
|
||||||
|
|
||||||
#endif /* lib/kernel/stdio.h */
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#ifndef __LIB_LIMITS_H
|
|
||||||
#define __LIB_LIMITS_H
|
|
||||||
|
|
||||||
#define CHAR_BIT 8
|
|
||||||
|
|
||||||
#define SCHAR_MAX 127
|
|
||||||
#define SCHAR_MIN (-SCHAR_MAX - 1)
|
|
||||||
#define UCHAR_MAX 255
|
|
||||||
|
|
||||||
#ifdef __CHAR_UNSIGNED__
|
|
||||||
#define CHAR_MIN 0
|
|
||||||
#define CHAR_MAX UCHAR_MAX
|
|
||||||
#else
|
|
||||||
#define CHAR_MIN SCHAR_MIN
|
|
||||||
#define CHAR_MAX SCHAR_MAX
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SHRT_MAX 32767
|
|
||||||
#define SHRT_MIN (-SHRT_MAX - 1)
|
|
||||||
#define USHRT_MAX 65535
|
|
||||||
|
|
||||||
#define INT_MAX 2147483647
|
|
||||||
#define INT_MIN (-INT_MAX - 1)
|
|
||||||
#define UINT_MAX 4294967295U
|
|
||||||
|
|
||||||
#define LONG_MAX 2147483647L
|
|
||||||
#define LONG_MIN (-LONG_MAX - 1)
|
|
||||||
#define ULONG_MAX 4294967295UL
|
|
||||||
|
|
||||||
#define LLONG_MAX 9223372036854775807LL
|
|
||||||
#define LLONG_MIN (-LLONG_MAX - 1)
|
|
||||||
#define ULLONG_MAX 18446744073709551615ULL
|
|
||||||
|
|
||||||
#endif /* lib/limits.h */
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef __LIB_PACKED_H
|
|
||||||
#define __LIB_PACKED_H
|
|
||||||
|
|
||||||
/* The "packed" attribute, when applied to a structure, prevents
|
|
||||||
GCC from inserting padding bytes between or after structure
|
|
||||||
members. It must be specified at the time of the structure's
|
|
||||||
definition, normally just after the closing brace. */
|
|
||||||
#define PACKED __attribute__ ((packed))
|
|
||||||
|
|
||||||
#endif /* lib/packed.h */
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
#include "random.h"
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "debug.h"
|
|
||||||
|
|
||||||
/* RC4-based pseudo-random number generator (PRNG).
|
|
||||||
|
|
||||||
RC4 is a stream cipher. We're not using it here for its
|
|
||||||
cryptographic properties, but because it is easy to implement
|
|
||||||
and its output is plenty random for non-cryptographic
|
|
||||||
purposes.
|
|
||||||
|
|
||||||
See http://en.wikipedia.org/wiki/RC4_(cipher) for information
|
|
||||||
on RC4.*/
|
|
||||||
|
|
||||||
/* RC4 state. */
|
|
||||||
static uint8_t s[256]; /* S[]. */
|
|
||||||
static uint8_t s_i, s_j; /* i, j. */
|
|
||||||
|
|
||||||
/* Already initialized? */
|
|
||||||
static bool inited;
|
|
||||||
|
|
||||||
/* Swaps the bytes pointed to by A and B. */
|
|
||||||
static inline void
|
|
||||||
swap_byte (uint8_t *a, uint8_t *b)
|
|
||||||
{
|
|
||||||
uint8_t t = *a;
|
|
||||||
*a = *b;
|
|
||||||
*b = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initializes or reinitializes the PRNG with the given SEED. */
|
|
||||||
void
|
|
||||||
random_init (unsigned seed)
|
|
||||||
{
|
|
||||||
uint8_t *seedp = (uint8_t *) &seed;
|
|
||||||
int i;
|
|
||||||
uint8_t j;
|
|
||||||
|
|
||||||
for (i = 0; i < 256; i++)
|
|
||||||
s[i] = i;
|
|
||||||
for (i = j = 0; i < 256; i++)
|
|
||||||
{
|
|
||||||
j += s[i] + seedp[i % sizeof seed];
|
|
||||||
swap_byte (s + i, s + j);
|
|
||||||
}
|
|
||||||
|
|
||||||
s_i = s_j = 0;
|
|
||||||
inited = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes SIZE random bytes into BUF. */
|
|
||||||
void
|
|
||||||
random_bytes (void *buf_, size_t size)
|
|
||||||
{
|
|
||||||
uint8_t *buf;
|
|
||||||
|
|
||||||
if (!inited)
|
|
||||||
random_init (0);
|
|
||||||
|
|
||||||
for (buf = buf_; size-- > 0; buf++)
|
|
||||||
{
|
|
||||||
uint8_t s_k;
|
|
||||||
|
|
||||||
s_i++;
|
|
||||||
s_j += s[s_i];
|
|
||||||
swap_byte (s + s_i, s + s_j);
|
|
||||||
|
|
||||||
s_k = s[s_i] + s[s_j];
|
|
||||||
*buf = s[s_k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a pseudo-random unsigned long.
|
|
||||||
Use random_ulong() % n to obtain a random number in the range
|
|
||||||
0...n (exclusive). */
|
|
||||||
unsigned long
|
|
||||||
random_ulong (void)
|
|
||||||
{
|
|
||||||
unsigned long ul;
|
|
||||||
random_bytes (&ul, sizeof ul);
|
|
||||||
return ul;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef __LIB_RANDOM_H
|
|
||||||
#define __LIB_RANDOM_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
void random_init (unsigned seed);
|
|
||||||
void random_bytes (void *, size_t);
|
|
||||||
unsigned long random_ulong (void);
|
|
||||||
|
|
||||||
#endif /* lib/random.h */
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#ifndef __LIB_ROUND_H
|
|
||||||
#define __LIB_ROUND_H
|
|
||||||
|
|
||||||
/* Yields X rounded up to the nearest multiple of STEP.
|
|
||||||
For X >= 0, STEP >= 1 only. */
|
|
||||||
#define ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP) * (STEP))
|
|
||||||
|
|
||||||
/* Yields X divided by STEP, rounded up.
|
|
||||||
For X >= 0, STEP >= 1 only. */
|
|
||||||
#define DIV_ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP))
|
|
||||||
|
|
||||||
/* Yields X rounded down to the nearest multiple of STEP.
|
|
||||||
For X >= 0, STEP >= 1 only. */
|
|
||||||
#define ROUND_DOWN(X, STEP) ((X) / (STEP) * (STEP))
|
|
||||||
|
|
||||||
/* There is no DIV_ROUND_DOWN. It would be simply X / STEP. */
|
|
||||||
|
|
||||||
#endif /* lib/round.h */
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#ifndef __LIB_STDARG_H
|
|
||||||
#define __LIB_STDARG_H
|
|
||||||
|
|
||||||
/* GCC has <stdarg.h> functionality as built-ins,
|
|
||||||
so all we need is to use it. */
|
|
||||||
|
|
||||||
typedef __builtin_va_list va_list;
|
|
||||||
|
|
||||||
#define va_start(LIST, ARG) __builtin_va_start (LIST, ARG)
|
|
||||||
#define va_end(LIST) __builtin_va_end (LIST)
|
|
||||||
#define va_arg(LIST, TYPE) __builtin_va_arg (LIST, TYPE)
|
|
||||||
#define va_copy(DST, SRC) __builtin_va_copy (DST, SRC)
|
|
||||||
|
|
||||||
#endif /* lib/stdarg.h */
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#ifndef __LIB_STDBOOL_H
|
|
||||||
#define __LIB_STDBOOL_H
|
|
||||||
|
|
||||||
#define bool _Bool
|
|
||||||
#define true 1
|
|
||||||
#define false 0
|
|
||||||
#define __bool_true_false_are_defined 1
|
|
||||||
|
|
||||||
#endif /* lib/stdbool.h */
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#ifndef __LIB_STDDEF_H
|
|
||||||
#define __LIB_STDDEF_H
|
|
||||||
|
|
||||||
#define NULL ((void *) 0)
|
|
||||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER)
|
|
||||||
|
|
||||||
/* GCC predefines the types we need for ptrdiff_t and size_t,
|
|
||||||
so that we don't have to guess. */
|
|
||||||
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
|
||||||
typedef __SIZE_TYPE__ size_t;
|
|
||||||
|
|
||||||
#endif /* lib/stddef.h */
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#ifndef __LIB_STDINT_H
|
|
||||||
#define __LIB_STDINT_H
|
|
||||||
|
|
||||||
typedef signed char int8_t;
|
|
||||||
#define INT8_MAX 127
|
|
||||||
#define INT8_MIN (-INT8_MAX - 1)
|
|
||||||
|
|
||||||
typedef signed short int int16_t;
|
|
||||||
#define INT16_MAX 32767
|
|
||||||
#define INT16_MIN (-INT16_MAX - 1)
|
|
||||||
|
|
||||||
typedef signed int int32_t;
|
|
||||||
#define INT32_MAX 2147483647
|
|
||||||
#define INT32_MIN (-INT32_MAX - 1)
|
|
||||||
|
|
||||||
typedef signed long long int int64_t;
|
|
||||||
#define INT64_MAX 9223372036854775807LL
|
|
||||||
#define INT64_MIN (-INT64_MAX - 1)
|
|
||||||
|
|
||||||
typedef unsigned char uint8_t;
|
|
||||||
#define UINT8_MAX 255
|
|
||||||
|
|
||||||
typedef unsigned short int uint16_t;
|
|
||||||
#define UINT16_MAX 65535
|
|
||||||
|
|
||||||
typedef unsigned int uint32_t;
|
|
||||||
#define UINT32_MAX 4294967295U
|
|
||||||
|
|
||||||
typedef unsigned long long int uint64_t;
|
|
||||||
#define UINT64_MAX 18446744073709551615ULL
|
|
||||||
|
|
||||||
typedef int32_t intptr_t;
|
|
||||||
#define INTPTR_MIN INT32_MIN
|
|
||||||
#define INTPTR_MAX INT32_MAX
|
|
||||||
|
|
||||||
typedef uint32_t uintptr_t;
|
|
||||||
#define UINTPTR_MAX UINT32_MAX
|
|
||||||
|
|
||||||
typedef int64_t intmax_t;
|
|
||||||
#define INTMAX_MIN INT64_MIN
|
|
||||||
#define INTMAX_MAX INT64_MAX
|
|
||||||
|
|
||||||
typedef uint64_t uintmax_t;
|
|
||||||
#define UINTMAX_MAX UINT64_MAX
|
|
||||||
|
|
||||||
#define PTRDIFF_MIN INT32_MIN
|
|
||||||
#define PTRDIFF_MAX INT32_MAX
|
|
||||||
|
|
||||||
#define SIZE_MAX UINT32_MAX
|
|
||||||
|
|
||||||
#endif /* lib/stdint.h */
|
|
||||||
@@ -1,655 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <round.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/* Auxiliary data for vsnprintf_helper(). */
|
|
||||||
struct vsnprintf_aux
|
|
||||||
{
|
|
||||||
char *p; /* Current output position. */
|
|
||||||
int length; /* Length of output string. */
|
|
||||||
int max_length; /* Max length of output string. */
|
|
||||||
};
|
|
||||||
|
|
||||||
static void vsnprintf_helper (char, void *);
|
|
||||||
|
|
||||||
/* Like vprintf(), except that output is stored into BUFFER,
|
|
||||||
which must have space for BUF_SIZE characters. Writes at most
|
|
||||||
BUF_SIZE - 1 characters to BUFFER, followed by a null
|
|
||||||
terminator. BUFFER will always be null-terminated unless
|
|
||||||
BUF_SIZE is zero. Returns the number of characters that would
|
|
||||||
have been written to BUFFER, not including a null terminator,
|
|
||||||
had there been enough room. */
|
|
||||||
int
|
|
||||||
vsnprintf (char *buffer, size_t buf_size, const char *format, va_list args)
|
|
||||||
{
|
|
||||||
/* Set up aux data for vsnprintf_helper(). */
|
|
||||||
struct vsnprintf_aux aux;
|
|
||||||
aux.p = buffer;
|
|
||||||
aux.length = 0;
|
|
||||||
aux.max_length = buf_size > 0 ? buf_size - 1 : 0;
|
|
||||||
|
|
||||||
/* Do most of the work. */
|
|
||||||
__vprintf (format, args, vsnprintf_helper, &aux);
|
|
||||||
|
|
||||||
/* Add null terminator. */
|
|
||||||
if (buf_size > 0)
|
|
||||||
*aux.p = '\0';
|
|
||||||
|
|
||||||
return aux.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper function for vsnprintf(). */
|
|
||||||
static void
|
|
||||||
vsnprintf_helper (char ch, void *aux_)
|
|
||||||
{
|
|
||||||
struct vsnprintf_aux *aux = aux_;
|
|
||||||
|
|
||||||
if (aux->length++ < aux->max_length)
|
|
||||||
*aux->p++ = ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Like printf(), except that output is stored into BUFFER,
|
|
||||||
which must have space for BUF_SIZE characters. Writes at most
|
|
||||||
BUF_SIZE - 1 characters to BUFFER, followed by a null
|
|
||||||
terminator. BUFFER will always be null-terminated unless
|
|
||||||
BUF_SIZE is zero. Returns the number of characters that would
|
|
||||||
have been written to BUFFER, not including a null terminator,
|
|
||||||
had there been enough room. */
|
|
||||||
int
|
|
||||||
snprintf (char *buffer, size_t buf_size, const char *format, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
va_start (args, format);
|
|
||||||
retval = vsnprintf (buffer, buf_size, format, args);
|
|
||||||
va_end (args);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes formatted output to the console.
|
|
||||||
In the kernel, the console is both the video display and first
|
|
||||||
serial port.
|
|
||||||
In userspace, the console is file descriptor 1. */
|
|
||||||
int
|
|
||||||
printf (const char *format, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
va_start (args, format);
|
|
||||||
retval = vprintf (format, args);
|
|
||||||
va_end (args);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* printf() formatting internals. */
|
|
||||||
|
|
||||||
/* A printf() conversion. */
|
|
||||||
struct printf_conversion
|
|
||||||
{
|
|
||||||
/* Flags. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
MINUS = 1 << 0, /* '-' */
|
|
||||||
PLUS = 1 << 1, /* '+' */
|
|
||||||
SPACE = 1 << 2, /* ' ' */
|
|
||||||
POUND = 1 << 3, /* '#' */
|
|
||||||
ZERO = 1 << 4, /* '0' */
|
|
||||||
GROUP = 1 << 5 /* '\'' */
|
|
||||||
}
|
|
||||||
flags;
|
|
||||||
|
|
||||||
/* Minimum field width. */
|
|
||||||
int width;
|
|
||||||
|
|
||||||
/* Numeric precision.
|
|
||||||
-1 indicates no precision was specified. */
|
|
||||||
int precision;
|
|
||||||
|
|
||||||
/* Type of argument to format. */
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
CHAR = 1, /* hh */
|
|
||||||
SHORT = 2, /* h */
|
|
||||||
INT = 3, /* (none) */
|
|
||||||
INTMAX = 4, /* j */
|
|
||||||
LONG = 5, /* l */
|
|
||||||
LONGLONG = 6, /* ll */
|
|
||||||
PTRDIFFT = 7, /* t */
|
|
||||||
SIZET = 8 /* z */
|
|
||||||
}
|
|
||||||
type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct integer_base
|
|
||||||
{
|
|
||||||
int base; /* Base. */
|
|
||||||
const char *digits; /* Collection of digits. */
|
|
||||||
int x; /* `x' character to use, for base 16 only. */
|
|
||||||
int group; /* Number of digits to group with ' flag. */
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct integer_base base_d = {10, "0123456789", 0, 3};
|
|
||||||
static const struct integer_base base_o = {8, "01234567", 0, 3};
|
|
||||||
static const struct integer_base base_x = {16, "0123456789abcdef", 'x', 4};
|
|
||||||
static const struct integer_base base_X = {16, "0123456789ABCDEF", 'X', 4};
|
|
||||||
|
|
||||||
static const char *parse_conversion (const char *format,
|
|
||||||
struct printf_conversion *,
|
|
||||||
va_list *);
|
|
||||||
static void format_integer (uintmax_t value, bool is_signed, bool negative,
|
|
||||||
const struct integer_base *,
|
|
||||||
const struct printf_conversion *,
|
|
||||||
void (*output) (char, void *), void *aux);
|
|
||||||
static void output_dup (char ch, size_t cnt,
|
|
||||||
void (*output) (char, void *), void *aux);
|
|
||||||
static void format_string (const char *string, int length,
|
|
||||||
struct printf_conversion *,
|
|
||||||
void (*output) (char, void *), void *aux);
|
|
||||||
|
|
||||||
void
|
|
||||||
__vprintf (const char *format, va_list args,
|
|
||||||
void (*output) (char, void *), void *aux)
|
|
||||||
{
|
|
||||||
for (; *format != '\0'; format++)
|
|
||||||
{
|
|
||||||
struct printf_conversion c;
|
|
||||||
|
|
||||||
/* Literally copy non-conversions to output. */
|
|
||||||
if (*format != '%')
|
|
||||||
{
|
|
||||||
output (*format, aux);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
format++;
|
|
||||||
|
|
||||||
/* %% => %. */
|
|
||||||
if (*format == '%')
|
|
||||||
{
|
|
||||||
output ('%', aux);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse conversion specifiers. */
|
|
||||||
format = parse_conversion (format, &c, &args);
|
|
||||||
|
|
||||||
/* Do conversion. */
|
|
||||||
switch (*format)
|
|
||||||
{
|
|
||||||
case 'd':
|
|
||||||
case 'i':
|
|
||||||
{
|
|
||||||
/* Signed integer conversions. */
|
|
||||||
intmax_t value;
|
|
||||||
|
|
||||||
switch (c.type)
|
|
||||||
{
|
|
||||||
case CHAR:
|
|
||||||
value = (signed char) va_arg (args, int);
|
|
||||||
break;
|
|
||||||
case SHORT:
|
|
||||||
value = (short) va_arg (args, int);
|
|
||||||
break;
|
|
||||||
case INT:
|
|
||||||
value = va_arg (args, int);
|
|
||||||
break;
|
|
||||||
case INTMAX:
|
|
||||||
value = va_arg (args, intmax_t);
|
|
||||||
break;
|
|
||||||
case LONG:
|
|
||||||
value = va_arg (args, long);
|
|
||||||
break;
|
|
||||||
case LONGLONG:
|
|
||||||
value = va_arg (args, long long);
|
|
||||||
break;
|
|
||||||
case PTRDIFFT:
|
|
||||||
value = va_arg (args, ptrdiff_t);
|
|
||||||
break;
|
|
||||||
case SIZET:
|
|
||||||
value = va_arg (args, size_t);
|
|
||||||
if (value > SIZE_MAX / 2)
|
|
||||||
value = value - SIZE_MAX - 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
NOT_REACHED ();
|
|
||||||
}
|
|
||||||
|
|
||||||
format_integer (value < 0 ? -value : value,
|
|
||||||
true, value < 0, &base_d, &c, output, aux);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'o':
|
|
||||||
case 'u':
|
|
||||||
case 'x':
|
|
||||||
case 'X':
|
|
||||||
{
|
|
||||||
/* Unsigned integer conversions. */
|
|
||||||
uintmax_t value;
|
|
||||||
const struct integer_base *b;
|
|
||||||
|
|
||||||
switch (c.type)
|
|
||||||
{
|
|
||||||
case CHAR:
|
|
||||||
value = (unsigned char) va_arg (args, unsigned);
|
|
||||||
break;
|
|
||||||
case SHORT:
|
|
||||||
value = (unsigned short) va_arg (args, unsigned);
|
|
||||||
break;
|
|
||||||
case INT:
|
|
||||||
value = va_arg (args, unsigned);
|
|
||||||
break;
|
|
||||||
case INTMAX:
|
|
||||||
value = va_arg (args, uintmax_t);
|
|
||||||
break;
|
|
||||||
case LONG:
|
|
||||||
value = va_arg (args, unsigned long);
|
|
||||||
break;
|
|
||||||
case LONGLONG:
|
|
||||||
value = va_arg (args, unsigned long long);
|
|
||||||
break;
|
|
||||||
case PTRDIFFT:
|
|
||||||
value = va_arg (args, ptrdiff_t);
|
|
||||||
#if UINTMAX_MAX != PTRDIFF_MAX
|
|
||||||
value &= ((uintmax_t) PTRDIFF_MAX << 1) | 1;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case SIZET:
|
|
||||||
value = va_arg (args, size_t);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
NOT_REACHED ();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (*format)
|
|
||||||
{
|
|
||||||
case 'o': b = &base_o; break;
|
|
||||||
case 'u': b = &base_d; break;
|
|
||||||
case 'x': b = &base_x; break;
|
|
||||||
case 'X': b = &base_X; break;
|
|
||||||
default: NOT_REACHED ();
|
|
||||||
}
|
|
||||||
|
|
||||||
format_integer (value, false, false, b, &c, output, aux);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'c':
|
|
||||||
{
|
|
||||||
/* Treat character as single-character string. */
|
|
||||||
char ch = va_arg (args, int);
|
|
||||||
format_string (&ch, 1, &c, output, aux);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 's':
|
|
||||||
{
|
|
||||||
/* String conversion. */
|
|
||||||
const char *s = va_arg (args, char *);
|
|
||||||
if (s == NULL)
|
|
||||||
s = "(null)";
|
|
||||||
|
|
||||||
/* Limit string length according to precision.
|
|
||||||
Note: if c.precision == -1 then strnlen() will get
|
|
||||||
SIZE_MAX for MAXLEN, which is just what we want. */
|
|
||||||
format_string (s, strnlen (s, c.precision), &c, output, aux);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'p':
|
|
||||||
{
|
|
||||||
/* Pointer conversion.
|
|
||||||
Format pointers as %#x. */
|
|
||||||
void *p = va_arg (args, void *);
|
|
||||||
|
|
||||||
c.flags = POUND;
|
|
||||||
format_integer ((uintptr_t) p, false, false,
|
|
||||||
&base_x, &c, output, aux);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'f':
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
case 'g':
|
|
||||||
case 'G':
|
|
||||||
case 'n':
|
|
||||||
/* We don't support floating-point arithmetic,
|
|
||||||
and %n can be part of a security hole. */
|
|
||||||
__printf ("<<no %%%c in kernel>>", output, aux, *format);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
__printf ("<<no %%%c conversion>>", output, aux, *format);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parses conversion option characters starting at FORMAT and
|
|
||||||
initializes C appropriately. Returns the character in FORMAT
|
|
||||||
that indicates the conversion (e.g. the `d' in `%d'). Uses
|
|
||||||
*ARGS for `*' field widths and precisions. */
|
|
||||||
static const char *
|
|
||||||
parse_conversion (const char *format, struct printf_conversion *c,
|
|
||||||
va_list *args)
|
|
||||||
{
|
|
||||||
/* Parse flag characters. */
|
|
||||||
c->flags = 0;
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
switch (*format++)
|
|
||||||
{
|
|
||||||
case '-':
|
|
||||||
c->flags |= MINUS;
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
c->flags |= PLUS;
|
|
||||||
break;
|
|
||||||
case ' ':
|
|
||||||
c->flags |= SPACE;
|
|
||||||
break;
|
|
||||||
case '#':
|
|
||||||
c->flags |= POUND;
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
c->flags |= ZERO;
|
|
||||||
break;
|
|
||||||
case '\'':
|
|
||||||
c->flags |= GROUP;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
format--;
|
|
||||||
goto not_a_flag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
not_a_flag:
|
|
||||||
if (c->flags & MINUS)
|
|
||||||
c->flags &= ~ZERO;
|
|
||||||
if (c->flags & PLUS)
|
|
||||||
c->flags &= ~SPACE;
|
|
||||||
|
|
||||||
/* Parse field width. */
|
|
||||||
c->width = 0;
|
|
||||||
if (*format == '*')
|
|
||||||
{
|
|
||||||
format++;
|
|
||||||
c->width = va_arg (*args, int);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (; isdigit (*format); format++)
|
|
||||||
c->width = c->width * 10 + *format - '0';
|
|
||||||
}
|
|
||||||
if (c->width < 0)
|
|
||||||
{
|
|
||||||
c->width = -c->width;
|
|
||||||
c->flags |= MINUS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse precision. */
|
|
||||||
c->precision = -1;
|
|
||||||
if (*format == '.')
|
|
||||||
{
|
|
||||||
format++;
|
|
||||||
if (*format == '*')
|
|
||||||
{
|
|
||||||
format++;
|
|
||||||
c->precision = va_arg (*args, int);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
c->precision = 0;
|
|
||||||
for (; isdigit (*format); format++)
|
|
||||||
c->precision = c->precision * 10 + *format - '0';
|
|
||||||
}
|
|
||||||
if (c->precision < 0)
|
|
||||||
c->precision = -1;
|
|
||||||
}
|
|
||||||
if (c->precision >= 0)
|
|
||||||
c->flags &= ~ZERO;
|
|
||||||
|
|
||||||
/* Parse type. */
|
|
||||||
c->type = INT;
|
|
||||||
switch (*format++)
|
|
||||||
{
|
|
||||||
case 'h':
|
|
||||||
if (*format == 'h')
|
|
||||||
{
|
|
||||||
format++;
|
|
||||||
c->type = CHAR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
c->type = SHORT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'j':
|
|
||||||
c->type = INTMAX;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'l':
|
|
||||||
if (*format == 'l')
|
|
||||||
{
|
|
||||||
format++;
|
|
||||||
c->type = LONGLONG;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
c->type = LONG;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 't':
|
|
||||||
c->type = PTRDIFFT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'z':
|
|
||||||
c->type = SIZET;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
format--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Performs an integer conversion, writing output to OUTPUT with
|
|
||||||
auxiliary data AUX. The integer converted has absolute value
|
|
||||||
VALUE. If IS_SIGNED is true, does a signed conversion with
|
|
||||||
NEGATIVE indicating a negative value; otherwise does an
|
|
||||||
unsigned conversion and ignores NEGATIVE. The output is done
|
|
||||||
according to the provided base B. Details of the conversion
|
|
||||||
are in C. */
|
|
||||||
static void
|
|
||||||
format_integer (uintmax_t value, bool is_signed, bool negative,
|
|
||||||
const struct integer_base *b,
|
|
||||||
const struct printf_conversion *c,
|
|
||||||
void (*output) (char, void *), void *aux)
|
|
||||||
{
|
|
||||||
char buf[64], *cp; /* Buffer and current position. */
|
|
||||||
int x; /* `x' character to use or 0 if none. */
|
|
||||||
int sign; /* Sign character or 0 if none. */
|
|
||||||
int precision; /* Rendered precision. */
|
|
||||||
int pad_cnt; /* # of pad characters to fill field width. */
|
|
||||||
int digit_cnt; /* # of digits output so far. */
|
|
||||||
|
|
||||||
/* Determine sign character, if any.
|
|
||||||
An unsigned conversion will never have a sign character,
|
|
||||||
even if one of the flags requests one. */
|
|
||||||
sign = 0;
|
|
||||||
if (is_signed)
|
|
||||||
{
|
|
||||||
if (c->flags & PLUS)
|
|
||||||
sign = negative ? '-' : '+';
|
|
||||||
else if (c->flags & SPACE)
|
|
||||||
sign = negative ? '-' : ' ';
|
|
||||||
else if (negative)
|
|
||||||
sign = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine whether to include `0x' or `0X'.
|
|
||||||
It will only be included with a hexadecimal conversion of a
|
|
||||||
nonzero value with the # flag. */
|
|
||||||
x = (c->flags & POUND) && value ? b->x : 0;
|
|
||||||
|
|
||||||
/* Accumulate digits into buffer.
|
|
||||||
This algorithm produces digits in reverse order, so later we
|
|
||||||
will output the buffer's content in reverse. */
|
|
||||||
cp = buf;
|
|
||||||
digit_cnt = 0;
|
|
||||||
while (value > 0)
|
|
||||||
{
|
|
||||||
if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0)
|
|
||||||
*cp++ = ',';
|
|
||||||
*cp++ = b->digits[value % b->base];
|
|
||||||
value /= b->base;
|
|
||||||
digit_cnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Append enough zeros to match precision.
|
|
||||||
If requested precision is 0, then a value of zero is
|
|
||||||
rendered as a null string, otherwise as "0".
|
|
||||||
If the # flag is used with base 8, the result must always
|
|
||||||
begin with a zero. */
|
|
||||||
precision = c->precision < 0 ? 1 : c->precision;
|
|
||||||
while (cp - buf < precision && cp < buf + sizeof buf - 1)
|
|
||||||
*cp++ = '0';
|
|
||||||
if ((c->flags & POUND) && b->base == 8 && (cp == buf || cp[-1] != '0'))
|
|
||||||
*cp++ = '0';
|
|
||||||
|
|
||||||
/* Calculate number of pad characters to fill field width. */
|
|
||||||
pad_cnt = c->width - (cp - buf) - (x ? 2 : 0) - (sign != 0);
|
|
||||||
if (pad_cnt < 0)
|
|
||||||
pad_cnt = 0;
|
|
||||||
|
|
||||||
/* Do output. */
|
|
||||||
if ((c->flags & (MINUS | ZERO)) == 0)
|
|
||||||
output_dup (' ', pad_cnt, output, aux);
|
|
||||||
if (sign)
|
|
||||||
output (sign, aux);
|
|
||||||
if (x)
|
|
||||||
{
|
|
||||||
output ('0', aux);
|
|
||||||
output (x, aux);
|
|
||||||
}
|
|
||||||
if (c->flags & ZERO)
|
|
||||||
output_dup ('0', pad_cnt, output, aux);
|
|
||||||
while (cp > buf)
|
|
||||||
output (*--cp, aux);
|
|
||||||
if (c->flags & MINUS)
|
|
||||||
output_dup (' ', pad_cnt, output, aux);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */
|
|
||||||
static void
|
|
||||||
output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux)
|
|
||||||
{
|
|
||||||
while (cnt-- > 0)
|
|
||||||
output (ch, aux);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Formats the LENGTH characters starting at STRING according to
|
|
||||||
the conversion specified in C. Writes output to OUTPUT with
|
|
||||||
auxiliary data AUX. */
|
|
||||||
static void
|
|
||||||
format_string (const char *string, int length,
|
|
||||||
struct printf_conversion *c,
|
|
||||||
void (*output) (char, void *), void *aux)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
if (c->width > length && (c->flags & MINUS) == 0)
|
|
||||||
output_dup (' ', c->width - length, output, aux);
|
|
||||||
for (i = 0; i < length; i++)
|
|
||||||
output (string[i], aux);
|
|
||||||
if (c->width > length && (c->flags & MINUS) != 0)
|
|
||||||
output_dup (' ', c->width - length, output, aux);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wrapper for __vprintf() that converts varargs into a
|
|
||||||
va_list. */
|
|
||||||
void
|
|
||||||
__printf (const char *format,
|
|
||||||
void (*output) (char, void *), void *aux, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start (args, aux);
|
|
||||||
__vprintf (format, args, output, aux);
|
|
||||||
va_end (args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dumps the SIZE bytes in BUF to the console as hex bytes
|
|
||||||
arranged 16 per line. Numeric offsets are also included,
|
|
||||||
starting at OFS for the first byte in BUF. If ASCII is true
|
|
||||||
then the corresponding ASCII characters are also rendered
|
|
||||||
alongside. */
|
|
||||||
void
|
|
||||||
hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii)
|
|
||||||
{
|
|
||||||
const uint8_t *buf = buf_;
|
|
||||||
const size_t per_line = 16; /* Maximum bytes per line. */
|
|
||||||
|
|
||||||
while (size > 0)
|
|
||||||
{
|
|
||||||
size_t start, end, n;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
/* Number of bytes on this line. */
|
|
||||||
start = ofs % per_line;
|
|
||||||
end = per_line;
|
|
||||||
if (end - start > size)
|
|
||||||
end = start + size;
|
|
||||||
n = end - start;
|
|
||||||
|
|
||||||
/* Print line. */
|
|
||||||
printf ("%08jx ", (uintmax_t) ROUND_DOWN (ofs, per_line));
|
|
||||||
for (i = 0; i < start; i++)
|
|
||||||
printf (" ");
|
|
||||||
for (; i < end; i++)
|
|
||||||
printf ("%02hhx%c",
|
|
||||||
buf[i - start], i == per_line / 2 - 1? '-' : ' ');
|
|
||||||
if (ascii)
|
|
||||||
{
|
|
||||||
for (; i < per_line; i++)
|
|
||||||
printf (" ");
|
|
||||||
printf ("|");
|
|
||||||
for (i = 0; i < start; i++)
|
|
||||||
printf (" ");
|
|
||||||
for (; i < end; i++)
|
|
||||||
printf ("%c",
|
|
||||||
isprint (buf[i - start]) ? buf[i - start] : '.');
|
|
||||||
for (; i < per_line; i++)
|
|
||||||
printf (" ");
|
|
||||||
printf ("|");
|
|
||||||
}
|
|
||||||
printf ("\n");
|
|
||||||
|
|
||||||
ofs += n;
|
|
||||||
buf += n;
|
|
||||||
size -= n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prints SIZE, which represents a number of bytes, in a
|
|
||||||
human-readable format, e.g. "256 kB". */
|
|
||||||
void
|
|
||||||
print_human_readable_size (uint64_t size)
|
|
||||||
{
|
|
||||||
if (size == 1)
|
|
||||||
printf ("1 byte");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static const char *factors[] = {"bytes", "kB", "MB", "GB", "TB", NULL};
|
|
||||||
const char **fp;
|
|
||||||
|
|
||||||
for (fp = factors; size >= 1024 && fp[1] != NULL; fp++)
|
|
||||||
size /= 1024;
|
|
||||||
printf ("%"PRIu64" %s", size, *fp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#ifndef __LIB_STDIO_H
|
|
||||||
#define __LIB_STDIO_H
|
|
||||||
|
|
||||||
#include <debug.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* Include lib/user/stdio.h or lib/kernel/stdio.h, as
|
|
||||||
appropriate. */
|
|
||||||
#include_next <stdio.h>
|
|
||||||
|
|
||||||
/* Predefined file handles. */
|
|
||||||
#define STDIN_FILENO 0
|
|
||||||
#define STDOUT_FILENO 1
|
|
||||||
|
|
||||||
/* Standard functions. */
|
|
||||||
int printf (const char *, ...) PRINTF_FORMAT (1, 2);
|
|
||||||
int snprintf (char *, size_t, const char *, ...) PRINTF_FORMAT (3, 4);
|
|
||||||
int vprintf (const char *, va_list) PRINTF_FORMAT (1, 0);
|
|
||||||
int vsnprintf (char *, size_t, const char *, va_list) PRINTF_FORMAT (3, 0);
|
|
||||||
int putchar (int);
|
|
||||||
int puts (const char *);
|
|
||||||
|
|
||||||
/* Nonstandard functions. */
|
|
||||||
void hex_dump (uintptr_t ofs, const void *, size_t size, bool ascii);
|
|
||||||
void print_human_readable_size (uint64_t sz);
|
|
||||||
|
|
||||||
/* Internal functions. */
|
|
||||||
void __vprintf (const char *format, va_list args,
|
|
||||||
void (*output) (char, void *), void *aux);
|
|
||||||
void __printf (const char *format,
|
|
||||||
void (*output) (char, void *), void *aux, ...);
|
|
||||||
|
|
||||||
/* Try to be helpful. */
|
|
||||||
#define sprintf dont_use_sprintf_use_snprintf
|
|
||||||
#define vsprintf dont_use_vsprintf_use_vsnprintf
|
|
||||||
|
|
||||||
#endif /* lib/stdio.h */
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user