Implement page fault for lazy loading executables, w/ G
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "filesys/inode.h"
|
||||
#include <list.h>
|
||||
#include <debug.h>
|
||||
#include <stdio.h>
|
||||
#include <round.h>
|
||||
#include <string.h>
|
||||
#include "filesys/filesys.h"
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include "userprog/gdt.h"
|
||||
#include "userprog/pagedir.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/thread.h"
|
||||
#include "threads/vaddr.h"
|
||||
#include "vm/page.h"
|
||||
|
||||
/* Number of page faults processed. */
|
||||
static long long page_fault_cnt;
|
||||
|
||||
static void kill (struct intr_frame *);
|
||||
static void page_fault (struct intr_frame *);
|
||||
static bool try_fetch_page (void *upage, bool not_present, bool write,
|
||||
bool user);
|
||||
|
||||
/* Registers handlers for interrupts that can be caused by user
|
||||
programs.
|
||||
@@ -145,6 +150,10 @@ page_fault (struct intr_frame *f)
|
||||
write = (f->error_code & PF_W) != 0;
|
||||
user = (f->error_code & PF_U) != 0;
|
||||
|
||||
void *upage = pg_round_down (fault_addr);
|
||||
if (try_fetch_page (upage, not_present, write, user))
|
||||
return;
|
||||
|
||||
/* To implement virtual memory, delete the rest of the function
|
||||
body, and replace it with code that brings in the page to
|
||||
which fault_addr refers. */
|
||||
@@ -156,3 +165,27 @@ page_fault (struct intr_frame *f)
|
||||
kill (f);
|
||||
}
|
||||
|
||||
static bool
|
||||
try_fetch_page (void *upage, bool not_present, bool write, bool user)
|
||||
{
|
||||
if (!not_present || !is_user_vaddr (upage) || upage == NULL)
|
||||
return false;
|
||||
|
||||
struct page_entry *page = page_get (upage);
|
||||
if (page == NULL)
|
||||
return false;
|
||||
|
||||
if (write && !page->writable)
|
||||
{
|
||||
pagedir_set_writable(thread_current()->pagedir, upage, write);
|
||||
page->writable = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (page->type) {
|
||||
case PAGE_EXECUTABLE:
|
||||
return page_load (page, write);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ process_execute (const char *cmd)
|
||||
|
||||
static void *get_usr_kpage (enum palloc_flags flags, void *upage);
|
||||
static void free_usr_kpage (void *kpage);
|
||||
static bool install_page (void *upage, void *kpage, bool writable);
|
||||
bool install_page (void *upage, void *kpage, bool writable);
|
||||
|
||||
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);
|
||||
@@ -707,11 +707,12 @@ load_segment (struct file *file, off_t ofs, uint8_t *upage,
|
||||
size_t page_zero_bytes = PGSIZE - page_read_bytes;
|
||||
|
||||
/* Add the page to the SPT */
|
||||
if (page_insert (file, ofs, upage, read_bytes, zero_bytes, writable,
|
||||
PAGE_EXECUTABLE) == NULL)
|
||||
if (page_insert (file, ofs, upage, page_read_bytes, page_zero_bytes,
|
||||
writable, PAGE_EXECUTABLE) == NULL)
|
||||
return false;
|
||||
|
||||
/* Advance. */
|
||||
ofs += page_read_bytes;
|
||||
read_bytes -= page_read_bytes;
|
||||
zero_bytes -= page_zero_bytes;
|
||||
upage += PGSIZE;
|
||||
@@ -782,7 +783,7 @@ free_usr_kpage (void *kpage)
|
||||
with palloc_get_page().
|
||||
Returns true on success, false if UPAGE is already mapped or
|
||||
if memory allocation fails. */
|
||||
static bool
|
||||
bool
|
||||
install_page (void *upage, void *kpage, bool writable)
|
||||
{
|
||||
struct thread *t = thread_current ();
|
||||
|
||||
@@ -8,4 +8,6 @@ int process_wait (tid_t);
|
||||
void process_exit (void);
|
||||
void process_activate (void);
|
||||
|
||||
bool install_page (void *upage, void *kpage, bool writable);
|
||||
|
||||
#endif /* userprog/process.h */
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
#include "page.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "filesys/file.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/palloc.h"
|
||||
#include "userprog/process.h"
|
||||
#include "vm/frame.h"
|
||||
|
||||
/* Hashing function needed for the SPT table. Returns a hash for an entry,
|
||||
based on its upage. */
|
||||
@@ -45,7 +51,7 @@ page_insert (struct file *file, off_t ofs, void *upage, uint32_t read_bytes,
|
||||
|
||||
/* Gets a page_entry from the starting address of the page. Returns NULL if no
|
||||
such page_entry exists in the hash map.*/
|
||||
static struct page_entry *
|
||||
struct page_entry *
|
||||
page_get (void *upage)
|
||||
{
|
||||
struct page_entry fake_page_entry;
|
||||
@@ -60,6 +66,36 @@ page_get (void *upage)
|
||||
return hash_entry (e, struct page_entry, elem);
|
||||
}
|
||||
|
||||
bool
|
||||
page_load (struct page_entry *page, bool writable)
|
||||
{
|
||||
void *frame = frame_alloc (PAL_USER, page->upage, thread_current());
|
||||
if (frame == NULL)
|
||||
return false; // TODO: Try to evict a page instead.
|
||||
|
||||
if (!install_page (page->upage, frame, writable))
|
||||
{
|
||||
frame_free (frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf ("Hi! 3\n");
|
||||
|
||||
file_seek (page->file, page->offset);
|
||||
int read = -1;
|
||||
if ((read = file_read (page->file, frame, page->read_bytes)) != (int) page->read_bytes)
|
||||
{
|
||||
printf ("%p, %p, %d %d\n", page->file, frame, read, page->offset);
|
||||
frame_free (frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf ("Hi! 4\n");
|
||||
|
||||
memset (frame + page->read_bytes, 0, page->zero_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Function to clean up a page_entry. Given the elem of that page_entry, frees
|
||||
the page_entry itself. */
|
||||
void
|
||||
|
||||
@@ -29,6 +29,8 @@ bool page_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
||||
struct page_entry *page_insert (struct file *file, off_t ofs, void *upage,
|
||||
uint32_t read_bytes, uint32_t zero_bytes,
|
||||
bool writable, enum page_type type);
|
||||
struct page_entry *page_get (void *upage);
|
||||
bool page_load (struct page_entry *page, bool writable);
|
||||
void page_cleanup (struct hash_elem *e, void *aux);
|
||||
void page_set_swap (struct thread *, void *, size_t);
|
||||
size_t page_get_swap (struct thread *, void *);
|
||||
|
||||
4
tests/devices/src/.gitignore
vendored
Normal file
4
tests/devices/src/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
cscope.files
|
||||
cscope.out
|
||||
TAGS
|
||||
tags
|
||||
95
tests/devices/src/LICENSE
Normal file
95
tests/devices/src/LICENSE
Normal file
@@ -0,0 +1,95 @@
|
||||
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.
|
||||
*/
|
||||
68
tests/devices/src/Make.config
Normal file
68
tests/devices/src/Make.config
Normal file
@@ -0,0 +1,68 @@
|
||||
# -*- 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)
|
||||
29
tests/devices/src/Makefile
Normal file
29
tests/devices/src/Makefile
Normal file
@@ -0,0 +1,29 @@
|
||||
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
|
||||
111
tests/devices/src/Makefile.build
Normal file
111
tests/devices/src/Makefile.build
Normal file
@@ -0,0 +1,111 @@
|
||||
# -*- 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)
|
||||
20
tests/devices/src/Makefile.kernel
Normal file
20
tests/devices/src/Makefile.kernel
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- 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
|
||||
52
tests/devices/src/Makefile.userprog
Normal file
52
tests/devices/src/Makefile.userprog
Normal file
@@ -0,0 +1,52 @@
|
||||
# -*- 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
Normal file
3
tests/devices/src/devices/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
build
|
||||
bochsrc.txt
|
||||
bochsout.txt
|
||||
7
tests/devices/src/devices/Make.vars
Normal file
7
tests/devices/src/devices/Make.vars
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- 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
tests/devices/src/devices/Makefile
Normal file
1
tests/devices/src/devices/Makefile
Normal file
@@ -0,0 +1 @@
|
||||
include ../Makefile.kernel
|
||||
223
tests/devices/src/devices/block.c
Normal file
223
tests/devices/src/devices/block.c
Normal file
@@ -0,0 +1,223 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
74
tests/devices/src/devices/block.h
Normal file
74
tests/devices/src/devices/block.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#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 */
|
||||
527
tests/devices/src/devices/ide.c
Normal file
527
tests/devices/src/devices/ide.c
Normal file
@@ -0,0 +1,527 @@
|
||||
#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 ();
|
||||
}
|
||||
|
||||
|
||||
6
tests/devices/src/devices/ide.h
Normal file
6
tests/devices/src/devices/ide.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef DEVICES_IDE_H
|
||||
#define DEVICES_IDE_H
|
||||
|
||||
void ide_init (void);
|
||||
|
||||
#endif /* devices/ide.h */
|
||||
52
tests/devices/src/devices/input.c
Normal file
52
tests/devices/src/devices/input.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#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);
|
||||
}
|
||||
12
tests/devices/src/devices/input.h
Normal file
12
tests/devices/src/devices/input.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#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 */
|
||||
114
tests/devices/src/devices/intq.c
Normal file
114
tests/devices/src/devices/intq.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
43
tests/devices/src/devices/intq.h
Normal file
43
tests/devices/src/devices/intq.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#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 */
|
||||
213
tests/devices/src/devices/kbd.c
Normal file
213
tests/devices/src/devices/kbd.c
Normal file
@@ -0,0 +1,213 @@
|
||||
#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;
|
||||
}
|
||||
9
tests/devices/src/devices/kbd.h
Normal file
9
tests/devices/src/devices/kbd.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef DEVICES_KBD_H
|
||||
#define DEVICES_KBD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void kbd_init (void);
|
||||
void kbd_print_stats (void);
|
||||
|
||||
#endif /* devices/kbd.h */
|
||||
324
tests/devices/src/devices/partition.c
Normal file
324
tests/devices/src/devices/partition.c
Normal file
@@ -0,0 +1,324 @@
|
||||
#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
|
||||
};
|
||||
8
tests/devices/src/devices/partition.h
Normal file
8
tests/devices/src/devices/partition.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef DEVICES_PARTITION_H
|
||||
#define DEVICES_PARTITION_H
|
||||
|
||||
struct block;
|
||||
|
||||
void partition_scan (struct block *);
|
||||
|
||||
#endif /* devices/partition.h */
|
||||
83
tests/devices/src/devices/pit.c
Normal file
83
tests/devices/src/devices/pit.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#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);
|
||||
}
|
||||
8
tests/devices/src/devices/pit.h
Normal file
8
tests/devices/src/devices/pit.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#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 */
|
||||
112
tests/devices/src/devices/rtc.c
Normal file
112
tests/devices/src/devices/rtc.c
Normal file
@@ -0,0 +1,112 @@
|
||||
#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);
|
||||
}
|
||||
8
tests/devices/src/devices/rtc.h
Normal file
8
tests/devices/src/devices/rtc.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef RTC_H
|
||||
#define RTC_H
|
||||
|
||||
typedef unsigned long time_t;
|
||||
|
||||
time_t rtc_get_time (void);
|
||||
|
||||
#endif
|
||||
228
tests/devices/src/devices/serial.c
Normal file
228
tests/devices/src/devices/serial.c
Normal file
@@ -0,0 +1,228 @@
|
||||
#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 ();
|
||||
}
|
||||
11
tests/devices/src/devices/serial.h
Normal file
11
tests/devices/src/devices/serial.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#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 */
|
||||
133
tests/devices/src/devices/shutdown.c
Normal file
133
tests/devices/src/devices/shutdown.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#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
|
||||
}
|
||||
19
tests/devices/src/devices/shutdown.h
Normal file
19
tests/devices/src/devices/shutdown.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#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 */
|
||||
68
tests/devices/src/devices/speaker.c
Normal file
68
tests/devices/src/devices/speaker.c
Normal file
@@ -0,0 +1,68 @@
|
||||
#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 ();
|
||||
}
|
||||
}
|
||||
8
tests/devices/src/devices/speaker.h
Normal file
8
tests/devices/src/devices/speaker.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#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 */
|
||||
82
tests/devices/src/devices/swap.c
Normal file
82
tests/devices/src/devices/swap.c
Normal file
@@ -0,0 +1,82 @@
|
||||
#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);
|
||||
}
|
||||
11
tests/devices/src/devices/swap.h
Normal file
11
tests/devices/src/devices/swap.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#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 */
|
||||
293
tests/devices/src/devices/timer.c
Normal file
293
tests/devices/src/devices/timer.c
Normal file
@@ -0,0 +1,293 @@
|
||||
#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;
|
||||
}
|
||||
29
tests/devices/src/devices/timer.h
Normal file
29
tests/devices/src/devices/timer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#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 */
|
||||
172
tests/devices/src/devices/vga.c
Normal file
172
tests/devices/src/devices/vga.c
Normal file
@@ -0,0 +1,172 @@
|
||||
#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;
|
||||
}
|
||||
6
tests/devices/src/devices/vga.h
Normal file
6
tests/devices/src/devices/vga.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef DEVICES_VGA_H
|
||||
#define DEVICES_VGA_H
|
||||
|
||||
void vga_putc (int);
|
||||
|
||||
#endif /* devices/vga.h */
|
||||
189
tests/devices/src/lib/arithmetic.c
Normal file
189
tests/devices/src/lib/arithmetic.c
Normal file
@@ -0,0 +1,189 @@
|
||||
#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);
|
||||
}
|
||||
28
tests/devices/src/lib/ctype.h
Normal file
28
tests/devices/src/lib/ctype.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#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 */
|
||||
32
tests/devices/src/lib/debug.c
Normal file
32
tests/devices/src/lib/debug.c
Normal file
@@ -0,0 +1,32 @@
|
||||
#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");
|
||||
}
|
||||
}
|
||||
39
tests/devices/src/lib/debug.h
Normal file
39
tests/devices/src/lib/debug.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#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 */
|
||||
48
tests/devices/src/lib/inttypes.h
Normal file
48
tests/devices/src/lib/inttypes.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#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 */
|
||||
372
tests/devices/src/lib/kernel/bitmap.c
Normal file
372
tests/devices/src/lib/kernel/bitmap.c
Normal file
@@ -0,0 +1,372 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
51
tests/devices/src/lib/kernel/bitmap.h
Normal file
51
tests/devices/src/lib/kernel/bitmap.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#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 */
|
||||
191
tests/devices/src/lib/kernel/console.c
Normal file
191
tests/devices/src/lib/kernel/console.c
Normal file
@@ -0,0 +1,191 @@
|
||||
#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);
|
||||
}
|
||||
8
tests/devices/src/lib/kernel/console.h
Normal file
8
tests/devices/src/lib/kernel/console.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#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 */
|
||||
123
tests/devices/src/lib/kernel/debug.c
Normal file
123
tests/devices/src/lib/kernel/debug.c
Normal file
@@ -0,0 +1,123 @@
|
||||
#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);
|
||||
}
|
||||
437
tests/devices/src/lib/kernel/hash.c
Normal file
437
tests/devices/src/lib/kernel/hash.c
Normal file
@@ -0,0 +1,437 @@
|
||||
/* 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);
|
||||
}
|
||||
|
||||
104
tests/devices/src/lib/kernel/hash.h
Normal file
104
tests/devices/src/lib/kernel/hash.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#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 */
|
||||
527
tests/devices/src/lib/kernel/list.c
Normal file
527
tests/devices/src/lib/kernel/list.c
Normal file
@@ -0,0 +1,527 @@
|
||||
#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;
|
||||
}
|
||||
181
tests/devices/src/lib/kernel/list.h
Normal file
181
tests/devices/src/lib/kernel/list.h
Normal file
@@ -0,0 +1,181 @@
|
||||
#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 */
|
||||
6
tests/devices/src/lib/kernel/stdio.h
Normal file
6
tests/devices/src/lib/kernel/stdio.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef __LIB_KERNEL_STDIO_H
|
||||
#define __LIB_KERNEL_STDIO_H
|
||||
|
||||
void putbuf (const char *, size_t);
|
||||
|
||||
#endif /* lib/kernel/stdio.h */
|
||||
34
tests/devices/src/lib/limits.h
Normal file
34
tests/devices/src/lib/limits.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#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 */
|
||||
10
tests/devices/src/lib/packed.h
Normal file
10
tests/devices/src/lib/packed.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#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 */
|
||||
83
tests/devices/src/lib/random.c
Normal file
83
tests/devices/src/lib/random.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#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;
|
||||
}
|
||||
10
tests/devices/src/lib/random.h
Normal file
10
tests/devices/src/lib/random.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#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 */
|
||||
18
tests/devices/src/lib/round.h
Normal file
18
tests/devices/src/lib/round.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#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 */
|
||||
14
tests/devices/src/lib/stdarg.h
Normal file
14
tests/devices/src/lib/stdarg.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#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 */
|
||||
9
tests/devices/src/lib/stdbool.h
Normal file
9
tests/devices/src/lib/stdbool.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#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 */
|
||||
12
tests/devices/src/lib/stddef.h
Normal file
12
tests/devices/src/lib/stddef.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#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 */
|
||||
51
tests/devices/src/lib/stdint.h
Normal file
51
tests/devices/src/lib/stdint.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#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 */
|
||||
655
tests/devices/src/lib/stdio.c
Normal file
655
tests/devices/src/lib/stdio.c
Normal file
@@ -0,0 +1,655 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
40
tests/devices/src/lib/stdio.h
Normal file
40
tests/devices/src/lib/stdio.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#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 */
|
||||
208
tests/devices/src/lib/stdlib.c
Normal file
208
tests/devices/src/lib/stdlib.c
Normal file
@@ -0,0 +1,208 @@
|
||||
#include <ctype.h>
|
||||
#include <debug.h>
|
||||
#include <random.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Converts a string representation of a signed decimal integer
|
||||
in S into an `int', which is returned. */
|
||||
int
|
||||
atoi (const char *s)
|
||||
{
|
||||
bool negative;
|
||||
int value;
|
||||
|
||||
ASSERT (s != NULL);
|
||||
|
||||
/* Skip white space. */
|
||||
while (isspace ((unsigned char) *s))
|
||||
s++;
|
||||
|
||||
/* Parse sign. */
|
||||
negative = false;
|
||||
if (*s == '+')
|
||||
s++;
|
||||
else if (*s == '-')
|
||||
{
|
||||
negative = true;
|
||||
s++;
|
||||
}
|
||||
|
||||
/* Parse digits. We always initially parse the value as
|
||||
negative, and then make it positive later, because the
|
||||
negative range of an int is bigger than the positive range
|
||||
on a 2's complement system. */
|
||||
for (value = 0; isdigit (*s); s++)
|
||||
value = value * 10 - (*s - '0');
|
||||
if (!negative)
|
||||
value = -value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Compares A and B by calling the AUX function. */
|
||||
static int
|
||||
compare_thunk (const void *a, const void *b, void *aux)
|
||||
{
|
||||
int (**compare) (const void *, const void *) = aux;
|
||||
return (*compare) (a, b);
|
||||
}
|
||||
|
||||
/* Sorts ARRAY, which contains CNT elements of SIZE bytes each,
|
||||
using COMPARE. When COMPARE is passed a pair of elements A
|
||||
and B, respectively, it must return a strcmp()-type result,
|
||||
i.e. less than zero if A < B, zero if A == B, greater than
|
||||
zero if A > B. Runs in O(n lg n) time and O(1) space in
|
||||
CNT. */
|
||||
void
|
||||
qsort (void *array, size_t cnt, size_t size,
|
||||
int (*compare) (const void *, const void *))
|
||||
{
|
||||
sort (array, cnt, size, compare_thunk, &compare);
|
||||
}
|
||||
|
||||
/* Swaps elements with 1-based indexes A_IDX and B_IDX in ARRAY
|
||||
with elements of SIZE bytes each. */
|
||||
static void
|
||||
do_swap (unsigned char *array, size_t a_idx, size_t b_idx, size_t size)
|
||||
{
|
||||
unsigned char *a = array + (a_idx - 1) * size;
|
||||
unsigned char *b = array + (b_idx - 1) * size;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
unsigned char t = a[i];
|
||||
a[i] = b[i];
|
||||
b[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compares elements with 1-based indexes A_IDX and B_IDX in
|
||||
ARRAY with elements of SIZE bytes each, using COMPARE to
|
||||
compare elements, passing AUX as auxiliary data, and returns a
|
||||
strcmp()-type result. */
|
||||
static int
|
||||
do_compare (unsigned char *array, size_t a_idx, size_t b_idx, size_t size,
|
||||
int (*compare) (const void *, const void *, void *aux),
|
||||
void *aux)
|
||||
{
|
||||
return compare (array + (a_idx - 1) * size, array + (b_idx - 1) * size, aux);
|
||||
}
|
||||
|
||||
/* "Float down" the element with 1-based index I in ARRAY of CNT
|
||||
elements of SIZE bytes each, using COMPARE to compare
|
||||
elements, passing AUX as auxiliary data. */
|
||||
static void
|
||||
heapify (unsigned char *array, size_t i, size_t cnt, size_t size,
|
||||
int (*compare) (const void *, const void *, void *aux),
|
||||
void *aux)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
/* Set `max' to the index of the largest element among I
|
||||
and its children (if any). */
|
||||
size_t left = 2 * i;
|
||||
size_t right = 2 * i + 1;
|
||||
size_t max = i;
|
||||
if (left <= cnt && do_compare (array, left, max, size, compare, aux) > 0)
|
||||
max = left;
|
||||
if (right <= cnt
|
||||
&& do_compare (array, right, max, size, compare, aux) > 0)
|
||||
max = right;
|
||||
|
||||
/* If the maximum value is already in element I, we're
|
||||
done. */
|
||||
if (max == i)
|
||||
break;
|
||||
|
||||
/* Swap and continue down the heap. */
|
||||
do_swap (array, i, max, size);
|
||||
i = max;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorts ARRAY, which contains CNT elements of SIZE bytes each,
|
||||
using COMPARE to compare elements, passing AUX as auxiliary
|
||||
data. When COMPARE is passed a pair of elements A and B,
|
||||
respectively, it must return a strcmp()-type result, i.e. less
|
||||
than zero if A < B, zero if A == B, greater than zero if A >
|
||||
B. Runs in O(n lg n) time and O(1) space in CNT. */
|
||||
void
|
||||
sort (void *array, size_t cnt, size_t size,
|
||||
int (*compare) (const void *, const void *, void *aux),
|
||||
void *aux)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
ASSERT (array != NULL || cnt == 0);
|
||||
ASSERT (compare != NULL);
|
||||
ASSERT (size > 0);
|
||||
|
||||
/* Build a heap. */
|
||||
for (i = cnt / 2; i > 0; i--)
|
||||
heapify (array, i, cnt, size, compare, aux);
|
||||
|
||||
/* Sort the heap. */
|
||||
for (i = cnt; i > 1; i--)
|
||||
{
|
||||
do_swap (array, 1, i, size);
|
||||
heapify (array, 1, i - 1, size, compare, aux);
|
||||
}
|
||||
}
|
||||
|
||||
/* Searches ARRAY, which contains CNT elements of SIZE bytes
|
||||
each, for the given KEY. Returns a match is found, otherwise
|
||||
a null pointer. If there are multiple matches, returns an
|
||||
arbitrary one of them.
|
||||
|
||||
ARRAY must be sorted in order according to COMPARE.
|
||||
|
||||
Uses COMPARE to compare elements. When COMPARE is passed a
|
||||
pair of elements A and B, respectively, it must return a
|
||||
strcmp()-type result, i.e. less than zero if A < B, zero if A
|
||||
== B, greater than zero if A > B. */
|
||||
void *
|
||||
bsearch (const void *key, const void *array, size_t cnt,
|
||||
size_t size, int (*compare) (const void *, const void *))
|
||||
{
|
||||
return binary_search (key, array, cnt, size, compare_thunk, &compare);
|
||||
}
|
||||
|
||||
/* Searches ARRAY, which contains CNT elements of SIZE bytes
|
||||
each, for the given KEY. Returns a match is found, otherwise
|
||||
a null pointer. If there are multiple matches, returns an
|
||||
arbitrary one of them.
|
||||
|
||||
ARRAY must be sorted in order according to COMPARE.
|
||||
|
||||
Uses COMPARE to compare elements, passing AUX as auxiliary
|
||||
data. When COMPARE is passed a pair of elements A and B,
|
||||
respectively, it must return a strcmp()-type result, i.e. less
|
||||
than zero if A < B, zero if A == B, greater than zero if A >
|
||||
B. */
|
||||
void *
|
||||
binary_search (const void *key, const void *array, size_t cnt, size_t size,
|
||||
int (*compare) (const void *, const void *, void *aux),
|
||||
void *aux)
|
||||
{
|
||||
const unsigned char *first = array;
|
||||
const unsigned char *last = array + size * cnt;
|
||||
|
||||
while (first < last)
|
||||
{
|
||||
size_t range = (last - first) / size;
|
||||
const unsigned char *middle = first + (range / 2) * size;
|
||||
int cmp = compare (key, middle, aux);
|
||||
|
||||
if (cmp < 0)
|
||||
last = middle;
|
||||
else if (cmp > 0)
|
||||
first = middle + size;
|
||||
else
|
||||
return (void *) middle;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
22
tests/devices/src/lib/stdlib.h
Normal file
22
tests/devices/src/lib/stdlib.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef __LIB_STDLIB_H
|
||||
#define __LIB_STDLIB_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Standard functions. */
|
||||
int atoi (const char *);
|
||||
void qsort (void *array, size_t cnt, size_t size,
|
||||
int (*compare) (const void *, const void *));
|
||||
void *bsearch (const void *key, const void *array, size_t cnt,
|
||||
size_t size, int (*compare) (const void *, const void *));
|
||||
|
||||
/* Nonstandard functions. */
|
||||
void sort (void *array, size_t cnt, size_t size,
|
||||
int (*compare) (const void *, const void *, void *aux),
|
||||
void *aux);
|
||||
void *binary_search (const void *key, const void *array, size_t cnt,
|
||||
size_t size,
|
||||
int (*compare) (const void *, const void *, void *aux),
|
||||
void *aux);
|
||||
|
||||
#endif /* lib/stdlib.h */
|
||||
375
tests/devices/src/lib/string.c
Normal file
375
tests/devices/src/lib/string.c
Normal file
@@ -0,0 +1,375 @@
|
||||
#include <string.h>
|
||||
#include <debug.h>
|
||||
|
||||
/* Copies SIZE bytes from SRC to DST, which must not overlap.
|
||||
Returns DST. */
|
||||
void *
|
||||
memcpy (void *dst_, const void *src_, size_t size)
|
||||
{
|
||||
unsigned char *dst = dst_;
|
||||
const unsigned char *src = src_;
|
||||
|
||||
ASSERT (dst != NULL || size == 0);
|
||||
ASSERT (src != NULL || size == 0);
|
||||
|
||||
while (size-- > 0)
|
||||
*dst++ = *src++;
|
||||
|
||||
return dst_;
|
||||
}
|
||||
|
||||
/* Copies SIZE bytes from SRC to DST, which are allowed to
|
||||
overlap. Returns DST. */
|
||||
void *
|
||||
memmove (void *dst_, const void *src_, size_t size)
|
||||
{
|
||||
unsigned char *dst = dst_;
|
||||
const unsigned char *src = src_;
|
||||
|
||||
ASSERT (dst != NULL || size == 0);
|
||||
ASSERT (src != NULL || size == 0);
|
||||
|
||||
if (dst < src)
|
||||
{
|
||||
while (size-- > 0)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst += size;
|
||||
src += size;
|
||||
while (size-- > 0)
|
||||
*--dst = *--src;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* Find the first differing byte in the two blocks of SIZE bytes
|
||||
at A and B. Returns a positive value if the byte in A is
|
||||
greater, a negative value if the byte in B is greater, or zero
|
||||
if blocks A and B are equal. */
|
||||
int
|
||||
memcmp (const void *a_, const void *b_, size_t size)
|
||||
{
|
||||
const unsigned char *a = a_;
|
||||
const unsigned char *b = b_;
|
||||
|
||||
ASSERT (a != NULL || size == 0);
|
||||
ASSERT (b != NULL || size == 0);
|
||||
|
||||
for (; size-- > 0; a++, b++)
|
||||
if (*a != *b)
|
||||
return *a > *b ? +1 : -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Finds the first differing characters in strings A and B.
|
||||
Returns a positive value if the character in A (as an unsigned
|
||||
char) is greater, a negative value if the character in B (as
|
||||
an unsigned char) is greater, or zero if strings A and B are
|
||||
equal. */
|
||||
int
|
||||
strcmp (const char *a_, const char *b_)
|
||||
{
|
||||
const unsigned char *a = (const unsigned char *) a_;
|
||||
const unsigned char *b = (const unsigned char *) b_;
|
||||
|
||||
ASSERT (a != NULL);
|
||||
ASSERT (b != NULL);
|
||||
|
||||
while (*a != '\0' && *a == *b)
|
||||
{
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return *a < *b ? -1 : *a > *b;
|
||||
}
|
||||
|
||||
/* Returns a pointer to the first occurrence of CH in the first
|
||||
SIZE bytes starting at BLOCK. Returns a null pointer if CH
|
||||
does not occur in BLOCK. */
|
||||
void *
|
||||
memchr (const void *block_, int ch_, size_t size)
|
||||
{
|
||||
const unsigned char *block = block_;
|
||||
unsigned char ch = ch_;
|
||||
|
||||
ASSERT (block != NULL || size == 0);
|
||||
|
||||
for (; size-- > 0; block++)
|
||||
if (*block == ch)
|
||||
return (void *) block;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Finds and returns the first occurrence of C in STRING, or a
|
||||
null pointer if C does not appear in STRING. If C == '\0'
|
||||
then returns a pointer to the null terminator at the end of
|
||||
STRING. */
|
||||
char *
|
||||
strchr (const char *string, int c_)
|
||||
{
|
||||
char c = c_;
|
||||
|
||||
ASSERT (string != NULL);
|
||||
|
||||
for (;;)
|
||||
if (*string == c)
|
||||
return (char *) string;
|
||||
else if (*string == '\0')
|
||||
return NULL;
|
||||
else
|
||||
string++;
|
||||
}
|
||||
|
||||
/* Returns the length of the initial substring of STRING that
|
||||
consists of characters that are not in STOP. */
|
||||
size_t
|
||||
strcspn (const char *string, const char *stop)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
for (length = 0; string[length] != '\0'; length++)
|
||||
if (strchr (stop, string[length]) != NULL)
|
||||
break;
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Returns a pointer to the first character in STRING that is
|
||||
also in STOP. If no character in STRING is in STOP, returns a
|
||||
null pointer. */
|
||||
char *
|
||||
strpbrk (const char *string, const char *stop)
|
||||
{
|
||||
for (; *string != '\0'; string++)
|
||||
if (strchr (stop, *string) != NULL)
|
||||
return (char *) string;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns a pointer to the last occurrence of C in STRING.
|
||||
Returns a null pointer if C does not occur in STRING. */
|
||||
char *
|
||||
strrchr (const char *string, int c_)
|
||||
{
|
||||
char c = c_;
|
||||
const char *p = NULL;
|
||||
|
||||
for (; *string != '\0'; string++)
|
||||
if (*string == c)
|
||||
p = string;
|
||||
return (char *) p;
|
||||
}
|
||||
|
||||
/* Returns the length of the initial substring of STRING that
|
||||
consists of characters in SKIP. */
|
||||
size_t
|
||||
strspn (const char *string, const char *skip)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
for (length = 0; string[length] != '\0'; length++)
|
||||
if (strchr (skip, string[length]) == NULL)
|
||||
break;
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Returns a pointer to the first occurrence of NEEDLE within
|
||||
HAYSTACK. Returns a null pointer if NEEDLE does not exist
|
||||
within HAYSTACK. */
|
||||
char *
|
||||
strstr (const char *haystack, const char *needle)
|
||||
{
|
||||
size_t haystack_len = strlen (haystack);
|
||||
size_t needle_len = strlen (needle);
|
||||
|
||||
if (haystack_len >= needle_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i <= haystack_len - needle_len; i++)
|
||||
if (!memcmp (haystack + i, needle, needle_len))
|
||||
return (char *) haystack + i;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Breaks a string into tokens separated by DELIMITERS. The
|
||||
first time this function is called, S should be the string to
|
||||
tokenize, and in subsequent calls it must be a null pointer.
|
||||
SAVE_PTR is the address of a `char *' variable used to keep
|
||||
track of the tokenizer's position. The return value each time
|
||||
is the next token in the string, or a null pointer if no
|
||||
tokens remain.
|
||||
|
||||
This function treats multiple adjacent delimiters as a single
|
||||
delimiter. The returned tokens will never be length 0.
|
||||
DELIMITERS may change from one call to the next within a
|
||||
single string.
|
||||
|
||||
strtok_r() modifies the string S, changing delimiters to null
|
||||
bytes. Thus, S must be a modifiable string. String literals,
|
||||
in particular, are *not* modifiable in C, even though for
|
||||
backward compatibility they are not `const'.
|
||||
|
||||
Example usage:
|
||||
|
||||
char s[] = " String to tokenize. ";
|
||||
char *token, *save_ptr;
|
||||
|
||||
for (token = strtok_r (s, " ", &save_ptr); token != NULL;
|
||||
token = strtok_r (NULL, " ", &save_ptr))
|
||||
printf ("'%s'\n", token);
|
||||
|
||||
outputs:
|
||||
|
||||
'String'
|
||||
'to'
|
||||
'tokenize.'
|
||||
*/
|
||||
char *
|
||||
strtok_r (char *s, const char *delimiters, char **save_ptr)
|
||||
{
|
||||
char *token;
|
||||
|
||||
ASSERT (delimiters != NULL);
|
||||
ASSERT (save_ptr != NULL);
|
||||
|
||||
/* If S is nonnull, start from it.
|
||||
If S is null, start from saved position. */
|
||||
if (s == NULL)
|
||||
s = *save_ptr;
|
||||
ASSERT (s != NULL);
|
||||
|
||||
/* Skip any DELIMITERS at our current position. */
|
||||
while (strchr (delimiters, *s) != NULL)
|
||||
{
|
||||
/* strchr() will always return nonnull if we're searching
|
||||
for a null byte, because every string contains a null
|
||||
byte (at the end). */
|
||||
if (*s == '\0')
|
||||
{
|
||||
*save_ptr = s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
/* Skip any non-DELIMITERS up to the end of the string. */
|
||||
token = s;
|
||||
while (strchr (delimiters, *s) == NULL)
|
||||
s++;
|
||||
if (*s != '\0')
|
||||
{
|
||||
*s = '\0';
|
||||
*save_ptr = s + 1;
|
||||
}
|
||||
else
|
||||
*save_ptr = s;
|
||||
return token;
|
||||
}
|
||||
|
||||
/* Sets the SIZE bytes in DST to VALUE. */
|
||||
void *
|
||||
memset (void *dst_, int value, size_t size)
|
||||
{
|
||||
unsigned char *dst = dst_;
|
||||
|
||||
ASSERT (dst != NULL || size == 0);
|
||||
|
||||
while (size-- > 0)
|
||||
*dst++ = value;
|
||||
|
||||
return dst_;
|
||||
}
|
||||
|
||||
/* Returns the length of STRING. */
|
||||
size_t
|
||||
strlen (const char *string)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
ASSERT (string != NULL);
|
||||
|
||||
for (p = string; *p != '\0'; p++)
|
||||
continue;
|
||||
return p - string;
|
||||
}
|
||||
|
||||
/* If STRING is less than MAXLEN characters in length, returns
|
||||
its actual length. Otherwise, returns MAXLEN. */
|
||||
size_t
|
||||
strnlen (const char *string, size_t maxlen)
|
||||
{
|
||||
size_t length;
|
||||
|
||||
for (length = 0; string[length] != '\0' && length < maxlen; length++)
|
||||
continue;
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Copies string SRC to DST. If SRC is longer than SIZE - 1
|
||||
characters, only SIZE - 1 characters are copied. A null
|
||||
terminator is always written to DST, unless SIZE is 0.
|
||||
Returns the length of SRC, not including the null terminator.
|
||||
|
||||
strlcpy() is not in the standard C library, but it is an
|
||||
increasingly popular extension. See
|
||||
http://www.courtesan.com/todd/papers/strlcpy.html for
|
||||
information on strlcpy(). */
|
||||
size_t
|
||||
strlcpy (char *dst, const char *src, size_t size)
|
||||
{
|
||||
size_t src_len;
|
||||
|
||||
ASSERT (dst != NULL);
|
||||
ASSERT (src != NULL);
|
||||
|
||||
src_len = strlen (src);
|
||||
if (size > 0)
|
||||
{
|
||||
size_t dst_len = size - 1;
|
||||
if (src_len < dst_len)
|
||||
dst_len = src_len;
|
||||
memcpy (dst, src, dst_len);
|
||||
dst[dst_len] = '\0';
|
||||
}
|
||||
return src_len;
|
||||
}
|
||||
|
||||
/* Concatenates string SRC to DST. The concatenated string is
|
||||
limited to SIZE - 1 characters. A null terminator is always
|
||||
written to DST, unless SIZE is 0. Returns the length that the
|
||||
concatenated string would have assuming that there was
|
||||
sufficient space, not including a null terminator.
|
||||
|
||||
strlcat() is not in the standard C library, but it is an
|
||||
increasingly popular extension. See
|
||||
http://www.courtesan.com/todd/papers/strlcpy.html for
|
||||
information on strlcpy(). */
|
||||
size_t
|
||||
strlcat (char *dst, const char *src, size_t size)
|
||||
{
|
||||
size_t src_len, dst_len;
|
||||
|
||||
ASSERT (dst != NULL);
|
||||
ASSERT (src != NULL);
|
||||
|
||||
src_len = strlen (src);
|
||||
dst_len = strlen (dst);
|
||||
if (size > 0 && dst_len < size)
|
||||
{
|
||||
size_t copy_cnt = size - dst_len - 1;
|
||||
if (src_len < copy_cnt)
|
||||
copy_cnt = src_len;
|
||||
memcpy (dst + dst_len, src, copy_cnt);
|
||||
dst[dst_len + copy_cnt] = '\0';
|
||||
}
|
||||
return src_len + dst_len;
|
||||
}
|
||||
|
||||
35
tests/devices/src/lib/string.h
Normal file
35
tests/devices/src/lib/string.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef __LIB_STRING_H
|
||||
#define __LIB_STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* Standard. */
|
||||
void *memcpy (void *, const void *, size_t);
|
||||
void *memmove (void *, const void *, size_t);
|
||||
char *strncat (char *, const char *, size_t);
|
||||
int memcmp (const void *, const void *, size_t);
|
||||
int strcmp (const char *, const char *);
|
||||
void *memchr (const void *, int, size_t);
|
||||
char *strchr (const char *, int);
|
||||
size_t strcspn (const char *, const char *);
|
||||
char *strpbrk (const char *, const char *);
|
||||
char *strrchr (const char *, int);
|
||||
size_t strspn (const char *, const char *);
|
||||
char *strstr (const char *, const char *);
|
||||
void *memset (void *, int, size_t);
|
||||
size_t strlen (const char *);
|
||||
|
||||
/* Extensions. */
|
||||
size_t strlcpy (char *, const char *, size_t);
|
||||
size_t strlcat (char *, const char *, size_t);
|
||||
char *strtok_r (char *, const char *, char **);
|
||||
size_t strnlen (const char *, size_t);
|
||||
|
||||
/* Try to be helpful. */
|
||||
#define strcpy dont_use_strcpy_use_strlcpy
|
||||
#define strncpy dont_use_strncpy_use_strlcpy
|
||||
#define strcat dont_use_strcat_use_strlcat
|
||||
#define strncat dont_use_strncat_use_strlcat
|
||||
#define strtok dont_use_strtok_use_strtok_r
|
||||
|
||||
#endif /* lib/string.h */
|
||||
34
tests/devices/src/lib/syscall-nr.h
Normal file
34
tests/devices/src/lib/syscall-nr.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef __LIB_SYSCALL_NR_H
|
||||
#define __LIB_SYSCALL_NR_H
|
||||
|
||||
/* System call numbers. */
|
||||
enum
|
||||
{
|
||||
/* Tasks 2 and later. */
|
||||
SYS_HALT, /* Halt the operating system. */
|
||||
SYS_EXIT, /* Terminate this process. */
|
||||
SYS_EXEC, /* Start another process. */
|
||||
SYS_WAIT, /* Wait for a child process to die. */
|
||||
SYS_CREATE, /* Create a file. */
|
||||
SYS_REMOVE, /* Delete a file. */
|
||||
SYS_OPEN, /* Open a file. */
|
||||
SYS_FILESIZE, /* Obtain a file's size. */
|
||||
SYS_READ, /* Read from a file. */
|
||||
SYS_WRITE, /* Write to a file. */
|
||||
SYS_SEEK, /* Change position in a file. */
|
||||
SYS_TELL, /* Report current position in a file. */
|
||||
SYS_CLOSE, /* Close a file. */
|
||||
|
||||
/* Task 3 and optionally task 4. */
|
||||
SYS_MMAP, /* Map a file into memory. */
|
||||
SYS_MUNMAP, /* Remove a memory mapping. */
|
||||
|
||||
/* Task 4 only. */
|
||||
SYS_CHDIR, /* Change the current directory. */
|
||||
SYS_MKDIR, /* Create a directory. */
|
||||
SYS_READDIR, /* Reads a directory entry. */
|
||||
SYS_ISDIR, /* Tests if a fd represents a directory. */
|
||||
SYS_INUMBER /* Returns the inode number for a fd. */
|
||||
};
|
||||
|
||||
#endif /* lib/syscall-nr.h */
|
||||
94
tests/devices/src/lib/user/console.c
Normal file
94
tests/devices/src/lib/user/console.c
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
#include <syscall-nr.h>
|
||||
|
||||
/* The standard vprintf() function,
|
||||
which is like printf() but uses a va_list. */
|
||||
int
|
||||
vprintf (const char *format, va_list args)
|
||||
{
|
||||
return vhprintf (STDOUT_FILENO, format, args);
|
||||
}
|
||||
|
||||
/* Like printf(), but writes output to the given HANDLE. */
|
||||
int
|
||||
hprintf (int handle, const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int retval;
|
||||
|
||||
va_start (args, format);
|
||||
retval = vhprintf (handle, format, args);
|
||||
va_end (args);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Writes string S to the console, followed by a new-line
|
||||
character. */
|
||||
int
|
||||
puts (const char *s)
|
||||
{
|
||||
write (STDOUT_FILENO, s, strlen (s));
|
||||
putchar ('\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Writes C to the console. */
|
||||
int
|
||||
putchar (int c)
|
||||
{
|
||||
char c2 = c;
|
||||
write (STDOUT_FILENO, &c2, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Auxiliary data for vhprintf_helper(). */
|
||||
struct vhprintf_aux
|
||||
{
|
||||
char buf[64]; /* Character buffer. */
|
||||
char *p; /* Current position in buffer. */
|
||||
int char_cnt; /* Total characters written so far. */
|
||||
int handle; /* Output file handle. */
|
||||
};
|
||||
|
||||
static void add_char (char, void *);
|
||||
static void flush (struct vhprintf_aux *);
|
||||
|
||||
/* Formats the printf() format specification FORMAT with
|
||||
arguments given in ARGS and writes the output to the given
|
||||
HANDLE. */
|
||||
int
|
||||
vhprintf (int handle, const char *format, va_list args)
|
||||
{
|
||||
struct vhprintf_aux aux;
|
||||
aux.p = aux.buf;
|
||||
aux.char_cnt = 0;
|
||||
aux.handle = handle;
|
||||
__vprintf (format, args, add_char, &aux);
|
||||
flush (&aux);
|
||||
return aux.char_cnt;
|
||||
}
|
||||
|
||||
/* Adds C to the buffer in AUX, flushing it if the buffer fills
|
||||
up. */
|
||||
static void
|
||||
add_char (char c, void *aux_)
|
||||
{
|
||||
struct vhprintf_aux *aux = aux_;
|
||||
*aux->p++ = c;
|
||||
if (aux->p >= aux->buf + sizeof aux->buf)
|
||||
flush (aux);
|
||||
aux->char_cnt++;
|
||||
}
|
||||
|
||||
/* Flushes the buffer in AUX. */
|
||||
static void
|
||||
flush (struct vhprintf_aux *aux)
|
||||
{
|
||||
if (aux->p > aux->buf)
|
||||
write (aux->handle, aux->buf, aux->p - aux->buf);
|
||||
aux->p = aux->buf;
|
||||
}
|
||||
25
tests/devices/src/lib/user/debug.c
Normal file
25
tests/devices/src/lib/user/debug.c
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <debug.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
/* Aborts the user program, 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, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
printf ("User process ABORT at %s:%d in %s(): ", file, line, function);
|
||||
|
||||
va_start (args, message);
|
||||
vprintf (message, args);
|
||||
printf ("\n");
|
||||
va_end (args);
|
||||
|
||||
debug_backtrace ();
|
||||
|
||||
exit (1);
|
||||
}
|
||||
10
tests/devices/src/lib/user/entry.c
Normal file
10
tests/devices/src/lib/user/entry.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <syscall.h>
|
||||
|
||||
int main (int, char *[]);
|
||||
void _start (int argc, char *argv[]);
|
||||
|
||||
void
|
||||
_start (int argc, char *argv[])
|
||||
{
|
||||
exit (main (argc, argv));
|
||||
}
|
||||
7
tests/devices/src/lib/user/stdio.h
Normal file
7
tests/devices/src/lib/user/stdio.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __LIB_USER_STDIO_H
|
||||
#define __LIB_USER_STDIO_H
|
||||
|
||||
int hprintf (int, const char *, ...) PRINTF_FORMAT (2, 3);
|
||||
int vhprintf (int, const char *, va_list) PRINTF_FORMAT (2, 0);
|
||||
|
||||
#endif /* lib/user/stdio.h */
|
||||
184
tests/devices/src/lib/user/syscall.c
Normal file
184
tests/devices/src/lib/user/syscall.c
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <syscall.h>
|
||||
#include "../syscall-nr.h"
|
||||
|
||||
/* Invokes syscall NUMBER, passing no arguments, and returns the
|
||||
return value as an `int'. */
|
||||
#define syscall0(NUMBER) \
|
||||
({ \
|
||||
int retval; \
|
||||
asm volatile \
|
||||
("pushl %[number]; int $0x30; addl $4, %%esp" \
|
||||
: "=a" (retval) \
|
||||
: [number] "i" (NUMBER) \
|
||||
: "memory"); \
|
||||
retval; \
|
||||
})
|
||||
|
||||
/* Invokes syscall NUMBER, passing argument ARG0, and returns the
|
||||
return value as an `int'. */
|
||||
#define syscall1(NUMBER, ARG0) \
|
||||
({ \
|
||||
int retval; \
|
||||
asm volatile \
|
||||
("pushl %[arg0]; pushl %[number]; int $0x30; addl $8, %%esp" \
|
||||
: "=a" (retval) \
|
||||
: [number] "i" (NUMBER), \
|
||||
[arg0] "g" (ARG0) \
|
||||
: "memory"); \
|
||||
retval; \
|
||||
})
|
||||
|
||||
/* Invokes syscall NUMBER, passing arguments ARG0 and ARG1, and
|
||||
returns the return value as an `int'. */
|
||||
#define syscall2(NUMBER, ARG0, ARG1) \
|
||||
({ \
|
||||
int retval; \
|
||||
asm volatile \
|
||||
("pushl %[arg1]; pushl %[arg0]; " \
|
||||
"pushl %[number]; int $0x30; addl $12, %%esp" \
|
||||
: "=a" (retval) \
|
||||
: [number] "i" (NUMBER), \
|
||||
[arg0] "g" (ARG0), \
|
||||
[arg1] "g" (ARG1) \
|
||||
: "memory"); \
|
||||
retval; \
|
||||
})
|
||||
|
||||
/* Invokes syscall NUMBER, passing arguments ARG0, ARG1, and
|
||||
ARG2, and returns the return value as an `int'. */
|
||||
#define syscall3(NUMBER, ARG0, ARG1, ARG2) \
|
||||
({ \
|
||||
int retval; \
|
||||
asm volatile \
|
||||
("pushl %[arg2]; pushl %[arg1]; pushl %[arg0]; " \
|
||||
"pushl %[number]; int $0x30; addl $16, %%esp" \
|
||||
: "=a" (retval) \
|
||||
: [number] "i" (NUMBER), \
|
||||
[arg0] "g" (ARG0), \
|
||||
[arg1] "g" (ARG1), \
|
||||
[arg2] "g" (ARG2) \
|
||||
: "memory"); \
|
||||
retval; \
|
||||
})
|
||||
|
||||
void
|
||||
halt (void)
|
||||
{
|
||||
syscall0 (SYS_HALT);
|
||||
NOT_REACHED ();
|
||||
}
|
||||
|
||||
void
|
||||
exit (int status)
|
||||
{
|
||||
syscall1 (SYS_EXIT, status);
|
||||
NOT_REACHED ();
|
||||
}
|
||||
|
||||
pid_t
|
||||
exec (const char *file)
|
||||
{
|
||||
return (pid_t) syscall1 (SYS_EXEC, file);
|
||||
}
|
||||
|
||||
int
|
||||
wait (pid_t pid)
|
||||
{
|
||||
return syscall1 (SYS_WAIT, pid);
|
||||
}
|
||||
|
||||
bool
|
||||
create (const char *file, unsigned initial_size)
|
||||
{
|
||||
return syscall2 (SYS_CREATE, file, initial_size);
|
||||
}
|
||||
|
||||
bool
|
||||
remove (const char *file)
|
||||
{
|
||||
return syscall1 (SYS_REMOVE, file);
|
||||
}
|
||||
|
||||
int
|
||||
open (const char *file)
|
||||
{
|
||||
return syscall1 (SYS_OPEN, file);
|
||||
}
|
||||
|
||||
int
|
||||
filesize (int fd)
|
||||
{
|
||||
return syscall1 (SYS_FILESIZE, fd);
|
||||
}
|
||||
|
||||
int
|
||||
read (int fd, void *buffer, unsigned size)
|
||||
{
|
||||
return syscall3 (SYS_READ, fd, buffer, size);
|
||||
}
|
||||
|
||||
int
|
||||
write (int fd, const void *buffer, unsigned size)
|
||||
{
|
||||
return syscall3 (SYS_WRITE, fd, buffer, size);
|
||||
}
|
||||
|
||||
void
|
||||
seek (int fd, unsigned position)
|
||||
{
|
||||
syscall2 (SYS_SEEK, fd, position);
|
||||
}
|
||||
|
||||
unsigned
|
||||
tell (int fd)
|
||||
{
|
||||
return syscall1 (SYS_TELL, fd);
|
||||
}
|
||||
|
||||
void
|
||||
close (int fd)
|
||||
{
|
||||
syscall1 (SYS_CLOSE, fd);
|
||||
}
|
||||
|
||||
mapid_t
|
||||
mmap (int fd, void *addr)
|
||||
{
|
||||
return syscall2 (SYS_MMAP, fd, addr);
|
||||
}
|
||||
|
||||
void
|
||||
munmap (mapid_t mapid)
|
||||
{
|
||||
syscall1 (SYS_MUNMAP, mapid);
|
||||
}
|
||||
|
||||
bool
|
||||
chdir (const char *dir)
|
||||
{
|
||||
return syscall1 (SYS_CHDIR, dir);
|
||||
}
|
||||
|
||||
bool
|
||||
mkdir (const char *dir)
|
||||
{
|
||||
return syscall1 (SYS_MKDIR, dir);
|
||||
}
|
||||
|
||||
bool
|
||||
readdir (int fd, char name[FNAME_MAX_LEN + 1])
|
||||
{
|
||||
return syscall2 (SYS_READDIR, fd, name);
|
||||
}
|
||||
|
||||
bool
|
||||
isdir (int fd)
|
||||
{
|
||||
return syscall1 (SYS_ISDIR, fd);
|
||||
}
|
||||
|
||||
int
|
||||
inumber (int fd)
|
||||
{
|
||||
return syscall1 (SYS_INUMBER, fd);
|
||||
}
|
||||
46
tests/devices/src/lib/user/syscall.h
Normal file
46
tests/devices/src/lib/user/syscall.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef __LIB_USER_SYSCALL_H
|
||||
#define __LIB_USER_SYSCALL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <debug.h>
|
||||
#include "../../filesys/file.h"
|
||||
|
||||
/* Process identifier. */
|
||||
typedef int pid_t;
|
||||
#define PID_ERROR ((pid_t) -1)
|
||||
|
||||
/* Map region identifier. */
|
||||
typedef int mapid_t;
|
||||
#define MAP_FAILED ((mapid_t) -1)
|
||||
|
||||
/* Typical return values from main() and arguments to exit(). */
|
||||
#define EXIT_SUCCESS 0 /* Successful execution. */
|
||||
#define EXIT_FAILURE 1 /* Unsuccessful execution. */
|
||||
|
||||
/* Tasks 2 and later. */
|
||||
void halt (void) NO_RETURN;
|
||||
void exit (int status) NO_RETURN;
|
||||
pid_t exec (const char *file);
|
||||
int wait (pid_t);
|
||||
bool create (const char *file, unsigned initial_size);
|
||||
bool remove (const char *file);
|
||||
int open (const char *file);
|
||||
int filesize (int fd);
|
||||
int read (int fd, void *buffer, unsigned length);
|
||||
int write (int fd, const void *buffer, unsigned length);
|
||||
void seek (int fd, unsigned position);
|
||||
unsigned tell (int fd);
|
||||
void close (int fd);
|
||||
|
||||
/* Task 3 and optionally task 4. */
|
||||
mapid_t mmap (int fd, void *addr);
|
||||
void munmap (mapid_t);
|
||||
|
||||
/* Task 4 only. */
|
||||
bool chdir (const char *dir);
|
||||
bool mkdir (const char *dir);
|
||||
bool readdir (int fd, char name[FNAME_MAX_LEN + 1]);
|
||||
bool isdir (int fd);
|
||||
int inumber (int fd);
|
||||
|
||||
#endif /* lib/user/syscall.h */
|
||||
57
tests/devices/src/lib/user/user.lds
Normal file
57
tests/devices/src/lib/user/user.lds
Normal file
@@ -0,0 +1,57 @@
|
||||
OUTPUT_FORMAT("elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Read-only sections, merged into text segment, at fixed start offset. */
|
||||
__executable_start = 0x08048000;
|
||||
. = 0x08048000;
|
||||
.text : { *(.text) }
|
||||
.rodata : { *(.rodata) }
|
||||
|
||||
/* Adjust the address for the data segment. We want to adjust up to
|
||||
the same address within the page on the next page up. */
|
||||
. = ALIGN (0x1000) - ((0x1000 - .) & (0x1000 - 1));
|
||||
. = DATA_SEGMENT_ALIGN (0x1000, 0x1000);
|
||||
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss) }
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
/DISCARD/ : { *(.note.GNU-stack) }
|
||||
/DISCARD/ : { *(.eh_frame) }
|
||||
}
|
||||
228
tests/devices/src/lib/ustar.c
Normal file
228
tests/devices/src/lib/ustar.c
Normal file
@@ -0,0 +1,228 @@
|
||||
#include <ustar.h>
|
||||
#include <limits.h>
|
||||
#include <packed.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Header for ustar-format tar archive. See the documentation of
|
||||
the "pax" utility in [SUSv3] for the the "ustar" format
|
||||
specification. */
|
||||
struct ustar_header
|
||||
{
|
||||
char name[100]; /* File name. Null-terminated if room. */
|
||||
char mode[8]; /* Permissions as octal string. */
|
||||
char uid[8]; /* User ID as octal string. */
|
||||
char gid[8]; /* Group ID as octal string. */
|
||||
char size[12]; /* File size in bytes as octal string. */
|
||||
char mtime[12]; /* Modification time in seconds
|
||||
from Jan 1, 1970, as octal string. */
|
||||
char chksum[8]; /* Sum of octets in header as octal string. */
|
||||
char typeflag; /* An enum ustar_type value. */
|
||||
char linkname[100]; /* Name of link target.
|
||||
Null-terminated if room. */
|
||||
char magic[6]; /* "ustar\0" */
|
||||
char version[2]; /* "00" */
|
||||
char uname[32]; /* User name, always null-terminated. */
|
||||
char gname[32]; /* Group name, always null-terminated. */
|
||||
char devmajor[8]; /* Device major number as octal string. */
|
||||
char devminor[8]; /* Device minor number as octal string. */
|
||||
char prefix[155]; /* Prefix to file name.
|
||||
Null-terminated if room. */
|
||||
char padding[12]; /* Pad to 512 bytes. */
|
||||
}
|
||||
PACKED;
|
||||
|
||||
/* Returns the checksum for the given ustar format HEADER. */
|
||||
static unsigned int
|
||||
calculate_chksum (const struct ustar_header *h)
|
||||
{
|
||||
const uint8_t *header = (const uint8_t *) h;
|
||||
unsigned int chksum;
|
||||
size_t i;
|
||||
|
||||
chksum = 0;
|
||||
for (i = 0; i < USTAR_HEADER_SIZE; i++)
|
||||
{
|
||||
/* The ustar checksum is calculated as if the chksum field
|
||||
were all spaces. */
|
||||
const size_t chksum_start = offsetof (struct ustar_header, chksum);
|
||||
const size_t chksum_end = chksum_start + sizeof h->chksum;
|
||||
bool in_chksum_field = i >= chksum_start && i < chksum_end;
|
||||
chksum += in_chksum_field ? ' ' : header[i];
|
||||
}
|
||||
return chksum;
|
||||
}
|
||||
|
||||
/* Drop possibly dangerous prefixes from FILE_NAME and return the
|
||||
stripped name. An archive with file names that start with "/"
|
||||
or "../" could cause a naive tar extractor to write to
|
||||
arbitrary parts of the file system, not just the destination
|
||||
directory. We don't want to create such archives or be such a
|
||||
naive extractor.
|
||||
|
||||
The return value can be a suffix of FILE_NAME or a string
|
||||
literal. */
|
||||
static const char *
|
||||
strip_antisocial_prefixes (const char *file_name)
|
||||
{
|
||||
while (*file_name == '/'
|
||||
|| !memcmp (file_name, "./", 2)
|
||||
|| !memcmp (file_name, "../", 3))
|
||||
file_name = strchr (file_name, '/') + 1;
|
||||
return *file_name == '\0' || !strcmp (file_name, "..") ? "." : file_name;
|
||||
}
|
||||
|
||||
/* Composes HEADER as a USTAR_HEADER_SIZE (512)-byte archive
|
||||
header in ustar format for a SIZE-byte file named FILE_NAME of
|
||||
the given TYPE. The caller is responsible for writing the
|
||||
header to a file or device.
|
||||
|
||||
If successful, returns true. On failure (due to an
|
||||
excessively long file name), returns false. */
|
||||
bool
|
||||
ustar_make_header (const char *file_name, enum ustar_type type,
|
||||
int size, char header[USTAR_HEADER_SIZE])
|
||||
{
|
||||
struct ustar_header *h = (struct ustar_header *) header;
|
||||
|
||||
ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
|
||||
ASSERT (type == USTAR_REGULAR || type == USTAR_DIRECTORY);
|
||||
|
||||
/* Check file name. */
|
||||
file_name = strip_antisocial_prefixes (file_name);
|
||||
if (strlen (file_name) > 99)
|
||||
{
|
||||
printf ("%s: file name too long\n", file_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fill in header except for final checksum. */
|
||||
memset (h, 0, sizeof *h);
|
||||
strlcpy (h->name, file_name, sizeof h->name);
|
||||
snprintf (h->mode, sizeof h->mode, "%07o",
|
||||
type == USTAR_REGULAR ? 0644 : 0755);
|
||||
strlcpy (h->uid, "0000000", sizeof h->uid);
|
||||
strlcpy (h->gid, "0000000", sizeof h->gid);
|
||||
snprintf (h->size, sizeof h->size, "%011o", size);
|
||||
snprintf (h->mtime, sizeof h->size, "%011o", 1136102400);
|
||||
h->typeflag = type;
|
||||
strlcpy (h->magic, "ustar", sizeof h->magic);
|
||||
h->version[0] = h->version[1] = '0';
|
||||
strlcpy (h->gname, "root", sizeof h->gname);
|
||||
strlcpy (h->uname, "root", sizeof h->uname);
|
||||
|
||||
/* Compute and fill in final checksum. */
|
||||
snprintf (h->chksum, sizeof h->chksum, "%07o", calculate_chksum (h));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parses a SIZE-byte octal field in S in the format used by
|
||||
ustar format. If successful, stores the field's value in
|
||||
*VALUE and returns true; on failure, returns false.
|
||||
|
||||
ustar octal fields consist of a sequence of octal digits
|
||||
terminated by a space or a null byte. The ustar specification
|
||||
seems ambiguous as to whether these fields must be padded on
|
||||
the left with '0's, so we accept any field that fits in the
|
||||
available space, regardless of whether it fills the space. */
|
||||
static bool
|
||||
parse_octal_field (const char *s, size_t size, unsigned long int *value)
|
||||
{
|
||||
size_t ofs;
|
||||
|
||||
*value = 0;
|
||||
for (ofs = 0; ofs < size; ofs++)
|
||||
{
|
||||
char c = s[ofs];
|
||||
if (c >= '0' && c <= '7')
|
||||
{
|
||||
if (*value > ULONG_MAX / 8)
|
||||
{
|
||||
/* Overflow. */
|
||||
return false;
|
||||
}
|
||||
*value = c - '0' + *value * 8;
|
||||
}
|
||||
else if (c == ' ' || c == '\0')
|
||||
{
|
||||
/* End of field, but disallow completely empty
|
||||
fields. */
|
||||
return ofs > 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Bad character. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Field did not end in space or null byte. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns true if the CNT bytes starting at BLOCK are all zero,
|
||||
false otherwise. */
|
||||
static bool
|
||||
is_all_zeros (const char *block, size_t cnt)
|
||||
{
|
||||
while (cnt-- > 0)
|
||||
if (*block++ != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parses HEADER as a ustar-format archive header for a regular
|
||||
file or directory. If successful, stores the archived file's
|
||||
name in *FILE_NAME (as a pointer into HEADER or a string
|
||||
literal), its type in *TYPE, and its size in bytes in *SIZE,
|
||||
and returns a null pointer. On failure, returns a
|
||||
human-readable error message. */
|
||||
const char *
|
||||
ustar_parse_header (const char header[USTAR_HEADER_SIZE],
|
||||
const char **file_name, enum ustar_type *type, int *size)
|
||||
{
|
||||
const struct ustar_header *h = (const struct ustar_header *) header;
|
||||
unsigned long int chksum, size_ul;
|
||||
|
||||
ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
|
||||
|
||||
/* Detect end of archive. */
|
||||
if (is_all_zeros (header, USTAR_HEADER_SIZE))
|
||||
{
|
||||
*file_name = NULL;
|
||||
*type = USTAR_EOF;
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Validate ustar header. */
|
||||
if (memcmp (h->magic, "ustar", 6))
|
||||
return "not a ustar archive";
|
||||
else if (h->version[0] != '0' || h->version[1] != '0')
|
||||
return "invalid ustar version";
|
||||
else if (!parse_octal_field (h->chksum, sizeof h->chksum, &chksum))
|
||||
return "corrupt chksum field";
|
||||
else if (chksum != calculate_chksum (h))
|
||||
return "checksum mismatch";
|
||||
else if (h->name[sizeof h->name - 1] != '\0' || h->prefix[0] != '\0')
|
||||
return "file name too long";
|
||||
else if (h->typeflag != USTAR_REGULAR && h->typeflag != USTAR_DIRECTORY)
|
||||
return "unimplemented file type";
|
||||
if (h->typeflag == USTAR_REGULAR)
|
||||
{
|
||||
if (!parse_octal_field (h->size, sizeof h->size, &size_ul))
|
||||
return "corrupt file size field";
|
||||
else if (size_ul > INT_MAX)
|
||||
return "file too large";
|
||||
}
|
||||
else
|
||||
size_ul = 0;
|
||||
|
||||
/* Success. */
|
||||
*file_name = strip_antisocial_prefixes (h->name);
|
||||
*type = h->typeflag;
|
||||
*size = size_ul;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
29
tests/devices/src/lib/ustar.h
Normal file
29
tests/devices/src/lib/ustar.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef __LIB_USTAR_H
|
||||
#define __LIB_USTAR_H
|
||||
|
||||
/* Support for the standard Posix "ustar" format. See the
|
||||
documentation of the "pax" utility in [SUSv3] for the the
|
||||
"ustar" format specification. */
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Type of a file entry in an archive.
|
||||
The values here are the bytes that appear in the file format.
|
||||
Only types of interest to PintOS are listed here. */
|
||||
enum ustar_type
|
||||
{
|
||||
USTAR_REGULAR = '0', /* Ordinary file. */
|
||||
USTAR_DIRECTORY = '5', /* Directory. */
|
||||
USTAR_EOF = -1 /* End of archive (not an official value). */
|
||||
};
|
||||
|
||||
/* Size of a ustar archive header, in bytes. */
|
||||
#define USTAR_HEADER_SIZE 512
|
||||
|
||||
bool ustar_make_header (const char *file_name, enum ustar_type,
|
||||
int size, char header[USTAR_HEADER_SIZE]);
|
||||
const char *ustar_parse_header (const char header[USTAR_HEADER_SIZE],
|
||||
const char **file_name,
|
||||
enum ustar_type *, int *size);
|
||||
|
||||
#endif /* lib/ustar.h */
|
||||
10
tests/devices/src/loader.ld
Normal file
10
tests/devices/src/loader.ld
Normal file
@@ -0,0 +1,10 @@
|
||||
OUTPUT_FORMAT(binary)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x7C00;
|
||||
.text : { *(.text*) }
|
||||
/DISCARD/ : { *(*) }
|
||||
}
|
||||
|
||||
ASSERT(SIZEOF(.text) == 512, "Size of bootloader is not 512 bytes");
|
||||
175
tests/devices/src/misc/gdb-macros
Normal file
175
tests/devices/src/misc/gdb-macros
Normal file
@@ -0,0 +1,175 @@
|
||||
#
|
||||
# A set of useful macros that can help debug PintOS.
|
||||
#
|
||||
# Include with "source" cmd in gdb.
|
||||
# Use "help user-defined" for help.
|
||||
#
|
||||
# Author: Godmar Back <gback@cs.vt.edu>, Feb 2006
|
||||
#
|
||||
# $Id: gdb-macros,v 1.1 2006-04-07 18:29:34 blp Exp $
|
||||
#
|
||||
|
||||
# for internal use
|
||||
define offsetof
|
||||
set $rc = (char*)&((struct $arg0 *)0)->$arg1 - (char*)0
|
||||
end
|
||||
|
||||
define list_entry
|
||||
offsetof $arg1 $arg2
|
||||
set $rc = ((struct $arg1 *) ((uint8_t *) ($arg0) - $rc))
|
||||
end
|
||||
|
||||
define hash_entry
|
||||
list_entry $arg0 $arg1 $arg2
|
||||
end
|
||||
|
||||
# dump a PintOS list
|
||||
define dumplist
|
||||
set $list = $arg0
|
||||
set $e = $list->head.next
|
||||
set $i = 0
|
||||
while $e != &(($arg0).tail)
|
||||
list_entry $e $arg1 $arg2
|
||||
set $l = $rc
|
||||
printf "pintos-debug: dumplist #%d: %p ", $i++, $l
|
||||
output *$l
|
||||
set $e = $e->next
|
||||
printf "\n"
|
||||
end
|
||||
end
|
||||
|
||||
document dumplist
|
||||
Dump the content of a PintOS list,
|
||||
invoke as dumplist name_of_list name_of_struct name_of_elem_in_list_struct
|
||||
end
|
||||
|
||||
# dump a PintOS hash_entry
|
||||
define dumphash
|
||||
set $hash = $arg0
|
||||
set $bkts = $hash.bucket_cnt
|
||||
set $i = 0
|
||||
while $i < $bkts
|
||||
printf "pintos-debug: [hash bucket #%d]:\n", $i
|
||||
set $list = &($hash.buckets[$i])
|
||||
set $e = $list->head.next
|
||||
set $j = 0
|
||||
while $e != &($list.tail)
|
||||
list_entry $e hash_elem list_elem
|
||||
set $l = $rc
|
||||
hash_entry $l $arg1 $arg2
|
||||
set $h = $rc
|
||||
printf "pintos-debug: bucket element #%d => %p \n", $j++, $h
|
||||
output *$h
|
||||
set $e = $e.next
|
||||
printf "\n"
|
||||
end
|
||||
set $i = $i+1
|
||||
end
|
||||
end
|
||||
|
||||
document dumphash
|
||||
Dump the contents of a PintOS hash,
|
||||
invoke as dumphash name_of_hash name_of_struct name_of_elem_in_hash_struct
|
||||
end
|
||||
|
||||
# print a thread's backtrace, given a pointer to the struct thread *
|
||||
define btthread
|
||||
if $arg0 == ($esp - ((unsigned)$esp % 4096))
|
||||
bt
|
||||
else
|
||||
set $saveEIP = $eip
|
||||
set $saveESP = $esp
|
||||
set $saveEBP = $ebp
|
||||
|
||||
set $esp = ((struct thread *)$arg0)->stack
|
||||
set $ebp = ((void**)$esp)[2]
|
||||
set $eip = ((void**)$esp)[4]
|
||||
|
||||
bt
|
||||
|
||||
set $eip = $saveEIP
|
||||
set $esp = $saveESP
|
||||
set $ebp = $saveEBP
|
||||
end
|
||||
end
|
||||
document btthread
|
||||
Show the backtrace of a thread,
|
||||
invoke as btthread pointer_to_struct_thread
|
||||
end
|
||||
|
||||
# print backtraces associated with all threads in a list
|
||||
define btthreadlist
|
||||
set $list = $arg0
|
||||
set $e = $list->head.next
|
||||
while $e != &(($arg0).tail)
|
||||
list_entry $e thread $arg1
|
||||
printf "pintos-debug: dumping backtrace of thread '%s' @%p\n", \
|
||||
((struct thread*)$rc)->name, $rc
|
||||
btthread $rc
|
||||
set $e = $e->next
|
||||
printf "\n"
|
||||
end
|
||||
end
|
||||
document btthreadlist
|
||||
Given a list of threads, print each thread's backtrace
|
||||
invoke as btthreadlist name_of_list name_of_elem_in_list_struct
|
||||
end
|
||||
|
||||
# print backtraces of all threads (based on 'all_list' all threads list)
|
||||
define btthreadall
|
||||
btthreadlist &all_list allelem
|
||||
end
|
||||
document btthreadall
|
||||
Print backtraces of all threads
|
||||
end
|
||||
|
||||
# print a correct backtrace by adjusting $eip
|
||||
# this works best right at intr0e_stub
|
||||
define btpagefault
|
||||
set $saveeip = $eip
|
||||
set $eip = ((void**)$esp)[1]
|
||||
backtrace
|
||||
set $eip = $saveeip
|
||||
end
|
||||
document btpagefault
|
||||
Print a backtrace of the current thread after a pagefault
|
||||
end
|
||||
|
||||
# invoked whenever the program stops
|
||||
define hook-stop
|
||||
# stopped at stub #0E = #14 (page fault exception handler stub)
|
||||
if ($eip == intr0e_stub)
|
||||
set $savedeip = ((void**)$esp)[1]
|
||||
# if this was in user mode, the OS should handle it
|
||||
# either handle the page fault or terminate the process
|
||||
if ($savedeip < 0xC0000000)
|
||||
printf "pintos-debug: a page fault exception occurred in user mode\n"
|
||||
printf "pintos-debug: hit 'c' to continue, or 's' to step to intr_handler\n"
|
||||
else
|
||||
# if this was in kernel mode, a stack trace might be useful
|
||||
printf "pintos-debug: a page fault occurred in kernel mode\n"
|
||||
btpagefault
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# load symbols for a PintOS user program
|
||||
define loadusersymbols
|
||||
shell objdump -h $arg0 | awk '/.text/ { print "add-symbol-file $arg0 0x"$4 }' > .loadsymbols
|
||||
source .loadsymbols
|
||||
shell rm -f .loadsymbols
|
||||
end
|
||||
document loadusersymbols
|
||||
Load the symbols contained in a user program's executable.
|
||||
Example:
|
||||
loadusersymbols tests/userprog/exec-multiple
|
||||
end
|
||||
|
||||
define debugpintos
|
||||
target remote localhost:1234
|
||||
end
|
||||
document debugpintos
|
||||
Attach debugger to pintos process
|
||||
end
|
||||
|
||||
set architecture i386
|
||||
1713
tests/devices/src/tests/Algorithm/Diff.pm
Normal file
1713
tests/devices/src/tests/Algorithm/Diff.pm
Normal file
File diff suppressed because it is too large
Load Diff
75
tests/devices/src/tests/Make.tests
Normal file
75
tests/devices/src/tests/Make.tests
Normal file
@@ -0,0 +1,75 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS))
|
||||
|
||||
PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS))
|
||||
TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS))
|
||||
EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES))
|
||||
|
||||
OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES))
|
||||
ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES))
|
||||
RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES))
|
||||
|
||||
ifdef PROGS
|
||||
include ../../Makefile.userprog
|
||||
endif
|
||||
|
||||
TIMEOUT = 60
|
||||
|
||||
clean::
|
||||
rm -f $(OUTPUTS) $(ERRORS) $(RESULTS)
|
||||
|
||||
grade:: results
|
||||
$(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@
|
||||
|
||||
check:: results
|
||||
@cat $<
|
||||
@COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ ]//g;'`"; \
|
||||
FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ ]//g;'`"; \
|
||||
if [ $$FAILURES = 0 ]; then \
|
||||
echo "All $$COUNT tests passed!"; \
|
||||
else \
|
||||
echo "Warning: $$FAILURES of $$COUNT tests failed!"; \
|
||||
fi
|
||||
|
||||
results: $(RESULTS)
|
||||
@for d in $(TESTS) $(EXTRA_GRADES); do \
|
||||
if echo PASS | cmp -s $$d.result -; then \
|
||||
echo "pass $$d"; \
|
||||
else \
|
||||
echo "FAIL $$d"; \
|
||||
fi; \
|
||||
done > $@
|
||||
|
||||
outputs:: $(OUTPUTS)
|
||||
|
||||
$(foreach prog,$(PROGS),$(eval $(prog).output: $(prog)))
|
||||
$(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES)))
|
||||
$(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test)))
|
||||
|
||||
# Prevent an environment variable VERBOSE from surprising us.
|
||||
VERBOSE =
|
||||
|
||||
TESTCMD = pintos -v -k -T $(TIMEOUT)
|
||||
TESTCMD += $(SIMULATOR)
|
||||
TESTCMD += $(PINTOSOPTS)
|
||||
ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
|
||||
TESTCMD += $(FILESYSSOURCE)
|
||||
TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file)))
|
||||
endif
|
||||
ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
|
||||
TESTCMD += --swap-size=8
|
||||
endif
|
||||
TESTCMD += -- -q
|
||||
TESTCMD += $(KERNELFLAGS)
|
||||
ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
|
||||
TESTCMD += -f
|
||||
endif
|
||||
TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F))
|
||||
TESTCMD += < /dev/null
|
||||
TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output
|
||||
%.output: kernel.bin loader.bin
|
||||
$(TESTCMD)
|
||||
|
||||
%.result: %.ck %.output
|
||||
perl -I$(SRCDIR) $< $* $@
|
||||
53
tests/devices/src/tests/arc4.c
Normal file
53
tests/devices/src/tests/arc4.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <stdint.h>
|
||||
#include "tests/arc4.h"
|
||||
|
||||
/* Swap bytes. */
|
||||
static inline void
|
||||
swap_byte (uint8_t *a, uint8_t *b)
|
||||
{
|
||||
uint8_t t = *a;
|
||||
*a = *b;
|
||||
*b = t;
|
||||
}
|
||||
|
||||
void
|
||||
arc4_init (struct arc4 *arc4, const void *key_, size_t size)
|
||||
{
|
||||
const uint8_t *key = key_;
|
||||
size_t key_idx;
|
||||
uint8_t *s;
|
||||
int i, j;
|
||||
|
||||
s = arc4->s;
|
||||
arc4->i = arc4->j = 0;
|
||||
for (i = 0; i < 256; i++)
|
||||
s[i] = (uint8_t) i;
|
||||
for (key_idx = 0, i = j = 0; i < 256; i++)
|
||||
{
|
||||
j = (j + s[i] + key[key_idx]) & 255;
|
||||
swap_byte (s + i, s + j);
|
||||
if (++key_idx >= size)
|
||||
key_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
arc4_crypt (struct arc4 *arc4, void *buf_, size_t size)
|
||||
{
|
||||
uint8_t *buf = buf_;
|
||||
uint8_t *s;
|
||||
uint8_t i, j;
|
||||
|
||||
s = arc4->s;
|
||||
i = arc4->i;
|
||||
j = arc4->j;
|
||||
while (size-- > 0)
|
||||
{
|
||||
i = (uint8_t) (i + 1);
|
||||
j = (uint8_t) (j + s[i]);
|
||||
swap_byte (s + i, s + j);
|
||||
*buf++ ^= s[(s[i] + s[j]) & 255];
|
||||
}
|
||||
arc4->i = i;
|
||||
arc4->j = j;
|
||||
}
|
||||
17
tests/devices/src/tests/arc4.h
Normal file
17
tests/devices/src/tests/arc4.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef TESTS_ARC4_H
|
||||
#define TESTS_ARC4_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Alleged RC4 algorithm encryption state. */
|
||||
struct arc4
|
||||
{
|
||||
uint8_t s[256];
|
||||
uint8_t i, j;
|
||||
};
|
||||
|
||||
void arc4_init (struct arc4 *, const void *, size_t);
|
||||
void arc4_crypt (struct arc4 *, void *, size_t);
|
||||
|
||||
#endif /* tests/arc4.h */
|
||||
29
tests/devices/src/tests/arc4.pm
Normal file
29
tests/devices/src/tests/arc4.pm
Normal file
@@ -0,0 +1,29 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub arc4_init {
|
||||
my ($key) = @_;
|
||||
my (@s) = 0...255;
|
||||
my ($j) = 0;
|
||||
for my $i (0...255) {
|
||||
$j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff;
|
||||
@s[$i, $j] = @s[$j, $i];
|
||||
}
|
||||
return (0, 0, @s);
|
||||
}
|
||||
|
||||
sub arc4_crypt {
|
||||
my ($arc4, $buf) = @_;
|
||||
my ($i, $j, @s) = @$arc4;
|
||||
my ($out) = "";
|
||||
for my $c (split (//, $buf)) {
|
||||
$i = ($i + 1) & 0xff;
|
||||
$j = ($j + $s[$i]) & 0xff;
|
||||
@s[$i, $j] = @s[$j, $i];
|
||||
$out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]);
|
||||
}
|
||||
@$arc4 = ($i, $j, @s);
|
||||
return $out;
|
||||
}
|
||||
|
||||
1;
|
||||
92
tests/devices/src/tests/cksum.c
Normal file
92
tests/devices/src/tests/cksum.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/* crctab[] and cksum() are from the `cksum' entry in SUSv3. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tests/cksum.h"
|
||||
|
||||
static unsigned long crctab[] = {
|
||||
0x00000000,
|
||||
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
|
||||
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
|
||||
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
|
||||
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
|
||||
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
|
||||
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
|
||||
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
|
||||
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
|
||||
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
|
||||
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||||
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
|
||||
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
|
||||
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
|
||||
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
|
||||
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
|
||||
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
|
||||
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||||
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
|
||||
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
|
||||
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
|
||||
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
|
||||
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
|
||||
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
|
||||
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
|
||||
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
|
||||
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
|
||||
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
|
||||
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||||
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
|
||||
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
|
||||
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
|
||||
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
|
||||
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
|
||||
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
|
||||
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||||
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
|
||||
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
|
||||
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||
};
|
||||
|
||||
/* This is the algorithm used by the Posix `cksum' utility. */
|
||||
unsigned long
|
||||
cksum (const void *b_, size_t n)
|
||||
{
|
||||
const unsigned char *b = b_;
|
||||
uint32_t s = 0;
|
||||
size_t i;
|
||||
for (i = n; i > 0; --i)
|
||||
{
|
||||
unsigned char c = *b++;
|
||||
s = (s << 8) ^ crctab[(s >> 24) ^ c];
|
||||
}
|
||||
while (n != 0)
|
||||
{
|
||||
unsigned char c = (unsigned char) n;
|
||||
n >>= 8;
|
||||
s = (s << 8) ^ crctab[(s >> 24) ^ c];
|
||||
}
|
||||
return ~s;
|
||||
}
|
||||
|
||||
#ifdef STANDALONE_TEST
|
||||
#include <stdio.h>
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
char buf[65536];
|
||||
int n = fread (buf, 1, sizeof buf, stdin);
|
||||
printf ("%lu\n", cksum (buf, n));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
8
tests/devices/src/tests/cksum.h
Normal file
8
tests/devices/src/tests/cksum.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef TESTS_CKSUM_H
|
||||
#define TESTS_CKSUM_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
unsigned long cksum(const void *, size_t);
|
||||
|
||||
#endif /* tests/cksum.h */
|
||||
87
tests/devices/src/tests/cksum.pm
Normal file
87
tests/devices/src/tests/cksum.pm
Normal file
@@ -0,0 +1,87 @@
|
||||
# From the `cksum' entry in SUSv3.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my (@crctab) =
|
||||
(0x00000000,
|
||||
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
|
||||
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
||||
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
|
||||
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
|
||||
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
|
||||
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
|
||||
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
|
||||
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
|
||||
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
|
||||
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
|
||||
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
|
||||
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
|
||||
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
|
||||
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
|
||||
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
|
||||
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
|
||||
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
|
||||
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
|
||||
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
|
||||
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
|
||||
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
|
||||
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
|
||||
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
|
||||
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
|
||||
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
|
||||
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
|
||||
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
|
||||
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
|
||||
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
|
||||
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
|
||||
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
|
||||
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
|
||||
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
|
||||
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
|
||||
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
|
||||
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
|
||||
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
|
||||
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
|
||||
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
|
||||
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
|
||||
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
|
||||
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4);
|
||||
|
||||
sub cksum {
|
||||
my ($b) = @_;
|
||||
my ($n) = length ($b);
|
||||
my ($s) = 0;
|
||||
for my $i (0...$n - 1) {
|
||||
my ($c) = ord (substr ($b, $i, 1));
|
||||
$s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
|
||||
$s &= 0xffff_ffff;
|
||||
}
|
||||
while ($n != 0) {
|
||||
my ($c) = $n & 0xff;
|
||||
$n >>= 8;
|
||||
$s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
|
||||
$s &= 0xffff_ffff;
|
||||
}
|
||||
return ~$s & 0xffff_ffff;
|
||||
}
|
||||
|
||||
sub cksum_file {
|
||||
my ($file) = @_;
|
||||
open (FILE, '<', $file) or die "$file: open: $!\n";
|
||||
my ($data);
|
||||
sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n";
|
||||
close (FILE);
|
||||
return cksum ($data);
|
||||
}
|
||||
|
||||
1;
|
||||
4
tests/devices/src/tests/devices/Grading
Normal file
4
tests/devices/src/tests/devices/Grading
Normal file
@@ -0,0 +1,4 @@
|
||||
# Percentage of the testing point total designated for each set of tests.
|
||||
|
||||
50.0% tests/devices/Rubric.alarmfunc
|
||||
50.0% tests/devices/Rubric.alarmrobust
|
||||
48
tests/devices/src/tests/devices/Make.tests
Normal file
48
tests/devices/src/tests/devices/Make.tests
Normal file
@@ -0,0 +1,48 @@
|
||||
# -*- makefile -*-
|
||||
|
||||
# Test names.
|
||||
tests/devices_TESTS = $(addprefix tests/devices/,alarm-single \
|
||||
alarm-multiple alarm-simultaneous alarm-no-busy-wait alarm-one \
|
||||
alarm-zero alarm-negative)
|
||||
|
||||
# Sources for tests.
|
||||
tests/devices_SRC = tests/devices/tests.c
|
||||
tests/devices_SRC += tests/devices/alarm-wait.c
|
||||
tests/devices_SRC += tests/devices/alarm-simultaneous.c
|
||||
tests/devices_SRC += tests/devices/alarm-no-busy-wait.c
|
||||
tests/devices_SRC += tests/devices/alarm-one.c
|
||||
tests/devices_SRC += tests/devices/alarm-zero.c
|
||||
tests/devices_SRC += tests/devices/alarm-negative.c
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
4
tests/devices/src/tests/devices/Rubric.alarmfunc
Normal file
4
tests/devices/src/tests/devices/Rubric.alarmfunc
Normal file
@@ -0,0 +1,4 @@
|
||||
Functionality of alarm clock:
|
||||
10 alarm-no-busy-wait
|
||||
5 alarm-single
|
||||
5 alarm-multiple
|
||||
5
tests/devices/src/tests/devices/Rubric.alarmrobust
Normal file
5
tests/devices/src/tests/devices/Rubric.alarmrobust
Normal file
@@ -0,0 +1,5 @@
|
||||
Robustness of alarm clock:
|
||||
10 alarm-simultaneous
|
||||
5 alarm-one
|
||||
5 alarm-zero
|
||||
5 alarm-negative
|
||||
4
tests/devices/src/tests/devices/alarm-multiple.ck
Normal file
4
tests/devices/src/tests/devices/alarm-multiple.ck
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- perl -*-
|
||||
use tests::tests;
|
||||
use tests::devices::alarm;
|
||||
check_alarm (7);
|
||||
15
tests/devices/src/tests/devices/alarm-negative.c
Normal file
15
tests/devices/src/tests/devices/alarm-negative.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Tests timer_sleep(-100). Only requirement is that it not crash. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tests/devices/tests.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
void
|
||||
test_alarm_negative (void)
|
||||
{
|
||||
timer_sleep (-100);
|
||||
pass ();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user